当前位置: 移动技术网 > IT编程>移动开发>IOS > iOS7、8、9相册适配

iOS7、8、9相册适配

2018年09月19日  | 移动技术网IT编程  | 我要评论

刘至翰老婆,黛芙薇尔唯一官网,猪猪电子书

前言

由于在ios8及以后苹果将原有的操作相册的alassetslibrary framework替换为photos framework,所以,如果在应用中使用到的相册需要支持ios8以下的版本的话,就需要了解photos framework以做不同的版本适配。

一、ios8以下

1. 几个重要的实体概念

alasset(ios8及以后使用phasset)
一个alasset实例对象代表一个资源实体,比如一张图片、一个视频。共有三种类型:

alassettypephoto // 图片
alassettypevideo // 视频
alassettypeunknown // 未知

通过这个实例,你可以获取到这个资源的创建时间、资源类型、缩略图、二进制数据等信息。

alassetsgroup(ios8及以后使用phassetcollection)
一个alassetsgroup实例对象代表一组资源的集合,也就是一个相册,可以是系统默认存在的相册(相机胶卷),也可以是开发者给用户创建的自定义相册(qq)。
通过它,你可以获取到这个相册的名称、封面缩略图等。

alassetslibrary(ios8及以后使用phphotolibrary)
一个alassetslibrary实例对象对所有的相册资源进行索引。
使用它,你可以获取指定类型的相册、单张图片等对象,也可以添加新的自定义相册或者图片到相薄。如果你想要知道有没有获取到系统相薄的访问权限,同样需要用到它。

2. 实体相关api

alassetslibrary

在ios7及以下系统中,如果你想要获取相册中的资源,或者对相册进行操作,那么你先要创建一个alassetslibrary实例对象。

获取指定类型的相册

- (void)enumerategroupswithtypes:(alassetsgrouptype)types usingblock:(alassetslibrarygroupsenumerationresultsblock)enumerationblock failureblock:(alassetslibraryaccessfailureblock)failureblock

注意
由于这里的遍历都是异步的操作,所以alassetslibrary的实例对象需要被一个静态变量或者成员变量引用来保证不被销毁,回调的block才能保证被成功调用。下面同此处。

根据一个相册url获取这个相册

- (void)groupforurl:(nsurl *)groupurl resultblock:(alassetslibrarygroupresultblock)resultblock failureblock:(alassetslibraryaccessfailureblock)failureblock

创建一个相册

- (void)addassetsgroupalbumwithname:(nsstring *)name resultblock:(alassetslibrarygroupresultblock)resultblock failureblock:(alassetslibraryaccessfailureblock)failureblock

相册授权状态

+ (alauthorizationstatus)authorizationstatus

alassetsgroup

ios7及以下系统的中每个相册都是一个alassetsgroup实例,你可以使用这个实例获取这个相册的相关信息。

获取相册信息

- (id)valueforproperty:(nsstring *)property  

参数property包含以下类型:
alassetsgrouppropertyname
alassetsgrouppropertytype
alassetsgrouppropertypersistentid
alassetsgrouppropertyurl

封面图

- (cgimageref)posterimage

相册包含实体(照片、视频)的数量

- (nsinteger)numberofassets

过滤规则

- (void)setassetsfilter:(alassetsfilter *)filter

注意
参数中的filter是一个alassetsfilter实例,这个实例只有三种类型:

+ (alassetsfilter *)allphotos; // 所有图片

+ (alassetsfilter *)allvideos; // 所有视频

+ (alassetsfilter *)allassets; // 所有视频及图片

在使用- (nsinteger)numberofassets获取相册实体数量时,会依赖该过滤规则。比如一个相薄中存在5个视频和5张图片,如果指定allphotos,则numberofassets返回值为5。同样使用- (void)enumerateassetsusingblock:(alassetsgroupenumerationresultsblock)enumerationblock获取相册中的所有资源时,也只能获取到指定过滤规则下的资源。

