如何从PHImageManager获取正方形缩略图?

21

有没有人知道如何从PHImageManager获取正方形的缩略图? PHImageContentModeAspectFill选项没有效果。

[[PHImageManager defaultManager]
  requestImageForAsset:(PHAsset *)_asset
            targetSize:CGSizeMake(80, 80)
           contentMode:PHImageContentModeAspectFill
               options:nil
         resultHandler:^(UIImage *result, NSDictionary *info) {
    // sadly result is not a squared image
    imageView.image = result;
}];

嗨,Pavel - 我的回答对你有用吗?如果是,请标记为已接受。 - Josh Heald
感谢您的回复,Josh。但是对我有效的真正解决方案是请求大小为100x100或更大的缩略图。您的解决方案只是针对PHImageManager中的“智能”算法的一种变通方法,所以很抱歉我无法接受它。 - Pavel Osipov
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Josh Heald
我的意思是我需要一个平方的UIImage,而不是UIImageView。 - Pavel Osipov
好的,说得对。我以为你在结果处理程序中使用它是为了imageView。我研究了一下normalizedCropRect方法,似乎框架中存在一个错误,因为它不起作用,其他开发人员已经在苹果开发者论坛上提出了这个问题。如果你同意,我建议你提交一个radar。我已经更新了我的答案,其中包含应该按照苹果文档工作的代码。 - Josh Heald
5个回答

20

更新:

在iOS 8.3中修复了从PHImageManager检索图像时裁剪图像的错误,因此对于该版本的iOS和更高版本,我的原始示例有效,如下所示:似乎在iOS 8.4及以下版本中仍存在错误,我可以使用标准iPhone 6s后置摄像头拍摄的全尺寸正方形裁剪图像重现这些错误。但是,在iOS 9.0中已经完全修复了这些错误,即使是63兆像素全景图的大型裁剪也能正常工作。

Apple定义的方法是在图像的坐标空间中传递一个CGRect,其中原点为(0,0),最大值为(1,1)。您将此矩形传递到PHImageRequestOptions对象中,同时将resizeMode设置为PHImageRequestOptionsResizeModeExact,然后您应该可以获得一个裁剪后的图像。

- (void)showSquareImageForAsset:(PHAsset *)asset
{
    NSInteger retinaScale = [UIScreen mainScreen].scale;
    CGSize retinaSquare = CGSizeMake(100*retinaScale, 100*retinaScale);

    PHImageRequestOptions *cropToSquare = [[PHImageRequestOptions alloc] init];
    cropToSquare.resizeMode = PHImageRequestOptionsResizeModeExact;

    CGFloat cropSideLength = MIN(asset.pixelWidth, asset.pixelHeight);
    CGRect square = CGRectMake(0, 0, cropSideLength, cropSideLength);
    CGRect cropRect = CGRectApplyAffineTransform(square,
                                                 CGAffineTransformMakeScale(1.0 / asset.pixelWidth,
                                                                            1.0 / asset.pixelHeight));

    cropToSquare.normalizedCropRect = cropRect;

    [[PHImageManager defaultManager]
     requestImageForAsset:(PHAsset *)asset
     targetSize:retinaSquare
     contentMode:PHImageContentModeAspectFit
     options:cropToSquare
     resultHandler:^(UIImage *result, NSDictionary *info) {
         self.imageView.image = result;
     }];
}

这个例子将其cropRect的边长设置为资产的宽度和高度中较小的那个,然后使用CGRectApplyAffineTransform将其转换到图像的坐标空间。您可能希望将square的原点设置为除(0,0)以外的其他位置,因为通常您希望裁剪正方形沿被裁剪的图像轴居中,但我将把这留给读者作为练习。 :-) 原始答案: John的答案让我完成了大部分工作,但是使用他的代码,我的图片会被拉伸和压缩。以下是如何使imageView显示从PHImageManager获取的正方形缩略图。
首先,请确保您的UIImageViewcontentMode属性设置为ScaleAspectFill。默认值为ScaleToFill,对于从PHImageManager显示正方形缩略图不起作用,因此无论您是在代码中还是在Storyboard中实例化了UIImageView,都要确保更改此设置。
//view dimensions are based on points, but we're requesting pixels from PHImageManager
NSInteger retinaMultiplier = [UIScreen mainScreen].scale;
CGSize retinaSquare = CGSizeMake(imageView.bounds.size.width * retinaMultiplier, imageView.bounds.size.height * retinaMultiplier);

[[PHImageManager defaultManager]
     requestImageForAsset:(PHAsset *)_asset
               targetSize:retinaSquare
              contentMode:PHImageContentModeAspectFill
                  options:nil
            resultHandler:^(UIImage *result, NSDictionary *info) {

    // The result is not square, but correctly displays as a square using AspectFill
    imageView.image = result;
}];

指定resizeModePHImageRequestOptionsResizeModeExact并非必需,因为除非您还提供normalizedCropRect,否则它不会给您裁剪后的图像,并且在这里不应该使用它,因为没有好处,使用它意味着您无法获得快速返回缓存图像的好处。 result中返回的UIImage与源具有相同的纵横比,但按正确比例缩放以用于设置为aspect fill的UIImageView,以显示为正方形,因此如果您只是要显示它,那么这是正确的方法。如果您需要将图像裁剪用于打印或导出到应用程序之外,则不是您想要的 - 请查看使用normalizedCropRect的方法。(编辑-请参见下面的示例,了解应该如何操作...)
除此之外,还要确保将UIImageView的内容模式设置为UIViewContentModeScaleAspectFill,并通过以下两行设置clipsToBounds = YES
imageView.contentMode=UIViewContentModeScaleAspectFill;
imageView.clipsToBounds=YES;

