iOS 9 开发高级技巧

为了庆祝 iOS 9 的正式发布,我们咨询了一些朋友,让他们分享一下在适配最新版本 iOS 的过程中所需注意的一些事情。下文列出的就是如何加快速度适配 iOS 9 的绝妙建议。


Dave Verwer

@daveverwerCurated创立者,目前正在维护iOS Dev Weekly

很难得的是,iOS SDK 中竟然有个新 API 可以有效帮助应用展示和推销自己,因此对我来说,iOS 9中最令人激动的新特性就是搜索 API 了。

这些功能是在 NSUserActivity 的基础上构建而成的,NSUserActivity 在 iOS 8 中为了支持 Handoff 而引进。向应用中加入 iOS 9 提供的额外元数据(metadata)以及深度 URL 连接,便可以让应用的信息出现在 iOS 9 的 spotlight 搜索队列当中,不仅可以给用户展示应用的名称,还可以让用户探索应用中的内容。

这项功能对您现有的用户带来的益处是显而易见的,不仅如此,苹果还会对这些搜索结果进行排序。当没有安装您应用的用户使用 spotlight 的时候,您的应用同样也会出现在建议列表当中。这就是所谓的免费营销?好吧,或许是的。

这项功能最有意思的一点是,苹果只会给新用户推荐您的应用;对于老用户来说,他们将会正常使用您所提供的搜索结果。它将会立即摒弃无用的应用,只显示那些真正提供实际信息的应用,这对 App Store 来说无疑是一件非常棒的事情。

虽然关于搜索 API 的内容还可大书特书,然而对于“小技巧”来说再说下去无疑是浪费篇幅了。因此,如果您对此项功能感兴趣的话,您可以在 WWDC 上观看Session 709 — Introducing Search APIs。此外,有一个非常好用的工具用来验证您的网站,以确保 iOS 能够正常地显示您的应用链接。


Tim Oliver

@TimOliverAUiComics创���人

随着 iPhone 6s 以及 iPhone 6s Plus 的发布,开发者们现在就可以为自己的应用配备上 3D Touch 功能了,从而给界面交互方式开启一个新的维度。

正如苹果所言,开发者可以通过非常简单的 API 来使用 3D Touch ,从根本上来说,也就是 UITouch 的一个简单的新属性。

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
     guard let touch = touches.first else { return }
     if traitCollection.forceTouchCapability == .Available {
        println("Touch pressure is \(touch.force), maximum possible force is \(touch.maximumPossibleForce)")
     }
}

这个新的 API 可以让应用发挥出巨大的潜力,比如说游戏中的额外控制选项、绘图应用中的细粒度(fine-grained)控制,甚至是用来替代我们在 iOS 设备中使用过的长按操作(tap-and-hold)的极佳选择。

除了 UITouch 中新增的 API 外,苹果还为应用提供了两个用来增加3D Touch 功能的类集:UIPreviewActionUIApplicationShortcutItem

UIPreviewAction允许开发者在用户使用 3D Touch 功能触控一个 UI 元素的时候,快速地在一个新的预览窗口中显示某些内容。这种快速浏览应用特定内容的方式真的非常棒,比如说我们可以快速预览邮件信息、照片,甚至是网页内容,而无需弹出一个完整的视图控制器。

UIApplicationShortcutItem对象能够让 iOS 主屏幕激活一项令人惊叹的新特性。当用户使用 3D Touch 按下某个应用的图标时,一个选项列表就会被弹出,允许用户快速跳转至应用的特定部分,或者执行某项应用内的功能。

iPhone 6s Application Shortcuts

总而言之,3D Touch 的引入给 iOS 设备解锁了一个全新的交互方式,并且将会给接下来的 iOS 应用带来新一代的创新。关于3D Touch 的实例代码和相关信息可以在苹果开发者网站的3D Touch网页上找到,祝你好运!


Alex Akers

@a2Facebook软件工程师

iOS 9.0 和 OS X 10.11 分别新引进了 UILayoutGuideNSLayoutGuide这两个类。它们允许您创建一个“类似视图”的对象,用来参与自动布局约束的计算而无需在屏幕上创建多余的视图。比如说,您可以使用这些新的类来作为占位图,而不是创建一个空白的视图来进行占位。