使用相应遍历规则获取相册中的所有alasset资源

- (void)enumerateassetswithoptions:(nsenumerationoptions)options usingblock:(alassetsgroupenumerationresultsblock)enumerationblock

3. 使用示例

获取所有相册,并过滤相册中的视频

+ (void)getassetsgroupsforios8belowsuccess:(void (^)(nsmutablearray *))success failure:(void (^)(nserror *error))failure {
nsmutablearray *assetsgroups = [nsmutablearray array];
[[self library] enumerategroupswithtypes:alassetsgroupall usingblock:^(alassetsgroup *group, bool *stop) {
    if (group != nil) {
        [group setassetsfilter:[alassetsfilter allphotos]];
        if (group.numberofassets > 0) {
            [assetsgroups addobject:group];
        }
    } else {
        if (success) {
            success(assetsgroups);
        }
    }
} failureblock:^(nserror *error) {
    if (failure) {
        failure(error);
    }
}];
}
+ (alassetslibrary *)library {
static alassetslibrary *library;
static dispatch_once_t oncetoken;
dispatch_once(&oncetoken, ^{
    library = [[alassetslibrary alloc] init];
});
return library;
}

获取一个相册中的所有资源

+ (void)gettimelinesectionmodelsforios8belowwithgroup:(mralbumgroupmodel *)group success:(void (^)(nsmutablearray *))success failure:(void (^)(nserror *))failure {
nsmutablearray *sectionmodels = [nsmutablearray array];
[group.assetgroup enumerateassetswithoptions:nsenumerationreverse usingblock:^(alasset *result, nsuinteger index, bool *stop) {
    if(result) {
        [sectionmodels addobject:result];
    }
}];
if (success != nil && sectionmodels.count > 0) {
    success(sectionmodels);
}
if (failure != nil && sectionmodels.count == 0) {
    failure(nil);
}
}

二、ios8及以上

1. 几个重要的实体概念

与alassetslibrary framework对应的几个实体

phasset
一个phasset实例对象与alasset类似,代表一个资源实体,比如一张图片、一个视频。与alasset不同之处在于,多了一种实体类型,共四种类型:

phassetmediatypeunknown = 0,
phassetmediatypeimage   = 1,
phassetmediatypevideo   = 2,
phassetmediatypeaudio   = 3,

同时,还多了更具体的子类型phassetmediasubtype:

phassetmediasubtypenone               = 0,

// photo subtypes
phassetmediasubtypephotopanorama      = (1ul << 0),
phassetmediasubtypephotohdr           = (1ul << 1),
phassetmediasubtypephotoscreenshot photos_available_ios_tvos(9_0, 10_0) = (1ul << 2),
phassetmediasubtypephotolive photos_available_ios_tvos(9_1, 10_0) = (1ul << 3),

// video subtypes
phassetmediasubtypevideostreamed      = (1ul << 16),
phassetmediasubtypevideohighframerate = (1ul << 17),
phassetmediasubtypevideotimelapse     = (1ul << 18),

通过这个实例,除了可以获取到这个资源的创建时间、资源类型、缩略图、二进制数据等信息,还可以获取到location等位置信息。

phcollection
phcollection是个基类,它有两个子类。分别是phassetcollectionphcollectionlistphassetcollection代表 photos 中的相册,phcollectionlist代表 photos 中的文件夹。phcollectionlist里可嵌套phassetcollection,也可以嵌套自身类型,同时支持多重嵌套。
一个phassetcollection实例对象与alassetsgroup类似,代表一组资源的集合,也就是一个相册。
通过它,你可以获取到这个相册的名称、封面缩略图。
以及相册类型phassetcollectiontype

phassetcollectiontypealbum      = 1, // 自定义相册,如qq
phassetcollectiontypesmartalbum = 2, // 相机胶卷、我的照片流、屏幕截图、全景照片等
phassetcollectiontypemoment     = 3, // 时刻

相册子类型phassetcollectionsubtype

