iOS11新特性

前言

虽然 WWDC 是一个开发者会议,但是 Keynote 并不是专门针对我们开发者的,它还承担了公司状况说明,新品发布等功能。作为技术人员,可能接下来的 session 会更有意义。要用一句话来评价今年 Keynote 所展现出来的内容的话,就是小步革新。大的技术方面可以说只有 ARKit 可堪研究,但是我们还是看到了类似跨 app 拖拽,新的 Files 应用这样进一步突破 iOS 原有桎梏的更新 (iMessage 转账什么的就不提了,我大天朝威武,移动支付领域领先世界至少三年)。iOS 11,特别是配合新的硬件,相信会给用户带来不错的体验。

新增框架

新加入 SDK 的大的框架有两个,分别是负责简化和集成机器学习的 Core ML 和用来创建增强现实 (AR) 应用的 ARKit。

Core ML

苹果文档

自从 AlphaGo 出现以来,深度学习毫无疑问成了行业热点。而 Google 也在去年就转变 Mobile-first 到 AI-first 的战略。可以说一线的互联网企业几乎都在押宝 AI,目前看来机器学习,特别是深度学习是最有希望的一条道路。

如果你不是很熟悉机器学习的话,我想我可以在这里“僭越”地做一些简介。你可以先把机器学习的模型看作一个黑盒函数,你给定一些输入 (可能是一段文字,或者一张图片),这个函数会给出特定的输出 (比如这段文字中的人名地名,或者图片中出现的商店名牌等)。一开始这个模型可能非常粗糙,完全不能给出正确的结果,但是你可以使用大量已有的数据和正确的结果,来对模型进行训练,甚至改进。在所使用的模型足够优化,以及训练量足够大的情况下,这个黑盒模型将不仅对训练数据有较高的准确率,也往往能对未知的实际输入给出正确的返回。这样的模型就是一个训练好的可以实际使用的模型。

对机器学习模型的训练是一项很重的工作,Core ML 所扮演的角色更多的是将已经训练好的模型转换为 iOS 可以理解的形式,并且将新的数据“喂给”模型,获取输出。抽象问题和创建模型虽然并不难,但是对模型的改进和训练可以说是值得研究一辈子的事情,这篇文章的读者可能也不太会对此感冒。好在 Apple 提供了一系列的工具用来将各类机器学习模型转换为 Core ML 可以理解的形式。籍此,你就可以轻松地在你的 iOS app 里使用前人训练出的模型。这在以前可能会需要你自己去寻找模型,然后写一些 C++ 的代码来跨平台调用,而且难以利用 iOS 设备的 GPU 性能和 Metal (除非你自己写一些 shader 来进行矩阵运算)。Core ML 将使用模型的门槛降低了很多。

Core ML 在背后驱动了 iOS 的视觉识别的 Vision 框架和 Foundation 中的语义分析相关 API。普通开发者可以从这些高层的 API 中直接获益,比如人脸图片或者文字识别等。这部分内容在以前版本的 SDK 中也存在,不过在 iOS 11 SDK 中它们被集中到了新的框架中,并将一些更具体和底层的控制开放出来。比如你可以使用 Vision 中的高层接口,但是同时指定底层所使用的模型。这给 iOS 的计算机视觉带来了新的可能。

Google 或者 Samsung 在 Android AI 上的努力,大多是在自带的应用中集成服务。相比起来,Apple 基于对自己生态和硬件的控制,将更多的选择权交给了第三方开发者。

ARKit

苹果文档

Keynote 上的 AR 的演示可以说是唯一的亮点了。iOS SDK 11 中 Apple 给开发者,特别是 AR 相关的开发者带来了一个很棒的礼物,那就是 ARKit。AR 可以说并非什么新技术,像是 Pokémon Go 这样的游戏也验证了 AR 在游戏上的潜力。不过除了 IP 和新鲜感之外,个人认为 Pokémon Go 并没有资格代表 AR 技术的潜力。现场的演示像我们展示了一种可能,粗略看来,ARKit 利用单镜头和陀螺仪,在对平面的识别和虚拟物体的稳定上做得相当出色。几乎可以肯定,那么不做最早,只做最好的 Apple 似乎在这一刻回到了舞台上

