Xcoe 11 及 iOS 13 新特性

苹果于当地时间 9 月 10 日上午 10 点(北京时间 9 月 11 日凌晨 1 点)宣布 iOS 13 正式版发布,并于 20 号向用户推送,目前最老的可升级机型为 iPhone6S。早在 WWDC 2019 开发者大会,苹果向开发者的新产品,包括 SwiftUI、Xcode 11 和 ARKit 3。本周热点为 iOS 13 适配及 Xcode 11 的使用。

一、Xcode 11

Xcode 11 主要包含如下新特性:

  • Xcode 工作流的改进(Workflows)
  • Swift 包管理工具(Swift Package Manager)
  • 代码管理(Source Control)
  • 设计工具(Design Tools)
  • 调试工具(Debug)
  • 测试模块(Testing)
  • 模拟器(Simulator)
  • 性能分析工具(Instruments)
  • SwiftUI

1.1 Xcode 工作流的改进(Workflows)

主要的变化总结如下:

  • Library(代码块 Snippets 和图片资源预览),有花括号改成了 “+” 号。原先 Snippets、图片预览两项功能扩展为:SwiftUI 控件库、SwiftUI Modifiers、Snippets 代码块、图片资源预览(包括后面提到的 SPM 中的图片资源、Symbols)、Color 选择器。
  • Version Editor 中把 Authors(以作者为视角查看代码更改记录)、Logs(以提交时间为视角查看代码更改记录)移出(集成到 Editor Options 中),只保留 Code Review(Comparison 代码比对)功能。
  • 新增 Editor Options 和 Add Editor 功能区。其中 Editor Options 包含:展示/隐藏 SwiftUI 画布、展示/隐藏 Authors 视角、MiniMap 等。Add Editor 可以新增多个编辑窗口,终于不需要几个编辑器窗口来回切换了。
  • 新增 MiniMap 功能,有了代码地图预览功能,查找代码不再繁琐。搜索代码,MiniMap 也同步高亮显示。鼠标在 MiniMap 移动时,也会有方法高亮提示。
  • 其他功能:代码补全能力提升、拖拽代码能力提升、拼写检查等。

总结:Xcode 正在往好的方向发展,MiniMap、和 Add Editor 是个亮点,有了这两个重要功能加持,跟其他 IDE 相比起码在易用性上不落下风。

1.2 Swift 包管理工具(Swift Package Manager)

Xcode 11 集成了 Swift Package Manager,用来管理 Swift Package,终于有了自己的可视化包管理工具了。

  • 可以轻松的使用 GitHub、Bitbucket、GitLab,或者在自己主机上发布的 Swift 包。
  • Xcode 通过依赖分析自动的检索和管理包。
  • 创建自己的包,以便在应用程序之间共享代码,或者发布到社区。
  • 在工程配置选项中,选择 Swift Package 选项,点击添加即可添加指定账户下或指定三方库地址下的仓库。添加之后,即可在代码中使用。

总结:跟 SPM 命令行工具相比,可视化的包管理工具显得更简单直接。(虽然离跟 Cocoapods、Carthage 说拜拜了还需要很长的路要走......)。SPM 可视化工具的出现具有划时代的意义,体现了 Apple 致力于完善 Swift 生态的坚定决心。

1.3 代码管理(Source Control)

在 Source Control 下拉菜单中新增了 Cherry-Pick、Stash Changes 选项。

总结:Xcode Git GUI 工具虽然加入了 Stash、Cherry-pick 功能,但对某些版本管理需求复杂的业务场景远远不够, 而对于基本的开发来讲完全够用了。

1.4 设计工具(Design Tools)

  • 可以随时查看各设备上的效果以及做出更改。
  • 在开发和调试时,随时在暗黑和明亮模式之间进行切换。
  • Asset 目录可以使你轻松的控制图像和颜色在暗黑和明亮模式之间进行切换。
  • 可以方便的使用浏览所有 SF 符号。

总结:感觉 Xcode 11 中,不管 SwiftUI 还是 Symbols,都跟 Flutter 正面怼上了,不知接下来 Xcode 是不是也会提供类似于 Material Design 风格的控件集。

1.5 调试工具(Debug)