// phassetcollectiontypealbum regular subtypes
phassetcollectionsubtypealbumregular         = 2,
phassetcollectionsubtypealbumsyncedevent     = 3,
phassetcollectionsubtypealbumsyncedfaces     = 4,
phassetcollectionsubtypealbumsyncedalbum     = 5,
phassetcollectionsubtypealbumimported        = 6,

// phassetcollectiontypealbum shared subtypes
phassetcollectionsubtypealbummyphotostream   = 100,
phassetcollectionsubtypealbumcloudshared     = 101,

// phassetcollectiontypesmartalbum subtypes
phassetcollectionsubtypesmartalbumgeneric    = 200,
phassetcollectionsubtypesmartalbumpanoramas  = 201,
phassetcollectionsubtypesmartalbumvideos     = 202,
phassetcollectionsubtypesmartalbumfavorites  = 203,
phassetcollectionsubtypesmartalbumtimelapses = 204,
phassetcollectionsubtypesmartalbumallhidden  = 205,
phassetcollectionsubtypesmartalbumrecentlyadded = 206,
phassetcollectionsubtypesmartalbumbursts     = 207,
phassetcollectionsubtypesmartalbumslomovideos = 208,
phassetcollectionsubtypesmartalbumuserlibrary = 209,
phassetcollectionsubtypesmartalbumselfportraits photos_available_ios_tvos(9_0, 10_0) = 210,
phassetcollectionsubtypesmartalbumscreenshots photos_available_ios_tvos(9_0, 10_0) = 211,

// used for fetching, if you don't care about the exact subtype
phassetcollectionsubtypeany = nsintegermax

phphotolibrary
phphotolibrary是个单例对象,与alassetslibrary相比发生了较大变化,只能申请获取photos权限以及授权状态获取等。
使用这个单例你也可以注册相册改动的监听者,在相册发生变化(比如添加或导入了新图片)时,做一些刷新或其他处理。

新的实体

在ios8及以上的相册资源获取中,都是使用fetch相关api的形式,过程类似core data。匹配资源的过程自然需要匹配规则,最终匹配结果也需要一个集合来记录。

phfetchoptions
phfetchoptions的实例对象代表获取资源时的匹配规则。
可以指定资源排序规则(nsarray *sortdescriptors;)、资源类型规则(nspredicate *predicate)、最大数量(nsuinteger fetchlimit)等。

phfetchresult
phfetchresult的实例对象代表相册资源的匹配结果集合。它包含零个或多个符合匹配规则的资源,这些资源可以是phassetcollection对象,也可以是phasset对象。

phimagemanager
phimagemanager是一个单例对象,不需要你手动创建,这个单例对象可以让你获取到一个phasset资源的实际二进制数据——如一张图片数据。

2. 实体相关api

phassetcollection

在ios8及以上系统中,获取相册不需要创建实例,直接使用phassetcollection的类方法进行操作即可。

获取指定类型的相册

// fetch asset collections of a single type and subtype provided (use phassetcollectionsubtypeany to match all subtypes)
+ (phfetchresult *)fetchassetcollectionswithtype:(phassetcollectiontype)type subtype:(phassetcollectionsubtype)subtype options:(nullable phfetchoptions *)options;

基本属性

@property (nonatomic, strong, readonly, nullable) nsstring *localizedtitle; // 相册标题
@property (nonatomic, assign, readonly) phassetcollectiontype assetcollectiontype; // 相册类型
@property (nonatomic, assign, readonly) phassetcollectionsubtype assetcollectionsubtype; // 子类型
@property (nonatomic, assign, readonly) nsuinteger estimatedassetcount; // 预估资源数量
@property (nonatomic, strong, readonly, nullable) nsdate *startdate; // 开始日期
@property (nonatomic, strong, readonly, nullable) nsdate *enddate; // 结束日期
@property (nonatomic, strong, readonly, nullable) cllocation *approximatelocation; // 位置信息
@property (nonatomic, strong, readonly) nsarray *localizedlocationnames; // 位置名称