ARKit 极大降低了普通开发者玩 AR 的门槛,也是 Apple 现阶段用来抗衡 VR 的选项。可以畅想一下更多类似 Pokémon Go 的 AR 游戏 (结合实境的虚拟宠物什么的大概是最容易想到的) 能在 ARKit 和 SceneKit 的帮助下面世,甚至在 iPad Pro 现有技能上做像是 AR 电影这样能全方位展示的多媒体可能也不再是单纯的梦想。

而与之相应的,是一套并不很复杂的 API。涉及的 View 几乎是作为 SceneKit 的延伸,再加上在真实世界的定为也已经由系统帮助处理,开发者需要做的大抵就是将虚拟物体放在屏幕的合适位置,并让物体之间互动。而利用 Core ML 来对相机内的实际物体进行识别和交互,可以说也让各类特效的相机或者摄影 app 充满了想像空间。

Xcode

编辑器和编译器

速度就是生命,而开发者的生命都浪费在了等待编译上。Swift 自问世以来就备受好评,但是缓慢的编译速度,时有时无的语法提示,无法进行重构等工具链上的欠缺成为了最重要的黑点。Xcode 9 中编辑器进行了重写,支持了对 Swift 代码的重构 (虽然还很基础),将 VCS 提到了更重要的位置,并添加了 GitHub 集成,可以进行同局域网的无线部署和调试。

Xcode 9 中的索引系统也使用了新的引擎,据称在大型项目中搜索最高可以达到 50 倍的速度。不过可能由于笔者所参加的项目不够大,这一点体会不太明显。项目里的 Swift 代码依然面临失色的情况。这可能是索引系统和编译系统没有能很好协同造成的,毕竟还是 beta 版本的软件,也许应该多给 Xcode 团队一些时间 (虽然可能到最后也就这样了)。

由于 Swift 4 编译器也提供了 Swift 3 的兼容 (在 Build Setting 中设置 Swift 版本即可),所以如果没有什么意外的话,我可能会在之后的日常开发中使用 Xcode 9 beta,然后在打包和发布时再切回 Xcode 8 了。毕竟每次完整编译节省一分半钟的时间,还是一件很诱人的事情。

这次的 beta 版本质量出人意料地好,也许是因为这一两年来都是小幅革新式的改良,让 Apple 的软件团队有相对充足的时间进行开发的结果?总之,Xcode 9 beta 现在已经能很好地工作了。

Named Color

现在你可以在 xcassets 里添加颜色,然后在代码或者 XIB 中引用这个颜色了。


iOS11新特性_第1张图片
named-colors.png

这个在 xcassets 中定义的颜色,可以直接在代码和 XIB 中引用这个颜色,以前在代码中定义的颜色只能在代码中引用,并不能直接在 XIB 中使用,每次用 XIB 创建 UI 的时候,都需要先在定义颜色的地方找到对应的颜色才能在 XIB 中获取这个颜色。现在只要定义在Named Color中的颜色,在代码和 XIB 中都可以引用,这个变化对于开发者来说是很实际的功能。

小的变更

  • 拖拽 - 很标准的一套 iOS API,不出意外地,iOS 系统帮助我们处理了绝大部分工作,开发者几乎只需要处理结果。UITextViewUITextField 原生支持拖拽,UICollectionViewUITableView 的拖拽有一系列专用的 delegate 来表明拖拽的发生和结束。而你也可以对任意 UIView 子类定义拖拽行为。和 mac 上的拖拽不同,iOS 的拖拽充分尊重了多点触控的屏幕,所以可能你需要对一次多个的拖拽行为做些特别处理。
  • 新的 Navigation title 设计 - iOS 11 的大多数系统 app 都采用了新的设计,放大了导航栏的标题字体。如果你想采用这项设计的话也非常简单,设置 navigation bar 的 prefersLargeTitles 即可。
  • FileProvider 和 FileProviderUI - 提供一套类似 Files app 的界面,让你可以获取用户设备上或者云端的文件。相信会成为以后文档相关类 app 的标配。
  • DeviceCheck - 每天要用广告 ID 追踪用户的开发者现在有了更好地选择 (当然前提是用来做正经事儿)。DeviceCheck 允许你通过你的服务器与 Apple 服务器通讯,并为单个设备设置两个 bit 的数据。简单说,你在设备上用 DeviceCheck API 生成一个 token,然后将这个 token 发给自己的服务器,再由自己的服务器与 Apple 的 API 进行通讯,来更新或者查询该设备的值。这两个 bit 的数据用来追踪用户比如是否已经领取奖励这类信息。
  • PDFKit - 这是一个在 macOS 上已经长期存在的框架,但却在 iOS 上姗姗来迟。你可以使用这个框架显示和操作 pdf 文件。
  • IdentityLookup - 可以自己开发一个 app extension 来拦截系统 SMS 和 MMS 的信息。系统的信息 app 在接到未知的人的短信时,会询问所有开启的过滤扩展,如果扩展表示该消息应当被拦截,那么这则信息将不会传递给你。扩展有机会访问到事先指定的 server 来进行判断 (所以说你可以光明正大地获取用户短信内容了,不过当然考虑到隐私,这些访问都是匿名加密的,Apple 也禁止这类扩展在 container 里进行写入)。
  • Core NFC - 在 iPhone 7 和 iPhone 7 Plus 上提供基础的近场通讯读取功能。看起来很 promising,只要你有合适的 NFC 标签,手机就可以进行读取。但是考虑到无法后台常驻,实用性就打了折扣。不过笔者不是很熟这块,也许能有更合适的场景也未可知。
  • Auto Fill - 从 iCloud Keychain 中获取密码,然后自动填充的功能现在开放给第三方开发者了。UITextInputTraits 的 textContentType 中添加了 username 和 password,对适合的 text view 或者 text field 的 content type 进行配置,并填写 Info.plist 的相关内容,就可以在要求输入用户名密码时获取键盘上方的自动填充,帮助用户快速登录。

