iOS12、iOS11、iOS10、iOS9常见适配
@(IOS各个版本适配)
[TOC]
一、iOS12(Xcode10)
1.1、升级Xcode10后项目报错
不允许多个info.plist
Xcode10是默认选中的最新的New Build System(Default)
,在这个编译系统的环境下,不允许多个info.plist
解决办法一:(推荐)
把build system
切换到 Legacy Build System
,换言之就是切换成老的编译系统,就OK了。Xcode->File->Project Settings-> Build System -> Legacy Build System.
image
<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>
image
<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>
解决办法二:
删除其他info.plist文件。
iOS 12移除了libstdc++, 用libc++替代
Xcode10中libstdc++相关的3个库(libstdc++、libstdc++.6、libstdc++6.0.9)应该都是被彻底废弃了,如果你使用的三方库中有依赖,请尽快和提供方沟通,告知他们迁移吧。如果自己开发使用,也尽快考虑迁移的事宜吧。
1.2、iPhone XR不支持3D-Touch
OC检测代码
if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) { } 复制代码
swift检测代码
self.traitCollection.forceTouchCapability == .availible 复制代码
二、iOS11(Xcode9)
2.1、安全区域(SafeArea)
iOS11为
UIViewController
和UIView
增加了两个新的属性safeAreaInsets
和safeAreaLayoutGuide
safeAreaInsets
适用于手动计算.safeAreaLayoutGuide
适用于自动布局.
UIViewController中新增: - (void)viewSafeAreaInsetsDidChange; UIView中新增: - (void)viewSafeAreaInsetsDidChange; 复制代码
在
Storyboard
使用Safe Area
最低只支持iOS9
,iOS8
的用户就要放弃了
<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>
当
UIViewController
调用- (void)viewDidLoad
时它的所有子视图的safeAreaInsets
属性都等于UIEdgeInsetsZero
。
viewSafeAreaInsetsDidChange
的调用时机如下:
- 1、
viewDidLoad
- 2、
viewWillAppear
- 3、
viewSafeAreaInsetsDidChange
- 4、
viewWillLayoutSubviews
- 5、
viewDidAppear
只有在调用
viewSafeAreaInsetsDidChange
后,才能获得view
以及viewController
的SafeArea(UIEdgeInsets)
。因此在viewDidload
中根据SafeArea
设置界面会有问题。
iPhone X:有导航栏的时候可以+44
竖屏 safeAreaInsets
= (top
= 44, left
= 0, bottom
= 34, right
= 0)
横屏 safeAreaInsets
= (top
= 0, left
= 44, bottom
= 21, right
= 44)
#import "Adaptive11VC.h" static inline UIEdgeInsets sgm_safeAreaInset(UIView *view) { if (@available(iOS 11.0, *)) { return view.safeAreaInsets; } return UIEdgeInsetsZero; } @interface Adaptive11VC () @end @implementation Adaptive11VC - (void)viewDidLoad { [super viewDidLoad]; } - (void)testSafeArea { UIEdgeInsets safeAreaInsets = sgm_safeAreaInset(self.view); NSLog(@"safeAreaInsets = %@", NSStringFromUIEdgeInsets(safeAreaInsets)); } - (void)viewSafeAreaInsetsDidChange { [super viewSafeAreaInsetsDidChange]; [self testSafeArea]; } @end 复制代码
2.2、UIScrollView
iOS 11废弃了UIViewController
的automaticallyAdjustsScrollViewInsets
属性,新增了contentInsetAdjustmentBehavior
属性,所以当超出安全区域时系统自动调整了SafeAreaInsets
,进而影响了adjustedContentInset
,在iOS11中决定tableView
内容与边缘距离的是adjustedContentInset
,所以需要设置UIScrollView
的contentInsetAdjustmentBehavior
属性。
// 方式一:(不推荐)修改额外的安全区域 if (@available(iOS 11.0, *)) { self.additionalSafeAreaInsets = UIEdgeInsetsMake(-44, 0, 0, 0); } else { // Fallback on earlier versions } // 方式二:(推荐)设置为不自动调整 if (@available(iOS 11.0, *)) { // 作用于指定的UIScrollView self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; // 作用与所有的UIScrollView UIScrollView.appearance.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } else { self.automaticallyAdjustsScrollViewInsets = NO; } 复制代码
2.3、tableview问题
iOS11开始UITableView
开启了自动估算行高,estimatedRowHeight``estimatedSectionHeaderHeight
estimatedSectionFooterHeight
三个高度估算属性由默认的0变成了UITableViewAutomaticDimension
,如果不实现-tableView: viewForFooterInSection:
和 -tableView: viewForHeaderInSection:
,那么estimatedRowHeight
estimatedSectionHeaderHeight
estimatedSectionFooterHeight
三个高度估算属性由默认的0变成了UITableViewAutomaticDimension
,导致高度计算不对,会产生空白。解决方法是实现对应方法或吧这三个属性设为0。
2.4、LocalAuthentication 本地认证
本地认证框架提供了从具有指定安全策略(密码或生物学特征)的用户请求身份验证的功能。例如,要求用户仅使用Face ID或Touch ID进行身份验证,可使用以下代码:
#import <LocalAuthentication/LocalAuthentication.h> /** 检测TouchID是否可用 */ - (void)checkBiometrics { LAContext *context = [[LAContext alloc] init]; BOOL success = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil]; if ( success ) { NSLog(@"can use"); } else { NSLog(@"can`t use "); } } /** 在验证TouchID可用的情况下使用 */ - (void)excuteBiometrics { LAContext *context = [[LAContext alloc] init]; context.localizedFallbackTitle = @"自定义标题"; [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"为什么使用TouchID写这里" reply:^(BOOL success, NSError * _Nullable error) { if ( success ) { // 指纹验证成功 } else { switch (error.code) { case LAErrorUserFallback:{ NSLog(@"用户选择输入密码"); break; } case LAErrorAuthenticationFailed:{ NSLog(@"验证失败"); break; } case LAErrorUserCancel:{ NSLog(@"用户取消"); break; } case LAErrorSystemCancel:{ NSLog(@"系统取消"); break; } // 以下三种情况如果提前检测TouchID是否可用就不会出现 case LAErrorPasscodeNotSet:{ break; } case LAErrorTouchIDNotAvailable:{ break; } case LAErrorTouchIDNotEnrolled:{ break; } default: break; } } }]; } 复制代码
2.5、启动图的适配
方法一:通过LaunchScreen.storyboard方式启动
方法二:使用Assets中的LaunchImage
<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>
- 给Brand Assets添加一张1125*2436大小的图片
- 打开Assets.xcassets文件夹,找到Brand Assets
- 右键Show in Finder
- 添加一张1125*2436大小的图片
- 修改Contents.json文件,添加如下内容
{ "extent" : "full-screen", "idiom" : "iphone", "subtype" : "2436h", "filename" : "1125_2436.png", "minimum-system-version" : "11.0", "orientation" : "portrait", "scale" : "3x" } 复制代码
2.6、定位相关
在 iOS 11 中必须支持
When In Use
授权模式(NSLocationWhenInUseUsageDescription
),在 iOS 11 中,为了避免开发者只提供请求 Always 授权模式这种情况,加入此限制,如果不提供When In Use
授权模式,那么Always
相关授权模式也无法正常使用。
如果要支持老版本,即 iOS 11 以下系统版本,那么建议在 info.plist 中配置所有的 Key(即使 NSLocationAlwaysUsageDescription
在 iOS 11及以上版本不再使用):
NSLocationWhenInUseUsageDescription NSLocationAlwaysAndWhenInUseUsageDescription NSLocationAlwaysUsageDescription NSLocationAlwaysAndWhenInUseUsageDescription // 为 iOS 11 中新引入的一个 Key。 复制代码
2.7、iOS11中 UIKit’s Bars 上的变化
三、iOS10(Xcode8)
3.1、(Why?Safe!)插件取消
Xcode8取消了三方插件(很多优秀的插件,本来可以显著提高效率)的功能,使用Extension代替 Xcode 8 Extension 推荐
3.2、证书问题
为了方便用户来管理,提供Automatically manage signing
。需要输入开发者账号!如果没有账号也没关系,在下面也可以选择Debug
、Realease
、inHouse
模式下对应的证书也可以!
3.3、隐私数据访问问题
iOS10,苹果加强了对隐私数据的保护,要对隐私数据权限做一个适配,iOS10调用相机,访问通讯录,访问相册等都要在info.plist中加入权限访问描述,不然之前你们的项目涉及到这些权限的地方就会直接crash掉。
解决办法: 只需要在info.plist
添加NSContactsUsageDescription
的key
, value
自己随意填写就可以,这里列举出对应的key(Source Code模式下):
<key>NSPhotoLibraryUsageDescription</key><string>App需要您的同意,才能访问相册</string> <key>NSCameraUsageDescription</key><string>App需要您的同意,才能访问相机</string> <key>NSMicrophoneUsageDescription</key><string>App需要您的同意,才能访问麦克风</string> <key>NSLocationUsageDescription</key><string>App需要您的同意,才能访问位置</string> <key>NSLocationWhenInUseUsageDescription</key><string>App需要您的同意,才能在使用期间访问位置</string> <key>NSLocationAlwaysUsageDescription</key><string>App需要您的同意,才能始终访问位置</string> <key>NSCalendarsUsageDescription</key><string>App需要您的同意,才能访问日历</string> <key>NSRemindersUsageDescription</key><string>App需要您的同意,才能访问提醒事项</string> <key>NSMotionUsageDescription</key><string>App需要您的同意,才能访问运动与健身</string> <key>NSHealthUpdateUsageDescription</key><string>App需要您的同意,才能访问健康更新 </string> <key>NSHealthShareUsageDescription</key><string>App需要您的同意,才能访问健康分享</string> <key>NSBluetoothPeripheralUsageDescription</key><string>App需要您的同意,才能访问蓝牙</string> <key>NSAppleMusicUsageDescription</key><string>App需要您的同意,才能访问媒体资料库</string> 复制代码
隐私数据 | 对应key值 |
相册 | NSPhotoLibraryUsageDescription |
相机 | NSCameraUsageDescription |
麦克风 | NSMicrophoneUsageDescription |
位置 | NSLocationUsageDescription |
在使用期间访问位置 | NSLocationWhenInUseUsageDescription |
始终访问位置 | NSLocationAlwaysUsageDescription |
日历 | NSCalendarsUsageDescription |
提醒事项 | NSRemindersUsageDescription |
运动与健身 | NSMotionUsageDescription |
健康更新 | NSHealthUpdateUsageDescription |
健康分享 | NSHealthShareUsageDescription |
蓝牙 | NSBluetoothPeripheralUsageDescription |
媒体资料库 | NSAppleMusicUsageDescription |
显示详细信息
3.4、跳转到app内的隐私数据设置页面
iOS 10 干掉了所有系统设置的 URL Scheme,这意味着你再也不可能直接跳转到系统设置页面(比如 WiFi、蜂窝数据、定位等)。
跳转方式
方式一:prefs:root=某项服务 适用于 小于 iOS10的系统; NSURL *url = [NSURL URLWithString:@"prefs:root=WIFI"];
方式二:prefs:root=bundleID 适用于 大于等于iOS8系统,小于iOS10的系统 NSURL *url = [NSURL URLWithString:@"prefs:root=bundleID"];
方式三:UIApplicationOpenSettingsURLString 适用于 大于等于iOS8的系统 NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
// iOS系统版本 >= 10.0 { NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url]; } } return; // iOS系统版本 >= 10.0 // But! 不建议这样做哦,官方文档中说过: // `URL is now considered a private API and use will result in app rejection`. // 虽然是有可能躲过苹果的检测,但是苹果如果发现你这样用了,app上架是有被拒的风险的. { NSURL *url = [NSURL URLWithString:@"APP-Prefs:root=WIFI"]; if ([[UIApplication sharedApplication] canOpenURL:url]) { if (@available(iOS 10.0, *)) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; } else { // Fallback on earlier versions } } } // iOS系统版本 < 10.0 { NSURL *url = [NSURL URLWithString:@"prefs:root=WIFI"]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url]; } } 复制代码
跳转目的地
- iOS系统版本 <= iOS7 , 只能跳转到 系统设置页面
- iOS系统版本 >= iOS8 ,支持跳转到第三方应用的设置界面中。使用
prefs:root=bundleID ,bundleID
是你第三方应用工程的唯一ID - iOS系统版本 >= iOS10,支持跳转到自己应用设置,不支持跳转到系统设置
3.5、字体变化
苹果的默认字体会随着iOS系统版本的不同而不同,iOS10中字体变大了。导致了原来的显示有问题,会造成...的出现。暂时没有好的解决办法,需要自己在一个个适配一下!
3.6、UICollectionViewCell的的优化
- <input checked="" disabled="" type="checkbox" style="font-family: inherit; font-size: inherit; font-style: inherit; font-variant-caps: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; overflow: visible; box-sizing: border-box; padding: 0px;"> 在iOS 10 之前,cell只能从重用队列里面取出,再走一遍生命周期,并调用cellForItemAtIndexPath创建或者生成一个cell.
- <input checked="" disabled="" type="checkbox" style="font-family: inherit; font-size: inherit; font-style: inherit; font-variant-caps: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; overflow: visible; box-sizing: border-box; padding: 0px;"> 在iOS 10 中,系统会cell保存一段时间,也就是说当用户把cell滑出屏幕以后,如果又滑动回来,cell不用再走一遍生命周期了,只需要调用willDisplayCell方法就可以重新出现在屏幕中了.
- <input checked="" disabled="" type="checkbox" style="font-family: inherit; font-size: inherit; font-style: inherit; font-variant-caps: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; overflow: visible; box-sizing: border-box; padding: 0px;"> iOS 10 中,系统是一个一个加载cell的,二以前是一行一行加载的,这样就可以提升很多性能;
- <input checked="" disabled="" type="checkbox" style="font-family: inherit; font-size: inherit; font-style: inherit; font-variant-caps: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; overflow: visible; box-sizing: border-box; padding: 0px;"> iOS 10 新增加的Pre-Fetching预加载
3.7、UIRefreshControl
在iOS 10 中, UIRefreshControl可以直接在UICollectionView和UITableView中使用,并且脱离了UITableViewController.现在RefreshControl是UIScrollView的一个属性.
3.8、UserNotifications(用户通知)
- <input checked="" disabled="" type="checkbox" style="font-family: inherit; font-size: inherit; font-style: inherit; font-variant-caps: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; overflow: visible; box-sizing: border-box; padding: 0px;"> iOS 10所有相关通知被统一到了UserNotifications.framework框架中。增加了撤销、更新、中途还可以修改通知的内容。通知不在是简单的文本了,可以加入视频、图片,自定义通知的展示等等。
- <input checked="" disabled="" type="checkbox" style="font-family: inherit; font-size: inherit; font-style: inherit; font-variant-caps: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; overflow: visible; box-sizing: border-box; padding: 0px;"> iOS 10相对之前的通知来说更加好用易于管理,并且进行了大规模优化,对于开发者来说是一件好事。
- <input checked="" disabled="" type="checkbox" style="font-family: inherit; font-size: inherit; font-style: inherit; font-variant-caps: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; overflow: visible; box-sizing: border-box; padding: 0px;"> iOS 10开始对于权限问题进行了优化,申请权限就比较简单了(本地与远程通知集成在一个方法中)。
四、iOS9(Xcode7)
4.1、Bitcode
Xcode7 默认启用 Bitcode,但是如果我们用到的第三方库编译时还没启用 Bitcode,主工程就会编译不过。Bitcode 是苹果 App Thinning 的机制之一,可以减少安装包的大小。App store 会将这个 Bitcode 编译为可执行的64位或32位程序。
解决办法一: 最简单的解决办法是先把 Bitcode 关掉:把 Build settings - Build Options - Enable Bitcode 改为 NO。
<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>
解决办法二: 移除不支持BitCode的平台SDK,或者寻找支持BitCode的替代品,或者联系SDK方支持BitCode。
4.2、HTTP 请求失败
iOS9 默认不支持 HTTP 请求,需要改用更安全的 HTTPS(默认用 TLS 1.2)。苹果还提供了配置,使得所有安全性更低的网络请求也能使用,解决方案就是在 info.plist 里面增加以下配置:
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict> 复制代码
如果复杂一些,还可以指定白名单域名,声明所支持 TLS 的最低版本。另外需要注意的是,即使写了上述配置,在 HTTPS 页面中,HTTP 的 javascript 或 css 不会被加载,因为苹果认为这降低了页面的安全性。
4.3、canOpenUrl 限制
canOpenUrl 可以用来判断用户是否安装了某个 APP。也许是出于用户隐私的考虑,iOS9 上对 canOpenUrl 做了限制,最多只能对 50 个 scheme 做判断。如果是用 Xcode7 编译,需要在 plist 里面声明这些 scheme,没有声明的会直接返回 NO:
<key>LSApplicationQueriesSchemes</key> <array> <string>weixin</string> <string>wechat</string> </array> 复制代码
4.4、UIStatusBar的问题
iOS9中废弃的方法
// 修改状态栏的样式为白色 // 'setStatusBarStyle(_:animated:)' was deprecated in iOS 9.0: Use -[UIViewController preferredStatusBarStyle] UIApplication.shared.setStatusBarStyle(.lightContent, animated: true) // 隐藏状态栏 // 'setStatusBarHidden(_:with:)' was deprecated in iOS 9.0: Use -[UIViewController prefersStatusBarHidden] UIApplication.shared.setStatusBarHidden(true, with: .fade) 复制代码
用下面两个方法替换
-[UIViewController preferredStatusBarstyle] -[UIViewController preferredStatusBarHidden] 复制代码