An image cropper for iOS like in the Contacts app with support for landscape orientation.
RSKImageCropper requires iOS 12.0 or later.
Using Swift Package Manager
-
To add the
RSKImageCropper
package to your Xcode project, select File > Swift Packages > Add Package Dependency and enter the repository URL.https://github.com/ruslanskorb/RSKImageCropper.git
Using CocoaPods
-
Add the pod
RSKImageCropper
to your Podfile.pod 'RSKImageCropper'
-
Run
pod install
from Terminal, then open your app's.xcworkspace
file to launch Xcode. -
Import the
RSKImageCropper.h
header. Typically, this should be written as#import <RSKImageCropper/RSKImageCropper.h>
Using Carthage
-
Add the
ruslanskorb/RSKImageCropper
project to your Cartfile.github "ruslanskorb/RSKImageCropper"
-
Run
carthage update
, then follow the additional steps required to add the iOS and/or Mac frameworks into your project. -
Import the RSKImageCropper framework/module.
- Using Modules:
@import RSKImageCropper
- Without Modules:
#import <RSKImageCropper/RSKImageCropper.h>
- Using Modules:
Import the class header.
#import <RSKImageCropper/RSKImageCropper.h>
Just create a view controller for image cropping and set the delegate.
- (IBAction)onButtonTouch:(UIButton *)sender
{
UIImage *image = [UIImage imageNamed:@"image"];
RSKImageCropViewController *imageCropViewController = [[RSKImageCropViewController alloc] initWithImage:image];
imageCropViewController.delegate = self;
[self.navigationController pushViewController:imageCropViewController animated:YES];
}
RSKImageCropViewControllerDelegate
provides three delegate methods. To use them, implement the delegate in your view controller.
@interface ViewController () <RSKImageCropViewControllerDelegate>
Then implement the delegate functions.
// Crop image has been canceled.
- (void)imageCropViewControllerDidCancelCrop:(RSKImageCropViewController *)controller
{
[self.navigationController popViewControllerAnimated:YES];
}
// The original image has been cropped. Additionally provides a rotation angle used to produce image.
- (void)imageCropViewController:(RSKImageCropViewController *)controller
didCropImage:(UIImage *)croppedImage
usingCropRect:(CGRect)cropRect
rotationAngle:(CGFloat)rotationAngle
{
self.imageView.image = croppedImage;
[self.navigationController popViewControllerAnimated:YES];
}
// The original image will be cropped.
- (void)imageCropViewController:(RSKImageCropViewController *)controller
willCropImage:(UIImage *)originalImage
{
// Use when `applyMaskToCroppedImage` set to YES.
[SVProgressHUD show];
}
RSKImageCropViewControllerDataSource
provides three data source methods. The method imageCropViewControllerCustomMaskRect:
asks the data source for a custom rect for the mask. The method imageCropViewControllerCustomMaskPath:
asks the data source for a custom path for the mask. The method imageCropViewControllerCustomMovementRect:
asks the data source for a custom rect in which the image can be moved. To use them, implement the data source in your view controller.
@interface ViewController () <RSKImageCropViewControllerDataSource>
Then implement the data source functions.
// Returns a custom rect for the mask.
- (CGRect)imageCropViewControllerCustomMaskRect:(RSKImageCropViewController *)controller
{
CGSize aspectRatio = CGSizeMake(16.0f, 9.0f);
CGFloat viewWidth = CGRectGetWidth(controller.view.frame);
CGFloat viewHeight = CGRectGetHeight(controller.view.frame);
CGFloat maskWidth;
if ([controller isPortraitInterfaceOrientation]) {
maskWidth = viewWidth;
} else {
maskWidth = viewHeight;
}
CGFloat maskHeight;
do {
maskHeight = maskWidth * aspectRatio.height / aspectRatio.width;
maskWidth -= 1.0f;
} while (maskHeight != floor(maskHeight));
maskWidth += 1.0f;
CGSize maskSize = CGSizeMake(maskWidth, maskHeight);
CGRect maskRect = CGRectMake((viewWidth - maskSize.width) * 0.5f,
(viewHeight - maskSize.height) * 0.5f,
maskSize.width,
maskSize.height);
return maskRect;
}
// Returns a custom path for the mask.
- (UIBezierPath *)imageCropViewControllerCustomMaskPath:(RSKImageCropViewController *)controller
{
CGRect rect = controller.maskRect;
CGPoint point1 = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
CGPoint point2 = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
CGPoint point3 = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGPoint point4 = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
UIBezierPath *rectangle = [UIBezierPath bezierPath];
[rectangle moveToPoint:point1];
[rectangle addLineToPoint:point2];
[rectangle addLineToPoint:point3];
[rectangle addLineToPoint:point4];
[rectangle closePath];
return rectangle;
}
// Returns a custom rect in which the image can be moved.
- (CGRect)imageCropViewControllerCustomMovementRect:(RSKImageCropViewController *)controller
{
if (controller.rotationAngle == 0) {
return controller.maskRect;
} else {
CGRect maskRect = controller.maskRect;
CGFloat rotationAngle = controller.rotationAngle;
CGRect movementRect = CGRectZero;
movementRect.size.width = CGRectGetWidth(maskRect) * fabs(cos(rotationAngle)) + CGRectGetHeight(maskRect) * fabs(sin(rotationAngle));
movementRect.size.height = CGRectGetHeight(maskRect) * fabs(cos(rotationAngle)) + CGRectGetWidth(maskRect) * fabs(sin(rotationAngle));
movementRect.origin.x = CGRectGetMinX(maskRect) + (CGRectGetWidth(maskRect) - CGRectGetWidth(movementRect)) * 0.5f;
movementRect.origin.y = CGRectGetMinY(maskRect) + (CGRectGetHeight(maskRect) - CGRectGetHeight(movementRect)) * 0.5f;
movementRect.origin.x = floor(CGRectGetMinX(movementRect));
movementRect.origin.y = floor(CGRectGetMinY(movementRect));
movementRect = CGRectIntegral(movementRect);
return movementRect;
}
}
- If you would like to request a new feature, feel free to raise an issue.
Build and run the RSKImageCropperExample
project in Xcode to see RSKImageCropper
in action.
Have fun. Fork and send pull requests. Figure out hooks for customization.
RSKImageCropper
doesn't require a privacy manifest. According to information received from Apple, we should avoid adding an empty privacy manifest to our frameworks.
Ruslan Skorb
This project is available under the MIT license. See the LICENSE file for more info. Attribution by linking to the project page is appreciated.