如何从PHImageManager获取平方缩略图?

时间:2023-01-23 16:38:05

Has anybody idea how to fetch squared thumbs from PHImageManager? PHImageContentModeAspectFill option has no effect.

有谁知道如何从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;
}];

4 个解决方案

#1


19  

Update:

更新:

The bug in cropping images as they were retrieved from PHImageManager was fixed in iOS 8.3, so for that version of iOS and later, my original example works, as follows: It seems the bugs are still there up to and including iOS 8.4, I can reproduce them with standard iPhone 6s back camera images, taking a full size square crop. They are properly fixed in iOS 9.0, where even large crops of a 63 megapixel panorama work fine.

在iOS 8.3中修复了从PHImageManager中检索图像时出错的错误,因此对于那个版本的iOS及更高版本,我的原始示例有效,如下所示:看来这些错误仍然存​​在,包括iOS 8.4,我可以使用标准iPhone 6s后置摄像头图像再现它们,拍摄全尺寸方形裁剪。它们在iOS 9.0中得到了适当的修复,即使是6300万像素全景的大型作物也能正常工作。

The approach Apple defines is to pass a CGRect in the co-ordinate space of the image, where the origin is (0,0) and the maximum is (1,1). You pass this rect in the PHImageRequestOptions object, along with a resizeMode of PHImageRequestOptionsResizeModeExact, and then you should get back a cropped image.

Apple定义的方法是在图像的坐标空间中传递CGRect,其中原点为(0,0),最大值为(1,1)。您在PHImageRequestOptions对象中传递此rect,以及PHImageRequestOptionsResizeModeExact的resizeMode,然后您应该返回裁剪的图像。

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

This example makes its cropRect of side length equal to the smaller of the width and height of the asset, and then transforms it to the co-ordinate space of the image using CGRectApplyAffineTransform. You may want to set the origin of square to something other than (0,0), as often you want the crop square centred along the axis of the image which is being cropped, but I'll leave that as an exercise for the reader. :-)

此示例使其cropRect的边长等于资产的宽度和高度中的较小者,然后使用CGRectApplyAffineTransform将其转换为图像的坐标空间。您可能希望将方形的原点设置为(0,0)以外的其他值,因为您通常希望裁剪方块沿着正在裁剪的图像轴居中,但我会将其留作读者练习。 :-)

Original Answer:

原答案:

John's answer got me most of the way there, but using his code I was getting stretched and squashed images. Here's how I got an imageView to display square thumbnails fetched from the PHImageManager.

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

Firstly, ensure that the contentMode property for your UIImageView is set to ScaleAspectFill. The default is to ScaleToFill, which doesn't work correctly for displaying square thumbnails from PHImageManager, so make sure you change this whether you've instantiated the UIImageView in code or in the storyboard.

首先,确保UIImageView的contentMode属性设置为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;
}];

Specifying PHImageRequestOptionsResizeModeExact for the resizeMode is not required, as it will not give you a cropped image unless you also supply a normalizedCropRect, and should not be used here as there's no benefit, and using it means you don't get the benefits of quickly returned cached images.

指定PHImageRequestOptionsResizeModeExact为resizeMode不是必需的,因为它不会给你一个裁剪的图像,除非您同时提供normalizedCropRect,并且因为没有好处不应该用在这里,并用它意味着你没有得到迅速恢复的好处缓存的图像。

The UIImage returned in result will be the same aspect ratio as the source, but scaled correctly for use in a UIImageView which is set to aspect fill to display as a square, so if you're just displaying it, this is the way to go. If you need to crop the image for print or export outside of the app, this isn't what you want - look into the use of normalizedCropRect for that. (edit- see below for example of what should work...)

结果中返回的UIImage将与源相同的宽高比,但正确缩放以在UIImageView中使用,UIImageView设置为宽高比填充以显示为正方形,因此如果您只是显示它,这是要走的路。如果您需要裁剪图像以进行打印或在应用程序外部导出,这不是您想要的 - 请查看normalizedCropRect的用法。 (编辑 - 见以下作为应该起作用的例子......)

Except this also make sure that the you set the content mode of the UIImageView to UIViewContentModeScaleAspectFill and that you set clipsToBounds = YES by the following 2 lines :

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

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

Edit to add normalizedCropRect usage example
WARNING - this doesn't work, but should according to Apple's documentation.

编辑以添加normalizedCropRect用法示例警告 - 这不起作用,但应根据Apple的文档。

The approach Apple defines is to pass a CGRect in the co-ordinate space of the image, where the origin is (0,0) and the maximum is (1,1). You pass this rect in the PHImageRequestOptions object, along with a resizeMode of PHImageRequestOptionsResizeModeExact, and then you should get back a cropped image. The problem is that you don't, it comes back as the original aspect ratio and the full image.

Apple定义的方法是在图像的坐标空间中传递CGRect,其中原点为(0,0),最大值为(1,1)。您在PHImageRequestOptions对象中传递此rect,以及PHImageRequestOptionsResizeModeExact的resizeMode,然后您应该返回裁剪的图像。问题是你没有,它以原始宽高比和完整图像的形式返回。

I've verified that the crop rect is created correctly in the image's co-ordinate space, and followed the instruction to use PHImageRequestOptionsResizeModeExact, but the result handler will still be passed an image in the original aspect ratio. This seems to be a bug in the framework, and when it is fixed, the following code should work.

我已经验证了裁剪矩形是在图像的坐标空间中正确创建的,并且遵循指令使用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;
     }];
}

All I can suggest is that if you have this problem, you file a radar with Apple to request that they fix it!

我可以建议的是,如果你有这个问题,你就向Apple提出雷达要求他们修复它!

#2


5  

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

    }];

#3


1  