// 创建Layout Guide
let layoutGuideA = UILayoutGuide()
let layoutGuideB = UILayoutGuide()

// 将它们添加到视图上
let view: UIView = ...
view.addLayoutGuide(layoutGuideA)
view.addLayoutGuide(layoutGuideB)

// 使用它们添加约束
layoutGuideA.heightAnchor.constraintEqualToAnchor(layoutGuideB.heightAnchor).active = true

// 您甚至可以设置它们的标识符... 
layoutGuideA.identifier = "layoutGuideA"
layoutGuideB.identifier = "layoutGuideB"

// 然后使用���们可以得到计算后的视图尺寸(只有当拥有 Layout Guide 的视图出现之后才有效)
print("layoutGuideA.layoutFrame -> \(layoutGuideA.layoutFrame)")

Indragie Karunaratne

@indragie — Mac、iOS 软件工程师,学生

在 iOS 9 中引入的 NSLayoutAnchor API 不仅让约束声明更加清晰明了,而且还通过静态类型检查以确保您的约束保证能够正常工作。比如说,我们有一个约束,是使用旧有的 NSLayoutConstraint API 进行创建的。

NSLayoutConstraint *constraint = 
    [NSLayoutConstraint constraintWithItem:view1 
                                 attribute:NSLayoutAttributeLeading 
                                 relatedBy:NSLayoutRelationEqual 
                                    toItem:view2 
                                 attribute:NSLayoutAttributeTop 
                                multiplier:1.0 
                                  constant:0.0];

这个约束是无效的,因为我们为 X 轴(左侧)属性以及 Y 轴(顶部)属性建立了一个相等的约束。然而,这种做法不会得到任何警告,它能够继续运行,然后_悄悄_地崩溃掉,让您的约束处于一个未定义的状态,接着留下一个棘手的烂摊子让您来处理,没有任何的记录能够提醒您,以便让您确认在几十个(甚至数百、上千)约束中哪一个出现了问题。

NSLayoutAnchor 通过使用在 Swift 和 Objective-C 中存在的泛型特性很好地解决了这个问题。UIView 上的锚点访问器(anchor accessors) 能够显示为继承自NSLayoutAnchor方法添加类型信息的 NSLayoutAnchor 的子类。对于 X 轴、Y 轴以及尺寸(宽、高) 锚点来说,都有着不同的NSLayoutAnchor 子类,因为某个类型的锚点只能够被与之相同类型的其他锚点所约束。通过NSLayoutAnchor中的方法来约束锚点参数以及作为接收器的相同泛型类型,API 变能够使用类型检查以确保能够创建出有效的约束。

让我们再回到前面的例子来,这里是一个使用 NSLayoutAnchor API 的等价约束声明:

NSLayoutConstraint *constraint = 
    [view1.leadingAnchor constraintEqualToAnchor:view2.topAnchor];

这不仅比旧有的 API 更加通俗易懂,并且这也会弹出一个“不兼容的指针类型”(Incompatible pointer type)的编译警告,因为编译器知道您不能够创建两个不同锚点类型的约束。

要了解更多信息,请参阅NSLayoutAnchor documentation

有关 Swift 和 Objective-C 泛型的注意事项:在写这篇文章的时候,笔者正使用 Xcode 7 beta 6 版本,Objective-C 的泛型并不能够很好的与 Swift 建立桥接。这意味着在这个提示中所提及的额外类型安全在 Swift 中并不能使用,但是却能够很好地在 Objective-C 中应用,正如Joe Groff所提到的那样


Ayaka Nonaka

