220

I've got some code that resizes an image so I can get a scaled chunk of the center of the image - I use this to take a UIImage and return a small, square representation of an image, similar to what's seen in the album view of the Photos app. (I know I could use a UIImageView and adjust the crop mode to achieve the same results, but these images are sometimes displayed in UIWebViews).

I've started to notice some crashes in this code and I'm a bit stumped. I've got two different theories and I'm wondering if either is on-base.

Theory 1) I achieve the cropping by drawing into an offscreen image context of my target size. Since I want the center portion of the image, I set the CGRect argument passed to drawInRect to something that's larger than the bounds of my image context. I was hoping this was Kosher, but am I instead attempting to draw over other memory that I shouldn't be touching?

Theory 2) I'm doing all of this in a background thread. I know there are portions of UIKit that are restricted to the main thread. I was assuming / hoping that drawing to an offscreen view wasn't one of these. Am I wrong?

(Oh, how I miss NSImage's drawInRect:fromRect:operation:fraction: method.)

Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
Jablair
  • 5,036
  • 4
  • 28
  • 37
  • If you're trying to diagnose a crash, you should be running the app under the debugger and making note of what happens when it crashes. You haven't even identified if there is an exception being thrown or you're getting EXC_BAD_ACCESS due to a dangling pointer. Once you know that, then you can start making theories. – benzado Oct 01 '08 at 19:29
  • Fair enough. I haven't repro'd this under the debugger, though I do have EXC_BAD_ACCESS messages in the crash log. When I posted this, I was working under the assumption that I'd made a stupid mistake in my implementation and somebody would jump on it (like forgetting a clipping path). – Jablair Oct 01 '08 at 21:25
  • For what it's worth and good answers below notwithstanding, there is a good survey of techniques and performance thereof on NSHipster: http://nshipster.com/image-resizing/. The purist in me wanted to use CIImage, but the pragmatist chose UIKit / image context. – Chris Conover May 26 '15 at 20:52

26 Answers26

238

Update 2014-05-28: I wrote this when iOS 3 or so was the hot new thing, I'm certain there are better ways to do this by now, possibly built-in. As many people have mentioned, this method doesn't take rotation into account; read some additional answers and spread some upvote love around to keep the responses to this question helpful for everyone.

Original response:

I'm going to copy/paste my response to the same question elsewhere:

There isn't a simple class method to do this, but there is a function that you can use to get the desired results: CGImageCreateWithImageInRect(CGImageRef, CGRect) will help you out.

Here's a short example using it:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
// or use the UIImage wherever you like
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);
HitScan
  • 8,651
  • 3
  • 22
  • 17
  • 13
    When the `imageOrientation` is set to anything but up, the `CGImage` is rotated with respect to the `UIImage` and the cropping rectangle will be wrong. You can rotate the cropping rectangle accordingly, but the freshly created `UIImage` will have `imageOrientation` up, so that the cropped `CGImage` will be rotated inside it. – zoul Apr 23 '10 at 09:11
  • 25
    I want to point out that on the retina display you need to double your cropRect width & height using this method. Just something I ran into. – petrocket Jun 14 '11 at 13:09
  • 21
    to make it works in any orientation use: ``[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];`` – Nicos Karalis Sep 14 '12 at 14:42
  • 3
    You should add support for Retina!! – Mário Carvalho Jul 22 '13 at 20:36
  • 1
    @NicosKaralis that will only rotate it into the right orientation. However the area, that you're cropping out of the image will still be wrong. – Tim Bodeit Sep 12 '13 at 16:13
  • I want to crop image in circular shape. So that we can see only circular path and other path remains transparent. – Alfa Dec 16 '13 at 07:58
  • this answer is not correct, below Sergii Rudchenko gives correct answer – andrewchan2022 May 27 '14 at 15:53
  • Note that the `CGRect` parameter to `CGImageCreateWithImageInRect` appears to be in units of pixels, not points. – Tom Hamming Dec 06 '16 at 23:02