添加使用normalizedCropRect的示例
警告 - 这个方法不起作用,但是应该能够根据苹果文档工作。

苹果定义的方法是在图像的坐标空间中传递一个CGRect,其中原点为(0,0),最大值为(1,1)。您需要在PHImageRequestOptions对象中传递此矩形,以及resizeModePHImageRequestOptionsResizeModeExact,然后您应该会得到一个裁剪后的图像。问题是您没有得到,它返回的仍然是原始的长宽比和完整的图像。

我已经验证了在图像的坐标空间中正确创建了裁剪矩形,并遵循了使用PHImageRequestOptionsResizeModeExact的指示,但结果处理程序仍将传递原始长宽比的图像。这似乎是框架中的一个错误,当它被修复后,以下代码应该可以工作。

- (void)showSquareImageForAsset:(PHAsset *)asset
{
    NSInteger retinaScale = [UIScreen mainScreen].scale;
    CGSize retinaSquare = CGSizeMake(100*retinaScale, 100*retinaScale);

    PHImageRequestOptions *cropToSquare = [[PHImageRequestOptions alloc] init];
    cropToSquare.resizeMode = PHImageRequestOptionsResizeModeExact;

    CGFloat cropSideLength = MIN(asset.pixelWidth, asset.pixelHeight);
    CGRect square = CGRectMake(0, 0, cropSideLength, cropSideLength);
    CGRect cropRect = CGRectApplyAffineTransform(square,
                                                 CGAffineTransformMakeScale(1.0 / asset.pixelWidth,
                                                                            1.0 / asset.pixelHeight));

    cropToSquare.normalizedCropRect = cropRect;

    [[PHImageManager defaultManager]
     requestImageForAsset:(PHAsset *)asset
     targetSize:retinaSquare
     contentMode:PHImageContentModeAspectFit
     options:cropToSquare
     resultHandler:^(UIImage *result, NSDictionary *info) {
         self.imageView.image = result;
     }];
}

我能提供的建议就是,如果你遇到这个问题,你可以向苹果公司 提交一个radar 请求他们修复它!

3
我认为你应该使用[UIScreen mainScreen].scale而不是2。这适用于iPhone 6 Plus和@3x等高分辨率设备。 - DCMaxxx
非常有用;需要注意的是,如果您是使用集合视图进行此操作,则有可能会创建一个 UICollectionViewCell 扩展,该扩展包装了您的 UIImageView。我遇到的问题之一是我做了 cell.contentMode = 而不是 cell.imageView.contentMode =(这是使用原始答案)。 - mike
我对这个解决方案有问题。我的比例是3.0(iPhone 6 Plus),我的目标大小是72x72。但我总是得到216x216(72x3.0)。如果我改变比例,我总是得到低于72.0的结果(例如72/2、72/3..)。 - Rikco
@Rikco 如果你忽略比例并使用 CGSize retinaSquare = CGSizeMake(72.0, 72.0);,你会得到什么?你需要目标大小为72,因为你要将其传递给某个外部系统吗?因为如果用于在手机上显示,你希望它是216x216,以避免由于放大图像而导致的模糊。 - Josh Heald

6
  PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; 
    options.resizeMode = PHImageRequestOptionsResizeModeExact;

  NSInteger retinaMultiplier = [UIScreen mainScreen].scale;
  CGSize retinaSquare = CGSizeMake(imageView.bounds.size.width * retinaMultiplier, imageView.bounds.size.height * retinaMultiplier);

    [[PHImageManager defaultManager]
             requestImageForAsset:(PHAsset *)_asset
                       targetSize:retinaSquare
                      contentMode:PHImageContentModeAspectFill
                          options:options
                    resultHandler:^(UIImage *result, NSDictionary *info) {

                    imageView.image =[UIImage imageWithCGImage:result.CGImage scale:retinaMultiplier orientation:result.imageOrientation];

    }];

1
使用 result.imageOrientation 代替硬编码的方向,以避免麻烦。 - pronebird

3
要获得一个精确的正方形,您需要通过传递选项来指定您想要的精确尺寸,例如:
    PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];

    // No, really, we want this exact size    
    options.resizeMode = PHImageRequestOptionsResizeModeExact

    [[PHImageManager defaultManager]
             requestImageForAsset:(PHAsset *)_asset
                       targetSize:CGSizeMake(160, 160)
                      contentMode:PHImageContentModeAspectFill
                          options:options
                    resultHandler:^(UIImage *result, NSDictionary *info) {

    // Happily, result is now a squared image
    imageView.image = result;
    }];

对我来说,这导致一些图像被压缩或拉伸。 - Josh Heald
仅仅因为你请求了确切的尺寸并不意味着内容模式被忽略。在这种情况下,内容模式仍然是Aspect Fill,因此你将会得到非正方形的图像(假设原始图像不是正方形)。 - EarlyRiser
如果您使用PHImageContentModeAspectFill,就不需要那个了,这样做没有意义,对吧? - pronebird

2
let squareSize = CGSize(100, 100)
let options = PHImageRequestOptions()
options.resizeMode = .exact
options.deliveryMode = .highQualityFormat
PHImageManager.default().requestImage(for: asset, targetSize: squareSize, contentMode: .aspectFill, options: options) { (image, _) in

    // Use the image.
}

0

这对我来说很好用:

 __block UIImage* imgThumb;

CGSize size=CGSizeMake(45, 45);// Size for Square image

[self.imageManager requestImageForAsset:<your_current_phAsset>
                             targetSize:size
                            contentMode:PHImageContentModeAspectFill
                                options:nil
                          resultHandler:^(UIImage *result, NSDictionary *info) {

                              imgThumb = result;

  }];

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