类的改变

UIViewController

  • topLayoutGuide, bottomLayoutGuide

    这2个属性被标记为过期了,但是在Storyboard里设置约束的时候,还是会出现这2个属性

  • automaticallyAdjustsScrollViewInsets

    这个属性也被标记过期了,可以使用UIScrollViewcontentInsetAdjustmentBehavior替代

UIScrollView

  • 新增contentInsetAdjustmentBehavior属性

    替代之前 UIViewControllerautomaticallyAdjustsScrollViewInsets ,作用类似,会根据某些情况自动调整 scrollview的contentInset(实际改变的是 adjustedContentInset 属性, contentInset 属性不会变)

    有4个可选值:

    public enum UIScrollViewContentInsetAdjustmentBehavior : Int {
      case automatic // Similar to .scrollableAxes, but will also adjust the top & bottom contentInset when the scroll view is owned by a view controller with automaticallyAdjustsScrollViewContentInset = YES inside a navigation controller, regardless of whether the scroll view is scrollable
      case scrollableAxes // Edges for scrollable axes are adjusted (i.e., contentSize.width/height > frame.size.width/height or alwaysBounceHorizontal/Vertical = YES)
      case never // contentInset is not adjusted
      case always // contentInset is always adjusted by the scroll view's safeAreaInsets
    }
    
  • 新增 safeAreaInsets: UIEdgeInsets 属性

    只读属性,为了配合 contentInsetAdjustmentBehavior 使用

  • 新增 adjustedContentInset: UIEdgeInsets 属性

    只读属性,这个属性会根据 safeAreaInsets 的变化而变化

  • 新增 scrollViewDidChangeAdjustedContentInset

    adjustedContentInset 变化时会调用

    UIScrollView的这几个Inset的改变需要引起注意,他改变了原来的contentInset的逻辑(比如现在contentInset不会受UINavigationBar的isTranslucent影响了),可能会对现有的项目中的页面展示有影响,在项目适配iOS11时需要留意下。

UINavigationBar

  • 新增 prefersLargeTitles: Bool 属性

    大标题,默认为false,当设置为true时,navigation bar会显示大标题,向上滑动页面,navigation bar 会变小直到显示成跟之前一样,同时title位置会发生变化

      navigationController?.navigationBar.prefersLargeTitles = true
    
    iOS11新特性_第2张图片
    navigationBar.gif
  • 新增 hidesSearchBarWhenScrolling:Bool 属性

    配合 searchController 使用的,默认是 true
    这个属性是控制 searchController 默认是否显示的。
    通过上图也可以看到, searchBar 默认是隐藏的,当下拉的时候才会显示出来,再上拉又会隐藏。
    当设置为 false 时, searchBar 会一直显示,当 scrollview 下拉时, searchBar 会随着 scrollview 往下走,上拉时,则固定在顶部不动。

    iOS11新特性_第3张图片
    searchController.gif

    当scrollview下拉时,navigation bar的高度是一直增大的(通过在scrollViewDidScroll代理里打印navigation bar的frame就会发现),也就是系统实际上是通过增大navigation bar的height,来让search bar紧随着scrollview的content的。
    查看层级关系,会发现,searchBar并不是navigation bar的subview。

    iOS11新特性_第4张图片
    层级图.png

