An image cropper / photo cropper for iOS like in the Contacts app with support for landscape orientation.

Overview

RSKImageCropper Build Status Coverage Status Carthage compatible

Sample

An image cropper for iOS like in the Contacts app with support for landscape orientation.

Installation

RSKImageCropper requires iOS 9.0 or later.

Using Swift Package Manager

  1. 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

  1. Add the pod RSKImageCropper to your Podfile.

    pod 'RSKImageCropper'
    
  2. Run pod install from Terminal, then open your app's .xcworkspace file to launch Xcode.

  3. Import the RSKImageCropper.h header. Typically, this should be written as #import <RSKImageCropper/RSKImageCropper.h>

Using Carthage

  1. Add the ruslanskorb/RSKImageCropper project to your Cartfile.

    github "ruslanskorb/RSKImageCropper"
    
  2. Run carthage update, then follow the additional steps required to add the iOS and/or Mac frameworks into your project.

  3. Import the RSKImageCropper framework/module.

    • Using Modules: @import RSKImageCropper
    • Without Modules: #import <RSKImageCropper/RSKImageCropper.h>

Basic Usage

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 *imageCropVC = [[RSKImageCropViewController alloc] initWithImage:image];
    imageCropVC.delegate = self;
    [self.navigationController pushViewController:imageCropVC animated:YES];
}

Delegate

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];
}

DataSource

RSKImageCropViewControllerDataSource provides three data source methods. The method imageCropViewControllerCustomMaskRect: asks the data source a custom rect for the mask. The method imageCropViewControllerCustomMaskPath: asks the data source a custom path for the mask. The method imageCropViewControllerCustomMovementRect: asks the data source 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;
    }
}

Coming Soon

  • If you would like to request a new feature, feel free to raise as an issue.

Demo

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.

Contact

Ruslan Skorb

License

This project is is available under the MIT license. See the LICENSE file for more info. Attribution by linking to the project page is appreciated.

