阅读 2313

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.

解决办法二:

删除其他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为UIViewControllerUIView增加了两个新的属性safeAreaInsetssafeAreaLayoutGuide

  • safeAreaInsets 适用于手动计算.
  • safeAreaLayoutGuide 适用于自动布局.
UIViewController中新增:
- (void)viewSafeAreaInsetsDidChange;
UIView中新增:
- (void)viewSafeAreaInsetsDidChange;
复制代码

Storyboard使用Safe Area最低只支持iOS9iOS8的用户就要放弃了

UIViewController调用- (void)viewDidLoad时它的所有子视图的safeAreaInsets属性都等于UIEdgeInsetsZero

viewSafeAreaInsetsDidChange的调用时机如下:

  • 1、viewDidLoad
  • 2、viewWillAppear
  • 3、viewSafeAreaInsetsDidChange
  • 4、viewWillLayoutSubviews
  • 5、viewDidAppear

只有在调用viewSafeAreaInsetsDidChange后,才能获得view以及viewControllerSafeArea(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废弃了UIViewControllerautomaticallyAdjustsScrollViewInsets属性,新增了contentInsetAdjustmentBehavior属性,所以当超出安全区域时系统自动调整了SafeAreaInsets,进而影响了adjustedContentInset,在iOS11中决定tableView内容与边缘距离的是adjustedContentInset,所以需要设置UIScrollViewcontentInsetAdjustmentBehavior属性。

// 方式一:(不推荐)修改额外的安全区域
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

  • 给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。需要输入开发者账号!如果没有账号也没关系,在下面也可以选择DebugRealeaseinHouse模式下对应的证书也可以!

3.3、隐私数据访问问题

iOS10,苹果加强了对隐私数据的保护,要对隐私数据权限做一个适配,iOS10调用相机,访问通讯录,访问相册等都要在info.plist中加入权限访问描述,不然之前你们的项目涉及到这些权限的地方就会直接crash掉。


解决办法: 只需要在info.plist添加NSContactsUsageDescriptionkey, 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的的优化

  • 在iOS 10 之前,cell只能从重用队列里面取出,再走一遍生命周期,并调用cellForItemAtIndexPath创建或者生成一个cell.

  • 在iOS 10 中,系统会cell保存一段时间,也就是说当用户把cell滑出屏幕以后,如果又滑动回来,cell不用再走一遍生命周期了,只需要调用willDisplayCell方法就可以重新出现在屏幕中了.

  • iOS 10 中,系统是一个一个加载cell的,二以前是一行一行加载的,这样就可以提升很多性能;

  • iOS 10 新增加的Pre-Fetching预加载

3.7、UIRefreshControl

在iOS 10 中, UIRefreshControl可以直接在UICollectionView和UITableView中使用,并且脱离了UITableViewController.现在RefreshControl是UIScrollView的一个属性.

3.8、UserNotifications(用户通知)

  • iOS 10所有相关通知被统一到了UserNotifications.framework框架中。增加了撤销、更新、中途还可以修改通知的内容。通知不在是简单的文本了,可以加入视频、图片,自定义通知的展示等等。

  • iOS 10相对之前的通知来说更加好用易于管理,并且进行了大规模优化,对于开发者来说是一件好事。

  • 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。

解决办法二: 移除不支持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]
复制代码

参考资料:

iOS12适配

iOS12AdaptationTips

关于iPhone X 的适配

iOS11适配iPhoneX总结

iOS 10 适配知识点总结

聊聊iOS 10更新以后跳转系统设置的几种方式

iOS 10 调用系统"设置"里的功能(全)

iOS TouchID验证和Keychain结合使用

iOS10AdaptationTips

适配iOS9

微信 iOS 9 适配总结

iOS9AdaptationTips

关注下面的标签,发现更多相似文章
评论