@ayanonagon — [Venmo] iOS 工程师(https://venmo.com)

关于 iOS 9 我给出的技巧就是抛弃 iOS 7,开始使用诸如UIAlertController (iOS 8+) 此类的新 API,我喜欢使用这个 API 的原因在于它在显示警告框、下拉列表以及UIStackView (iOS 9+)的时候会强制只显示唯一一个视图控制器,这可以让我们以一种理智的方式考虑布局。说真的,如果您还没有试过的话那么我推荐您去试试,它真的非常好用。如果您想要用回原来的 iOS 版本的弹出框,那么您可以使用一些开源库(PSTAlertController and TZStackView)。因此,一旦您准备抛弃 iOS 7 或者 iOS 8 的时候,那么就使用这个新功能来替换原来的版本吧!


Conrad Kramer

@conradev — [Workflow]创立者(https://my.workflow.is/)

苹果在 iOS 9 中引入了应用传输安全功能,它默认需要所有的应用使用 HTTPS 协议。由于不是所有的服务都由 HTTPS 提供,因此苹果还提供了一个禁用 ATS 的方式,既可选择性的使用也可全局应用。

如果您的应用需要能够加载所有的 URL(比如说在 UIWebview中),那么您可能需要通过设置NSAllowsArbitraryLoads 键值为 YES 来全局禁用 ATS。这完全是可以的,但是一旦您全局禁用了 ATS 功能,那么您需要在重要区域启用 ATS 服务。您需要使用 NSExceptionDomains 键来完成此项功能。比如说,这是 Workflow 的 Info.plist 文件的一部分内容:

Workflow Info Plist

您可以看到,我们支持用户通过 HTTP 下载文件,但是我们同样也支持通过 HTTPS 来连接 workflow.is(以及所有 Workflow 使用的 API )。

还需要注意的是,每一个包都需要应用 ATS 功能。这意味着您不仅需要给您主应用的 Info.plist 文件添加 ATS 字典,而且还要同时给扩展的 Info.plist 文件添加。


Jake Marsh

@jakemarshLittle Bites of Cocoa创立者

竭尽所能地实现新的搜索功能!竭尽所能地实现新的搜索功能!竭尽所能地实现新的搜索功能!重要的事情说三遍。搜索功能确实是一项重大的进步,iOS 9 仅仅只是个开始。我建议尽快让您的应用支持 NSUserActivity 。这个操作是相当地简单,当您完成之后,您会发现您的应用将同时支持 Handoff 以及 新的系统。如果您的应用还可以有任何形式的搜索内容,那么您一定要 体验新的 Core Spotlight 框架的优势并且告诉系统如何对其建立索引。您还应当确保和您应用相关联的所有网页内容都针对 iOS 9 的新搜索视图进行了优化,苹果对此发布了一个绝佳的指南,指导如何使用一个简单的meta标签来完成诸多的功能

系统对用户所做所为了解得越多,它所提供给用户的建议和选项也就更加智能,更加贴合用户。


Sam Ritchie

@FakeSamRitchiecodesplice首席CodeSplicer

每个在共享的 iOS 代码平台上工作的人在面对 storyboard 文件合并冲突的时候都会抓狂,这往往导致您不得不在 IB 中手动重制 storyboard 中的更改。这也是许多团队放弃使用 storyboard 开发并且在source control shingle上提出诸多问题的原因。

如果您没有任何好用的处理工具的话,那么很不幸,减少合并冲突的最好办法是将您的 UI 分解成更小的 storyboard 文件。在过去这意味着您需要在代码中实现导航栏,这样才能够跨 storyboard 使用。但是在 Xcode 7 以及 iOS 9 当中,您能够通过一个名为 Storyboard Reference 的普通 segue 来完成此项功能。它能带来如同单一 storyboard 导航栏一样的便利,并且还允许您将文件切割成小份以减少合并冲突。

分离硕大的 storyboard 最快也是最简单的方法就是将 storyboard 界面放大,然后按下 Shift 键选择要分离并且相关联的场景,然后选择菜单栏的 ‘Editor > Refactor to Storyboard’ (没错,storyboard 在 Swift 出现之前就支持重构了)即可。然而,这种做法会导致每个被分离的场景仍会在原 storyboard 中留下其引用,并且在必要的时候会自动产生非常难看的 storyboard ID,因此我个人更倾向于使用 ‘File > Duplicate…‘,然后删除多余的场景即可。

如果您已经拥有了多个 storyboard,那么为自己欢呼一下吧——现在只需要删除代码就好了!从对象库(Object Library)中拖入一个 Storyboard Reference,然后配置一下 segue,接着重重的按下删除键,将导航栏代码删除,大功告成!


Natasha Murashev

@NatashaTheRobotCapital One iOS 工程师, Natasha The Robot博主

我并没有特别关注 iOS 9 的相关内容,不过我倒是在 watchOS 2 上进行的开发工作。如果您拥有一个 Apple Watch 应用的话,那么我建议您应当使用全新的 Watch Connectivity 框架对应用从头开始进行重写。WatchOS 2 与原先发布的 WatchKit 扩展完全不同,并且功能更加强大。WatchOS 2 代表着未来,因此仍然维护 WatchKit 扩展无疑只是徒增困扰而已。

此外,如果您目前在 Apple Watch 上使用的是 Notifications、Glances 的话,那么我推荐向您的 Watch 应用中添加 Complication 组件。将来的 #1 (也可能是即将到来的 #1)趋势正是 Complication,试想当用户每次抬起手腕之后,您的应用就能够正好出现,那该是多么美妙的用户体验!

要了解更多内容,您可以查看以下资源:


Riley Testut

@rileytestutGBA4iOS创立者, 就读于南加利福尼亚大学

如果您和我一样,让代码尽早跳出以便更好地进行逻辑、数据内容的确认,虽然 Swift 自发布的第一天起就让代码提早跳出变得可能,但是它仍然还有一些需要注意的地方。首先,您需要检查不需要的条件(比如说变量为空),而不是检查您需要的条件。更重要的是,绝大多数情况下,当变量为空的时候,您往往想要让代码跳出,然后如果变量不为空的时候就继续运行,但是接下来如果您想要在剩余的代码中使用此变量的话,就需要对变量进行手动拆包,。

在 Swift 2当中,Swift 团队给我们提供了一个完美的关键词:guard 来帮助我们提早跳出代码。guard同时修复了上面提出的两个问题。试想,您正在玩一个游戏,由于开发者懒得提供不同的回调,因此所有的输入变化都在一个回调函数中进行处理:

func gameController(gameController: GameControllerType, didActivateInput input: InputType?)

在输入无效的时候使用二次回调似乎更加有效,但是这反而���明了 guard 的极佳用处。如果可选的input是非空值,那么表示某个按钮被按下了,那么游戏会继续进行。而如果按钮不再被按下,那么input就会为空。如果我们只关注于按钮被按下的情况,我们无需使用如下所示的 Swift 1中所使用的提早退出机制:

func gameController(gameController: GameControllerType, didActivateInput input: InputType?) 
{
    if input == nil
    {
        return
    }
    
    self.performExampleMethodWithNonOptionalInput(input!)
}

注意到,我们将输入和我们不想处理的情况进行了比较,这里是“输入为空”的情况。更重要的是,当前的输入值仍然还是一个可选值,因此之后在函数中的使用我们都必须要使用强制解包,即使我们已经知道它是非空值。在 Swift 2 中,就变得轻松多了:

func gameController(gameController: GameControllerType, didActivateInput input: InputType?) 
{
    guard let unwrappedInput = input else { return } 
    // 在实际开发中,我往往使用和原变量相同的名字来解包,
    // 然而,unwrappedInput 这种命名方式要更为清晰一些。
    self.performExampleMethodWithNonOptionalInput(unwrappedInput)
}

在这里,我们“监视”着input,一旦其为非空值就将其存入到 unwrappedInput 当中,否则的话就退出方法。现在我们就能够使用非空值的 unwrappedInput 了,皆大欢喜!因此,guard 能够帮助 iOS 9 代码变得更加清晰易懂,减少错误的发生。


Janie Clayton

@RedQueenCoder — iOS/Mac 开发者, Red Queen Coder博主, NSBrief管理员

我的项目和绝大多数人的有一些小小的不同,我编写的是 Mac 上的机器人控制程序。面临 iOS 9、Swift 2以及 El Capitan 的发布,我们已经做好了准备,其中最重要的一点就是 Swift 2 中新出现的错误处理(Error Handling)机制了。当 Swift 1发布之后,我们自行编写了自己的错误处理方法,因为在使用 NSError 的时候,我们面临了许许多多的问题。

由于我们要和硬件打交道,因此错误处理对于我们的软件来说非常重要。因为如果您不能恰当地处理错误或者先于错误发生前做点什么,那么很有可能会对硬件造成极大的伤害,这个错误的代价就十分地昂贵了!因此,如果要让我提供关于 iOS 9 的小技巧的话,我想说的是如果您在使用 Swift,那么请尽快掌握错误处理。虽然我知道这货和单元测试一样,属于“我们知道应当做,但是它太无趣了而且好像做不做都没啥关系”一类,但是我建议,我们并不能控制外界因素所造成的错误,因此想想一旦错误发生之后,您的应用应当如何应对。


Glen Low

@pixelglowInstaviz创始人, Apple Design Award 2004年度获胜者

为了领悟 UIKit 编程之道,您不应当墨守成规、坚守老旧系统,而是应当追随 UIKit 框架更新的脚步,不然的话,每当苹果发布一次新的 iOS 版本,您就会发现苹果渐行渐远,而您已无力追随。

抓住一点:UIKit 的目的在于让您目前所展示的视图控制器“切实”地显示在屏幕上。比如说,对于 Popover 弹出视图,无论屏幕的尺寸和方向如何,它都能够重新调整结点位置,自行适应。

开发者可以在 UIAadptivePresentationControllerDelegate 或者 UIPopoverPresentationControllerDelegate 中,获取系统所适应的显示默认值以及提供一个新的弹出锚点。在屏幕尺寸或者尺寸级别(Size Class)改变的过程中千万不要移除您的视图控制器。此外,这两个委托只能够提供一些简单的自定义功能:当屏幕在自适应的时候,可以很方便地用一个新的视图控制器替换掉当前视图控制器,但是实际改变现有的视图控制器确实一件相当困难的事情,比如说禁用返回按钮。

如果不遵守这些约定的话,在旋转设备、执行 iPad 多任务或者在最新的新功能当中,您的应用轻则会表现出诡异的行为,重则会导致崩溃,从而让您在漫漫长夜中不得不熬夜苦战,解决 BUG。


感谢您的阅读!现在就行动起来,把这些激动人心的新功能用在您的应用上面吧!

Receive news and updates from Realm straight to your inbox

About the content

This content has been published here with the express permission of the author.


Tim Oliver

Tim Oliver hails from Perth, Australia! He has been an iOS developer for 6 years, and recently joined Realm in March 2015. Tim has a cool app called iComics and he loves karaoke! He does, in fact, also sometimes have the problem of too many kangaroos in his backyard.

Alexsander Akers

Alexsander Akers

Dave Verwer

Dave Verwer

Indragie Karunaratne

Indragie Karunaratne

Ayaka Nonaka

Ayaka leads the iOS team at Venmo, where they write only Swift these days. She’s been doing iOS development since iOS 4 and loves writing Swift while listening to Taylor Swift. In the past, she’s given talks on NLP in Swift, Swift Scripting, and rewriting the Venmo app in Swift. She was born in Tokyo and was super excited to give her first talk there! 宜しくお願いします。

Conrad Kramer

Conrad Kramer started developing for iOS after he got involved with the jailbreak scene back in 2010 with his first tweak Graviboard. Since then, he has gotten into regular iOS development and worked on various open source projects in the Cocoa community, like AFOAuth2Client and WFNotificationCenter. Conrad now spends all of his waking hours on Workflow, an automation tool for iOS, where he works on anything from the server backend to building the complex drag and drop interactions.

Jake Marsh

Sam Ritchie

Sam Ritchie

Natasha Murashev

Natasha is secretly a robot that loves to learn about Swift and iOS development. She previously worked as a senior iOS Engineer at Capital One in San Francisco, but now she travels around, writing about her experiences with new technologies. In her free time, she works on personal projects, speaks at meetups and conferences, contributes to open source, and likes to cross things off her bucket list.

Riley Testut

Riley Testut

Janie Clayton

Janie Clayton

4 design patterns for a RESTless mobile integration »

close