注意
estimatedassetcount并不是一个准确的值,当phassetcollection对象不能马上计算出当前相册中的资源数量时,会返回nsnotfound,如果想要获取资源的具体数量,需要使用phasset来fetch所有资源,计算资源数量。

phasset

获取某相册中的phasset资源

+ (phfetchresult *)fetchassetsinassetcollection:(phassetcollection *)assetcollection options:(nullable phfetchoptions *)options;

获取某相册的封面资源

+ (nullable phfetchresult *)fetchkeyassetsinassetcollection:(phassetcollection *)assetcollection options:(nullable phfetchoptions *)options;

注意
这个api可以获取到系统默认的一些封面图资源,这个结果由零个或多个phasset组成,在相册是smartalbum类型的情况下,可能会有零个结果的情况。

获取中photos中的所有phasset资源

+ (phfetchresult *)fetchassetswithoptions:(nullable phfetchoptions *)options;

注意
这里并不是获取某个相册中的phasset资源,而是手机中所有的phasset资源。
比如手机中有10个相册,每个相册中10个phasset资源,如果不指定phfetchoptions,使用该方法会获取到所有的100个phasset资源。

获取photos中指定资源类型的所有phasset资源

+ (phfetchresult *)fetchassetswithmediatype:(phassetmediatype)mediatype options:(nullable phfetchoptions *)options;

参数中的mediatype可选:

phassetmediatypeunknown = 0,
phassetmediatypeimage   = 1,
phassetmediatypevideo   = 2,
phassetmediatypeaudio   = 3,

phimagemanager

获取phasset图片资源的图片数据

- (phimagerequestid)requestimageforasset:(phasset *)asset targetsize:(cgsize)targetsize contentmode:(phimagecontentmode)contentmode options:(nullable phimagerequestoptions *)options resulthandler:(void (^)(uiimage *__nullable result, nsdictionary *__nullable info))resulthandler;

注意
参数中的phimagerequestoptions对象可以设置目标图片的一些属性,其中synchronous表示是否同步获取,默认是no,也就是异步获取。

3. 使用示例

获取指定类型的相册

+ (nsmutablearray *)getcollecionswithsmartalbumsubtype:(phassetcollectionsubtype)subtype {
phfetchoptions *useralbumsoptions = [phfetchoptions new];
phfetchresult *useralbumsresult = [phassetcollection fetchassetcollectionswithtype:phassetcollectiontypealbum
                                                                           subtype:phassetcollectionsubtypeany
                                                                           options:useralbumsoptions];
phfetchresult *usersmartalbumsresult = [phassetcollection fetchassetcollectionswithtype:phassetcollectiontypesmartalbum
                                                                                subtype:subtype
                                                                                options:useralbumsoptions];
nsmutablearray *collections = [nsmutablearray array];
void (^albumenumerateobjectsusingblock)(phassetcollection *, nsuinteger idx, bool *) = ^(phassetcollection * _nonnull collection, nsuinteger idx, bool * _nonnull stop) {
    if (collection.estimatedassetcount == 0) {
        return ;
    }
    nsuinteger numberofassets = 0;
    phfetchresult *assetsresult = [phasset fetchassetsinassetcollection:collection options:nil];
    numberofassets = [assetsresult countofassetswithmediatype:phassetmediatypeimage];
    if (numberofassets == 0) {
        return;
    }
    [collections addobject:collection];
};
[usersmartalbumsresult enumerateobjectsusingblock:albumenumerateobjectsusingblock];
[useralbumsresult enumerateobjectsusingblock:albumenumerateobjectsusingblock];
return collections;
}

获取一个相册中的所有图片资源,按创建时间降序排序

