iOS Photokit - PHAsset pixelWidth и pixelHeight не соответствуют изображению высокого разрешения
У моей компании большие проблемы с получением метаданных правильного размера путем загрузки PHAssets. Мы разработали приложения для iOS, которые позволяют клиентам выбирать изображения из библиотеки, получать размер (в пикселях) для каждого из них, вычислять координаты для адаптации к продаваемым гаджетам, а затем загружать высококачественную версию изображения на наш сервер для печати гаджетов. Для некоторых наших клиентов проблема заключается в том, что размер в пикселях некоторых высококачественных версий отправляемых изображений не соответствует pixelWidth и pixelHeight, заданным объектом PHAsset. Чтобы сделать пример, у нас есть картина, которая:
- сообщается, что объект PHAsset имеет размер 2096x3724
- но когда запрашивается полноразмерное изображение, генерируется изображение 1536x2730
Картинка не в iCloud, а отправлена iPhone 6 SE под управлением iOS 10.2. Это код для получения полноразмерной версии изображения:
PHImageRequestOptions *imgOpts = [[PHImageRequestOptions alloc] init];
imgOpts.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
imgOpts.networkAccessAllowed = YES;
imgOpts.resizeMode = PHImageRequestOptionsResizeModeExact;
imgOpts.version = PHImageRequestOptionsVersionCurrent;
PHCachingImageManager *imageManager = [[PHCachingImageManager alloc] init];
[imageManager requestImageForAsset:imageAsset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:imgOpts resultHandler:^(UIImage * result, NSDictionary * info) {
NSData * imageData = UIImageJPEGRepresentation(result, 0.92f);
//UPLOAD OF imageData TO SERVER HERE
}]
Также пытался с методом requestImageDataForAsset, но безуспешно:
PHImageRequestOptions *imgOpts = [[PHImageRequestOptions alloc] init];
imgOpts.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
imgOpts.networkAccessAllowed = YES;
imgOpts.resizeMode = PHImageRequestOptionsResizeModeExact;
imgOpts.version = PHImageRequestOptionsVersionCurrent;
PHCachingImageManager *imageManager = [[PHCachingImageManager alloc] init];
[imageManager requestImageDataForAsset:imageAsset options:imgOpts resultHandler:^(NSData * imageData, NSString * dataUTI, UIImageOrientation orientation, NSDictionary * info) {
//UPLOAD OF imageData TO SERVER HERE
}]
Получение точного размера каждой версии изображения в высоком разрешении перед загрузкой не является для нас вариантом, поскольку это может значительно снизить производительность при выборе большого количества ресурсов из библиотеки.
Мы пропускаем или делаем что-то не так? Есть ли способ получить размер активов в пикселях без загрузки изображения с полным разрешением в память? Спасибо за помощь
1 ответ
Это связано с ошибкой в Photos
фреймворк. Подробности об ошибке можно найти здесь.
Иногда после редактирования фотографии создается уменьшенная версия. Это происходит только для некоторых больших фотографий.
Звонить либо requestImageForAsset:
(с PHImageManagerMaximumSize
) или же requestImageDataForAsset:
(с PHImageRequestOptionsDeliveryModeHighQualityFormat
) будет читать данные из уменьшенной версии файла при попытке получить отредактированную версию (PHImageRequestOptionsVersionCurrent
).
info
в обратном вызове вышеперечисленных методов будет указан путь к изображению. В качестве примера: PHImageFileURLKey = "file:///[...]DCIM/100APPLE/IMG_0006/Adjustments/IMG_0006.JPG";
Изучив эту папку, я смог найти другое изображение, FullSizeRender.jpg
- этот файл имеет полный размер и содержит последние изменения. Таким образом, одним из способов будет попытаться прочитать из FullSizeRender.jpg
когда такой файл присутствует.
Начиная с iOS 9, также можно получить последние изменения в самом высоком разрешении, используя
PHAssetResourceManager
:// if (@available(iOS 9.0, *)) {
// check if a high quality edit is available
NSArray<PHAssetResource *> *resources = [PHAssetResource assetResourcesForAsset:_asset];
PHAssetResource *hqResource = nil;
for (PHAssetResource *res in resources) {
if (res.type == PHAssetResourceTypeFullSizePhoto) {
// from my tests so far, this is only present for edited photos
hqResource = res;
break;
}
}
if (hqResource) {
PHAssetResourceRequestOptions *options = [[PHAssetResourceRequestOptions alloc] init];
options.networkAccessAllowed = YES;
long long fileSize = [[hqResource valueForKey:@"fileSize"] longLongValue];
NSMutableData *fullData = [[NSMutableData alloc] initWithCapacity:fileSize];
[[PHAssetResourceManager defaultManager] requestDataForAssetResource:hqResource options:options dataReceivedHandler:^(NSData * _Nonnull data) {
// append the data that we're receiving
[fullData appendData:data];
} completionHandler:^(NSError * _Nullable error) {
// handle completion, using `fullData` or `error`
// uti == hqResource.uniformTypeIdentifier
// orientation == UIImageOrientationUp
}];
}
else {
// use `requestImageDataForAsset:`, `requestImageForAsset:` or `requestDataForAssetResource:` with a different `PHAssetResource`
}
Вы можете попробовать это, чтобы получить фото ролл камеры:
__weak __typeof(self) weakSelf = self;
PHFetchResult<PHAssetCollection *> *albums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumSelfPortraits options:nil];
[albums enumerateObjectsUsingBlock:^(PHAssetCollection * _Nonnull album, NSUInteger idx, BOOL * _Nonnull stop) {
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.wantsIncrementalChangeDetails = YES;
options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d",PHAssetMediaTypeImage];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsInAssetCollection:album options:options];
if(assets.count>0)
{
[assets enumerateObjectsUsingBlock:^(PHAsset * _Nonnull asset, NSUInteger idx, BOOL * _Nonnull stop) {
if(asset!=nil)
{
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage *result, NSDictionary *info)
{
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf addlocalNotificationForFilters:result];
// [weakSelf.buttonGalery setImage:result forState:UIControlStateNormal];
});
}];
*stop = YES;
}
else{
[weakSelf getlatestAferSelfie];
}
}];
}