在 Xcode 11 中,可以通过模拟设备状态来调试代码(如网络、温度等)。

1.6 测试模块(Testing)

更新后的测试模块可以让你更好的控制测试工具:

  • 通过配置不同的 schemes 来实现测试代码的复用。
  • 自定义测试中包含的所有内容。
  • 可以指定多种配置选项。
  • 支持 iPad App for Mac 以及 SwifiUI 的测试。

1.7 模拟器(Simulator)

  • 直接在 Watch 模拟器上运行 Watch app -- 基本没用到。
  • 基于 Metal 多模拟器进行了更新,Metal 程序可以直接在模拟器上运行。
  • 模拟器 FPS 达到 60 帧每秒。
  • CPU 占用率减少至少 90% --NB。
  • 热启动至少提速 2 倍以上。

1.8 性能分析工具(Instruments)

  • 层次更加分明,更容易浏览和更正数据。
  • 完全重写了 Metal System Trace 模板,速度提升了 10 倍以上。

1.9 SwiftUI

SwiftUI 无疑是开发者最关注的一个新技术。它是一个新的 Swift 框架和附加的设计工具,为构建用户界面提供了全新的方法。SwiftUI 集编辑、运行、调试于一体。


二、iOS 13

2.1 CNCopyCurrentNetworkInfo 变化

从 iOS 13 开始,苹果将对 APP 中通过 CNCopyCurrentNetworkInfo API 来获取设备当前所连接的 wifi (获取到 SSID 和 BSSID) 的功能进行限制;

iOS 12 开始,调用该函数将默认返回 nil,需要在 Xcode 项目中开启「Access WiFi Information」后才会返回正确的值。而在 iOS 13 中,使用这项函数的条件将变得更为严格。除了开启「Access WiFi Information」以外,APP 还需要符合下列三项条件中的至少一项才会返回正确的 CNCopyCurrentNetworkInfo 函数值,否则仍然会返回 nil :

  • 使用定位功能,并且获得了定位服务权限的应用;
  • 使用 NEHotspotConfiguration 配置过的 Wi-Fi;
  • 应用程序已安装有效的 VPN 配置;

对于大部分的 APP 来说,可以通过使用定位功能,只需要写好获取定位服务权限的代码就可以解决 CNCopyCurrentNetworkInfo 无法调用的问题。

2.2 增加一直使用蓝牙的权限申请

CBCentralManager,iOS 13 以前,使用蓝牙时可以直接用,不会出现权限提示,iOS 13 后,再使用就会提示了。iOS 13 修改了蓝牙权限申请描述文字的键名。2019 年 9 月 18 日,Appstore 修改了审核规则,不符合调整的拒绝上线。 需要在 info.plist 里增加

<key>NSBluetoothAlwaysUsageDescription</key>
<string>我们需要一直使用您的蓝牙</string>

2.3 UIWebView 的废弃

iOS 有 UIWebview 和 WKWebview 两种 webview。从 iOS 13 开始苹果将 UIWebview 列为 Deprecated API。苹果未来将会停止使用 UIWebview 的应用上线,暂时还没有准确的停止时间(预计至少会留一段过渡时间)。

2.4 即将废弃的 LaunchImage

在 APP 启动进入的第一个页面就是 launchPage, launchPage的两种方式 LaunchScreen & LaunchImage,如果两者都设置了,那么会优先加载 LaunchScreen。

从 iOS 8 的时候,苹果就引入了 LaunchScreen,我们可以设置 LaunchScreen来作为启动页。当然,现在你还可以使用 LaunchImage 来设置启动图。不过使用 LaunchImage 的话,要求我们必须提供各种屏幕尺寸的启动图,来适配各种设备,随着苹果设备尺寸越来越多,这种方式显然不够 Flexible。而使用 LaunchScreen 的话,情况会变的很简单, LaunchScreen 是支持 AutoLayout + SizeClass 的,所以适配各种屏幕都不在话下。 注意啦,从 2020 年 4 月开始,所有使⽤ iOS13 SDK 的 APP 将必须提供 LaunchScreen,LaunchImage即将退出历史舞台。

2.5 第三方登录(Sign in with Apple)

