【问题标题】:How to fetch squared thumbnails from PHImageManager?如何从 PHImageManager 获取平方缩略图?
【发布时间】:2014-11-19 07:52:49
【问题描述】:

有人知道如何从 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 - 我的回答对你有用吗?如果是,请您将其标记为已接受?
  • 感谢乔希的回复,但真正适合我的解决方案是请求大小为 100x100 或更大的拇指。您的解决方案只是针对 PHImageManager 中的“智能”算法的一种解决方法,所以很抱歉我不能接受它。
  • 这不是视网膜屏幕的比例问题吗?我假设目标是一个适合 80 点方形 UIImageView 的图像,您的代码在其中请求一个 80 像素的图像,这在视网膜尺度上太小了——这可能就是为什么请求超过 100x100 会改善问题。我的代码可以使用哪些算法?我认为它可以与 Photos 框架正常工作,并且我在我的应用程序中拥有它,所以如果它与我想知道的任何东西一起工作,因为我想充分利用框架。如果您有更好的解决方案,我们鼓励您回答自己的问题:)
  • 我的意思是我需要平方 UIImage,而不是 UIImageView。
  • 好吧,很公平,我认为它是针对 imageView 的,因为这就是您在结果处理程序中对它所做的。我研究了 normalizedCropRect 方法,框架中似乎有一个错误,因为它不起作用并且其他开发人员在 Apple 开发人员论坛上提出了它。如果您同意,我建议您提交雷达。我已经用应该根据 Apple 文档工作的代码更新了我的答案。

标签: ios ios8 photosframework


【解决方案1】:

要获得精确的正方形,您必须通过传递选项来表明您想要精确的大小,如下所示:

    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;
    }];

【讨论】:

  • 对我来说,这会导致一些图像被压扁或拉伸。
  • 仅仅因为您要求确切的大小并不意味着内容模式被忽略。在这种情况下,内容模式仍然是方面填充,因此您将返回非方形图像(假设原始图像不是方形的)。
  • 如果你使用 PHImageContentModeAspectFill 就不需要了,这没有任何意义吧?
【解决方案2】:

更新:

PHImageManager 检索图像时裁剪图像的错误已在 iOS 8.3 中修复,因此对于该版本的 iOS 及更高版本,我的原始示例如下所示: 似乎错误仍然存​​在,包括 iOS 8.4,我可以用标准的 iPhone 6s 后置摄像头图像重现它们,并采用全尺寸方形裁剪。它们在 iOS 9.0 中得到了适当的修复,即使是 63 兆像素全景图的大裁剪也能正常工作。

Apple 定义的方法是在图像的坐标空间中传递一个CGRect,其中原点为 (0,0),最大值为 (1,1)。你在PHImageRequestOptions 对象中传递这个矩形,以及PHImageRequestOptionsResizeModeExactresizeMode,然后你应该得到一个裁剪的图像。

- (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) 以外的其他值,因为您通常希望裁剪正方形沿正在裁剪的图像的轴居中,但我将把它留作练习读者。 :-)

原答案:

约翰的回答让我大部分时间都在那里,但使用他的代码,我得到了拉伸和压扁的图像。下面是我如何获得一个imageView 来显示从 PHImageManager 获取的方形缩略图。

首先,确保您的UIImageViewcontentMode 属性设置为ScaleAspectFill。默认为ScaleToFill,它无法正常显示来自PHImageManager 的方形缩略图,因此无论您是在代码中还是在情节提要中实例化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;
}];

不需要为resizeMode 指定PHImageRequestOptionsResizeModeExact,因为除非您还提供normalizedCropRect,否则它不会为您提供裁剪的图像,并且不应在此处使用,因为没有任何好处,使用它意味着您不要获得快速返回的缓存图像的好处。

result 中返回的UIImage 将与源的纵横比相同,但可以正确缩放以在设置为纵横比填充以显示为正方形的UIImageView 中使用,所以如果你只是显示它,这是要走的路。如果您需要裁剪图像以在应用程序之外打印或导出,这不是您想要的 - 请查看 normalizedCropRect 的使用。 (编辑 - 请参阅下面的示例,了解应该如何工作......)

除此之外,还要确保将 UIImageView 的内容模式设置为 UIViewContentModeScaleAspectFill 并通过以下两行设置 clipsToBounds = YES:

imageView.contentMode=UIViewContentModeScaleAspectFill;
imageView.clipsToBounds=YES;

编辑以添加 normalizedCropRect 用法示例
警告 - 这不起作用,但应根据 Apple's documentation

Apple 定义的方法是在图像的坐标空间中传递一个CGRect,其中原点为 (0,0),最大值为 (1,1)。你在PHImageRequestOptions 对象中传递这个矩形,以及PHImageRequestOptionsResizeModeExactresizeMode,然后你应该得到一个裁剪的图像。问题是你没有,它以原始纵横比和完整图像的形式返回。

我已验证在图像的坐标空间中正确创建了裁剪矩形,并按照说明使用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;
     }];
}

我只能建议,如果你有这个问题,你可以file a radar Apple 请求他们修复它!

【讨论】:

  • 我认为你应该使用[UIScreen mainScreen].scale 而不是2。 iPhone 6 Plus、@3x 等等。
  • 非常有帮助;需要注意的一件事是,如果您使用集合视图执行此操作,则有可能您创建了一个 UICollectionViewCell 扩展名,它包装了您的 UIImageView。我遇到的问题之一是我在做cell.contentMode = 而不是cell.imageView.contentMode = (这是使用原始答案)
  • 我对这个解决方案有疑问。我的比例是 3.0 (iPhone 6 Plus),我的目标尺寸是 72x72。但我总是以 216x216 (72x3.0) 结束。如果我改变比例,我总是低于 72.0(例如 72/2、72/3 ..)
  • @Rikco 如果你忽略比例并使用CGSize retinaSquare = CGSizeMake(72.0, 72.0);,你会得到什么?您是否需要将目标大小设置为 72,因为您要将其传递给某个外部系统?因为如果是用来在手机上显示的,你希望它是216x216,避免放大图片造成的模糊。
【解决方案3】:
  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];

    }];

【讨论】:

  • 使用result.imageOrientation而不是硬编码的方向以避免麻烦。
【解决方案4】:

这对我来说很好用:

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

  }];

【讨论】:

    【解决方案5】:
    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.
    }
    

    【讨论】:

      猜你喜欢
      • 2017-02-11
      • 2012-09-25
      • 2015-12-17
      • 2020-08-03
      • 2020-05-16
      • 1970-01-01
      • 1970-01-01
      • 2012-10-28
      相关资源
      最近更新 更多