94

To crop retina images while keeping the same scale and orientation, use the following method in a UIImage category (iOS 4.0 and above):

- (UIImage *)crop:(CGRect)rect {
    if (self.scale > 1.0f) {
        rect = CGRectMake(rect.origin.x * self.scale,
                          rect.origin.y * self.scale,
                          rect.size.width * self.scale,
                          rect.size.height * self.scale);
    }

    CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}
Arne
  • 1,537
  • 10
  • 9
  • 14
    You could just kill the conditional here and always multiple by `self.scale`, no? – Michael Mior Aug 30 '12 at 20:53
  • 2
    You're right, Michael, but I think the above solution is a tiny bit faster on non-retina devices as it only does a single check instead of four multiplications plus an assignment. On retina-devices it's just a single boolean check more than necessary, so it's a question of personal preference or target fit. – CGee Aug 29 '13 at 12:52
  • 2
    Is it right to use `CGImageRelease(imageRef);` with ARC enabled? – CGee Aug 29 '13 at 12:55
  • 3
    @Dschee Yes. See http://stackoverflow.com/questions/7800174/does-arc-work-with-core-graphics-objects – Klaas Oct 18 '13 at 22:53
  • 3
    Actually 4 multiplications might be faster than evaluating one condition :) Multiplications can be optimised pretty well on modern CPUs whereas conditions can only be predicted. But you are right, it is a matter of preference. I prefer the shorter / simpler code because of the increased readability. – marsbear Dec 10 '15 at 17:20
  • @benhi This is an extension of UIImage so self.scale already exists. But in another class you could get it like this: self.scale = [[UIScreen mainScreen] scale]; – Julius Mar 17 '17 at 22:12
66

You can make a UIImage category and use it wherever you need. Based on HitScans response and comments bellow it.

@implementation UIImage (Crop)

- (UIImage *)crop:(CGRect)rect {

    rect = CGRectMake(rect.origin.x*self.scale, 
                      rect.origin.y*self.scale, 
                      rect.size.width*self.scale, 
                      rect.size.height*self.scale);       

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef 
                                          scale:self.scale 
                                    orientation:self.imageOrientation]; 
    CGImageRelease(imageRef);
    return result;
}

@end

You can use it this way:

UIImage *imageToCrop = <yourImageToCrop>;
CGRect cropRect = <areaYouWantToCrop>;   

//for example
//CGRectMake(0, 40, 320, 100);

UIImage *croppedImage = [imageToCrop crop:cropRect];
Sam B
  • 27,273
  • 15
  • 84
  • 121
Vilém Kurz
  • 3,401
  • 2
  • 34
  • 43
  • 3
    Shouldn't `[[UIScreen mainScreen] scale]` just be `self.scale`? The scale of the image may not be the same as the scale of the screen. – Michael Mior Sep 05 '12 at 18:11
  • Hi, I tried your answer and it gives me `No visible @interface for 'UIImage' declares the selector 'crop'` even though I put the .h and .m category files in my project and imported the .h in the class I'm using the category. Any idea? – Ali Oct 06 '13 at 16:06
  • Fixed it. I missed putting the method header in the UIImage+Crop.h file. – Ali Oct 06 '13 at 16:28
  • I want to crop image in circular shape. So that we can see only circular path and other path remains transparent. – Alfa Dec 16 '13 at 07:58
  • Than you can use CGImageCreateWithMask or CGImageCreateWithMaskingColors instead of CGImageCreateWithImageInRect – Vilém Kurz Dec 19 '13 at 08:47
  • Alright I have the whole thing implemented, but I'm having a problem. With my cropRect created like this: `CGRect cropRect = CGRectMake(0, 150, 640, 640);` Im only getting a tiny tiny corner of the actual picture. Im trying to grab a 320x320 picture from the center origin.. what am I doing wrong? – Chisx Jul 07 '15 at 00:05
  • Try CGRect cropRect = CGRectMake(0, 150, 320, 320); – Vilém Kurz Aug 26 '15 at 16:55
  • Hi. I try to face shape image crop but not perfect crop and cropview move and scale whole screen for objective c – Ramani Hitesh Oct 04 '17 at 05:09
