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.
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!
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With