iOS 从相册中获取 GIF 的一些小 Tip


从相册中获取 GIF 的一些小 Tip

在 iOS 8 的时候,Apple 推出了 PhotoKit 框架,提供了一系列丰富的接口,想了解 PhotoKit 的同学,可以看下 Apple 的示例:Example app using Photos framework

如何判断 GIF 资源

1. 获取 PHAsset 的资源对象 PHAssetResource,通过其 uniformTypeIdentifier 或 originalFilename 属性来判断是否为 GIF:

extension PHAsset {
  var isGIF: Bool {
    let resource = PHAssetResource.asssetResources(for: self).first!
  	
  	// 通过统一类型标识符(uniform type identifier) UTI 来判断
  	let uti = resource.uniformTypeIdentifier as CFString
	return UTTypeConformsTo(uti, kUTTypeGIF) 
	
	// 或者通过文件名后缀来判断
	return assetSource.originalFilename.hasSuffix("GIF")
  }
}

  

关于PHAssetResource,每个 PHAsset 对象都会引用一个或多个资源(resource),一个被修改过的图片的PHAsset 对象会包含图片编辑之前和之后的 resource,以及关于描述这次编辑的PHAdjustmentData对象的 resource。我们可以将一个修改过后的 GIF 的 PHAsset 所包含的 PHAssetResource 打印出来看下:

let resources = PHAssetResource.assetResources(for: asset)
resources.map { print($0) }
/* 输出:
修改之前:
<PHInternalAssetResource: 0x6000002e0f80> type=photo size={636, 400} fileSize=668682 uti=com.compuserve.gif filename=IMG_0006.GIF assetLocalIdentifier=46ACADB2-357B-44B6-8837-4F686D2092BF/L0/001
修改之后:
<PHInternalAssetResource: 0x6080000f2d80> type=photo size={636, 400} fileSize=668682 uti=public.jpeg filename=IMG_0006.GIF assetLocalIdentifier=46ACADB2-357B-44B6-8837-4F686D2092BF/L0/001
<PHInternalAssetResource: 0x6080000f3200> type=photo_full size={636, 400} fileSize=15481 uti=public.jpeg filename=FullSizeRender.jpg assetLocalIdentifier=46ACADB2-357B-44B6-8837-4F686D2092BF/L0/001
<PHInternalAssetResource: 0x6000000f2e80> type=adjustment size={0, 0} fileSize=776 uti=com.apple.property-list filename=Adjustments.plist assetLocalIdentifier=46ACADB2-357B-44B6-8837-4F686D2092BF/L0/001
*/

  

从打印出的信息可以看到,在 GIF 被修改之后,UTI 从com.compuserve.gif变成了 public.jpeg ,所以这里如果还通过 UTI 来判断的话,就会错漏,只能通过 fileName 来判断。

另外, PHAssetResource 类只支持 iOS 9.0+。

2. 通过获取 PHAsset 的元数据来判断:

let requestOption = PHImageRequestOptions()
requestOption.version = .unadjusted
requestOption.isSynchronous = false
PHImageManager.default().requestImageData(for: asset,
                                          options: requestOption,
                                          resultHandler: { (data, uti, orientation, info) in
	if let UTI = uti, UTTypeConformsTo(UTI as CFString, kUTTypeGIF) {
		// It's GIF
	}
})

  

同样,这里也需要考虑到 GIF 被修改的情况,requestOption.version 默认是 current ,即如果图片被修改过的话,返回的就是包含所有调整和修改的图像数据,因此我们需要将其设置为 unadjusted 来获取原始的图像数据。

相比上面的方法,这种方法更快速,用时更少。

关于 localIdentifier

localIdentifier 是 PHAsset 的父类 PHObject 的一个属性,是每个图片资源独有的标识符。

我在开发 notGIF 的时候,每次启动时都需要遍历相册中的所有图片来获取其中的 GIF,这个操作非常的耗时,十分影响用户体验。我尝试过将其拆分成多个任务,分发到多个线程同时进行,虽然有些效果,但还是不尽如人意,毕竟随着相册中照片数量的增加,其所消耗的时间是线性增长的。

这时候,localIdentifier 就有了用武之地。因为 localIdentifier 是识别图片资源的唯一标识符,所以,我们可以在第一次获取到相册中的 GIF 的时候,将其获取到的所有 GIF 的 localIdentifier 记录下来。这样,下次启动的时候,就可以通过这些 localIdentifier 来直接获取 GIF 资源:

 

let gifAssets = PHAsset.fetchAssets(withLocalIdentifiers: gifIDs, options: fetchOptions)


