首页 » iOS编程基础:Swift、Xcode和Cocoa入门指南 » iOS编程基础:Swift、Xcode和Cocoa入门指南全文在线阅读

《iOS编程基础:Swift、Xcode和Cocoa入门指南》9.1 设备架构与条件代码

关灯直达底部

在创建项目时(File→New→Project),当选择好项目模板后,在项目命名界面上会弹出Devices菜单,提供了iPad、iPhone与Universal选项。可以稍后修改这个设置,在编辑应用目标时使用General页签中的Devices弹出菜单;不过如果这里就能做出正确的决定,那么情况会变得更加简单,因为你的决定会影响新项目所使用的模板细节信息。在Devices弹出菜单中所做的选择还会影响项目的Targeted Device Family构建设置:

1(iPhone)

应用可以运行在iPhone或iPod touch上;还可以运行在iPad上,但并不是作为原生iPad应用运行(它会运行在一个简化的、可放大的窗口中,称为iPhone模拟器;Apple有时称为“兼容模式”)。

2(iPad)

应用只会运行在iPad上。

1,2(通用)

应用可以运行在这两种设备上。

有两个项目级的构建设置可以决定设备运行在什么系统上:

Base SDK

应用可运行的最新系统。本书编写之际,在Xcode 7.0中,你有两个选择:iOS 9.0与Latest iOS(iOS 9.0)。它们看起来是一样的,但后者会更好一些(也是新项目的默认值)。如果更新Xcode来开发后续系统,那么已经设为Latest iOS的现有项目都会自动将新系统的SDK作为Base SDK,你不必手工更新Base SDK设置。

iOS Deployment Target

应用可运行的最老的系统:在Xcode 7中,这可以一直追溯到iOS 6.0。要想修改项目的iOS Deployment Target设置,请编辑项目并切换至Info页签,然后从iOS Deployment Target弹出菜单中进行选择。

9.1.1 向后兼容

如果应用的Deployment Target不同于Base SDK(也就是说,应用要兼容于老版本的系统),那就是一件很有挑战的事情。主要会有两个问题:

改变的行为

对于每个新系统来说,Apple都可能会改变一些特性的运作方式。结果就是不同系统上的一些特性可能会根据系统的不同而表现出不同的行为。一整块的功能可能会在不同的系统上得到不同的处理,这要求你实现或调用不同的方法,或使用完全不同的类来处理。甚至有可能相同的方法会表现出完全不同的行为,这取决于应用运行在什么系统上。

不支持的特性

对于每个新系统,Apple都会增加一些新特性。如果在执行时遇到了系统不支持的特性,那么应用就会崩溃。

改变的行为是非常麻烦的事情,这里也没什么更好的建议。通常,问题要么是完全破坏的行为,要么是先破坏,后来又修复了。比如,使用了UIProgressView的progressImage属性的代码在iOS 7上可以正常运行,但却无法运行在iOS 7.1到iOS 8.4上,不过在iOS 9上又可以正常使用了。除了尝试然后根据错误来修正外,别无他法,这总是非常棘手的一个问题。

在iOS 7及之前的版本中,警告视图是通过UIAlertView呈现的,不过在iOS 8及之后的版本中变成了UIAlertController。最简单的解决方案就是即便在iOS 8及之后的版本中也还是继续使用UIAlertView,不过你无法保证这么做总是可行的,因为UIAlertView在iOS 9中已经标记为不建议使用,最终可能会被丢弃掉,你还失去了使用UIAlertController的机会,它是个更棒的API。弹出框也是类似的(UIPopoverController与UIPopoverPresentationController)。这样,系统的更迭与改进就会给开发者造成这样一种困境:这些改进是开发者所需要的,但它们会导致向后兼容变得更加困难。

不过在Xcode 7中,编译器至少提供了之前所没有的一个功能,它使得代码很难在不支持某个特性的目标系统上使用该特性。在Xcode 7之前,如果将项目的Deployment Target设为一个老系统,那么代码是可以编译通过的,应用也可以运行在这个老系统之上的,即便代码中包含了老系统上并不存在的特性亦如此;如果遇到了这些特性,那么应用就会崩溃。在Xcode 7中,编译器会在一开始就防止这种情况的出现。

比如:


let arr = self.view.layoutGuides  

UIView layoutGuides属性只存在于iOS 9.0及之后的版本中。之前,即便部署目标设为了iOS 8.0,编译器还是会允许该代码编译通过;你要确保代码绝对不能运行在iOS 8上。不过现在,编译器会阻止你这么做,并报错:“layoutGuides is only available on iOS 9.0 or newer”。除非你告诉编译器代码只会运行在iOS 9及之后的版本中,否则是无法继续下去的。Xcode的Fix-It特性会告诉你该如何做:


if #available(iOS 9.0, *) {    let arr = self.view.layoutGuides} else {    // Fallback on earlier versions}  

#available条件(一个可用性检查)会比较当前系统与声明中所指定的特性要求。layoutGuides属性声明前有如下注解(在Swift中):


@available(iOS 9.0, *)

请查看文档来了解该注解的具体信息。不过,你无须理解它!#available条件会匹配该注解,Xcode的Fix-It会确保如此。可以在if条件或guard条件中使用#available。

可以使用@available特性来注解自己的类型和成员声明,代码接下来还需要进行可用性检查。比如,如果方法声明使用了@available(iOS 9.0,*),那么当部署目标早于iOS 9时,就无法调用该方法了,这里也无须进行可用性检查。在该方法中,无须再进行#available(iOS 9.0,*)可用性检查,因为你已经确保该方法不会运行在iOS 9之前的系统上。

要想在老系统上测试应用,你需要一个运行着老系统的设备(物理设备或模拟器)。可以通过Xcode的Downloads首选项窗格下载iOS 8 SDK(参见第6章),不过要想测试更老版本的系统,你需要更老版本的Xcode,最好还要有一个更老的设备。请不要向App Store提交尚未在某个运行系统上测试过的应用。

9.1.2 设备类型

对于通用应用,能够知道代码运行在iPad还是iPhone或iPod上是很有用的。通过当前的UIDevice(或层次体系中任何UIViewController、UIView的traitCollection)可以获得当前设备的类型并作为userInterfaceIdiom返回给你,在iPhone上是UIUserInterfaceIdiom.Phone,在iPad上则是UIUserInterfaceIdiom.Pad。

可以根据设备类型或屏幕分辨率有条件地加载资源。对于从应用包顶层加载的图片,可以使用名字后缀,如@2x和@3x来表示屏幕分辨率,或~iphone和~ipad来表示设备类型;不过,更简单的做法则是使用资源类别,在Xcode 7与iOS 9中,可以针对任何类型的数据资源采取这种做法。

与之类似,某些Info.plist设置带有名字后缀,这样就可以在一种设备类型上使用一种设置,在另外的设备类型上使用另一种设置。比如,对于通用应用,常见的做法是在iPhone上使用一套方向,在iPad上使用另一套:一般来说,iPhone版本只允许有限的方向,iPad版本则允许所有方向。可以在编辑目标时通过General窗格来配置:

1.将Devices弹出菜单切换至iPhone,并勾选iPhone上所需要的Device Orientation复选框。

2.将Devices弹出菜单切换至iPad,并勾选iPad上所需要的Device Orientation复选框。

3.将Devices弹出菜单切换至Universal。

即便现在只看到一套方向,但实际上这两套方向都会保存起来。实际上,你所做的是在Info.plist中配置了两组“Supported interface orientations”设置,一组通用设置(ISupportedInterfaceOrientations),一组针对iPad的设置,当应用在iPad上运行时,它会覆盖通用设置(UISupportedInterfaceOrientations~ipad)。可以查看Info.plist文件了解详情。

按照相同的方式,应用可以加载不同的nib文件,因此也会显示不同的界面,这取决于设备的类型。比如,你可以拥有两个主故事板,如果在iPhone上运行,那么应用启动时就加载其中一个;如果在iPad上运行,那就加载另一个。编辑目标时依然可以通过General窗格进行配置。实际上,你所做的是让Info.plist设置“Main storyboard file base name”出现两次,一次针对通用情况(UIMainStoryboardFile),另一次针对iPad(UIMainStoryboardFile~ipad)。如果应用根据名字加载nib,那么该nib文件的命名就会像图片文件一样:如果运行在iPad上,并且拥有相同的名字且后缀为~ipad的nib文件,那么它就会被自动加载。

不过,现在很少需要区分设备类型了。在iOS 7及之前的版本中,整个界面对象类(如弹出框)都只能在iPad上使用;iOS 8及后续版本中已经不存在只针对iPad的类了,如果代码运行在iPhone上,界面类本身会自适应。与之类似,在iOS 7及之前的版本中,通用应用可能需要完全不同的界面,因此根据设备类型的不同需要不同的nib文件;在iOS 8及后续版本中,可以通过尺寸等级根据设备类型的不同有条件地配置nib文件。一般来说,应用在iPad与iPhone上的物理差别并不像过去那么明显:这要归功于尺寸居于二者之间的iPhone 6,特别是iPhone 6 Plus,使得尺寸看起来更加连贯。