65

Swift 3 version

func cropImage(imageToCrop:UIImage, toRect rect:CGRect) -> UIImage{
    
    let imageRef:CGImage = imageToCrop.cgImage!.cropping(to: rect)!
    let cropped:UIImage = UIImage(cgImage:imageRef)
    return cropped
}


let imageTop:UIImage  = UIImage(named:"one.jpg")! // add validation

enter image description here

with help of this bridge function CGRectMake -> CGRect (credits to this answer answered by @rob mayoff):

 func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
    return CGRect(x: x, y: y, width: width, height: height)
}

The usage is:

if var image:UIImage  = UIImage(named:"one.jpg"){
   let  croppedImage = cropImage(imageToCrop: image, toRect: CGRectMake(
        image.size.width/4,
        0,
        image.size.width/2,
        image.size.height)
    )
}

Output:

enter image description here

Community
  • 1
  • 1
Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • Not sure why it was down voted, I thought it was a good answer. The only thing I can think of is that it doesn't handle scaling so it will not work if you have @2x/@3x images and your function names don't match up. – William T. Sep 30 '15 at 06:35
  • 4
    You need to `guard` the first line in case `UIImage.CGImage` returns `nil` and why use `var` when `let` works perfectly. – rolling_codes Jun 27 '16 at 18:29
  • 2
    This is correct, HOWEVER, read the docs on `cropping(to:)` very carefully. The resulting `CGImage` holds a strong reference to the larger `CGImage` from which it was cropped. So if you want to *only* hang on to the cropped image and have no need for original, take a look at the other solutions. – jsadler Aug 22 '17 at 21:00
  • This doesn't work with images that have orientation tags. The crop region doesn't get rotated properly. – Max Jul 17 '18 at 13:41
  • 1
    @Max this is because cgImage does not have orientation support. If UIImage is having `right` then the cgImage should be rotated ccw by 90 degrees, hence the crop rect should be rotated too – MK Yung Nov 21 '18 at 03:07
49

Here is my UIImage crop implementation which obeys the imageOrientation property. All orientations were thoroughly tested.

inline double rad(double deg)
{
    return deg / 180.0 * M_PI;
}