获取相册中图片的 url
如果 localIdentifier 所对应的图片资源被删除或不存在的话,PHAsset.fetchAssets(withLocalIdentifiers: options:) 会自动过滤掉。同时,我们可以在后台去检测相册是否有变化,如果有,则更新 UI 以及 所存储的 localIdentifier 的信息。

在适配 iMessage Extension 的时候,需要通过图片的 url 来获取和发送图片,PhotoKit 也提供了获取图片资源 url 的方法:

asset.requestContentEditingInput(with: requestOptions,
                                 completionHandler: { (editingInput, info) in
	if let input = editingInput, let picURL = input.fullSizeImageURL {
		// insert attachment
	}
})

 

 

 

 

#import "ViewController.h"

#import "TZImagePickerController.h"

#import "TZImageCropManager.h"

#import <Photos/Photos.h>

#import <CoreServices/CoreServices.h>

 

@interface ViewController ()

 

@property (weak, nonatomic) IBOutlet UIImageView *avatarImageView;

 

@end

 

@implementation ViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view.

}

 

- (IBAction)openBtnClicked:(id)sender {

    

    TZImagePickerController *vc = [[TZImagePickerController alloc] initWithMaxImagesCount:1 delegate:nil];

    

    vc.isSelectOriginalPhoto = YES;

    vc.allowTakePicture = YES;

    vc.allowTakeVideo = NO;

    vc.allowPickingMultipleVideo = YES;

    vc.allowPickingVideo = NO;

    vc.allowPickingImage = YES;

    vc.allowPickingOriginalPhoto = NO;

    vc.allowPickingGif = YES;

    vc.sortAscendingByModificationDate = NO;

    vc.showSelectedIndex = YES;

    vc.statusBarStyle = UIStatusBarStyleDefault;

    vc.naviBgColor = UIColor.whiteColor;

    vc.naviTitleColor = UIColor.blackColor;

    vc.naviTitleFont = [UIFont systemFontOfSize:15];

    vc.barItemTextColor = UIColor.blackColor;

    vc.barItemTextFont = [UIFont systemFontOfSize:15];

    vc.navLeftBarButtonSettingBlock = ^(UIButton *leftButton) {

        UIImage *image = [UIImage imageNamed:@"nav_back"];

        [leftButton setImage:image forState:UIControlStateNormal];

        leftButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 44 - image.size.width);

    };

    

    vc.showPhotoCannotSelectLayer = YES;

    vc.cannotSelectLayerColor = UIColor.whiteColor;

    vc.oKButtonTitleColorNormal = UIColor.blackColor;

    vc.oKButtonTitleColorDisabled = [UIColor colorWithWhite:0 alpha:0.3];

    vc.iconThemeColor = [UIColor orangeColor];

    vc.takePictureImage = [UIImage imageNamed:@"image_picker_camera"];

    vc.photoDefImage = [UIImage imageNamed:@"checkbox3_n"];

    vc.photoSelImage = [UIImage imageNamed:@"checkbox3_s2"];

    vc.photoPreviewPageUIConfigBlock = ^(UICollectionView *collectionView, UIView *naviBar, UIButton *backButton, UIButton *selectButton, UILabel *indexLabel, UIView *toolBar, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel) {

        UIImage *image = [UIImage imageNamed:@"nav_back_white"];

        [backButton setImage:image forState:UIControlStateNormal];

        backButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 44 - image.size.width);

        

        [doneButton setTitleColor: [UIColor whiteColor] forState:UIControlStateNormal];

    };

    

    vc.didFinishPickingPhotosHandle = ^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {

        

        PHAsset *asset = assets.firstObject;

        if ([self isGif:asset]) {

            [self getGIFImage:assets.firstObject];

        } else {

            self.avatarImageView.image = photos.firstObject;

        }

    };

    

  

    [self presentViewController:vc animated:YES completion:nil];

}

 

- (void)getGIFImage:(PHAsset *)asset {

    [[TZImageManager manager] getOriginalPhotoDataWithAsset:asset progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {

        

    } completion:^(NSData *data, NSDictionary *info, BOOL isDegraded) {

        if (!isDegraded) {

            self.avatarImageView.image = [UIImage sd_tz_animatedGIFWithData:data];

        }

    }];

}

 

- (BOOL)isGif:(PHAsset *)asset {

    PHAssetResource *resource = [[PHAssetResource assetResourcesForAsset:asset] firstObject];

    if (resource != nil) {

        return [resource.uniformTypeIdentifier isEqualToString: (__bridge NSString *)kUTTypeGIF];

    }

    return NO;

}

@end

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM