從 iOS 8 以后相冊的資源對象變成了 PHAsset,現在來簡單介紹一下在使用 PHAsset 的過程中遇到的坑.
原本用的方法是:
/**
通過資源獲取圖片的數據
@param mAsset 資源文件
@param imageBlock 圖片數據回傳
*/
- (void)fetchImageWithAsset:(PHAsset*)mAsset imageBlock:(void(^)(NSData*))imageBlock {
[[PHImageManager defaultManager] requestImageForAsset:mAsset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:nil resultHandler:^(UIImage *resultImage, NSDictionary *info) {
if ([[info valueForKey:@"PHImageResultIsDegradedKey"]integerValue] == 0){
// 這里獲取的 resultImage 的確是原圖
// 所以需要將 resultImage 轉成 NSData 類型的
// 方案一
NSData* imageData = UIImagePNGRepresentation(resultImage);
{ // 方案二
imageData = UIImageJPEGRepresentation(resultImage, 1.0);
}
// 通過 imageBlock 返回
if (imageBlock) {
imageBlock(imageData);
}
}
}];
}
看這代碼,沒毛病。但是,實際上有很大的毛病,并且不容易被發現。
上傳到服務器的圖片那時相當的大,我有一張1.3M的圖片,上傳之后變成了9.3M了。厲害了,我的鍋!?。。?!就是從 UIImage 轉 NSData 的那一步出了貓膩,不管是 方案一 還是 方案二。
遇到這樣的情況,實在讓人著急。一看 QQ 的圖片上傳,沒有毛病。看來是自己的問題了。。。。。
最終,找到了如下方法:
/**
通過資源獲取圖片的數據
@param mAsset 資源文件
@param imageBlock 圖片數據回傳
*/
- (void)fetchImageWithAsset:(PHAsset*)mAsset imageBlock:(void(^)(NSData*))imageBlock {
[[PHImageManager defaultManager] requestImageDataForAsset:mAsset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
// 直接得到最終的 NSData 數據
if (imageBlock) {
imageBlock(imageData);
}
}];
}
終于可以緩一口氣了,經測試也木有毛病,上傳到服務器的大小完全匹配。
什么情況?????? 又出問題了。。。。。
有的圖片被旋轉了,趕緊改!!??!厲害了,還是我的鍋。。。
圖片旋轉的原因,在網上有一些說法。但是我發現通過 PHPhotoLibrary 保存到相冊的方式,會發生旋轉的概率是百分之百。
經過一番的折騰,找到了這個iPhone上關于相機拍照的圖片的imageOrientation的問題,于是靈機一動,就寫成下面的方法,我也是盡力了。
/**
通過資源獲取圖片的數據
@param mAsset 資源文件
@param imageBlock 圖片數據回傳
*/
- (void)fetchImageWithAsset:(PHAsset*)mAsset imageBlock:(void(^)(NSData*))imageBlock {
[[PHImageManager defaultManager] requestImageDataForAsset:mAsset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
if (orientation != UIImageOrientationUp) {
UIImage* image = [UIImage imageWithData:imageData];
// 盡然彎了,那就板正一下
image = [image fixOrientation];
// 新的 數據信息 (不準確的)
imageData = UIImageJPEGRepresentation(image, 0.5);
}
// 直接得到最終的 NSData 數據
if (imageBlock) {
imageBlock(imageData);
}
}];
}
到這里,也就算是結束了。但是,關于 圖片被旋轉了 的這個問題,還是沒有完全被解決,因為通過 UIImageJPEGRepresentation 處理過的數據是未知的。個人感覺要徹底解決這個問題,應該是要手動的去篡改 imageOrientation 這個值了, 但是我不會。要是有大神知道更好的解決方案,期待你的分享。
補充 fixOrientation 方法 BY 2018-07-18
/** 解決旋轉90度問題 */
+ (UIImage *)fixOrientation:(UIImage *)aImage
{
// No-op if the orientation is already correct
if (aImage.imageOrientation == UIImageOrientationUp)
return aImage;
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;
switch (aImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}
switch (aImage.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage), 0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch (aImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;
default:
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
// And now we just create a new UIImage from the drawing context
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
這是一個 Class 方法, 可以放在分類中,換成 instance 方法。