如何在'适应宽高比'模式下裁剪UIImageView并生成新的UIImage?

9

enter image description here

我希望通过裁剪UIImageView中的图像来创建一个新的UIImage。例如,在上面的图片中,我想要一个新的UIImage,它的范围被绿色轮廓线框起来。我找到了一些代码来实现这个功能,通常它看起来像这样(作为UIImage类别):

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

非常有道理。然而,我的UIImageView处于“aspect fit”内容模式。似乎这使得CGImageCreateWithImageInRect的CGRect计算变得复杂,我还没有能够找到正确的解决方案。
我想我需要对绿色矩形应用与图像相同的变换?
另外:我在这里找到了另一个问题(How to know the image size after applying aspect fit for the image in an UIImageView),它似乎展示了一种暴力获取大小的方法,但我仍然不确定它如何适用于裁剪情况。
有什么想法吗?

编辑:在我的情况下,我在UIImageView上方绘制了一个正方形覆盖层,如上所示。因此,虽然我意识到我正在裁剪UIImage(在UIImageView内部),但我需要以某种方式更改绿色CGRect,使其“匹配”应用“等比缩放”时所做的变换。例如,如果我将UIImageView保留在“左上角”模式中,则一切都正常工作,因为底层UIImage和UIImageView之间存在1:1映射。在“左上角”模式中的问题是,并非始终显示整个图像,因为它可能比我的UIImageView大。

4个回答

10

根据我在其他答案中对类似问题的回答,我为UIImageView类别编写了这个方法:

-(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);
}

如果您的绿色矩形是图像视图的子视图,您可以传递它的框架并获取裁剪UIImage所需的裁剪矩形。请注意,这仅适用于“按比例缩放”模式!不过,我没有测试过。所以,请告诉我它是否有效!


还没有机会实现这个,但我会尽快。谢谢你,看起来很有道理。 - pj4533
谢谢!那太完美了。 - itscoderslife
2
你会如何调整这个算法来适应2x和3x的图像?对于1x的图像,这个算法运行得很完美,但是对于2x和3x比例的图像,它无法准确裁剪。 - zic10
@Felix 很好的回答! - Lance Samaria

4
我知道这个问题已经有答案了,但我在解决它的过程中遇到了一些问题,所以我也想发表我的解决方案。
问题是图像使用纵横比适配缩放,但我们知道宽度和高度的缩放因子。然后我们可以很容易地计算出正确的裁剪矩形:
-(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;
}

1

我认为你没有理解自己在做什么。你不能“裁剪”UIImageView - 只能裁剪UIImages。所以你的croppedImage方法应该在UIImage类别而不是UIImageView类别中。 传递给裁剪方法的边界矩形必须与图像大小相关,而不是与imageview边界相关。 图像视图的contentMode无关紧要 - 图片的裁剪始终都会按相同的方式进行。首先从图像视图获取图像。然后从其中创建一个新的裁剪图像,最后将裁剪图像设置为图像视图。这可以用一行代码完成:

imageView.image = [imageView.image croppedImage:rect];

由于您的contentMode是'aspect fit',新图像将自动拉伸以适应视图框架。您还可以尝试根据image.size更改视图框架,以避免像素化效果。


我试图重新表述这个问题。我确实理解UIImageView和UIImage之间的区别。请看我上面的编辑,现在有意义了吗? - pj4533
我不确定我是否正确理解您的意思。您想遮盖图像视图,以便它仅显示绿色矩形中的部分吗?那么就不需要裁剪。还是您只想在正确的位置绘制绿色矩形? - Felix
我想要创建一个全新的UIImage,其中包含绿色矩形内的部分。 - pj4533

0

我使用以下方法。

-(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;

   }

为OP的需求添加该方法如何工作的说明。 - NSNoob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接