历代iOS版本 — iOS8

1、应用扩展(Extension)
这是一个千呼万唤始出来的特性,也是一个可以发挥无限想象力的特性。现在
Apple允许我们在app中添加一个新的target,用来提供一些扩 展功能:比如在系
统的通知中心中显示一个自己的widget,在某些应用的Action中加入自己的操
作,在分享按扭里加入自己的条目,更甚至于添加自定义的键盘等等。每一种操作
对应这一个应用扩展的入口,在开发中我们只需要在工程中新建立一个对应相应入
口的target,就能从一个很好的模板开始进行一些列开发,来实现这些传统意义,上
可能需要越狱才能实现的功能。
对于应用扩展,Apple 将其定义为App的功能的自然延伸,因此不是单独存在的,
而是随着应用本体的包作为附属而被一同下载和安装到用户的设备中的,用户需要
在之后选择将其开启。
2、App开发时的统一
随着一代代的iPhone和iPad的出现,ios设备的屏幕尺寸也开始出现分裂的趋
势。之前一套屏幕两个方向吃遍全世界的美好时光已然不再,现在至少已经有3.5
寸,4寸和10(7)寸三种分辨率/尺寸的机型需要进行适配,再考虑到每个尺寸的横
竖两种方向,以及日益呼声愈高的4.7寸和5.5寸的iPhone,可以相见现在的布局
方式已然不堪重负。虽然在iOs 6 Apple就推出了Auto Layout来辅助完成布局工
作,解决了原来的相对布局的一-些问题,但是在以绝对尺寸为度量的坐标系统中,
难免还是有所掣肘。在ios 8中,Apple 的工程师们可以说“极富想象力”地干脆把
限制和表征屏幕尺寸的长宽数字给去掉了,取而代之使用size classes的概念,将
长宽尺寸按照设备类型和方向归类为regular和compact两类。通过为不同的设备
定义尺寸分类,用来定义同类型的操作特性,这使得开发者更容易利用一套UI来适
配不同的屏幕。
iOs 8在UlKit中添加了一整套使用size classes来进行布局的API, 并且将原有的
比较复杂(或者说有些冗余)的API作废了。结合新的Interface Builder和Auto
Layout,可以说对于多尺寸屏幕的适配得到了前所未有的简化。
Auto Layout正如其名,只是一个根据约束来进行布局的方案,而在对应不同设备的
具体情况下的体验.上还有欠缺。一个最明显的问题是它不能根据设备类型来确定不
同的交互体验。很多时候你还是需要判断设备到底是iPhone还是iPad,以及现在
的设备方向究竟是竖直还是水平来做出判断。
不再根据设备屏幕的具体尺寸来进行区分,而是通过它们的感官表现,将其分为普
通(Regular)和紧密(Compact)两个种类(class)。开发者便可以无视具体的尺寸,
而是对这这两类和它们的组合进行适配。这样不论在设计时还是代码上,我们都可
以不再受限于具体的尺寸,而是变成遵循尺寸的视觉感官来进行适配。
简单来说,现在的iPad不论横屏还是竖屏,两个方向均是Regular的;而对于
iPhone,竖屏时竖直方向为Regular,水平方向是Compact,而在横屏时两个方向
都是Compact。要注意的是,这里和谈到的设备和方向,都仅仅只是为了给大家一
个直观的印象。相信随着设备的变化,这个分类也会发生变动和更新。Size Classes
的设计哲学就是尺寸无关,在实际中我们也应该尽量把具体的尺寸抛开脑后,而去
尽快习惯和适应新的体系。
为了表征Size Classes,Apple 在ios 8中引入了一个新的类,UITraitCollection。
这个类封装了像水平和竖直方向的SizeClass等信息。ios8的UlKit中大多数UI
的基础类(包括UIScreen,UIWindow, UIViewController 和UIView)都实现
了UITraitEnvironment这个接口,通过其中的traitCollection这个属性,我们可以
拿到对应的UITraitCollection对象,从而得知当前的Size Class,并进一步确定界
面的布局。
和UlKit中的响应者链正好相反,traitCollection 将会在view hierarchy中自,上而下
地进行传递。对于没有指定traitCollection的UI部件,将使用其父节点
的traitCollection。这在布局包含childViewController的界面的时候会相当有用。
在UITraitEnvironment这个接口中另一个非常有用的是-
traitCollectionDidChange:。在traitCollection发生变化时,这个方法将被调用。
在实际操作时,我们往往会在ViewController中重写-
traitCollectionDidChange:或者-
willTransitionToTraitCollection:withTransitionCoordinator:方法(对于
ViewController来说的话,后者也许是更好的选择,因为提供了转场上下文方便进
行动画;但是对于普通的View来说就只有前面一个方法了),然后在其中对当前
的traitCollection进行判断,并进行重新布局以及动画。代码看起来大概会是这个
样子:

  • (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection
    withTransitionCoordinator:(id
    <UIViewControllerTransitionCoordinator> )coordinator
    [super willTransitionToTraitCollection:newCollection
    withTransitionCoordinator:coordinator];
    [coordinator animateAlongsideTransition:^(id
    <UIViewControllerTransitionCoordinatorContext> context) {
    if (newCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) {
    //To Do: modify something for compact vertical size
    } else {
    //To Do: modify something for other vertical size
    [self.view setNeedsLayout];
    } completion:nil];
    }
    在两个To Do中,我们应该删除或者添加或者更改不同条件下的Auto Layout约束
    (当然,你也可以干其他任何你想做的事情),然后调用-setNeedsLayout来在.上下
    文中触发转移动画。如果你坚持用代码来处理的话,可能需要面临对于不同Size
    Classes来做移除旧的约束和添加新的约束这样的事情,可以说是很麻烦(至少我觉
    得是麻烦的要死)。但是如果我们使用IB的话,这些事情和代码都可以省掉,我们可
    以非常方便地在IB中指定各种Size Classes的约束(稍后会介绍如何使用IB来对
    应Size Classes)。另外使用IB不仅可以节约成百上千行的布局代码,更可以从新
    的Xcode和IB中得到很多设计时就可以实时监视,查看并且调试的特性。可以说
    手写UI和使用IB设计的时间消耗和成本差距被进一步拉大,并且出现了很多手写
    UI无法实现,但是IB可以不假思索地完成的任务。从这个意义,上来说,新的IB和
    Size Classes系统可以说无情地给手写代码判了个死缓。
    另外,新的API和体系的引入也同时给很多我们熟悉的UIViewController的有关旋
    转的老朋友判了死刑,比如下面这些API都弃用了:
    @property(nonatomic, readonly) UlInterfaceOrientation interfaceOrientation
  • willRotateToInterfaceOrientation:duration:
  • willAnimateRotationToInterfaceOrientation:duration:
  • didRotateFromInterfaceOrientation:
  • shouldAutomaticallyForwardRotationMethods
    现在全部统一到 了viewWillTransitionToSize:withTransitionCoordinator:, 旋转的
    概念不再被提倡使用。其实仔细想想,所谓旋转,不过就是一种Size的改变而已,
    我们都被Apple骗了好多年,不是么?
    在Interface Builder中使用Size Classes
    第一次接触Xcode 6和打开IB的时候你可能会惊呼,为什么我的画布变成正方形
    了。我在第一天Keynote结束后在Moscone Center的食堂里第一 次打开的时候,
    还满以为自己找到了iWatch方形显示屏的确凿证据。到后来才知道,这是新的
    Size Classes对应的编辑方式。
    既然我们不需要关心实际的具体尺寸,那么我们也就完全没有必要在IB中使用像
    3.5/4寸的iPhone或是10寸的iPad来分开对界面进行编辑。使用一一个通用的具有
    "代表"性质的尺寸在新体系中确实更不容易使人迷惑。
    在现在的IB界面的正下方,你可以看到一个wAny hAny的按钮(因为今年NDA的
    -个明确限制是不能发相关软件截图,虽然其实可能没什么太大问题,但是还是尊
    重license比较好),这代表现在的IB是对应任意高度和任意宽度的。点击后便可以
    选择需要为哪种Size Class进行编辑。默认情况在Any Any下的修改会对任意设备
    和任意方向生效,而如果先进行选择后再进行编辑,就表示编辑只对选中的设定生
    效。这样我们就很容易在同一个storyboard文件里对不同的设备进行适配:按照设
    备需要添加或者编辑某些约束,或者是在特定尺寸下隐藏某些view (使用Attribute
    Inspector里的Installed选框的加号添加)。这使得使用IB制作通用程序变简单
    .了,我们不再需要为iPhone和iPad准备两套storyboard了。
    可以发挥的想象空间实在太大,一套界面布局通吃所有设备的画面太美好,我都不
    敢想。
    Size Classes和Image Asset及UlAppearence
    Image Asset里也加入了对Size Classes的支持,也就是说,我们可以对不同的
    Size Class指定不同的图片了。在lmage Asset的编辑面板中选择某张图片,
    Inspector里现在多了一个Width和Height的组合,添加我们需要对应的Size
    Class,然后把合 适的图拖上去,这样在运行时SDK就将从中挑选对应的Size的图
    进行替换了。不仅如此,在IB中我们也可以选择对应的size来直接在编辑时查看
    变化(新的Xcode和IB添加了非常多编辑时的可视化特性,关于这方面我有计划单
    独写一篇可视化开发的文章进行说明)。
    这个特性-一个最有用的地方在于对于不同屏幕尺寸可能我们需要的图像尺寸也有所
    不同。比如我们希望在iPhone竖屏或者iPad时的按钮高一些, 而iPhone横屏时
    由于屏幕高度实在有限,我们希望得到一个扁-些的按钮。对于纯色按钮我们可以
    通过简单的约束和拉伸来实现,但是对于有图案的按钮,我们之前可能就需要在VC
    里写一些脏代码来处理了。现在,只需要指定好特定的Image Asset,然后配置合
    适的(比如不含有尺寸限制)约束,我们就可以一行代码不写,就完成这样复杂的各
    个机型和方向的适配了。
    实际做起来实在是太简单了..但拿个demo说明一下吧,比如下面这个实现了竖直方
    向Compact的时候将笑脸换成哭脸--当然了,- -行代码都不需要。
    另外,在iOs 7中Ullmage添加了一个renderingMode属性。我们可以使用
    imageWithRenderingMode:并传入一个合适的UllmageRenderingMode来指定
    这个image要不要以Template的方式进行渲染。在新的Xcode中,我们可以直接
    在Image Asset里的Render As选项来指定是不是需要作为template使用。而相
    应的,在UlApperance中,Apple 也为我们对于Size Classes添加了相应的方
    法。使用+appearanceForTraitCollection:方法,我们就可以针对不同trait下的应
    用的apperance进行很简单的设定。比如在.上面的例子中,我们想让笑脸是绿色,
    而哭脸是红色的话,不要太简单。首先在Image Asset里的渲染选项设置
    为Template Image,然后直接在AppDelegate里加上这样两行:
    UIView.appearanceForTraitCollection(UlTraitCollection(verticalSizeClass..Comp
    act)).tintColor = UIColor.redColor()
    UIView.appearanceForTraitCollection(UlTraitCollection(verticalSizeClass..Regul
    arl).tintColor = UIColor.greenColor()
    UIViewController的表现方式
    UISplitViewController
    在用Regular和Compact统一了IB 界面设计之后,Apple 的工程师可能发现了一
    个让人两难的历史问题,这就是UISplitViewController。 一直做iPhone而没太涉
    及iPad的童鞋可能对着这个类不是很熟悉,因为它们是iPadOnly的。iPad推出时
    为了适应突然变大的屏幕,并且远离"放大版iTouch"的诟病,Apple 为iPad专门
    设计了这个主从关系的ViewControlle容器。事实也证明了这个设计在iPad.上确实
    是被广泛使用,是非常成功的。
    现在的问题是,如果我们只有一套UI画布的话,我们要怎么在这个单-一的画布上处
    理和表现这个iPad Only的类呢?
    答案是,让它在iPhone.上也能用就行了。没错,现在你可以直接在iPhone.上使用
    SplitViewController了。在Regular的宽度时,它保持原来的特性,在
    DetailViewController中显示内容,这是毫无疑问的。而在Compact中,我们第-
    想法就是以push的表现形式展示。在以前,我们可能需要写不少代码来处理这些
    事情,比如在AppDelegate中就在一开始判断设备是不是iPad,然后为应用设定两
    套完全不同的导航:一套 基于UINavigationController,另一套基
    于UISplitViewController。而现在我们只需要一套 UISplitViewController,并将它
    的MasterViewController设定为一个navgationController就可以轻松搞定所有情
    况了。
    也许你会想,即使这样,我是不是还是需要判断设备是不是iPad,或者现在的话是
    判断Size Class是不是Compact,来决定是要做的到底是navVC的push还是改
    变splitVC的viewControllers。其实不用,我们现在可以无痛地不加判断,直接用
    统一的方式来完成两种表现方式。这其中的奥妙在于我们不需要使用(事实上ios 8
    后Apple也不再提倡使
    用) UINavigationController的pushViewController:animated: 方法了(又一个老朋
    友要和我们说再见了)。其实虽然很常用,但是这个方法是一直受到社区的议论的:
    因为正是这个方法的存在使得ViewController的耦合特性上了一个档次。在某个
    ViewController中这个方法的存在时,就意味着我们需要确保当前的
    ViewController必须处于一个导航栈的上下文中,这是完全和上下文耦合的一种方
    式(虽然我们也可以很蛋疼地用判断navController是不是nil 来绕开,但是毕竟真
    的很丑,不是么)。
    我们现在有了新的展示viewController的方法,- showViewController:sender:以
    及-showDetailViewController:sender:。调用这两个方法时,将顺着包括调用vCc
    自身的响应链而上,寻找最近的实现了这个方法的ViewController来执行相应代
    码。在ios SDK的默认实现中,在UISplitViewController这样的容器类中,已经
    有这两个方法的实现方式,而UINavigationController 也实现了-
    showViewController:sender:的版本。对于在navController栈中的VC,会调用
    push方式进行展示,而对splitVC,showViewController:sender: 将在
    MasterViewController中进行push。而showDetailViewController:sender:将根
    据水平方向的Size的情况进行选择:对于Regular的情况,将在
    DetailViewController中显示新的vc,而对于Compact的情况,将由所在上下文情
    况发回给下级的navController或者是直接以modal的方式展现。关于这部分的具
    体内容,可以仔细看看这个示例项目和相关的文档(beta版)。
    这么设计的好处是显而易见的,首先是解除了原来的耦合,使得我们的
    ViewController可以不被局限于导航控制器上下文中;另外,这几个方法都是公开
    的,也就是说我们的ViewController可以实现这两个方法,截断响应链的响应,并
    实现我们自己的呈现方式。这在自定义Container Controller的时候会非常有用。
    UIPresentationController
    iOs 7中加入了一套实现非常漂亮的自定义转场动画的方法(如果你还不知道或者不
    记得了,可以看看我去年的这篇笔记)。Apple 在解耦和重用上的努力确实令人惊
    叹。而今年,顺着自适应和平台开发统一的东风,在呈现ViewController的方式,上
    Apple也做出了从ios SDK诞生以来最大的改变。iOs 8中新加入了一个非常重要
    的类UIPresentationController,这个NSObject的子类将用来管理所有的
    ViewController的呈现。在实现方式上,这个类和去年的自定义转场的几个类一
    样,是完全解耦合的。而Apple也在自己的各种viewController呈现上完全统一地
    使用了这个类。
    再见UIPopoverController
    和SplitViewController类似,UlPopoverController 原来也只是iPad使用的,现在
    iPhone.上也将适用。准确地说,现在我们不再使用UlPopoverController这个类
    (虽然现在文档还没有将其标为deprecated,但是估计也是迟早的事儿了),而是改
    用一-个新的类UIPopoverPresentationController。这
    是UlPresentationController的子类,专门门用来负责呈现以popover的形式呈现内
    容,是ios 8中用来替代原有的UlPopoverController的类。
    比起原来的类,新的方式有什么优点呢?最大的优势是自适应,这
    和UISplitViewController在ios 8下的表现是类似的。在Compact的宽度条件
    下,UlPopoverPresentationController的呈现将会直接变成modal出来。这样我
    们基本就不再需要去判断iPhone还是iPad(其实相关的判定方法也已经被标记成弃
    用了),就可以对应不同的设备了。以前我们可能要写类似这样的代码:
    if UIDevice.currentDevice().userlnterfaceldiom== .Pad {
    let popOverController = UIPopoverController( contentViewController: nextVC)
    popOverController.presentPopoverFromRect(aRect, inView: self.view,
    permittedArrowDirections: .Any, animated: true)
    } else {
    presentViewController(nextVC, animated: true, completion: ni)
    而现在需要做的是:
    nextVC.modalPresentationStyle = .Popover
    let popover = nextVC.popoverPresentationController
    popover. sourceRect = aRect
    popover.permittedArrowDirections = .Any
    presentViewController(nextVC, animated: true, completion: nil)
    没有可恶的条件判断,一切配置井井有条,可读性也非常好。
    除了自适应之外,新方式的另一个优点是非常容易自定义。我们可以通过继
    承UlPopoverPresentationController来实现我们自己想要的呈现方式。其实更准确
    地说,我们应该继承的是UlPresentationController,主要通过实现-
    presentationTransitionWillBegin和-presentationTransitionDidEnd:来自定义我们
    的展示。像以前我们想要实现只占半个屏幕,后面原来的view还可见的modal,或
    者是将从下到上的动画改为百叶窗或者渐隐渐现,那都是可费劲儿的事情。而
    在UIPresentationController的帮助下,-切变得十分自然和简单。在自己
    的UIPresentationController子类中:
    override func presentationTransitionWillBegin() {
    let transitionCoordinator =
    self.presentingViewController.transitionCoordinator()
    transitionCoordinator. animateAlongsideTransition({context in
    //Do animation here
    }, completion: nil)
    override
    func presentationTransitionDidEnd(completed: Bool) {
    //Do clean here
    具体的用法和iOs7里的自定义转场很类似,设定需要进行呈现操作的
    ViewController的transition delegate,
    在UIViewControllerTransitioningDelegate的-
    presentationControllerForPresentedViewController:sourceViewController:方法
    中使用-initWithPresentedViewController:presentingViewController:生成对应
    的UIPresentationController子类对象返回给SDK,然后就可以喝茶看戏了。
    不仅如此,像是原来iPad专有的SplitController等也被以适应不同regular和
    compact的尺寸类型的形式port到了iPhone. 上,在程序设计方面两者更加统一
    了。另外,一直陪伴我们的UIAlertView和UIActionSheet这些老面孔也将退出舞
    台,取而代之全部统一以UIViewController来呈现。
    这是一个好的开始,也是一个好的变化。可以看到Apple在避免平台碎片化.上正在
    努力。

ios8 CLLocationManager的变化
之前获取用户位置信息的代码:

  • (void)restartLocationStandardUpdatesCompleted:
    (GGBlockLocation)block
    self.locationBlock = block;
    CLLocationManager *locationManager = [AppDelegate locationManager];
    [locationManager stopUpdatingL ocation];
    [locationManager startUpdatingLocation];
  • (CLLocationManager *)locationManager [
    static CLLocationManager * sharedManager = nil;
    static dispatch_ once t onceToken;
    dispatch_ _once( &onceToken, 'f
    sharedManager = [[CLLocationManager alloc] init];
    sharedManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    sharedManager.distanceFilter = 100;
    sharedManager.delegate = [AppDelegate appDelegate];
    ]);
    return sharedManager;
  • (void)locationManager:(CLLocationManager *)manager didUpdateLocations:
    (NSArray *)locations {
    }
    在ios8以前,调用这些代码,是可以获取到用户地理位置的;但ios8的时候,这些
    代码不会起任何作用,既不会调用didFail函数,也不会给出任何警告信息。
    在ios8,需要两步,来获取定位:

1,在info.plist中 添加key:
NSLocationWhenlnUseUsageDescription : 当app在前台的时候,才可
以获取到定位信息
NSLocationAlwaysUsageDescription :app在前台、 后台、挂起、结束进程
状态时,都可以获取到定位信息
这两个key对应的值都是NSString,简要填写为何需要定位,比如Location is
required to find out how many steps you run. (某些sport类型app)
关于如何对这两个key多语言: (以中英文为例)
xcode->File->New-> File,选择String File,命名为InfoPlist.strings,然后对此文件
国际化: .


image.png

第一句加入到中文Infoplist.strings,第=句加入到英文Infoplist.strings
"NSLocationWhenlnUseUsageDescription" = "需要知道您跑了多少步";
"NSL ocationWhenlnUseUsageDescription" = "Location is required to find out
how many steps you run";
2,向用户申请获取定位请求:
[locationManager requestWhenlnUseAuthorization];//1EX5 /info.plistq ÉJ
NSLocationWhenlnUseUsageDescription
[locationManager requestAlwaysAuthorization];//1EXf /info.plistHéj
NSLocationAlwaysUsageDescription
综上,完整的代码如下:

  • (void)restartLocationStandardUpdatesCompleted:
    (GGBlockL ocation)block
    self.locationBlock = block;
    CLLocationManager *locationManager = [AppDelegate locationManager];
    //以此来判断,是否是ios8
    if ([locationManager
    respondsToSelector:@selector(requestWhenlnUseAuthorization)]) f
    [locationManager requestWhenlnUseAuthorization];
    [locationManager stopUpdatingLocation];
    [locationManager startUpdatingLocation];
    }

二. ios8下所有授权状态说明:
kCL AuthorizationStatusNotDetermined:用户还没有被请求获取授权
kCLAuthorizationStatusRestricted:用户在设置里关闭了位置服务
kCL AuthorizationStatusDenied:用户收到获取授权的请求,但点击了NO,或者在
设置里关闭了
kCL AuthorizationStatusAuthorized:用户收到获取授权的请求,点击了YES; (此
状态在ios8废弃了,ios7以及 以下可用)
kCL AuthorizationStatusAuthorizedAlways = kCL AuthorizationStatusAuthorized
用户授权app在任何时候获取位置信息
kCLAuthorizationStatusAuthorizedWhenInUse:用户授权app在前台获取位置信

举例:当app现在的权限是kCLAuthorizationStatusAuthorizedWhenInUse,但过
会儿又需要kCLAuthorizationStatusAuthorizedAlways权限时,app内部需要提示
用户到设置中改变,在ios8,可以调用设置的URL
UlApplicationOpenSettingsURL String代码如
1 - (void)requestAlwaysAuthorization
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
// If the status is denied or only granted for when in use, display an alert
if (status == kCLAuthorizationStatusAuthorizedWhenlnUse 11 status ==
kCL AuthorizationStatusDenied) [
NSString *title;
title = (status == kCL AuthorizationStatusDenied) ? @"Location services
are off" : @"Background location is not enabled";
NSString *message = @"To use background location you must turn on
'Always' in the Location Services Settings";
UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"Settings", nil];
[alertView show];
1/ The user has not enabled any location services. Request background
authorization.
else if (status == kCLAuthorizationStatusNotDetermined) f
[self.locationManager requestAlwaysAuthorization];

  • (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:
    (NSInteger)buttonIndex
    if (buttonIndex == 1) {
    // Send the user to the Settings for this app
    NSURL *settingsURL = [NSURL
    URLWithString:UlApplicationOpenSettingsURL String];
    [[UlApplication sharedApplication] openURL:settingsURL];
    }

Health Kit和Home Kit
这是对应两个现在很热的领域--可穿戴式设备和智能家电--所加入的框架。基本
.上来说Apple想做的事情就是以ios为基础,为其他app建立一个平台以及成为用
户数据的管理者。
Health Kit就是- -个用户体征参数的数据库,第三方应用可以向用户申请权限使用其
中的数据或是向其中汇报数据。而Home Kit则以家庭,房间和设备的组织形式来管
理和控制家中适配了Home Kit的智能家电。这两个超级年轻的框架的API相对都
还比较简单,结构也很好,相信稍有经验的ios开发者都能在很快掌握用法。唯一
的限制在于作为普通开发者(比如我这样的只能自己业余玩的)可能手边现在不会
有合适的设备来进行测试,所以很多东西其实没有办法验证。不过对于Home Kit,
Apple给我们提供了一个模拟器来模拟智能家电设备,您可以在Xcode 6的Open
Developer Tool菜单中找到Home Kit Accessory Simulator。使用模拟器可以发
现,添加并且控制自定义的智能家电,用来前期开发还是蛮方便的。
HealthKit框架提供了一个结构,应用可以使用它来分享健康和健身数据。HealthKit
管理从不同来源获得的数据,并根据用户的偏好设置,自动将不同来源的所有数据
合并起来。应用还可以获取每个来源的原始数据,然后执行自己的数据合并。
HealthKit也可以直接与健康和健身设备一起工作。在iOS8.0中,系统可以自动将兼
容的低功耗蓝牙心率仪的数据直接保存在HealthKit存储中。如果有M7运动协处理
器,系统还可以自动导入计步数据。其他的设备和数据源必须要有配套的应用才可
以获取数据并保存在HealthKit中。
HealthKit另外提供了一个应用来帮助管理用户的健康数据。健康应用为用户展示
HealthKit的数据。用户可以使用健康应用来查看、添加、删除或者管理其全部的健
康和健身数据。用户还可以编辑每种数据类型的分享权限。
HealthKit和健康应用在iPad上都不可用。HealthKit框架不能用于应用扩展。
HealthKit和隐私
由于健康数据可能是敏感的,HealthKit通过精确控制哪些信息允许应用读取,从而
让用户可以控制这些数据。用户必须明确设置每个应用在HealthKit存储中读写的权
限。用户可以单独为每种数据类型设置准许或拒绝的权限。例如,用户可以允许你
的应用读取计步数据,但是不允许读取血糖水平。为了防止可能的信息泄露,应用
是不知道它是否被禁止读取数据的。从应用的角度来看,如果被禁止读取数据,就
是没有那种类型的数据存在。
HealthKit的数据不会保存在iCloud中,也不会在多设备间同步。这些数据只会保存
在用户的本地设备中。为了安全考虑,当设备没有解锁时,HealthKit存 储的数据是
加密的。
另外,你的应用如果主要不是提供健康或健身服务的话,那就不能调用HealthKit的
API。如果你的应用提供健康和健身服务,就必须要在销售文本和用户界面.上明确的
表明。特别是下面几条指导适用于所有HealthKit应用。
1.你的应用不应该将HealthKit收集的数据用于广告或类似的服务。注意,可能在使
用HealthKit框架应用中还是要服务广告,但是你不能使用HealthKit中的数据来服务
广告。
2.在没有用户的明确允许下,你不能向第三方展示任何HealthKit收集的数据。即使
用户允许,你也只能向提供健康或健身服务的第三方展示这些数据。
3.你不能将HealthKit收集的数据出售给广告平台、数据代理人或者信息经销商。
4.如果用户允许,你可以将HealthKit数据共享给第三方用于医学研究。
5.你必须明确说明,你和你的应用会怎样使用用户的HealthKit数据。
你必须为每个使用HealthKit框架的应用提供- - -份隐私策略。你可以在以下网站找到
创建隐私策略的指导:

  1. Personal Health Record model (for non-HIPAA apps): http://www.healthit.gov/
    policy-researchers-implementers/personal-health-record-phr-model-privacy-
    notice
  2. HIPAA model (for HIPAA covered apps): http://www.hhs.gov/ocr/privacy/
    hipaa/modelnotices.html
    这些模板由ONC开发,通过直白的语言和平易近人的设计来解释用户数据是如何被
    收集和共享的,从而提高用户的体验和理解。这些不是用来替代基本的互联网隐私
    策略,而且开发者应该咨询ONC来了解你的应用适用哪种模板。这些模板仅供参
    考,Apple不承担你使用这些模板带来的后果。
    注意:
    这一节提供了一些指导来帮助你创建一个HealthKit应用, 并尊重用户的隐私。了解
    Apple对使用HealthKit和用户健康相关数据的要求是很有必要的。参看App Store
    Review Guidelines中的HealthKit章节来了解更多,可以在Apple的App Review
    Support页面中找到。
    关于使用敏感用户数据的更多技术信息,参见App Programming Guide for ios中
    的Best Practices for Maintaining User Privacy。
    从接入HealthKit中获益
    健康和健身应用可以从接入HealthKit中获益颇多。下面列出了一些主要的好处。
    分离数据收集、数据处理和社交化。现代的健康和健身体验设计许多不同的方面,
    例如收集和分析数据,为用户提供可操作的信息和有用的可视化信息,以及允许用
    户参与到社区讨论中。现在由HealthKit负责实现这些方面,而你可以专注于实现你.
    最感兴趣的方面,把其他的任务交给更专业的应用。
    这些责任的分离也可以让用户受益。每个用户都可以随意选择最喜爱的体重追踪应
    用、计步应用和健康挑战应用。这意味着用户可以选择一套应用,每个应用都能很
    好的满足用户的某个需求。但是由于这些应用可以自由地交换数据,所以这一整套
    应用比单个应用能提供更好的体验。
    例如,一些朋友决定参加一个日常的计步挑战。每个人都可以使用他偏爱的硬件设
    备或应用来追踪计步数据,但是他们都还可以使用相同的社交应用来挑战。
    减少应用间分享的障碍。HealthKit使应 用间共享数据变得更容易。对于开发者来
    说,你不在需要下载API并编写代码来和其他应用共享。当有新的HealthKit应用时,
    它们就通过HealthKit自动开始共享数据了。
    由于不需要手动设置应用关联或者导入导出他们的数据,这对用户来说很好。用户
    仍然需要设置哪些应用可以读写HealthKit中的数据,还有每个应用可以读取到哪些
    数据。一旦用户允许访问,应用就可以自由无阻地读取数据了。
    提供更丰富的数据和更有意义的内容。应用可以读取到范围更广的数据,从而可以
    得到一个完整的关于用户健康和健身需求。在许多情况下,应用可以基于HealthKit
    中的额外信息修改它的计量单位或者提示。例如,训练应用不仅可以根据用户已经
    消耗的热量,还可以参考他今天已经吃的食物种类和数量,来给出一个训练后吃什
    么的建议。
    让应用参与到一个更大的生态系统中。应用通过共享它使用HealthKit收集的数据来
    获益。成为这个大生态系统的一部分能帮助提高你的应用的曝光度和实用性。更重
    要的是,接入HealthKit能让你的应用和用户已经拥有喜爱的应用一起工作。如果你
    的应用不能和其他已经在使用的应用共享数据,那么用户很可能就去寻找别的应用
    了。
    HealthKit的设计思路
    HealthKit是用来在应用间以-种有意义的方式共享数据。为了达到这点,框架限制
    只能使用预先定义好的数据类型和单位。这些限制保证了其他应用能理解这些数据
    是什么意思已经怎样使用。因此,开发者不能创建自定义数据类型和单位。而
    HealthKit尽量会提供一个应该完整的数据类型和单位。
    框架还大量使用了子类化,在相似的类间创建层级关系。通常这些类间都有- -些细
    微但是重要的差别。还有不少很相关的类,需要正确地区别开才能一起工作。例如
    HKObject和HKObjectType抽象类有很多平行层级的子类。当使用object和object
    type时,必须确保使用匹配的子类。
    HealthKit中所有的对象都是HKObject的子类。大部分HKObject对象子类都是不可
    变的。每个对象都有下列属性:
  3. UUID。 每个对象的唯一标示符。
  4. Source。 数据的来源。来源可以是直接把数据存进HealthKit的设备,或者是应
    用。当一个对象保存进HealthKit中时,HealthKit会 自动设置其来源。只有从
    HealthKit中获取的数据source属性才可用。
  5. Metadata。 -一个包含关于该对象额外信息的字典。元数据包含预定义和自定义的
    键。预定义的键用来帮助在应用间共享数据。自定义的键用来扩展HealthKit对象类
    型,为对象添加针对应用的数据。
    HealthKit对象主要分为2类:特征和样本。特征对象代表一些基本不变的数据。包
    括用户的生日、血型和生理性别。你的应用不能保存特征数据。用户必须通过健康
    应用来输入或者修改这些数据。
    样本对象代表某个特定时间的数据。所有的样本对象都是HKSample的子类。它们都
    有下列属性:
  6. Type。样本类型。例如,这可能包括一个睡眠分析样本、一个身高样本或者- -个
    计步样本。
  7. Start date。样本的开始时间。
  8. End date。样本的结束时间。如果样本代表时间中的某-刻,结束时间和开始时
    间相同。如果样本代表-段时间内收集的数据,结束时间应该晚于开始时间。
    样本可以细分为四个样本类型。
    1.类别样本。这种样本代表-些可以被分为有限种类的数据。在iOS8.0中,只有一
    种类别样本,睡眠分析。更多信息,参见HKCategorySample Class Reference。
    2.数量样本。这种样本代表一些可以存储为数值的数据。数量样本是HealthKit中最
    常见的数据类型。这些包括用户的身高和体重,还有一些其他数据,例如行走的步
    数,用户的体温和脉搏率。更多信息,参见HKQuantitySample Class Reference。
  9. Correlation。 这种样本代表复合数据,包含一个或多个样本。在iOS8.0中,
    HealthKit使用correlation来代表食物和血压。在创建书屋或者血压数据时,你应该
    使用correlation。更多信息,参见HKCorrelation Class Reference。
  10. Workout。Workout代表某 些物理活动,像跑步、游泳,甚至游戏。Workout通
    常有类型、时长、距离、和消耗能量这些属性。你还可以为一个workout关联许多详
    细的样本。不像correlation, 这些样本是不包含在workout里的。但是,它们可以通
    过workout获取到。更多信息,参见HKWorkout Class Reference。
    设置HealthKit
    在你开始使用HealthKit之前,必须要执行下列步骤:
    1.在Xcode中打开HealthKit功能。
    2.调用isHealthDataAvailable方法来查看HealthKit在该设备上是否可用。
    HealthKit在iPad.上不可用。
    3.为你的应用实例化一个HKHealthStore对象。每个应用只需要一个HealthKit存储
    实例。这个存储实例就是你和HealthKit数据库交互的主要接口。
    4.使用requestAuthorizationToShareTypes:readTypes:completion:方法来请求获
    取HealthKit数据的权限。对每种类型的数据,你都必须请求许可来共享和读取。
    如果用户允许分享某种类型的数据,那么你可以创建这种类型的新样本,并保存在
    HealthKit中。你应该使用authorizationStatusForType:来检查是否允许分享这种类
    型的数据。
    如果用户允许读取某种类型的数据,那么你就可以从HealthKit中读取这些数据。不
    幸的是,即使知道用户拒绝读取某种类型的数据,也可能会显示出潜在的健康问
    题。因此,你的应用无法确定用户是否允许读取数据。如果你没有得到读取某种数
    据的许可,那简单来看就好像是HealthKit中没有这种类型的数据。
    关于设置HealthKit的更多内容,参见HKHealthStore Class Reference。
    有一个展示了如何设置和使用HealthKit的例子,参见Fit: Store and Retrieve
    HealthKit Data。
    向HealthKit存储中添加样本
    你的应用可以创建新的样本,并添加到HealthKit存储中。对所有样本类型来说,大
    致的过程都是类似的,但是又有一些不同。
    1.在HealthKit Constants Reference中找到正确的类型标识符。
    2.使用类型标识符创建一个匹配的 HKObjectType子类。有一些便捷的方法,参
    见HKObjectType Class Reference。
    3.使用对象类型,创建一个匹配的HKSample子类。
    4.使用saveObject:withCompletion:方法将对象保存到HealthKit存储中。
    每个HKSample子类都有自己的便捷方法来实例化样本对象。流程如下图所示。


    image.png

    对于数量样本,你必须创建一个 HKQuantity类的实例。数量的单位必须和类型标
    识符文档中描述的可用单位相关。例如,HKQuantityTypeldentifierHeight 文档中
    说明它使用长度单位,因此,你的数量必须使用厘米、米、英尺、英寸或者其他长
    度单位。更多信息,参见HKQuantitySample Class Reference。


    image.png

    对于类别样本,它的值必须和类型标识符文档中描述的枚举值相关。例
    如,HKCategoryTypeldentifierSleepAnalysis 文档中说明它使
    用HKCategoryValueSleepAnalysis枚举值。因此你在创建样本时必须从这个枚举
    中传递一个值。更多信息,参见HKCategorySample Class Reference。
    image.png

    Xf Fcorrelation, 1TU5 êJicorrelationg ÓJF# #4 o correlationJ# #!JT
    识符描述了它可以包含的类型和对象的数量。不要把被包含的对象存进HealthKit。
    它们是以correlation的一部分存储的。更多信息,参见HKCorrelation Class
    Reference.
    image.png

    workout和其他类型的样本有些不同。首先,创建HKWorkoutType实例并不需要指
    定类型标识符。所有的workout都是用同样的类型标识符。第二,对于每个workout
    你都需要提供一个HKWorkoutActivityType值。这个值定义了workout中执行的活
    动的类型。最后,当workout保存 到HealthKit后,你可以给workout关联额外的样
    本。这些样本提供了workout的详细信息。更多信息,参见HKWorkout Class
    Reference。
    读取HealthKit数据
    HealthKit提供了许多方法来读取数据。
  11. 直接方法调用。HealthKit提供了直接读取特征数据的方法。这些方法只能用于读
    取特征数据。更多信息,参见HKHealthStore Class Reference。
    2.样本查询。这是使用最多的查询。使用样本查询来读取任何类型的样本数据。当
    你想要对结果进行排序或者限制返回的样本总数时,样本查询就特别有用。更多信
    息,参见HKSampleQuery Class Reference。
    3.观察者查询。这是一个长时间运行的查询,它会检测HealthKit存储,并在匹配到
    的样本发生变化时通知你。如果当存储发生变化时你想得到通知,就使用观察者查
    询。你可以让这些查询在后台执行。更多信息,参见HKObserverQuery Class
    Reference。
    4.锚定对象查询。用这种查询来搜索添加进存储的项。当锚定查询第一次执行时 ,
    会返回存储中所有匹配的样本。在接下来的执行中,只会返回上一-次执行之后添加
    的项目。通常,锚定对象查询会和观察者查询一起使用。观察者查询告诉你某些项
    目发生了变化,而锚定对象查询来决定有哪些(如果有的话)项目被添加进了存
    储。更多信息,参见HKAnchoredObjectQuery Class Reference。
    5.统计查询。使用这种查询来在一系列匹配的样本中执行统计运算。你可以使用统
    计查询来计算样本的总和、最小值、最大值或平均值。更多信息,参见
    HKStatisticsQuery Class Reference。
    6.统计集合查询。使用这种查询来在一系列长度固定的时间间隔中执行多次统计查
    询。通常使用这种查询来生成图表。查询提供了一些简单的方法来计算某些值,例
    如,每天消耗的总热量或者每5分钟行走的步数。统计集合查询是长时间运行的。查
    询可以返回当前的统计集合,也可以监测HealthKit存储,并对更新做出响应。更多
    信息,参见HKStatisticsCollectionQuery Class Reference。
  12. Correlation查询。 使用这种查询来在correlation查找数据。这种查询可以为
    correlation中每个样本类型包含独立的谓词。如果你只是想匹配correlation类型,
    那么请使用样本查询。更多信息,参见HKCorrelation Class Reference。
    8.来源查询。使用这种查询来查找HealthKit存储中的匹配数据的来源(应用和设
    备)。来源查询会列出储存的特定样本类型的所有来源。更多信息,参见
    HKSourceQuery Class Reference 。
    单位换算
    HealthKit使用HKUnit和HKQuantity类来支持单位。HKUnit 提供了单一单位的表;
    示。它支持大部分的公制和英制单位,当然还包括基本单位和符合单位。基本单位
    代表单一的度量,例如米、磅或者秒。复合单位使用数学运算连接一个或多个基本
    单位,例如m/s或者lb/ft2。
    HKUnit提供了便捷方法来创建HealthKit支持的所有基本单位。它还提供了构建复合
    单位需要的数学运算。最后,你还可以通过直接使用恰当的格式化的单位字符串来
    创建复合单位。
    更多信息,参见HKUnit Class Reference。
    HKQuantity类存储了给定单位的值。之后你可以用任何兼容的单位来取值。这样,
    你的应用就可以很轻松的完成单位换算。
    更多信息,参见HKQuantity Class Reference。
    在某些场合,你可以使用格式化器来本地化数量。iOS8提供了提供了新的格式化器
    来处理长度(NSLengthFormatter)、 质 量(NSMassFormatter) 和能量
    (NSEnergyFormatter)。 对于其他的数量,你需要自己来换算单位和本地化数
    据。
    多线程
    HealthKit存储是线程安全的,大部分HealthKit对象 是不可变的。通常来说,你可以
    在多线程环境中安全地使用HealthKit。
    注意:
    所有HealthKit API的完成回调都在一个私有的后台队列中执行。所以在你更新用户
    界面或者修改- -些只能在主线程中处理的资源之前,你应该把这些数据传回主线
    程。
    关于多线程和并发编程的更多信息,参见Concurrency Programming Guide。
    添加数字签名
    HealthKit中的数字签名元数据对象提供了由可信设备生成的样本记录的数据完整
    性。元数据项存储了一份数字签名过的样本记录副本。签名是由设备生成的(由于
    设备存储了私有签名密钥,所以设备应该是防篡改的)。这样数据的使用者就可以
    通过设备的公钥来检查签名,进而验证记录数据是否被修改过。由于每条记录都是
    独立签名的,所以每条记录总共能存储大约1K bytes的签名。因此,这种元数据签
    名项是给那些样本率最多在每天几次的记录使用的。更高的样本率需要对样本组进
    行签名,这已经超出了本文档的范围。
    通常,私钥是在制造时置入防篡改的测量设备中的。(私 钥重置或证书更新的政策
    或机制不在本文档的范围内)相关的公钥会由设备制造商公布,例如在它们的网站
    上。设备应该将每个样本的样本数据和签名传给iOS应用,这两者都会储存在
    HealthKit数据库中。注意,数字签名的公私钥是用来提供数据完整性检查的,不是
    用来加密的。数据记录中的实际值都是明文。
    数字签名的格式是IETF RFC 5652中的CMS (Cryptographic Message Syntax)。
    签名使用ASN.1的DER (Distinguished Encoding Rules)编码。使用的信息摘要应
    该是SHA256,签名加密应该是FIPS PUB 186-4 Digital Signature Standard
    Elliptic Curve P-256。这样长度和效率都有保证。另外,整个签名都应该使用.
    bsae64编码,这样就能存储在HealthKit NSString元数据对象中。
    签名应该是ASN.1 Signed-data Content Type:
    SignedData := SEQUENCE { version CMSVersion,digestAlgorithms DigestAl
    gorithmldentifiers,encapContentInfo EncasulatedContentInfo,signerInfos Si
    gnerInfo }
    SignerInfo Type是:
    SignerInfo ::= SEQUENCE { version CMSVersion,sid Signerldentifier,digest
    Algorithm DigestAlgorithmldentifier,signatureAlgorithem SignatureAlgorithml
    dentifier, signatureSignatureValue }
    上面就是摘要和签名的算法。可选项目都没有包含在其中。Signerldentifier用来取
    得适当的公钥,来对签名进行验证。
    EncapsulatedContentInfo是从由设备生成的样本记录中的相关项目的副本。副本应
    该用ASN.1 DER编码,并应至少包含一个样本时间戳和采样值。将记录数据拷贝进
    签名内,是为了有一个稳定的定义良好的二进制编码(ASN.1 DER),这对于生成
    一个可验证的签名来说是必要的。ASN.1编码的plist结构(键值对) ,应该能满足大
    部分记录类型。(参 见“使用ASN.1 DER来编码plist结构”)
    4、游戏方面
    最大的改变莫过于Scene Kit的加入了。不过游戏天生的容易跨平台的特性(并且也
    有这方面的强烈需求),与平台限制的Sprite Kit是冲突的,所以去年的Sprite Kit
    也还没多少人用。暂时看来这个世界现在是,并且在一段时间内还会是被
    Cocos2dx/Unity所统治的。Scene Kit的未来估计会和Sprite Kit比较类似,作为
    对于一直进行ios应用开发的开发者来说,有着不需要学习和熟悉新语言的优势,
    容易与系统的其他框架进行集成,所以用来转型还算不错的选择。但除此之外其他
    方面可能也并没有多少可以吸引人的地方了。
    另一个重大改变是对于A7和以上级别的GPU推出了一套全新的称为Metal的绘制
    API,从Keynote的Zen Garden的演示来看,Metal 的性能毋庸置疑是令人折服
    的,Metal的渲染方式和着色器也相当有趣。但是其实这些内容更多地是偏向底层
    以及面向引擎开发的,对于使用游戏引擎来制作游戏的大多数开发者来说,并不需
    要知道或者理解其中的东西。在A7的芯片下使用Apple自家的Sprite Kit或者
    Scene Kit 的话,就可以直接受益于Metal,而其他一些知名的第三方引擎,比如
    Unity和UE也都会在ios 8推出后支持Metal。因此,作为引擎使用者,并不需要
    做出除了升级开发使用的游戏引擎之外的任何改变。
    5、其他方面的改动
    Local和Remote通知的变化
    现在需要显示UI或者播放声音的通知,包括Local通知也需要实现弹窗获得用户许
    可了。使用-registerUserNotificationSettings:来向用户获取许可。作为补偿,现
    在对于不需要打扰用户(也就是iOs 7加入的静默通知)的类型不再需要弹框获取
    用户许可。不过因为本地推送是需要许可的,所以无论怎样如果你想要依靠通知来
    提高用户留存率的话,现在都绕不开用户许可了。
    另外,通知中心加入了非常方便的Action特性,用户可以在收到通知后,在不打开
    应用的情况下完成- -些操作。可以说配合通知中心的Today扩展,用户现在在很可
    能可以在不打开应用的情况下就获取到他们想要的信息,并完成互动。这对于开发
    者可以说是一件喜忧参半的事情,一方面我们可以给用户提供更好更快的使用体
    验,但是另一方面这将降低用户打开应用的意愿。不过Apple现在的总体思路还是
    app的体验才是最重要的,所以正确的道路应该还是优先做好app的体验,并且摸
    索一个应用和通知之间的平衡点,让大家都满意。

通知栏Widget
iOS 5的时候苹果为iPhone加入了通知中心这项功能,到iOS 8前,苹果的通知
中心真的只有一个功能,那就是给你提供通知,到了iOS 8苹果终于允许第三方应用
可以在通知栏中添加Widget了,
比如在我的iPhone中,Calendars 5,Evernote, Day One,Kindle, UpWord,
等等都在第--时间提供了通知栏Widget,直接在通知最底部进行编辑就行。
但并不代表这些Widget都是好用的。如果体验过iOS 8短信的快捷回复的话,那么
一定会被觉得这个功能太便捷了,不用进入短信界面,就可以直接进行回复短信的
操作,到了通知栏Widget这,我也希望能够有这样的体验,但令人失望,虽然
Evernote等应用都提供了新增快捷键,但是点击之后还是需要跳转到应用内部进行
相应操作。而Kindle这类只是在通知栏提供了一个软件的入口,可以更方便的进行
阅读。做的最绝的就属Clear了,作为一款to-do应用,Clear在没有通知的情况下会
在通知栏提供名人名言,成功变身心灵鸡汤应用。Calendars 5的Widget只提供新
增功能,而日程安排的提示依然只会出现在通知里面。
或许是只是因为铺苹果刚刚开放通知栏Widget,所以大家都还不知道改怎么改进这
个通知栏。
现阶段比较出色Widget也有很多,比如Launcher,其实Launcher的功能和
Launch Center Pro是一样的只不过Launcher占领了通知栏,通过通知栏来进行快
速的开启应用等功能。相信Launch Center Pro肯定也会很快支持通知栏Widget。
还有Wdgts这个应用,它在通知栏中集成了日历,计算器,汇率转换,放置照片等
多个Widget,都可以在通知栏中直接操作,无需跳转至应用本身
而Launcher和Wdgts这两款应用都是免费的,相对应的也没有什么必要购买高级版
的内购。
对于通知栏Widget这块肯定会有越来越有意思的应用出现,但是有一点是需要明白
的,通知中心的作用还是体现在通知上,Widget放的太多不仅不会体现高效,只会
让通知中心更加臃肿。

CoreLocation
CoreLocation室内定位。现在CL可以给出在建筑物中的楼层定位信息了,直接访
问CLLocation实例的floor,如果当前位置可用的话,会返回一个包含位置信息的
非nil的CLFloor以标识当前楼层。这个使得定位应用的可能性大大扩展了,想象一
下在复杂的地铁站或者大厦里迷路的时候,还可以依赖定位系统,幸福感涌上心头
啊。

Touch ID
Touch ID API,说是开放了Touch ID的验证,但是实际.上能做的事情还是比较有
限。因为现在提供的API只能验证用户是不是手机主人本人,而不能给出一个识别
的标志或者唯-编码,所以想用Touch ID做注册登陆什么的话可能还是不太现实。
不过在进行支付验证之类的已登录后的再次确认操作时就比较好用。现在看来的话
这组API就是为了简化像Paypal或者支付宝这样的第三方支付和确认的流程的。希
望之后能继续放开,如果能给一个唯一标识的话,也许就可以干掉整个讨厌的注册
和登陆系统了。
I0S8 SDK向开发者公开了Touch ID的API。对于我们的产品和IOS开
发,我们就有了一个新的吸引用户的眼球的方法。如果,我们app内有
个指纹验证,是不是让用户眼球- -亮呢?
好了,下面直接上代码:
使用Touch ld需要引入LocalAuthentication框架

import <LocalAuthentication/L ocalAuthentication.h>

然后我直接在控制器加了个按钮,给它建了Touch Up Inside事件,先
检查TouchID是否可用,然后执行认证策略
. (IBAction)onclickButton:(id)sender {
//新建LAContext实例
LAContext * authenticationContext= [[LAContext alloc]init];
NSError *error;
//1:检查Touch ID是否可用
if ([authenticationContext
canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
error:&error]) {
NSL og(@"touchld可用");
//2:执行认证策略
[authenticationContext
evaluatePolicy:L .APolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:@"需要验证您的指纹来确认您的身份信息" reply:^(BOOL
success, NSError *. _Nullable error) {
if (success) {
NSLog(@"通过了Touch ld指纹验证");
}else{
NSLog(@"error===%@" ,error);
NSLog( @"code= ===%d" , error.code);
NSLog(@"errorStr ======%@" ,[error.userInfo
objectForKey:NSLocalizedDescriptionKey]);
if (error.code == -2) {//点击了取消按钮
NSLog(@"点击了取消按钮");
}else if (error.code == -3){//点输入密码按钮
NSLog(@"点输入密码按钮");
}else if (error.code == -1){//连续三次指纹识别错误
NSLog(@"连续三次指纹识别错误");
}else if (error.code == -4)/{/按下电源键
NSLog(@"按下电源键");
}else if (error.code == -8){/Touch ID功能被锁定,下一 次需要输入系统密码
NSLog(@"Touch ID功能被锁定,下一 次需要输入系统密码");
}
NSLog(@"未通过Touch Ild指纹验证");
}
}];
}else{
//todo goto输入密码页面
NSLog( @"error====%@" ,error);
NSLog( @"抱歉,touchld 不可用");
}
运行效果:


image.png

我们只需要对于error来做不同业务处理。
验证Touch ID是否可用,error的不同情况
例如,在iphone5 上运行结果:
Error Domain=com.apple.LocalAuthentication Code=-6 "Biometry is not
available on this device." Userlnfo=0x15ec5a00
{NSLocalizedDescription=Biometry is not available on this device.
例如,在模拟器.上运行结果:
Error Domain=com.apple.L ocalAuthentication Code=-1000 "Simulator is not
supported." UserInfo=0x7ffe604b0790 {NSLocalizedDescription=Simulator is
not supported.
例如,在有TouchID的功能下,没有设置TouchID的运行结果:
Error Domain=com.apple.LocalAuthentication Code=-7 "No fingers are enrolled
with Touch ID." UserInfo=0x170075fc0 {NSLocalizedDescription=No fingers are
enrolled with Touch ID.
认证策略,error的不同情况
例如:连续三次指纹识别错误:
Error Domain=com.apple.LocalAuthentication Code=-1 "Aplication retry limit
exceeded." Userlnfo=0x1740797c0 {NSLocalizedDescription=Aplication retry
limit exceeded.}
例如:用户在Touch ID对话框中点击了取消按钮:
Error Domain=com. apple.L ocalAuthentication Code=-2 "Canceled by user."
Userlnfo=0x17006c780 {NSL ocalizedDescription=Canceled by user.
例如:用户在Touch ID对话框中点击输入密码按钮:
Error Domain=com.apple.L ocalAuthentication Code=-3 "Fallback authentication
mechanism selected." Userlnfo=0x17407e040
{NSLocalizedDescription=Falback authentication mechanism selected.
例如:按下电源键:
Error Domain=com.apple. LocalAuthentication Code=-4 "UI canceled by
system." Userlnfo=0x170065900 {NSLocalizedDescription=Ul canceled by
system.}
例如:Touch ID功能被锁定,下一 次需要输入系统密码时的运行结果:
Error Domain=com.apple.LocalAuthentication Code=-8 "Biometry is locked
out." Userlnfo={NSLocalizedDescription= Biometry is locked out.
被锁定之后,再次点击这个按钮,会出现如下的页面:

相机和照片
新增加了Photos.framework 框架,这个框架用于与系统内置的Photo应用进行交
互,不仅可以替代原来的Assets Library作为照片和视频的选取,还能与iCloud照
片流进行交互。除此之外,一个很重要的特性是还可以监听其他应用对于照片的改
变,可以说整个框架非常灵活。

  1. Model
    PHAsset、PHAssetCollection、 PHCollectionList 是Photos框架中的模型类,
    PHAsset类模型是图片或者视频文件数据; PHAssetCollection即图 片或者视频文件
    的集合,包括相册、moments、智能相册以及共享照片流; PHCollectionList是一
    组资源集合,可能是-组Assets,也可能是一组collection。 它们关 系如下图:
    image.png

    1.1 PHAsset
    PHAsset是iOS8平台的新接口,用来获取图片和视频文件的元数据,相当于以前的
    ALAsset接口,但比起ALAsset,PhotoKit提供了额外的关于用户资源的元数据,而这
    些数据在以前使用ALAssetsLibrary框架中是没有办法访问,或者很难访问到。我
    们可以用PHAsset保存图片和视频资源对象,然后展示或者修改它.它有几个重要属
    性:
    ●mediaType :资源类型,图片或者音频或视频
    image.png

    ●Paste_ Image.png
    ●mediaSubtypes :图片又包含全景图(Panorama)、HDR图片、屏幕截图、
    livePhoto.live photo加3Dtouch效果太赞! [视频链接].我们可以使用照片 资源
    的mediaSubtypes属性验证资源库中的图像在捕捉时是否开启了HDR,拍摄
    时是否使用了相机应用的全景模式.
    image.png

    ●Paste_ lmage.png
    ●Creation date
    ●Location
    ●Favorite 布尔值,用户是否标记资源为"收藏",我们平时浏览照片或视频,在下
    方点就表示收藏这张图。
    ●hidden要验证一个资源是否被用户标记为收被隐藏,只要检查PHAsset实例的
    hidden属性即可。
    ●sourceType :资源可以来源自用户相册、iCloud、 iTunes同步
    image.png

    ●Paste
    lmage.png

    representsBurst和burstSelectionTypes:对于一个资源,如果其PHAsset的
    representsBurst属性为true,则表示这个资源是一系列连拍照片中的代表照片
    (多张照片是在用户按住快门时拍摄的)。它还有-个属性是burstldentifier,如
    果想要获取连拍照片中的剩余的其他照片,可以通过将这个值传入
    fetchAssetsWithBurstldetifier(..)方法来获取。用户可以在连拍的照片中做
    标记;此外,系统也会自动用各种试探来标记用户可能会选择的潜在代表照
    片。这个元数据是可以通过PHAsset的burstSelectionTypes属性来访问。这
    个属性是用三个常量组成的位掩码: .UserPick 表示用户手动标记的资
    源,.AutoPick表示用户可能标记的潜在资源,.None 表示没有标记的资源。
    image.png

    我模拟器相册中某张图片:
    <PHAsset: 0x7f8293659e20> B84E8479-475C-4727-A4A4-B77AA9980897/
    L0/001 mediaType=1/0, sourceType=1, (4288x2848),
    creationDate=2009-10-09 21:09:20 +0000, location=0, hidden=0, favorite=0
    localldentifier
    Photos框架中的根类PHObject只有一个公开接口localldentifier,是对象唯一唯一标 .
    志符.PHObject实现了-isEqual和-hash方法.可以直接使用localldentifier属性对
    PHObject及其子类对象进行对比是否同一个对象。
    在我的模拟器中“最近删除”这个相册的localldentifier为72053882- BF20-4D4A-
    B1A5-03D1DDAE1707/L0/040
    1.2 PHAssetCollection
    PHAssetCollection是一组有序的资源集合,包括相册、moments、智能相册以及共
    享照片流.它的重要属性;

    assetCollectionType资源集合类型,比如相册或者“时刻”相册typedef
    NS_ ENUM(NSInteger, PHAssetCollectionType) {

    PHAssetCollectionTypeAlbum= 1,
    ●PHAssetCollectionTypeSmartAlbum = 2,
    ●PHAssetCollectionTypeMoment= 3,}
    ●NS_ ENUM_ AVAILABLE I0S(8 _0);
    ●assetCollectionSubtype 子类型
    enum PHAssetCollectionType : Int {
    case Album /从iTunes同步来的相册,以及用户在Photos中自己建立的相册
    case SmartAlbum 1/经由相机得来的相册
    case Moment //Photos为我们自动生成的时间分组的相册
    }
    enum PHAssetCollectionSubtype : Int {
    case AlbumRegular //用户在Photos中创建的相册
    case AlbumSyncedEvent //使用iTunes从Photos照片库或者iPhoto照片库同步
    过来的事件。然而,在iTunes 12以及ios 9.0 beta4上,选用该类型没法获取同步
    的事件相册,而必须使用AlbumSyncedAlbum。
    case AlbumSyncedFaces //使用iTunes从Photos照片库或者iPhoto照片库同步
    的人物相册。
    case AlbumSyncedAlbum //做了AlbumSyncedEvent 应该做的事
    case Albumlmported //从相机或是外部存储导入的相册,完全没有这方面的使用经
    验,没法验证。
    case AlbumMyPhotoStream //用户的iCloud照片流
    case AlbumCloudShared //用户使用iCloud共享的相册
    case
    SmartAlbumGeneric //文档解释为非特殊类型的相册,主要包括从iPhoto同
    步过来的相册。
    case SmartAlbumPanoramas //相机拍摄的全景照片
    case SmartAlbumVideos //相机拍摄的视频
    case SmartAlbumFavorites //收藏文件夹
    case SmartAlbumTimelapses //延时视频文件夹,同时也会出现在视频文件夹中
    case SmartAlbumAllHidden //包含隐藏照片或视频的文件夹
    case SmartAlbumRecentlyAdded //相机近期拍摄的照片或视频.
    case SmartAlbumBursts //连拍模式拍摄的照片
    case SmartAlbumUserLibrary //这个命名最神奇了,就是相机相册,所有相机拍摄
    的照片或视频都会出现在该相册中,而且使用其他应用保存的照片也会出现在这
    里。
    case Any //包含所有类型
    }
    ●startDate
    ●endDate
    ●estimatedAssetCount :估算的asset数量,不精确我模拟器智能相册里,“最
    近删除”这个相册的对象信息
    < PHAssetCollection: 0x7f82951bd640> 72053882-BF20-4D4A-
    B1A5-03D1DDAE1707/L0/040 Recently Deleted
    assetCollectionType=2/1000000201.
    1.3 PHCollectionList
    -组有序的资源集合的集合,暂时还没用过。几个重要属性:
    ●collectionListType
    ●collectionListSubtype

    startDate
    endDate

2.获取模型数据
PHAsset、PHCollection、 PHCollectionList有一 系 列类方法可供我们访问资源的
元数据
2.1比如PHAsset提供了-系列获取资源对象的方法

  • fetchAssetsInAssetCollection:options:+
    fetchAssetsWithMediaType:options:+
    fetchAssetsWithL ocalldentifiers:options:+
    fetchKeyAssetsInAssetCollection:options:+
    fetchAssetsWithOptions:+
    fetchAssetsWithBurstldentifier:options:+
    fetchAssetsWithALAssetURLs:options: .
    其中fetchAssetsInAssetCollection:options:方法可以获取资源集合中的所有asset
    对象。每个方法中的PHFetchOptions参数,是获取asset对象的一些配置,我们可
    以设置获取asset的条件,比如获取哪种资源,如何分类。获取的时候,如果该参数为
    空,则使用系统的默认值,当我们调用如。 上所示方法获取时,可以直接传nil。
    例子:
    PHFetchOptions * allPhotosOptions = [[PHFetchOptions alloc] init];
    //按图片生成时间排序
    allPhotosOptions.sortDescriptors = @[[NSSortDescriptor
    sortDescriptorWithKey:@"creationDate" ascending:YES];
    //获取图片
    PHFetchResult * allPhotos = [PHAsset
    fetchAssetsWithOptions:allPhotosOptions];
    //获取智能相册
    PHFetchResult *smartAlbums = [PHAssetCollection
    fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
    subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
    //获取用户创建的相册
    PHFetchResult *topL evelUserCollections = [PHCollectionList
    fetchTopLevelUserCollectionsWithOptions:nil];
    2.2 PHFetchOptions
    predicate :做选择的约束条件。比如,只获取图片,不获取视频。指定
    PHAssetMediaType)image.// swift:
    options.predicate = NSPredicate(format: "mediaType = %d" ,
    PHAssetMediaType.lmage.rawValue)
    o sortDescriptors可指定字段用来対荻取結果迸行排序
    ●includeHiddenAssets获取结果是否包括被隐藏的资源
    includeAllBurstAssets 3F EXEt Rẵ5 EjE jÆ JÉ3 )5PHFetchOptions *options
    = [[PHFetchOptions alloc] init];
    options.wantsIncrementalChangeDetails = YES;
    options.includeAllBurstAssets = YES;
    options.includeHiddenAssets = YES;
    II RB
    options.predicate = [NSPredicate predicateWithFormat:@"mediaType ==
    %d", ,PHAssetMediaTypelmage];
    options.sortDescriptors = @[[NSSortDescriptor
    sortDescriptorWithKey:@"creationDate" ascending:YES]];
    PHFetchResult *albums = [PHAssetCollection
    fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
    subtype:PHAssetCollectionSubtypeSmartAlbumAllHidden options:nil];
    2.3 PHFetchResult
    类似数组,存储获取到asset对象集合。
    同步快速获取结果,
    即使结果集很大,框架也能保证获取速度.因为它不会-次性将所有结果放进内
    存,而是按需批量加载
    可以用类似NSArray的接口来访问PHFetchResult结果内的集合。
    ELXA :
    PHCollection *collection = smartAlbums[n];

3.资源对象的增删改
上面PHAsset PHAssetCollection PHCollectionList对象都是不可改变的。那么我们
如何实现资源增删改呢?要借助request API :


image.png

比如:这段代码用来修改一张图片的资源属性:是否被收藏。
//创建
request = [PHAssetChangeRequest
creationRequestForAssetFromlmage:image]

  • (void)toggleFavoriteForAsset:(PHAsset *)asset {
    [[PHPhotoL ibrary sharedPhotoLibrary] performChanges:^{
    //改变
    PHAssetChangeRequest *request = [PHAssetChangeRequest
    changeRequestForAsset:asset];
    request.favorite = !asset.favorite;
    } completionHandler:^(BOOL success, NSError *error) {
    NSLog(@"Finished updating asset. %@", (success ? @"Success." :
    error);
    }];
    (1)创建PHAssetChangeRequest对象。想要修改资源,需要创建一个
    PHAssetChangeRequest。然后你就可以修改创建创建日期,资源位置,以及是否
    将隐藏资源,是否将资源看做用户收藏等。此外,你还可以从用户的库里删除资
    源。类似地,若要修改资源集合或集合列表,需要创建一个
    PHAssetCollectionChangeRequestE PHCollectionListChangeRequestXE.5
    后你就可以修改集合标题,添加或删除集合成员,或者完全删除集合。
    (2)操作的请求都要求在PHPhotoLibrary的performChanges的changeBlock中执行
    (3)如果有更新UI操作,需要遵守PHPhotoLibraryChangeObserver
    tiX,FphotoLibraryDidChange(changelnfo: PHChange!)Fit.
    photoLibraryDidChange- j#íJUI5 ãFTFRKF .
    比如,下面2段代码用来向册中新增一张图片:
    //为image对象生成PHAsset对象,并加入collection
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:f
    PHAssetChangeRequest *assetChangeRequest =
    [PHAssetChangeRequest creationRequestForAssetFromlmage:image];
    if (self.assetCollection) f
    PHAssetCollectionChangeRequest *assetCollectionChangeRequest =
    [PHAssetCollectionChangeRequest
    changeRequestForAssetCollection:self.assetCollection];
    [assetCollectionChangeRequest addAssets:@[[assetChangeRequest
    placeholderForCreatedAsset]]];
    ] completionHandler:(BOOL success, NSError *error) f
    if (!success) f
    NSLog(@"Error creating asset: %@", error);
    ];
    创建- -个新资源只要用creationRequestForAssetFromX(...)工厂方法,来创建变
    化请求,并传入资源图像数据(或一一个URL)。如果你需要对新建的资源做额外的修
    改,你可以用创建变化请求的placeholderForCreatedAsset属性。它会返回一个可
    用的placeholder来代替“真实的”PHAsset引用.
    // PHPhotoLibraryChangeObserver协议的方法,更新相册UI
    (void)photoLibraryDidChange:(PHChange *)changeInstance {
    // Check if there are changes to the assets we are showing.
    PHFetchResultChangeDetails collectionChanges = [changeInstance
    changeDetailsForFetchResult:self.assetsFetchResults];
    if (collectionChanges == nil) {
    return; .
    }
    /

    Change notifications may be made on a background queue. Re-dispatch
    to the
    main queue before acting on the change as we'll be updating the UI.
    dispatch_ async(dispatch_ get_ main_ queue(), ^{
    // Get the new fetch result.
    self.assetsFetchResults = [collectionChanges fetchResultAfterChanges];
    UICollectionView *collectionView = self.collectionView;
    if (![collectionChanges hasIncrementalChanges] Il [collectionChanges
    hasMoves]) f
    // Reload the collection view if the incremental diffs are not available
    [collectionView reloadData];
    ] else f
    Tell the collection view to animate insertions and deletions if we
    have incremental diffs.
    */
    [collectionView performBatchUpdates:l
    NSIndexSet *removedIndexes = [collectionChanges
    removedlndexes];
    if ([removedIndexes count]> 0) l
    [collectionView deleteltemsAtlndexPaths:[removedlndexes
    aapl_ indexPathsFromIndexesWithSection:0]];
    NSIndexSet *insertedIndexes = [collectionChanges
    insertedlndexes];
    if ([insertedIndexes count] > 0) f
    [collectionView insertltemsAtlndexPaths:[insertedlndexes
    aapl_ indexPathsFromlndexesWithSection:0]];
    ]
    NSIndexSet *changedIndexes = [collectionChanges
    changedIndexes ];
    if ([changedIndexes count]> 0) f
    [collectionView reloadltemsAtlndexPaths:[ changedIndexes
    aapl_ indexPathsFromIndexesWithSection:0]];
    ] completion:NULL];
    ]
    [self resetCachedAssets];
    ]);
    3.1 PHPhotoLibrary
    系统中PHPhotoLibrary单例对象是用来维护用户照片库。当我们需要编辑资源对象
    元数据、资源内容、或者插入新的资源对象等,都可以借助通过PHPhotoLibrary单
    例对象执行block,block中 创建我们指定的请求对象(比如
    PHAssetChangeRequest,PHAssetCollectionChangeRequest,
    PHCollectionListChangeRequestÉJXf E). photoLibraryDidChange(changelnfo:
    PHChange!)中进行.
    3.2协议PHPhotoLibraryChangeObserver
    PHPhotoLibraryChangeObserver协议能让我们知道照片相册库中的改变。Photos
    会发送系统图片改变的消息,我们可以遵守PHPhotoLibraryChangeObserver协
    议,并通过PHPhotoLibrary的registerChangeObserver方法将对象注册为观察
    者,时时接收照片改变的消息。
    3.3 PHChange
    PHphoto框架会提供给我们PHChange对象,我们可以调用
    changeDetailsForObject或者changeDetailsForFetchResult方法,它返回给我们
    -个PHObjectChangeDetails对象,是对最新的照片实体对象的引用,可以告诉我
    们对象的图像数据是否曾变化过、对象是否曾被删除过。
    4.请求图片和视频数据
    4.1 PHImageManager
    PHlmageManager在框架中是个单例对象,用[PHImageManager defaultManager]
    获取,它提供了加载图片和视频的方法。比如:图像请求是通过
    requestlmageForAset...)方法派发的。这些方法的参数:
    ●一个PHAsset对象,
    ●PHlmageRequestOptions 参数对象:可以设置返回图像的大小和图像的其它可
    选项
    ●结果回调(result handler)。
    请求图片
    -requestlmageForAsset:targetSize:contentMode:options:resultHandler:
    -requestlmageDataForAsset:options:resultHandler:
    请求视频
  • requestPlayerltemForVideo:options:resultHandler:
  • requestExportSessionForVideo:options:exportPreset:resultHandler:
  • requestAVAssetForVideo:options:resultHandler:
    默认情况下,这些API是异步执行,但是我们可以通过options参数中的
    synchronous属性,设置为同步执行,这些方法会阻塞当前调用线程直到下载完成
    或者发生错误。

4.2请求图片
//加载160X160像素的图片
[manager requestlmageForAsset:photo
targetSize:CGSizeMake(160,160)
contentMode:PHlmageContentModeAspectFill
options:nil
resultHandler:^(Ullmage *result, NSDictionary *info) {
if (result) {
[cell setlmage:result];
} else {
}];
targetSize:返回图片的尺寸
contentMode:决定了照片应该以按比例缩放还是按比例填充的方式放到目标大小
内。
options: PHlmageRequestOptions类用于定制请求。上 面的方法返回指定尺寸的图
像,如果你仅仅指定必要的参数而没有对options进行配置的话,返回的图像尺寸
将会是原始图像的尺寸。或者,你指定的尺寸很小,这时候会按照你的要求来返回
接近该尺寸的图像。
4.2.1 PHlmageRequestOptions有以下几个重要的属性:
synchronous:指定请求是否同步执行。
resizeMode:对请求的图像怎样缩放。有三种选择: None, 不缩放; Fast, 尽快
地提供接近或稍微大于要求的尺寸; Exact, 精准提供要求的尺寸。
deliveryMode:图像质量。 有三种值: Opportunistic, 在速度与质量中均衡;
HighQualityFormat,不管花费多长时间,提供高质量图像; FastFormat, 以最快
速度提供好的质量。
normalizedCropRect:用于对原始尺寸的图像进行裁剪,基于比例坐标。只在
resizeMode为Exact时有效。
resizeMode默认是None,这也造成了返回图像尺寸与要求尺寸不符。这点需要注
意。要返回一个指定尺寸的图像需要避免两层陷阱:一定要指定options参数,
resizeMode不能为None。


image.png

ÆSỄd targetSize. contentModeЖ#5J, #èy JRTJ EtargetSize.
contentMode. resizeModejaÆ o
//下载原图:
options.deliveryMode =
PHlmageRequestOptionsDeliveryModeHighQualityFormat;
options.resizeMode = PHlmageRequestOptionsResizeModeExact;
options.progressHandler = (PHAssetlmageProgressHandler)progress;
targetSize = PHlmageManagerMaximumSize;
contentMode = PHlmageContentModeDefault;
//全屏图
options.deliveryMode =
PHlmageRequestOptionsDeliveryModeHighQualityFormat;
options.resizeMode = PHlmageRequestOptionsResizeModeExact;
options.progressHandler = (PHAssetlmageProgressHandler)progress;
targetSize = UIScreenSize();
contentMode = PHlmageContentModeAspectFit;
4.2.2 resultHandler可能被多次调用
见上图,
●如果synchronous属 性为YES,同步请求,resultHandler只会执行一 -次,并且
返回高质量的图片。
SWIFT
var synchronous: Bool
Discussion
If false (the default), the requestImageForAsset: targetSize: contentMode:options: resultHandler:
method returmns immediately. Depending on the deliveryMode property, Photos may call your resul tHandler
block before the method returns, at some later time, or both.
If true, the requestImageForAsset:targetSize:contentMode:opt ions: resultHandler: method blocks the
calling thread until image data is ready or an error occurs. Photos calls your result handler block exactly once.
Paste_ Image.png
●但是如果synchronous
为NO,resultHandler是否 会被多次调用取决于deliveryMode
属性:
0如果deliveryMode
是.HighQualityFormat,resultHandler调用一 次,框架只返回高质量图。
O如果deliveryMode
是FastFormat,resultHandler也只 被调用一-次,保证速度尽可能保证图片
质量。
0 deliveryMode是 .Opportunistic Photos可能会先提供低质量的图像以供临
时显示,随后会将指定尺寸的图像返回。如果指定尺寸的高质量的图像有缓
存,那么直接提供高质量的图像。如图,第一次同步调用请求返回- -张模糊
的图,第二次再去下载一张高质量的图片:

image.png

resultHandler中的info字典提供了关于当前请求状态的信息,比如:
D图像是否必须从iCloud请求(如果你初始化时将networkAccessAllowed设置
成false,那么就必须重新请求图像)一PHlmageResultlsInCloudKey 。
●当前递送的Ullmage是否是最终结果的低质量格式。当高质量图像正在下载
时,这个可以让你给用户先展示一个预览图像一
PHImageResultlsDegradedKey。
●请求ID(可以便捷的取消请求),以及请求是否已经被取消.-
PHImageResultRequestlDKey和PHImageCancelledKey。如果没有图像提供
给result handler,字典内还会有一个错误信息一PHImageErrorKey。
4.3请求视频
//视频请求对象
PHVideoRequestOptions *options = [PHVideoRequestOptions new];
//最高质量的视频,
options.deliveryMode =
PHVideoRequestOptionsDeliveryModeHighQualityFormat;
//可从iCloud中获取图片
options.networkAccessAllowed = YES;
options.progressHandler = ^(double progress, NSError *error, BOOL *stop) {
[self updateUserVisibleProgress:progress error:error];
//此回调block可能不在Ul线程中,我们需要在Ul线程中更新UI
dispatch_ async(dispatch. get. main_ _queue(), ^{
self.progressView.progress = progress;
});
[manager requestExportSessionForVideo:video options:options ..]

5.滚动性能:缓存
5.1 PHCachinglmageManager
滚动一系列缩略图时,我们可以在可视区域前后维护一些数据缓存。如图:


image.png

1.生成PHCachinglmageManager实例
2.调用方startCachinglmagesForAssets:targetSize:contentMode:options.,并
指定目标尺寸target size,内容模式content mode,选项options参数
3.需要从asset对象中获取图片的时候,调用
requestlmageForAsset:targetSize:contentMode:options:resultHandler
PHCachinglmageManager *cim = [[PHCachinglmageManager alloc] init];
NSArray *soonToBeVisibleAssets = [cim
startCachinglmagesForAssets:soonToBeVisibleAssets
targetSize:targetSize
contentMode:PHlmageContentModeAspectFill
options:nil];
NSArray *previouslyVisibleAssets = [cim
stopCachinglmagesForAssets:previouslyVisibleAssets
targetSize:targetSize
contentMode:PHlmageContentModeAspectFill
options:nil];
11 METFHiXtEA
PHCachinglmageManager *cim = [[PHCachinglmageManager alloc] init];
NSArray * soonToBeVisibleAssets = [cim
startCachinglmagesForAssets:soonToBeVisibleAssets
targetSize:targetSize
contentMode:PHlmageContentModeAspectFill
options:nil];
NSArray *previouslyVisibleAssets = [cim
stopCachinglmagesForAssets:previouslyVisibleAssets
targetSize:targetSize
contentMode:PHlmageContentModeAspectFill
options:nil];
官方demo中计算缓存区域的算法比较有意思

  • (void)computeDifferenceBetweenRect:(CGRect)oldRect andRect:
    (CGRect)newRect removedHandler:(void (r)(CGRect
    removedRect))removedHandler addedHandler:(void ()(CGRect
    addedRect))addedHandler l
    if (CGRectlntersectsRect(newRect, oldRect)) [
    CGFloat oldMaxY = CGRectGetMaxY(oldRect);
    CGFloat oldMinY = CGRectGetMinY(oldRect);
    CGFloat newMaxY = CGRectGetMaxY(newRect);
    CGFloat newMinY = CGRectGetMinY(newRect);
    if (newMaxY > oldMaxY) [
    CGRect rectToAdd = CGRectMake(newRect.origin.x, oldMaxY,
    newRect.size.width, (newMaxY - oldMaxY));
    addedHandler(rectToAdd);
    }
    if (oldMinY > newMinY) {
    CGRect rectToAdd = CGRectMake(newRect.origin.x, newMinY,
    newRect.size.width, (oldMinY - newMinY));
    addedHandler(rectToAdd);
    if (newMaxY < oldMaxY) {
    CGRect rectToRemove = CGRectMake(newRect.origin.x, newMaxY,
    newRect.size.width, (oldMaxY - newMaxY));
    removedHandler(rectToRemove);
    if (oldMinY < newMinY) {
    CGRect rectToRemove = CGRectMake(newRect.origin.x, oldMinY,
    newRect. size.width, (newMinY - oldMinY));
    removedHandler(rectToRemove);
    }else {
    addedHandler(newRect);
    removedHandler(oldRect);
    }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335