UIImage* UIImageCrop(UIImage* img, CGRect rect)
{
    CGAffineTransform rectTransform;
    switch (img.imageOrientation)
    {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -img.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -img.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -img.size.width, -img.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, img.scale, img.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:img.scale orientation:img.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}
Sergii Rudchenko
  • 5,170
  • 2
  • 28
  • 24
  • 7
    Warning `implicit declaration of function 'rad' is invalid in c99` may be removed by replacing: rad(90), rad(-90), rad(-180) -> M_PI_2, -M_PI_2, -_M_PI – Sound Blaster Apr 17 '13 at 10:07
  • Oops, sorry. Added the rad() utility function to the source snippet. – Sergii Rudchenko Apr 17 '13 at 10:22
  • `contains undefined reference for architecture armv7` Am I missing a library? CoreGraphics is imported. – Bob Spryn May 08 '13 at 05:39
  • 1
    @SergiiRudchenko "All orientation were thoroughly tested." - Does this include the mirrored orientations? – Tim Bodeit Sep 12 '13 at 16:16
  • @BobSpryn No you are not missing a library. While I can't explain what the error means, the replacement of rad(), like SoundBlaster suggested, fixes this error as well. – Tim Bodeit Sep 12 '13 at 16:23
  • Big plus +1 to this answer. This is actually the most complete response as the other solutions don't account for the challenge of properly handling the image orientation which is a must. – Jim Jeffers Dec 26 '13 at 05:45
  • This one works for me. I tried others before this one. This is the only one that was a complete solution for me. Thx. – John Erck Jul 14 '14 at 06:18
  • Thank you! This one got it right. All the other examples it would mess up and get the orientation wrong somewhere and not crop correctly. – Doug Voss Aug 13 '16 at 19:30
44

Heads up: all these answers assume a CGImage-backed image object.

image.CGImage can return nil, if the UIImage is backed by a CIImage, which would be the case if you created this image using a CIFilter.

In that case, you might have to draw the image in a new context, and return that image (slow).

UIImage* crop(UIImage *image, rect) {
    UIGraphicsBeginImageContextWithOptions(rect.size, false, [image scale]);
    [image drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}
Jigar Tarsariya
  • 3,189
  • 3
  • 14
  • 38
colinta
  • 3,536
  • 1
  • 28
  • 18
  • 6
    This is exactly what I needed, works in all scenarios therefore better than any of the other solutions! – David Thompson Oct 20 '13 at 18:02
  • 2
    image.size in the first line should be rect.size UIGraphicsBeginImageContextWithOptions( rect.size, false, image.scale); – Brett Jan 16 '14 at 04:56
  • 2
    Thanks Brett, definitely right there. I updated the code to include this fix. – colinta Mar 12 '14 at 17:03
  • Why is it -rect.origin.x not just rect.origin.x? – Aggressor Sep 23 '14 at 12:56
  • 2
    Because we are cropping; in other words, the 'rect' arg is saying "I want the top-left of the new image to start at, say, [10, 10]". To do that we draw the image so that [10, 10] is at the new image's origin. – colinta Sep 23 '14 at 17:07
  • dang it, i can't include ascii art in a comment. here: https://gist.github.com/colinta/652120bd917b3f3cbe1e – colinta Sep 23 '14 at 17:08
  • Great solution, the others just give wrong results sometimes depending on the orientations. Thanks! – Carles Estevadeordal Dec 21 '14 at 00:17
  • This is the only one that works in Mono. Glad you posted it. – Gandalf458 Sep 01 '16 at 13:28
  • I want to mark It's the best solution. put it in Category of UIImage - (UIImage*)cropImage:(CGRect)rect{ UIGraphicsBeginImageContextWithOptions(rect.size, false, [self scale]); [self drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)]; UIImage* cropped_image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return cropped_image; } – Linh Nguyen Jun 28 '17 at 11:49
37

None of the answers here handle all of the scale and rotation issues 100% correctly. Here's a synthesis of everything said so far, up-to-date as of iOS7/8. It's meant to be included as a method in a category on UIImage.

- (UIImage *)croppedImageInRect:(CGRect)rect
{
    double (^rad)(double) = ^(double deg) {
        return deg / 180.0 * M_PI;
    };

    CGAffineTransform rectTransform;
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -self.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -self.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -self.size.width, -self.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, self.scale, self.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);

    return result;
}
awolf
  • 1,862
  • 22
  • 29
  • 1
    Why is this answer not upvoted more? I tried all the above answers and ran into tons of issues (notably converting from UIImage to CIImage and losing proper transformation data). THIS IS THE ONLY ONE THAT WORKS FOR ME! – Aggressor Sep 23 '14 at 15:43
  • 1
    Thanks @Aggressor, glad this works for you. My answer was only posted a month ago whereas many of these answers have been here for 5 years. I'm guessing that explains the lack of comparable up votes. – awolf Sep 23 '14 at 18:20
  • I am using this to swipe filters across images (like how snapchat does) and I am getting slight scaling issues. Is there anything in your code you can suggest I look at which might hold the cause of this scaling error? I posted my solution which uses your code here: http://stackoverflow.com/questions/23319497/swipe-between-filtered-images/26021647#26021647 – Aggressor Sep 24 '14 at 17:49
  • a very useful function, thank you. any tips what should be modified to crop focusing on the central part of the image? – Adrian Ilie Mar 26 '15 at 17:42
  • THIS SHOULD BE THE ANSWER. – horseshoe7 Sep 17 '17 at 11:09
  • This is THE answer. Like a charm – Jack Guo Mar 10 '18 at 15:13
  • This should be answer – Hien.Nguyen May 16 '23 at 06:16
27

Swift version of awolf's answer, which worked for me:

public extension UIImage {
    func croppedImage(inRect rect: CGRect) -> UIImage {
        let rad: (Double) -> CGFloat = { deg in
            return CGFloat(deg / 180.0 * .pi)
        }
        var rectTransform: CGAffineTransform
        switch imageOrientation {
        case .left:
            let rotation = CGAffineTransform(rotationAngle: rad(90))
            rectTransform = rotation.translatedBy(x: 0, y: -size.height)
        case .right:
            let rotation = CGAffineTransform(rotationAngle: rad(-90))
            rectTransform = rotation.translatedBy(x: -size.width, y: 0)
        case .down:
            let rotation = CGAffineTransform(rotationAngle: rad(-180))
            rectTransform = rotation.translatedBy(x: -size.width, y: -size.height)
        default:
            rectTransform = .identity
        }
        rectTransform = rectTransform.scaledBy(x: scale, y: scale)
        let transformedRect = rect.applying(rectTransform)
        let imageRef = cgImage!.cropping(to: transformedRect)!
        let result = UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
        return result
    }
}
Jan Berkel
  • 3,373
  • 1
  • 30
  • 23
Mark Leonard
  • 2,056
  • 19
  • 28
11

swift3

extension UIImage {
    func crop(rect: CGRect) -> UIImage? {
        var scaledRect = rect
        scaledRect.origin.x *= scale
        scaledRect.origin.y *= scale
        scaledRect.size.width *= scale
        scaledRect.size.height *= scale
        guard let imageRef: CGImage = cgImage?.cropping(to: scaledRect) else {
            return nil
        }
        return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
    }
}
neoneye
  • 50,398
  • 25
  • 166
  • 151
10
CGSize size = [originalImage size];
int padding = 20;
int pictureSize = 300;
int startCroppingPosition = 100;
if (size.height > size.width) {
    pictureSize = size.width - (2.0 * padding);
    startCroppingPosition = (size.height - pictureSize) / 2.0; 
} else {
    pictureSize = size.height - (2.0 * padding);
    startCroppingPosition = (size.width - pictureSize) / 2.0;
}
// WTF: Don't forget that the CGImageCreateWithImageInRect believes that 
// the image is 180 rotated, so x and y are inverted, same for height and width.
CGRect cropRect = CGRectMake(startCroppingPosition, padding, pictureSize, pictureSize);
CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], cropRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:originalImage.imageOrientation];
[m_photoView setImage:newImage];
CGImageRelease(imageRef);

