Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to crop a UIImageView to a new UIImage in 'aspect fit' mode?

enter image description here

I would like to create a new UIImage by cropping the image inside a UIImageView. For example in the above pic, I want a new UIImage of the area outlined in green. I found code to do this, normally it looks something like this (as a UIImage category):

- (UIImage *)croppedImage:(CGRect)bounds {
    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], bounds);
    UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    return croppedImage;
}

Makes perfect sense. HOWEVER, my UIImageView is in contentMode 'aspect fit'. Seems like that complicates the CGRect calculations for CGImageCreateWithImageInRect and I haven't been able to figure out the right solution.

I figure I need to apply the same transformations to the green rect as were done to the image?

ALSO: I found this other question here (How to know the image size after applying aspect fit for the image in an UIImageView), which seems to show a brute force way of getting the size, but I am still not sure how that fits into a cropping situation.

Any ideas?

EDIT: In my case, I literally have a square as shown above drawn in an overlay view over the UIImageView. So while I realize I am cropping the UIImage (inside the UIImageView), I need to somehow change the green CGRect such that it 'matches' the transformation done when 'aspect fit' is applied. For example, if I leave my UIImageView in 'top left' mode, then everything works fine, because there is a 1:1 mapping between the underlying UIImage and the UIImageView. The problem in 'top left' mode is that the entire image doesn't always display, because it might be bigger than my UIImageView.

like image 477
pj4533 Avatar asked Jun 02 '12 04:06

pj4533


2 Answers

Based on my other answer to a similar question I have written this method for a UIImageView category:

-(CGRect) cropRectForFrame:(CGRect)frame
{
    NSAssert(self.contentMode == UIViewContentModeScaleAspectFit, @"content mode must be aspect fit");

    CGFloat widthScale = self.bounds.size.width / self.image.size.width;
    CGFloat heightScale = self.bounds.size.height / self.image.size.height;

    float x, y, w, h, offset;
    if (widthScale<heightScale) {
        offset = (self.bounds.size.height - (self.image.size.height*widthScale))/2;
        x = frame.origin.x / widthScale;
        y = (frame.origin.y-offset) / widthScale;
        w = frame.size.width / widthScale;
        h = frame.size.height / widthScale;
    } else {
        offset = (self.bounds.size.width - (self.image.size.width*heightScale))/2;
        x = (frame.origin.x-offset) / heightScale;
        y = frame.origin.y / heightScale;
        w = frame.size.width / heightScale;
        h = frame.size.height / heightScale;
    }
    return CGRectMake(x, y, w, h);
}

You can pass in the frame of your green rect (assuming it is a subview of the image view) and get the crop rect back for cropping the UIImage. Note that it only works for 'aspect fit' mode! I haven't tested it though. So, tell me if it works!

like image 83
Felix Avatar answered Oct 01 '22 02:10

Felix


I know the answer has been but I had some problems getting above right so, I wont to post my solution too.

The problem is the image is scaled with a aspect fit, but we know the scale factor for both the width and the height. Then we can calculate the right cropping rectangle pretty easy:

-(UIImage*)crop:(CGRect)frame
{
    // Find the scalefactors  UIImageView's widht and height / UIImage width and height
    CGFloat widthScale = self.bounds.size.width / self.image.size.width;
    CGFloat heightScale = self.bounds.size.height / self.image.size.height;

    // Calculate the right crop rectangle 
    frame.origin.x = frame.origin.x * (1 / widthScale);
    frame.origin.y = frame.origin.y * (1 / heightScale);
    frame.size.width = frame.size.width * (1 / widthScale);
    frame.size.height = frame.size.height * (1 / heightScale);

    // Create a new UIImage
    CGImageRef imageRef = CGImageCreateWithImageInRect(self.image.CGImage, frame);
    UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);

    return croppedImage;
}
like image 38
DNRN Avatar answered Oct 01 '22 00:10

DNRN