+ (void)gettimelinesectionmodelsforios8abovewithgroup:(mralbumgroupmodel *)group success:(void (^)(nsmutablearray *))success failure:(void (^)(nserror *))failure {
phfetchresult *assetsresult = [phasset fetchassetsinassetcollection:group.collection options:[self fetchoptions]];
nsmutablearray *sectionmodels = [nsmutablearray array];
[assetsresult enumerateobjectsusingblock:^(phasset * _nonnull asset, nsuinteger idx, bool * _nonnull stop) {
    [sectionmodels addobject:asset];
}];
if (success != nil && sectionmodels.count > 0) {
    success(sectionmodels);
}
if (failure != nil && sectionmodels.count == 0) {
    failure(nil);
}
}
+ (phfetchoptions *)fetchoptions {
phfetchoptions *fetchoptions = [phfetchoptions new];
fetchoptions.predicate = [nspredicate predicatewithformat:@"mediatype = %@", @(phassetmediatypeimage)];
fetchoptions.sortdescriptors = @[[nssortdescriptor sortdescriptorwithkey:@"creationdate" ascending:no]];
return fetchoptions;
}

三、适配参考

我在不同系统的相册适配中采用适配器的方式进行的适配,仅供参考。

1. 适配器

相册适配主要是为了解决在ios8以上的系统中,使用allibrary相关api可能存在一些不兼容的问题,导致资源获取不一致情况的出现。

在业务展现上并不期望因系统不同而给用户差别较大的体验,所以可以只针对相册资源数据获取的数据测适配即可,而无需改动ui。

这种场景可以在数据层对alassetsgroupphassetcollection以及alassetphasset做抽象,抽取适配器即可。

2. 简单示例

assetgroupmodel

@interface albumgroupmodel : nsobject

@property (nonatomic, strong) uiimage *posterimage;
@property (nonatomic, assign) nsuinteger numberofassets;
@property (nonatomic, copy) nsstring *assetsgrouppropertyname;

@property (nonatomic, strong) phassetcollection *collection;
@property (nonatomic, strong) alassetsgroup *assetgroup;

@end
@implementation albumgroupmodel
- (uiimage *)posterimage {
if (_posterimage == nil) {
    if ([xslappinfotool systemversion] < 8.0) {
        _posterimage = [uiimage imagewithcgimage:self.assetgroup.posterimage];
    } else {
        phfetchresult *groupresult = [phasset fetchassetsinassetcollection:self.collection options:self.class.fetchoptions];
        phasset *asset = groupresult.firstobject;
        phimagerequestoptions *requestoptions = [[phimagerequestoptions alloc] init];
        requestoptions.resizemode = phimagerequestoptionsresizemodeexact;

        cgfloat scale = [uiscreen mainscreen].scale;
        cgfloat dimension = 80.0f;
        cgsize size = cgsizemake(dimension * scale, dimension * scale);
        __block uiimage *resultimage = nil;
        [[phimagemanager defaultmanager] requestimageforasset:asset targetsize:size contentmode:phimagecontentmodeaspectfill options:requestoptions resulthandler:^(uiimage *result, nsdictionary *info) {
            resultimage = result;
        }];
        _posterimage = resultimage;
    }
}
return _posterimage;
}
@end

assetmodel

@interface albumassetmodel : nsobject

@property (nonatomic, strong, nullable) phasset *assetph;
@property (nonatomic, strong, nullable) alasset *assetal;

- (void)thumbnail:(nullable void(^)(uiimage * _nullable thumbnail))resultcallback;

@end
@implementation albumassetmodel

- (void)thumbnail:(void (^)(uiimage *))resultcallback {
    if (self.assetph) {
        phimagemanager *manager = [phimagemanager defaultmanager];
        [manager requestimageforasset:self.assetph targetsize:cgsizemake(100, 100) contentmode:phimagecontentmodedefault options:nil resulthandler:^(uiimage * _nullable result, nsdictionary * _nullable info) {
            if (resultcallback) {
                resultcallback(result);
            }
        }];
    } else if (self.assetal) {
        if (resultcallback) {
            resultcallback([uiimage imagewithcgimage:[self.assetal thumbnail]]);
        }
    }
}

@end

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网