Most of the responses I've seen only deals with a position of (0, 0) for (x, y). Ok that's one case but I'd like my cropping operation to be centered. What took me a while to figure out is the line following the WTF comment.

Let's take the case of an image captured with a portrait orientation:

  1. The original image height is higher than its width (Woo, no surprise so far!)
  2. The image that the CGImageCreateWithImageInRect method imagines in its own world is not really a portrait though but a landscape (That is also why if you don't use the orientation argument in the imageWithCGImage constructor, it will show up as 180 rotated).
  3. So, you should kind of imagine that it is a landscape, the (0, 0) position being the top right corner of the image.

Hope it makes sense! If it does not, try different values you'll see that the logic is inverted when it comes to choosing the right x, y, width, and height for your cropRect.

Jordan
  • 551
  • 3
  • 11
7

Swift Extension

extension UIImage {
    func crop(var rect: CGRect) -> UIImage {
        rect.origin.x*=self.scale
        rect.origin.y*=self.scale
        rect.size.width*=self.scale
        rect.size.height*=self.scale

        let imageRef = CGImageCreateWithImageInRect(self.CGImage, rect)
        let image = UIImage(CGImage: imageRef, scale: self.scale, orientation: self.imageOrientation)!
        return image
    }
}
Epic Byte
  • 33,840
  • 12
  • 45
  • 93
4

Best solution for cropping an UIImage in Swift, in term of precision, pixels scaling ...:

private func squareCropImageToSideLength(let sourceImage: UIImage,
    let sideLength: CGFloat) -> UIImage {
        // input size comes from image
        let inputSize: CGSize = sourceImage.size

        // round up side length to avoid fractional output size
        let sideLength: CGFloat = ceil(sideLength)

        // output size has sideLength for both dimensions
        let outputSize: CGSize = CGSizeMake(sideLength, sideLength)

        // calculate scale so that smaller dimension fits sideLength
        let scale: CGFloat = max(sideLength / inputSize.width,
            sideLength / inputSize.height)

        // scaling the image with this scale results in this output size
        let scaledInputSize: CGSize = CGSizeMake(inputSize.width * scale,
            inputSize.height * scale)

        // determine point in center of "canvas"
        let center: CGPoint = CGPointMake(outputSize.width/2.0,
            outputSize.height/2.0)

        // calculate drawing rect relative to output Size
        let outputRect: CGRect = CGRectMake(center.x - scaledInputSize.width/2.0,
            center.y - scaledInputSize.height/2.0,
            scaledInputSize.width,
            scaledInputSize.height)

        // begin a new bitmap context, scale 0 takes display scale
        UIGraphicsBeginImageContextWithOptions(outputSize, true, 0)

        // optional: set the interpolation quality.
        // For this you need to grab the underlying CGContext
        let ctx: CGContextRef = UIGraphicsGetCurrentContext()
        CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh)

        // draw the source image into the calculated rect
        sourceImage.drawInRect(outputRect)

        // create new image from bitmap context
        let outImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()

        // clean up
        UIGraphicsEndImageContext()

        // pass back new image
        return outImage
}