当 Sign In with Apple 服务正式上线以后,所有已接入其它第三方登录的 App,Sign In with Apple 将被要求作为一种登录选择,否则有可能就不给过。如果 APP 支持三方登陆(Facbook、Google、微信、QQ、支付宝等),就必须支持苹果登录,且要放前边。

2.6 StatusBar

在 iOS 13 中,StatusBar也增加了一种模式,由之前的两种,变成了三种, 其中 default 由之前的黑色内容,变成了会根据系统模式,自动选择当前展示 lightContent 还是 darkContent

public enum UIStatusBarStyle : Int {
    case `default` // Automatically chooses light or dark content based on the user interface style

    @available(iOS 7.0, *)
    case lightContent // Light content, for use on dark backgrounds

    @available(iOS 13.0, *)
    case darkContent // Dark content, for use on light backgrounds
}

2.7 Frame 获取

只有等执行完 UIViewController 的 viewDidAppear 方法以后,才能获取到正确的值,在 viewDidLoad 等地方 frame Size 为 0,例如:

[[UIApplication sharedApplication] statusBarFrame];

2.8 暗黑模式

暗黑模式 是 iOS 13 的重头戏,原理:

  • 将同一个资源,创建出两种模式的样式。系统根据当前选择的样式,自动获取该样式的资源
  • 每次系统更新样式时,应用会调用当前所有存在的元素调用对应的一些重新方法,进行重绘视图,可以在对应的方法做相应的改动
2.8.1 资源文件适配
  • 创建一个 Assets 文件(或在现有的 Assets 文件中)
  • 新建一个图片资源文件(或者颜色资源文件、或者其他资源文件)
  • 选中该资源文件, 打开 Xcode ->View ->Inspectors ->Show Attributes Inspectors (或者 Option+Command+4 )视图,将 Apperances 选项 改为 Any,Dark
  • 执行完第三步,资源文件将会有多个容器框,分别为 Any Apperance 和 Dark Apperance. Any Apperance 应用于默认情况(Unspecified)与高亮情况(Light), Dark Apperance 应用于暗黑模式(Dark)
  • 代码默认执行时,就可以正常通过名字使用了,系统会根据当前模式自动获取对应的资源文件

注意
同一工程内多个Assets文件在打包后,就会生成一个Assets.car 文件,所以要保证Assets内资源文件的名字不能相同

UIView:

traitCollectionDidChange(_:)
layoutSubviews()
draw(_:)
updateConstraints()
tintColorDidChange()

UIViewController:

traitCollectionDidChange(_:)
updateViewConstraints()
viewWillLayoutSubviews()
viewDidLayoutSubviews()

UIPresentationController:

traitCollectionDidChange(_:)
containerViewWillLayoutSubviews()
containerViewDidLayoutSubviews()
2.8.2 全局关闭黑暗模式
方式一 配置plist文件

在 Info.plist 文件中,添加 UIUserInterfaceStyle key 名字为 User Interface Style 值为String,将 UIUserInterfaceStyle key 的值设置为 Light

在开发中,如果用的系统控件(如 cell、tableview 的背景色)未设置背景色(或者为透明),则进入暗黑模式后,控件背景色变为黑色。
可以每一个页面设置,当然也可以整体设置,
一般我们的 APP 都是在一个 window 下的,那就整体设置 APP 里的 window

方式二 代码关闭黑暗模式

强制关闭暗黑模式

#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if(@available(iOS 13.0,*)){
self.window.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}
#endif

单个界面不遵循暗黑模式

UIViewController 与 UIView 都新增一个属性 overrideUserInterfaceStyle

将 overrideUserInterfaceStyle 设置为对应的模式,则强制限制该元素与其子元素以设置的模式进行展示,不跟随系统模式改变进行改变

  • 设置 ViewController 的该属性, 将会影响视图控制器的视图和子视图控制器采用该样式
  • 设置 View 的该属性, 将会影响视图及其所有子视图采用该样式
  • 设置 Window 的该属性, 将会影响窗口中的所有内容都采用样式,包括根视图控制器和在该窗口中显示内容的所有演示控制器(UIPresentationController)

Refer

  • [iOS 13 features](https://www.apple.com/ios/ios-13/features/ "iOS 13 features")

  • [Xcode](https://developer.apple.com/xcode/whats-new/ "Xcode")