Comments
  • Using .Circle but getting non-circular result in didCropImage

    Using .Circle but getting non-circular result in didCropImage

    Here's how I'm creating my crop controller:

    let imageCropViewController = RSKImageCropViewController(image: image)
    imageCropViewController.delegate = self
    imageCropViewController.cropMode = .Circle
    imageCropViewController.avoidEmptySpaceAroundImage = true
    

    It doesn't happen often, but occasionally I'll see a crash where the croppedImage passed into didCropImage has width != height. I added some additional logging to try and figure out what's happening: the latest occurence, the original image had the same dimensions as the croppedImage (w=823.0, h=960.0).

    Is there any reason this would happen? It doesn't seem to be related to phone/os version.

    needs more information 
    opened by edenman 29
  • 'Use of undeclared identifier '__tg_promote1'' error in Objective-C++ project

    'Use of undeclared identifier '__tg_promote1'' error in Objective-C++ project

    I have to use the project which is created by cocos creator , and the project include a module register so that i can't use < tgmath.h >. The error of Xcode is" Use of undeclared identifier '__tg_promote1' "and"Expanded from macro 'floor' ". I have no idea about how to work around and redeclaring the things ,please help me. Your help is greatly appreciated . :)

    bug 
    opened by uestchang 22
  • cropImage layout was wrong when use RSKImageCropModeCustom

    cropImage layout was wrong when use RSKImageCropModeCustom

    hello, cropImage wrong when use RSKImageCropModeCustom Reproduction steps:

    1. Set the example to cropMode:RSKImageCropModeCustom;
    2. Use CGSize aspectRatio = CGSizeMake (16.0f, 9.0f);
    3. Rotate 90° and move the picture downward. Discovery: The picture flashed and the layout was wrong.
    bug 
    opened by yangqingren 17
  • NSLocalizedString table and bundle name

    NSLocalizedString table and bundle name

    It would be nice to be able to provide table and bundle name, which RSKImageCropper would use in all NSLocalizedString calls. Currently with Pods localization is not possible.

    enhancement 
    opened by tapz 16
  • Use CustomMode clip. Get wrong image.

    Use CustomMode clip. Get wrong image.

    c6960693-4649-499f-afad-45eedbd24a24

    I want to clip a human face.But i got this wrong shape.Last version I use get the correct image.But this time,I update the pod and update to newest version of RSKImageCropper.It's work something wrong.

    826c5fbf-f61a-4711-a24a-96fe0f893993

    Can u help me to see what is the question?Here is the code.Thanks.

    - (CGRect)imageCropViewControllerCustomMaskRect:(RSKImageCropViewController *)controller
    {
        CGSize maskSize;
        if ([controller isPortraitInterfaceOrientation]) {
            maskSize = CGSizeMake(250, 250);
    //        maskSize = CGSizeMake(180, 220);
        } else {
            maskSize = CGSizeMake(220, 220);
        }
        
        CGFloat viewWidth = CGRectGetWidth(controller.view.frame);
        CGFloat viewHeight = CGRectGetHeight(controller.view.frame);
        
        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
    {
        return [self cropPathWithRect:controller.maskRect];
    }
    
    // Returns a custom rect in which the image can be moved.
    - (CGRect)imageCropViewControllerCustomMovementRect:(RSKImageCropViewController *)controller
    {
        return [self cropPathWithRect:controller.maskRect].bounds;
    }
    
    - (UIBezierPath *)cropPathWithRect:(CGRect)rect
    {
        UIBezierPath *path = [UIBezierPath bezierPath];
    
        CGPoint top_point = (CGPoint){CGRectGetMidX(rect), CGRectGetMinY(rect) + 30};
        CGPoint left_ctrl_point = (CGPoint){CGRectGetMinX(rect), CGRectGetMinY(rect) + 20};
        CGPoint right_ctrl_point = (CGPoint){CGRectGetMaxX(rect), CGRectGetMinY(rect) + 20};
        CGPoint left_bottom_point = (CGPoint){CGRectGetMinX(rect) + 45, CGRectGetMaxY(rect)*3/4 + 20};
        CGPoint right_bottom_point = (CGPoint){CGRectGetMaxX(rect) - 45, CGRectGetMaxY(rect)*3/4 + 20};
        CGPoint bottom_ctrl_point = (CGPoint){CGRectGetMidX(rect), CGRectGetMaxY(rect) + 50};
        
    
        [path moveToPoint:top_point];
        [path addQuadCurveToPoint:right_bottom_point controlPoint:right_ctrl_point];
        [path addQuadCurveToPoint:left_bottom_point controlPoint:bottom_ctrl_point];
        [path addQuadCurveToPoint:top_point controlPoint:left_ctrl_point];
        [path closePath];
    
        return path;
    }
    
    bug 
    opened by karthuszY 15
  • Size of the cropping rect is too small

    Size of the cropping rect is too small

    When I zoom in to maximum and crop the image, size of the cropped image is lesser than it should according to the values provided in datasource methods. The problem is caused by taking

    CGFloat maxScale = MAX(xScale, yScale); when calculating max possible scale. If we replace this with CGFloat maxScale = MIN(xScale, yScale);, then cropped rect when image is zoomed in to maximum will be exactly as value provided by datasource methods.

    wontfix 
    opened by stp-bopa 10
  • Redefinition of module 'RSKImageCropper'

    Redefinition of module 'RSKImageCropper'

    RSKImageCropper/RSKImageCropper/module.modulemap:1:18: Redefinition of module 'RSKImageCropper'
    

    image

    I have updated the latest version of the RSKImageCropper, which is 1.6.1, but the problem remains.

    Using QBImagePickerController (3.4.0)
    Using RSKImageCropper (1.6.1)
    
    opened by MonFig 9
  • How to a add rectangular crop for image

    How to a add rectangular crop for image

    I would like to add rectangular crop for some images and circular for some images, So I implemented data source methods.

    "imageCropViewControllerCustomMovementRect" in this method I'm passing mask size. maskSize = CGSize(width: 270, height: 180) but in "imageCropViewControllerCustomMaskPath" method controller's maskrect is coming as zero's

    Could u please help me How to achieve rectangular crop ?

    question 
    opened by perlasivakrishna 9
  • Thin white border added to cropped image

    Thin white border added to cropped image

    I've noticed the cropper is adding a thin white border around the edges of masked photos, here's my code:

    // MARK: Cropper Data Source
    
    func imageCropViewControllerCustomMaskRect(controller: RSKImageCropViewController) -> CGRect {
        let maskSize = CGSizeMake(225, 400)
        let viewWidth: CGFloat = CGRectGetWidth(controller.view.frame)
        let viewHeight: CGFloat = CGRectGetHeight(controller.view.frame)
        let maskRect: CGRect = CGRectMake((viewWidth - maskSize.width) * 0.5, (viewHeight - maskSize.height) * 0.5, maskSize.width, maskSize.height)
        return maskRect
    }
    
    func imageCropViewControllerCustomMaskPath(controller: RSKImageCropViewController) -> UIBezierPath {
        return UIBezierPath(rect: controller.maskRect)
    }
    
    func imageCropViewControllerCustomMovementRect(controller: RSKImageCropViewController) -> CGRect {
        return controller.maskRect
    }
    

    And an example result

    What am I doing wrong?

    needs more information 
    opened by brod-ie 9
  • Do you submit your code for pod?

    Do you submit your code for pod?

    when i use pod 'RSKImageCropper', and run pod install, i found that the code missing the custom UI feature?

    /** The mode for cropping. */ @property (assign, nonatomic) RSKImageCropMode cropMode;

    /// ------------------------------------------- /// @name Checking of the Interface Orientation /// -------------------------------------------

    /** Returns a Boolean value indicating whether the user interface is currently presented in a portrait orientation.

    @return YES if the interface orientation is portrait, otherwise returns NO. */

    • (BOOL)isPortraitInterfaceOrientation;

    @end

    opened by Jerry-Wang-Developer 8
  • Question: Can I crop rectangular image to smaller square area

    Question: Can I crop rectangular image to smaller square area

    Hi, this is a duplicate question as https://github.com/ivpusic/react-native-image-crop-picker/issues/890 As react-native-image-crop-picker uses RSKImageCropper Maybe @ruslanskorb can help me find the answer

    question 
    opened by zinoviev 7
Releases(4.0.0)
Owner
Ruslan Skorb
"The best way to predict the future is to create it" – Abraham Lincoln. Let's create our future together! Founder & Senior iOS Software Engineer @ R.SK Lab
Ruslan Skorb
Jogendra 113 Nov 28, 2022
DTPhotoViewerController - A fully customizable photo viewer ViewController to display single photo or collection of photos, inspired by Facebook photo viewer.

DTPhotoViewerController Example Demo video: https://youtu.be/aTLx4M4zsKk DTPhotoViewerController is very simple to use, if you want to display only on

Tung Vo 277 Dec 17, 2022
Photo Browser / Viewer inspired by Facebook's and Tweetbot's with ARC support, swipe-to-dismiss, image progress and more

IDMPhotoBrowser IDMPhotoBrowser is a new implementation based on MWPhotoBrowser. We've added both user experience and technical features inspired by F

Thiago Peres 2.7k Dec 21, 2022
Photo-Sharing-App - Photo Sharing App With Swift

Photo Sharing App You can register and log in to this application and share your

Yağız Savran 2 Jun 14, 2022
DGCropImage - A photo cropping tool which mimics Photo.app written by Swift

DGCropImage A photo cropping tool which mimics Photo.app written by Swift. This

donggyu 11 Jul 14, 2022
A photo gallery for iOS with a modern feature set. Similar features as the Facebook photo browser.

EBPhotoPages ”A photo gallery can become a pretty complex component of an app very quickly. The EBPhotoPages project demonstrates how a developer coul

Eddy Borja 1.7k Dec 8, 2022
React-native-photo-editor - Photo editor using native modules for iOS and Android

?? Image editor using native modules for iOS and Android. Inherit from 2 available libraries, ZLImageEditor (iOS) and PhotoEditor (Android)

Baron Ha. 244 Jan 5, 2023
An instagram-like image editor that can apply preset filters passed to it and customized editings to a binded image.

CZImageEditor CZImageEditor is an instagram-like image editor with clean and intuitive UI. It is pure swift and can apply preset filters and customize

null 8 Dec 16, 2022
FMPhotoPicker is a modern, simple and zero-dependency photo picker with an elegant and customizable image editor

FMPhotoPicker is a modern, simple and zero-dependency photo picker with an elegant and customizable image editor Quick demo Batch select/deselect Smoo

Cong Nguyen 648 Dec 27, 2022
Get a photo which has effects like the system camera’s Portrait mode (on compatible devices).

Blurring background like Portrait mode in the iPhone camera If we want to have the blurring effect like the Portrait mode in the iOS Camera app. What

Kien (Bradley) Hoang 4 Sep 20, 2022
Agrume - 🍋 An iOS image viewer written in Swift with support for multiple images.

Agrume An iOS image viewer written in Swift with support for multiple images. Requirements Swift 5.0 iOS 9.0+ Xcode 10.2+ Installation Use Swift Packa

Jan Gorman 601 Dec 26, 2022
A complete Mac App: drag an image file to the top section and the bottom section will show you the text of any QRCodes in the image.

QRDecode A complete Mac App: drag an image file to the top section and the bottom section will show you the text of any QRCodes in the image. QRDecode

David Phillip Oster 2 Oct 28, 2022
add text(multiple line support) to imageView, edit, rotate or resize them as you want, then render the text on image

StickerTextView is an subclass of UIImageView. You can add multiple text to it, edit, rotate, resize the text as you want with one finger, then render the text on Image.

Textcat 478 Dec 17, 2022
Image viewer (or Lightbox) with support for local and remote videos and images

Table of Contents Features Focus Browse Rotation Zoom tvOS Setup Installation License Author Features Focus Select an image to enter into lightbox mod

Nes 534 Jan 3, 2023
Asynchronous image downloader with cache support as a UIImageView category

This library provides an async image downloader with cache support. For convenience, we added categories for UI elements like UIImageView, UIButton, M

null 24.4k Jan 5, 2023
A tool support convert image gif to gif3d

Trick convert gif to gif3d using Swift Features Simple way to convert gif to gif3d Change background Change size eraser tool How to work Add a layer w

Chuong Tran 16 Jan 4, 2023
An image download extension of the image view written in Swift for iOS, tvOS and macOS.

Moa, an image downloader written in Swift for iOS, tvOS and macOS Moa is an image download library written in Swift. It allows to download and show an

Evgenii Neumerzhitckii 330 Sep 9, 2022
AsyncImage before iOS 15. Lightweight, pure SwiftUI Image view, that displays an image downloaded from URL, with auxiliary views and local cache.

URLImage URLImage is a SwiftUI view that displays an image downloaded from provided URL. URLImage manages downloading remote image and caching it loca

Dmytro Anokhin 1k Jan 4, 2023
Twitter Image Pipeline is a robust and performant image loading and caching framework for iOS clients

Twitter Image Pipeline (a.k.a. TIP) Background The Twitter Image Pipeline is a streamlined framework for fetching and storing images in an application

Twitter 1.8k Dec 17, 2022