Instructions used to call this function:

let image: UIImage = UIImage(named: "Image.jpg")!
let squareImage: UIImage = self.squareCropImageToSideLength(image, sideLength: 320)
self.myUIImageView.image = squareImage

Note: the initial source code inspiration written in Objective-C has been found on "Cocoanetics" blog.

King-Wizard
  • 15,628
  • 6
  • 82
  • 76
3

Below code snippet might help.

import UIKit

extension UIImage {
    func cropImage(toRect rect: CGRect) -> UIImage? {
        if let imageRef = self.cgImage?.cropping(to: rect) {
            return UIImage(cgImage: imageRef)
        }
        return nil
    }
}
Prasad Devadiga
  • 2,573
  • 20
  • 43
luhuiya
  • 2,129
  • 21
  • 20
2

Looks a little bit strange but works great and takes into consideration image orientation:

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)
MuniekMg
  • 141
  • 9
1
- (UIImage *)getSubImage:(CGRect) rect{
    CGImageRef subImageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    CGRect smallBounds = CGRectMake(rect.origin.x, rect.origin.y, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));

    UIGraphicsBeginImageContext(smallBounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawImage(context, smallBounds, subImageRef);
    UIImage* smallImg = [UIImage imageWithCGImage:subImageRef];
    UIGraphicsEndImageContext();

    return smallImg;
}
Golden
  • 139
  • 1
  • 1
  • 12
1
 (UIImage *)squareImageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
    double ratio;
    double delta;
    CGPoint offset;

    //make a new square size, that is the resized imaged width
    CGSize sz = CGSizeMake(newSize.width, newSize.width);

    //figure out if the picture is landscape or portrait, then
    //calculate scale factor and offset
    if (image.size.width > image.size.height) {
        ratio = newSize.width / image.size.width;
        delta = (ratio*image.size.width - ratio*image.size.height);
        offset = CGPointMake(delta/2, 0);
    } else {
        ratio = newSize.width / image.size.height;
        delta = (ratio*image.size.height - ratio*image.size.width);
        offset = CGPointMake(0, delta/2);
    }

    //make the final clipping rect based on the calculated values
    CGRect clipRect = CGRectMake(-offset.x, -offset.y,
                                 (ratio * image.size.width) + delta,
                                 (ratio * image.size.height) + delta);


    //start a new context, with scale factor 0.0 so retina displays get
    //high quality image
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0);
    } else {
        UIGraphicsBeginImageContext(sz);
    }
    UIRectClip(clipRect);
    [image drawInRect:clipRect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}