UITableView

  • 新增 separatorInsetReference 属性

    分割线相关的,有2个可选值:

    public enum UITableViewSeparatorInsetReference : Int {
      // The value set to the separatorInset property is interpreted as an offset from the edges of the cell.
      case fromCellEdges
      // The value set to the separatorInset property is interpreted as an offset from the automatic separator insets.
      case fromAutomaticInsets
    }
    

    举个例子,TableView的separator默认左边会留15,如果要去掉这个空隙,顶头显示

    iOS 11之前的写法:

    table.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    cell.layoutMargins = .zero
    

    iOS 11之后的写法:

    table.separatorInsetReference = .fromCellEdges //默认就是fromCellEdges,所以可以不写这行代码
    cell.separatorInset = .zero
    
  • 新增 performBatchUpdates 函数,支持批量操作了

Swipe actions

主要是实现了 TableViewCell 的左划和右划手势功能

UITableViewDelegate 中,新增了两个delegate,如下:

// These methods supersede -editActionsForRowAtIndexPath: if implemented
// return nil to get the default swipe actions
@available(iOS 11.0, *)
optional public func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?

@available(iOS 11.0, *)
optional public func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?

可以看到,这2个delegate是为了取代原有的editActionsForRowAtIndexPath的,并且细化了是左滑还是右滑,同时提供了很不错的交互体验。

下面代码是实现了一个Star功能的左滑手势

func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {

   let action = UIContextualAction(style: .normal, title: "Star") { (action, view, handler) in
        self.starAction(indexPath: indexPath)
        handler(true)
    }
    action.backgroundColor = .green
    if let stared = stars[indexPath], stared {
        action.title = "Unstar"
        action.backgroundColor = .red
    }

    return UISwipeActionsConfiguration(actions: [action])
}
iOS11新特性_第5张图片
cell左滑效果图.gif

方法解读:

  • UIContextualAction

    表示的是滑开后显示的一个操作按钮,它有下面几个属性:

    参数:

    • style : 有2个可选值:.normal.destructive,区别是背景色不同,.normal 是灰色,.destructive 是红色。当然如果手动设置了action.backgroundColor,则以 backgroundColor 为准。

    • title : action显示的文字,目前没有发现api可以修改这个title的font和color

    • image : 设置action的图片,设置了image就不会显示文字了

    • handler : 点击action后的回调,它的定义如下:

        // call the completionHandler to reset the context to its normal state (e.g. when swiping, resets to unswiped state)
        // pass YES to the completionHandler if the action was actually performed, to show a visual indication of the successful completion
        public typealias UIContextualActionHandler = (UIContextualAction, UIView, (Bool) -> Swift.Void) -> Swift.Void
      

      可以看到有3个参数:

      • UIContextualAction : 就是当前所属的action
      • UIView : 可以理解成action所呈现出来的那个视图。如果是action是文字的,view是UIButtonLable,如果是image的,view则是UIImageView
      • (Bool) -> Swift.Void : 这个参数是一个闭包,他的作用是一个
      • completionHandler : 在handler的定义上面,已经给出了说明,意思是在handler里你应该调用这个completionHandler,以恢复到正常状态(可以看上面那个效果图,点击action后,cell会恢复到未左滑的状态)如果不调用,点击后就会是保持现有的左侧滑开的状态。

      【关于completionHandler】

      这个completionHandler也需要一个Bool类型的参数,传true和传false有什么区别呢?官方的说明是pass YES to the completionHandler if the action was actually performed
      其实这个就是style中的normal和destructive的另一个区别。
      我们知道,destructive的意思是危险操作,一般表示的是删除。如果你调用completionHandler传的是true,当style=.destructive时,系统会删掉这个cell,没错,删掉这个cell!按照官方的解释可以理解成,destructive就是删除,你传了true,说明action actually performed,那系统就会删掉这个cell.
      对于style=.normal的,我试了,传true和false,没区别。

    • UISwipeActionsConfiguration : 它只有两个属性,一个是actions数组,表明你可以添加多个action操作;还有一个叫performsFirstActionWithFullSwipe,默认是true,意思是当你full swipe(完全滑动)的时候,系统会自动执行第一个action的handler,这个在上面的效果图上也能看到。

你可能感兴趣的:(iOS11新特性)