To get an exact square, you'll have to indicate that you want an exact size by passing options, like so:

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

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

#4


0  

This is working fine for me :

这对我来说很好:

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

  }];

#1


19  

Update:

更新:

The bug in cropping images as they were retrieved from PHImageManager was fixed in iOS 8.3, so for that version of iOS and later, my original example works, as follows: It seems the bugs are still there up to and including iOS 8.4, I can reproduce them with standard iPhone 6s back camera images, taking a full size square crop. They are properly fixed in iOS 9.0, where even large crops of a 63 megapixel panorama work fine.

在iOS 8.3中修复了从PHImageManager中检索图像时出错的错误,因此对于那个版本的iOS及更高版本,我的原始示例有效,如下所示:看来这些错误仍然存​​在,包括iOS 8.4,我可以使用标准iPhone 6s后置摄像头图像再现它们,拍摄全尺寸方形裁剪。它们在iOS 9.0中得到了适当的修复,即使是6300万像素全景的大型作物也能正常工作。

The approach Apple defines is to pass a CGRect in the co-ordinate space of the image, where the origin is (0,0) and the maximum is (1,1). You pass this rect in the PHImageRequestOptions object, along with a resizeMode of PHImageRequestOptionsResizeModeExact, and then you should get back a cropped image.

Apple定义的方法是在图像的坐标空间中传递CGRect,其中原点为(0,0),最大值为(1,1)。您在PHImageRequestOptions对象中传递此rect,以及PHImageRequestOptionsResizeModeExact的resizeMode,然后您应该返回裁剪的图像。

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

This example makes its cropRect of side length equal to the smaller of the width and height of the asset, and then transforms it to the co-ordinate space of the image using CGRectApplyAffineTransform. You may want to set the origin of square to something other than (0,0), as often you want the crop square centred along the axis of the image which is being cropped, but I'll leave that as an exercise for the reader. :-)

此示例使其cropRect的边长等于资产的宽度和高度中的较小者,然后使用CGRectApplyAffineTransform将其转换为图像的坐标空间。您可能希望将方形的原点设置为(0,0)以外的其他值,因为您通常希望裁剪方块沿着正在裁剪的图像轴居中,但我会将其留作读者练习。 :-)

Original Answer:

原答案:

John's answer got me most of the way there, but using his code I was getting stretched and squashed images. Here's how I got an imageView to display square thumbnails fetched from the PHImageManager.

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

Firstly, ensure that the contentMode property for your UIImageView is set to ScaleAspectFill. The default is to ScaleToFill, which doesn't work correctly for displaying square thumbnails from PHImageManager, so make sure you change this whether you've instantiated the UIImageView in code or in the storyboard.

首先,确保UIImageView的contentMode属性设置为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;
}];

Specifying PHImageRequestOptionsResizeModeExact for the resizeMode is not required, as it will not give you a cropped image unless you also supply a normalizedCropRect, and should not be used here as there's no benefit, and using it means you don't get the benefits of quickly returned cached images.

指定PHImageRequestOptionsResizeModeExact为resizeMode不是必需的,因为它不会给你一个裁剪的图像,除非您同时提供normalizedCropRect,并且因为没有好处不应该用在这里,并用它意味着你没有得到迅速恢复的好处缓存的图像。

The UIImage returned in result will be the same aspect ratio as the source, but scaled correctly for use in a UIImageView which is set to aspect fill to display as a square, so if you're just displaying it, this is the way to go. If you need to crop the image for print or export outside of the app, this isn't what you want - look into the use of normalizedCropRect for that. (edit- see below for example of what should work...)

结果中返回的UIImage将与源相同的宽高比,但正确缩放以在UIImageView中使用,UIImageView设置为宽高比填充以显示为正方形,因此如果您只是显示它,这是要走的路。如果您需要裁剪图像以进行打印或在应用程序外部导出,这不是您想要的 - 请查看normalizedCropRect的用法。 (编辑 - 见以下作为应该起作用的例子......)

Except this also make sure that the you set the content mode of the UIImageView to UIViewContentModeScaleAspectFill and that you set clipsToBounds = YES by the following 2 lines :

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

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

Edit to add normalizedCropRect usage example
WARNING - this doesn't work, but should according to Apple's documentation.

编辑以添加normalizedCropRect用法示例警告 - 这不起作用,但应根据Apple的文档。

The approach Apple defines is to pass a CGRect in the co-ordinate space of the image, where the origin is (0,0) and the maximum is (1,1). You pass this rect in the PHImageRequestOptions object, along with a resizeMode of PHImageRequestOptionsResizeModeExact, and then you should get back a cropped image. The problem is that you don't, it comes back as the original aspect ratio and the full image.

Apple定义的方法是在图像的坐标空间中传递CGRect,其中原点为(0,0),最大值为(1,1)。您在PHImageRequestOptions对象中传递此rect,以及PHImageRequestOptionsResizeModeExact的resizeMode,然后您应该返回裁剪的图像。问题是你没有,它以原始宽高比和完整图像的形式返回。

I've verified that the crop rect is created correctly in the image's co-ordinate space, and followed the instruction to use PHImageRequestOptionsResizeModeExact, but the result handler will still be passed an image in the original aspect ratio. This seems to be a bug in the framework, and when it is fixed, the following code should work.

我已经验证了裁剪矩形是在图像的坐标空间中正确创建的,并且遵循指令使用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;
     }];
}

All I can suggest is that if you have this problem, you file a radar with Apple to request that they fix it!

我可以建议的是,如果你有这个问题,你就向Apple提出雷达要求他们修复它!

#2


5  

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

    }];

#3


1  

To get an exact square, you'll have to indicate that you want an exact size by passing options, like so:

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

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

#4


0  

This is working fine for me :

这对我来说很好:

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

  }];