bummi
  • 27,123
  • 14
  • 62
  • 101
Bhushan_pawar
  • 157
  • 1
  • 5
1

On iOS9.2SDK ,I use below method to convert frame from UIView to UIImage

-(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
{
  CGSize cropSize = rect.size;
  CGFloat widthScale = image.size.width/self.imageViewOriginal.bounds.size.width;
  CGFloat heightScale = image.size.height/self.imageViewOriginal.bounds.size.height;
  cropSize = CGSizeMake(rect.size.width*widthScale, 
              rect.size.height*heightScale);
  CGPoint pointCrop = CGPointMake(rect.origin.x*widthScale,
             rect.origin.y*heightScale);
  rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, cropSize.height);
  CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
  UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
  CGImageRelease(subImage);

  return croppedImage;
}
SnareChops
  • 13,175
  • 9
  • 69
  • 91
1

Swift 2.0 Update (CIImage compatibility)

Expanding off of Maxim's Answer but works if your image is CIImage based, as well.

public extension UIImage {
    func imageByCroppingToRect(rect: CGRect) -> UIImage? {
        if let image = CGImageCreateWithImageInRect(self.CGImage, rect) {
            return UIImage(CGImage: image)
        } else if let image = (self.CIImage)?.imageByCroppingToRect(rect) {
            return UIImage(CIImage: image)
        }
       return nil
   }
}
Community
  • 1
  • 1
rolling_codes
  • 15,174
  • 22
  • 76
  • 112
1

Here's an updated Swift 3 version based on Noodles answer

func cropping(to rect: CGRect) -> UIImage? {

    if let cgCrop = cgImage?.cropping(to: rect) {
        return UIImage(cgImage: cgCrop)
    }
    else if let ciCrop = ciImage?.cropping(to: rect) {
        return UIImage(ciImage: ciCrop)
    }

    return nil
}
Community
  • 1
  • 1
voidref
  • 1,113
  • 11
  • 15
1

Follow Answer of @Arne. I Just fixing to Category function. put it in Category of UIImage.

