iOS 13 适配之旅(上)
前段时间官方开放了 iOS 13.0 的公测版本,不得不说,今年的 iOS 带给开发者的 “惊喜” 还真是蛮多的呀,不多说了,直接进入主题,开始进行新系统适配工作吧。首先,来看看 iOS 13 给开发者带来的变化:
- Dark Model 黑暗模式来袭
- Apple ID 快捷登录
- SF Symbols 系统内置图标库
- KVC 使用限制
- PresentViewController 默认跳转交互方式更改
- 3D Touch 按压力度变化
- UISegmentedControl 默认样式改变
- CNCopyCurrentNetworkInfo 变化
- UITabbar 层次发生改变,无法通过设置 shadowImage 去掉上面的线
- App 启动过程中,部分 View 可能无法实时获取到 Frame
- UIActivityIndicatorView style 变更
- UIStatusBar style 变更
Dark Model
这应该是 iOS 13 中最磨人的一个适配项了。
Dark Model 适配主要有以下几点: 1. UIColor 2. UIImage 3. UIBlurEffect
并且苹果规定: iOS 13.0 正式版发布以后,所有项目**必须强制适配**该功能,适配任务刻不容缓。
1. UIColor 适配
iOS 13 中,UIColor 不再是一种颜色,而是一个动态颜色。在 Light Mode 和 Dark Mode 可以分别设置不同的颜色。以下是官方提供的一些动态色:
1 | @property (class, nonatomic, readonly) UIColor *systemIndigoColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos); |
系统提供的动态 UIColor 样式和之前方法一样,不需要其他的适配操作。但在实际开发过程,系统提供的这些颜色还远远不够,因此我们需要创建更多的动态颜色。
在 iOS 13 中 UIColor 增加了两个初始化方法,使用以下方法可以创建动态 UIColor。
1 | + (UIColor *)colorWithDynamicProvider:(UIColor * (^)(UITraitCollection *traitCollection))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos); |
- 这两个方法要求传一个 block 进去
- 当系统在 LightMode 和 DarkMode 之间相互切换时就会触发此回调
- 这个 block 会返回一个 UITraitCollection 类
- 我们需要使用其属性 userInterfaceStyle,它是一个枚举类型,会告诉我们当前是 LightMode 还是DarkMode
1 | typedef NS_ENUM(NSInteger, UIUserInterfaceStyle) { |
实际使用代码演示
1 | + (UIColor *)darkWhiteColor { |
2. UIImage 适配
- 打开 Assets.xcassets
- 打开右侧工具栏,点击最后一栏,找到 Appearances,选择 Any,Dark
- 将两种模式下不同的图片资源都拖进去
或者,你可以这样写:
1 | + (UIImage *)imageWithNamed:(NSString *)name drakImageWithName:(NSString *)drakName { |
3. UIBlurEffect 适配
iOS 13 中,UIBlurEffect 增加了几个新的枚举值。
1 | UIBlurEffectStyleSystemUltraThinMaterial API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(watchos, tvos), |
其中,最上面的 5 项是会根据系统模式改变而自动而改变的(类似于动态 UIColor),后面的则是单一的模式。
实际使用代码演示
1 | UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; |
4. 其他
获取当前模式
1 | if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { |
监听模式切换
重写方法:
1 | - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { |
如何在模式切换时打印日志
在 Arguments 中的 Arguments Passed On Launch 里面添加下面这行命令。
UITraitCollectionChangeLoggingEnabled YES
Sign In with Apple
Sign In with Apple 也是 iOS 13 中的一个特色,或许是苹果爸爸眼红了三方登录市场,要求所有的 App 凡是有微信、微博、Facebook、Google 等第三方平台登陆的功能,都**必须强制增加** Sign In with Apple,并且要排在前面,在一屏内必须能发现其位置,不可通过滑动界面才能看到。Apple 为开发者提供了三种样式,更多资料见官方文档这里就不再过多介绍。
使用 Sign In With Apple 的流程为:
设置 ASAuthorizationAppleIDButton 相关布局,添加相应地授权处理;
获取授权码;
验证;
处理 Sign In With Apple 授权状态变化;
注意事项
- 和往常一样,需要在苹果后台打开 Sign In with Apple 选项,并且重新生成 Profiles 配置文件,并安装到 Xcode 11
- 在 Xcode 11 的 Signing & Capabilities中 添加 Sign In With Apple
如图所示:
开发工作
- 导入
#import <AuthenticationServices/AuthenticationServices.h>
- 遵循
ASAuthorizationControllerPresentationContextProviding
和ASAuthorizationControllerDelegate
协议
1 | if (@available(iOS 13.0, *)) { |
系统为我们提供了三种样式,分别是白底黑字无边框、白底黑字有边框、黑底白字无边框:
- ASAuthorizationAppleIDButtonStyleWhite,
- ASAuthorizationAppleIDButtonStyleWhiteOutline,
- ASAuthorizationAppleIDButtonStyleBlack,
1 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 |
在授权登录成功回调中,我们可以拿到以下几类数据
- UserID: 苹果用户唯一标识符,该值在同一个开发者账号下的所有 App 下是一样的,开发者可以用该唯一标识符与自己后台系统的账号体系绑定起来(这与国内的微信、QQ、微博等第三方登录流程基本一致)
- Verification data: Identity token, code,验证数据,用于传给开发者后台服务器,然后开发者服务器再向苹果的身份验证服务端验证,本次授权登录请求数据的有效性和真实性,详见 Sign In with Apple REST API
- Account information: 苹果用户信息,包括全名、邮箱等,注意:如果用户登录时拒绝提供真实的邮箱账号,苹果会生成虚拟的邮箱账号
- 验证: 关于验证的这一步,需要传递授权码给自己的服务端,自己的服务端调用苹果 API 去校验授权码 Generate and validate tokens。如果验证成功,可以根据 userIdentifier 判断账号是否已存在,若存在,则返回自己账号系统的登录态,若不存在,则创建一个新的账号,并返回对应的登录状态给 App.
监听授权状态变化
监听授权状态改变,并且做出相应处理。授权状态有:
- ASAuthorizationAppleIDProviderCredentialRevoked 授权状态失效(用户停止使用AppID 登录App)
- ASAuthorizationAppleIDProviderCredentialAuthorized 已授权(已使用AppleID 登录过App)
- ASAuthorizationAppleIDProviderCredentialNotFound 授权凭证缺失(可能是使用AppleID 登录过App)
处理改变有 2 种处理方式,一种是通过通知的方式,另一种是监听当前的 appleIDCredential.user 的授权状态。
- 监听 appleIDCredential.user 的授权状态
1 | #pragma mark - Private functions |
建议这部分代码可以放到 AppDelegate 的 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
中,判断是否需要展示出登录控制器.
- 使用通知的方式检测是否授权应用支持 Sign In With Apple 变化情况。如下的代码可以根据自己的业务场景去考虑放置的位置。
1 | // 添加苹果登录的状态通知 |