-(UIImage*)cropImage:(CGRect)rect{

    UIGraphicsBeginImageContextWithOptions(rect.size, false, [self scale]);
    [self drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    UIImage* cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}
Linh Nguyen
  • 2,006
  • 1
  • 20
  • 16
1

Swift 5:

extension UIImage {
    func cropped(rect: CGRect) -> UIImage? {
        guard let cgImage = cgImage else { return nil }

        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
        let context = UIGraphicsGetCurrentContext()

        context?.translateBy(x: 0.0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.draw(cgImage, in: CGRect(x: rect.minX, y: rect.minY, width: self.size.width, height: self.size.height), byTiling: false)


        let croppedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return croppedImage
    }
}
huynguyen
  • 7,616
  • 5
  • 35
  • 48
  • This resized the image instead of cropping it. If that sounds incorrect to you, please include more information about the rect that you are passing in. – Mark May 11 '21 at 18:53
0

I wasn't satisfied with other solutions because they either draw several time (using more power than necessary) or have problems with orientation. Here is what I used for a scaled square croppedImage from a UIImage * image.

CGFloat minimumSide = fminf(image.size.width, image.size.height);
CGFloat finalSquareSize = 600.;

//create new drawing context for right size
CGRect rect = CGRectMake(0, 0, finalSquareSize, finalSquareSize);
CGFloat scalingRatio = 640.0/minimumSide;
UIGraphicsBeginImageContext(rect.size);

//draw
[image drawInRect:CGRectMake((minimumSide - photo.size.width)*scalingRatio/2., (minimumSide - photo.size.height)*scalingRatio/2., photo.size.width*scalingRatio, photo.size.height*scalingRatio)];

UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();
Matthieu Rouif
  • 2,843
  • 1
  • 19
  • 28
0

I use the method below.

  -(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
  {
    CGSize cropSize = rect.size;
    CGFloat widthScale =  
    image.size.width/self.imageViewOriginal.bounds.size.width;
    CGFloat heightScale = 
    image.size.height/self.imageViewOriginal.bounds.size.height;
    cropSize = CGSizeMake(rect.size.width*widthScale,  
    rect.size.height*heightScale);
    CGPoint  pointCrop = CGPointMake(rect.origin.x*widthScale, 
    rect.origin.y*heightScale);
    rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, 
    cropSize.height);
    CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
    UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
    CGImageRelease(subImage);
    return croppedImage;

}

0

Look at https://github.com/vvbogdan/BVCropPhoto

- (UIImage *)croppedImage {
    CGFloat scale = self.sourceImage.size.width / self.scrollView.contentSize.width;

    UIImage *finalImage = nil;
    CGRect targetFrame = CGRectMake((self.scrollView.contentInset.left + self.scrollView.contentOffset.x) * scale,
            (self.scrollView.contentInset.top + self.scrollView.contentOffset.y) * scale,
            self.cropSize.width * scale,
            self.cropSize.height * scale);

    CGImageRef contextImage = CGImageCreateWithImageInRect([[self imageWithRotation:self.sourceImage] CGImage], targetFrame);

    if (contextImage != NULL) {
        finalImage = [UIImage imageWithCGImage:contextImage
                                         scale:self.sourceImage.scale
                                   orientation:UIImageOrientationUp];

        CGImageRelease(contextImage);
    }

    return finalImage;
}


- (UIImage *)imageWithRotation:(UIImage *)image {


    if (image.imageOrientation == UIImageOrientationUp) return image;
    CGAffineTransform transform = CGAffineTransformIdentity;

    switch (image.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, image.size.width, image.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;

        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate(transform, image.size.width, 0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;

        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, 0, image.size.height);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            break;
    }

    switch (image.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, image.size.width, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;

        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, image.size.height, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            break;
    }

    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx = CGBitmapContextCreate(NULL, image.size.width, image.size.height,
            CGImageGetBitsPerComponent(image.CGImage), 0,
            CGImageGetColorSpace(image.CGImage),
            CGImageGetBitmapInfo(image.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (image.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr...
            CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.height, image.size.width), image.CGImage);
            break;

        default:
            CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
            break;
    }

    // And now we just create a new UIImage from the drawing context
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;

}
0

Swift 5.0 update

public extension UIImage {
    func cropped(rect: CGRect) -> UIImage? {
        if let image = self.cgImage!.cropping(to: rect) {
            return UIImage(cgImage: image)
        } else if let image = (self.ciImage)?.cropped(to: rect) {
            return UIImage(ciImage: image)
        }
       return nil
   }
}
jmrueda
  • 1,362
  • 19
  • 30
0

For Swift 5 I used next solution (I was needed centered cropped image)

public extension UIImage {
func cropped(rect: CGRect) -> UIImage? {
    if let image = self.cgImage?.cropping(to: rect) {
        return UIImage(cgImage: image, scale:self.scale, orientation:self.imageOrientation)
    }
    return nil
}

}

And somewhere in your code you have some "image" as UIImage:

    let baseSize = image.size
    let delta = abs(baseSize.height - baseSize.width)/2.0

    let croppedFrame = baseSize.height > baseSize.width ? CGRect(x: 0, y: delta, width: baseSize.width, height: baseSize.width) :
                                                          CGRect(x: delta, y: 0, width: baseSize.height, height: baseSize.height)
    
    guard let croppedImage = image.cropped(rect: croppedFrame) else {
        return
    }
SupaFil
  • 21
  • 4