iOS 10 的适配问题

Posted 喵渣渣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS 10 的适配问题相关的知识,希望对你有一定的参考价值。

前言

最近闲来无事,刚好看到之前做的一些关于ios10的适配问题的笔记,下面就来做一下归纳

1.系统判断方法失效

在你的项目中,当需要判断系统版本的话,不要使用下面的方法:

#define isiOS10 ([[[[UIDevice currentDevice] systemVersion] substri
     ngToIndex:1] intValue]>=10)

它永远返回NO,substring’ToIndex:1 在iOS 10 会被检测成iOS 1了,应该使用下面的方法:
Objective-C中这样写:

#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] syst
     emVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
     #define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice]
     systemVersion] compare:v options:NSNumericSearch] == NSOrderedDesce
     nding)
     #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice cur
     rentDevice] systemVersion] compare:v options:NSNumericSearch] != NS
     OrderedAscending)
     #define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] sys
     temVersion] compare:v options:NSNumericSearch] == NSOrderedAscendin
     g)
     #define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice curren
     tDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrd
     eredDescending)

或者


     if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(N
     SOperatingSystemVersion).majorVersion = 9, .minorVersion = 1, .pat
     chVersion = 0])  NSLog(@"Hello from > iOS 9.1");
     if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSO
     peratingSystemVersion)9,3,0])  NSLog(@"Hello from > iOS 9.3");

或者使用:

 if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_9_0)
      // do stuff for iOS 9 and newer else  // do stuff for older ver
     sions than iOS 9

有时候会缺少一些常量,NSFoundationVersionNumber是在NSObjecRuntime.h中定义的。在iOS 10(Xcode 8) 中,苹果补充了缺少的数字,设置有未来的版本。


     #define NSFoundationVersionNumber_iOS_9_0 1240.1
     #define NSFoundationVersionNumber_iOS_9_1 1241.14
     #define NSFoundationVersionNumber_iOS_9_2 1242.12
     #define NSFoundationVersionNumber_iOS_9_3 1242.12
     #define NSFoundationVersionNumber_iOS_9_4 1280.25
     #define NSFoundationVersionNumber_iOS_9_x_Max 1299

swift中这样写:

if NSProcessInfo().isOperatingSystemAtLeastVersion(NSOperatingSystemVersion(m
ajorVersion: 10, minorVersion: 0, patchVersion: 0)) 
    //  代码块  

或者使用

if #available(iOS 10.0, *)  //    
 else 
//    

2.隐私数据访问问题

你的项目中访问了隐私数据,比如:相机、相册、联系人等,在Xcode8中打开编译的话,统统会crash,控制台会输出下面这样的日志:

这是因为iOS对用户的安全和隐私的增强,在申请很多私有权限的时候都需要添加描述,但是,在使用Xcode 8之前的Xcode还是使用系统的权限通知框。
要解决这个问题,只需要在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>UIBackgroundModes</key>
<array>
<!-- 在这里写上你在后台模式下要使用的权限对应的key --> <string>location</string>
...
</array>

或者在Xcode中选中当前的target,选择Capabilities,找到Background Modes,打开它,在里面选择对应权限

3.UIColor的问题

官方文档中说:大多数core开头的图形框架和AVFoundation都提高了对扩展像素和宽色域色彩空间的支持。通过图形堆栈扩展这种方式比以往支持广色域的显示设备更加容易。限制对UIKit扩展可以在sRGB的色彩空间下工作,性能更好,也可以在更广泛的色域来搭配sRGB颜色。如果你对项目中是通过低级别的api自己实现图形处理的,建议使用sRGB,也就是说在项目中使用RGB转化颜色的,建议转为使用sRGB,在UIColor类汇总新增了两个api:


- (UIColor *)initWithDisplayP3Red:(CGFloat)displayP3Red green:(CGFl
oat)green blue:(CGFloat)blue alpha:(CGFloat)alpha NS_AVAILABLE_IOS(
10_0);
+ (UIColor *)colorWithDisplayP3Red:(CGFloat)displayP3Red green:(CGF
loat)green blue:(CGFloat)blue alpha:(CGFloat)alpha NS_AVAILABLE_IOS
(10_0);

4.真彩色的显示

真彩色的显示会根据光感应器来自动调节达到特定环境下显示域性能的平衡效果,如果需要这个功能的话,可以在info.plist里面配置(Source Code模式下):

<key>UIWhitePointAdaptivityStyle</key>

它有五种取值,分别是:


<string>UIWhitePointAdaptivityStyleStandard</string> //  标准模式 
<string>UIWhitePointAdaptivityStyleReading</string> //  阅读模式    
<string>UIWhitePointAdaptivityStylePhoto</string> //  图片模式
<string>UIWhitePointAdaptivityStyleVideo</string> //   视频模式
<string>UIWhitePointAdaptivityStyleStandard</string> //   游戏模式  

也就是说如果你的项目是阅读类的,就选择UIWhitePointAdaptivityStyleReading 这种模式,五种模式的显示效果是从上往下递减的,也就是说你的项目是图片处理类的,你选择的是阅读模式,给选择太好的效果会影响性能。

5.ATS的问题

1.在iOS 9的时候,默认非HTTPS得网络是被禁止的,我们可以在info.plist文件中添加NSAPPTransportSecurity字典,将NSAllowsArbitraryLoads设置为YES来禁用ATS;

2.从2017年1月1日起,所有提交的app默认不允许使用NSAllowsArbitraryLoads来然后ATS的限制,默认情况下你的app可以访问加密足够强的(TLS V1.2以上)HTTPS内容;

3.可以选择使用NSExceptionDomains设置白名单的方式对特定的域名开放HTTP内容来通过审核,比如说你的应用集成了第三方登录分享的SDK,可以通过这种方式来做,下面以新浪SDK作为示范(Source Code 模式下):

<key>NSAppTransportSecurity</key>
 <dict>
  <key>NSExceptionDomains</key>
  <dict>
   <key>sina.cn</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
   <key>weibo.cn</key>
   <dict>
<key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
   <key>weibo. com</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
   <key>sinaimg.cn</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
   <key>sinajs.cn</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
   <key>sina.com.cn</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
  </dict>
 </dict>

4.在iOS 10种info.plist文件新加入了NSAllowsArbitraryLoadsInWebContent键,允许任意web页面的加载,同时苹果会用ATS来保护你的app。

5.安全传输不在支持SSLv3,建议尽快使用SHA1和3DES算法。

6.UIStatusBar的问题

在iOS 10中,如果还使用以前设置UIStatusBar类型或者控制隐藏还是显示的方法,会报警告,方法过渡期,如下图

上面方法到iOS 10不能再使用了,想要修改UIStatusBar的样式或者状态使用下面的属性或者方法:


@property(nonatomic, readonly) UIStatusBarStyle preferredStatusBarS
tyle NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED; // Defaults to UIStat
usBarStyleDefault
@property(nonatomic, readonly) BOOL prefersStatusBarHidden NS_AVAIL
ABLE_IOS(7_0) __TVOS_PROHIBITED; // Defaults to NO
- (UIStatusBarStyle)preferredStatusBarStyle NS_AVAILABLE_IOS(7_0) _
_TVOS_PROHIBITED; // Defaults to UIStatusBarStyleDefault
- (BOOL)prefersStatusBarHidden NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBI
TED; // Defaults to NO
// Override to return the type of animation that should be used for
 status bar changes for this view controller. This currently only a
ffects changes to prefersStatusBarHidden.
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation NS_AVAILA
BLE_IOS(7_0) __TVOS_PROHIBITED; // Defaults to UIStatusBarAnimation
Fade

7.UITextField

在iOS 10中,UITextField新增了textContentType字段,是UITextContentType类型,它是一个枚举,作用是可以指定输入框的类型,以便系统可以分析出用户的语义。是电话类型就建议一些电话,是地址类型就建议一些地址。可以在#import

UIKIT_EXTERN UITextContentType const UITextContentTypeName
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeNamePrefix
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeGivenName
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeMiddleName
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeFamilyName
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeNameSuffix
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeNickname
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeJobTitle
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeOrganizationN
ame          NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeLocation
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeFullStreetAdd
ress         NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeStreetAddress
Line1        NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeStreetAddress
Line2        NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeAddressCity
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeAddressState
             NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeAddressCityAn
dState       NS_AVAILABLE_IOS(10_0);
     UIKIT_EXTERN UITextContentType const UITextContentTypeSublocality
                  NS_AVAILABLE_IOS(10_0);
     UIKIT_EXTERN UITextContentType const UITextContentTypeCountryName
                  NS_AVAILABLE_IOS(10_0);
     UIKIT_EXTERN UITextContentType const UITextContentTypePostalCode
                  NS_AVAILABLE_IOS(10_0);
     UIKIT_EXTERN UITextContentType const UITextContentTypeTelephoneNumb
     er           NS_AVAILABLE_IOS(10_0);
     UIKIT_EXTERN UITextContentType const UITextContentTypeEmailAddress
                  NS_AVAILABLE_IOS(10_0);
     UIKIT_EXTERN UITextContentType const UITextContentTypeURL
                  NS_AVAILABLE_IOS(10_0);
     UIKIT_EXTERN UITextContentType const UITextContentTypeCreditCardNum
     ber          NS_AVAILABLE_IOS(10_0);

8.UserNotification(用户通知)

iOS 10中将通知相关的API都统一了,在此基础上很多用户定义的通知,并且可以捕捉到各个通知状态的回调。以前通知的概念:大家想接受的提取做好准备,然后一下全分发,没收到也不管了,也不关心发送者,限制的用户通知做成了类似于网络请求,先发出一个request的到了response的流程,还封装了error,可以在各个状态的方法中做一些额外的操作,并且能获得一些字段,比如发送者之类的。这个功能的头文件是#import <UserNotifications/UserNotifications.h>
主要有以下文件:

#import <UserNotifications/NSString+UserNotifications.h>
#import <UserNotifications/UNError.h>
#import <UserNotifications/UNNotification.h>
#import <UserNotifications/UNNotificationAction.h>
#import <UserNotifications/UNNotificationAttachment.h>
#import <UserNotifications/UNNotificationCategory.h>
#import <UserNotifications/UNNotificationContent.h>
#import <UserNotifications/UNNotificationRequest.h>
#import <UserNotifications/UNNotificationResponse.h>
#import <UserNotifications/UNNotificationSettings.h>
#import <UserNotifications/UNNotificationSound.h>
#import <UserNotifications/UNNotificationTrigger.h>
#import <UserNotifications/UNUserNotificationCenter.h>
#import <UserNotifications/UNNotificationServiceExtension.h>

9.UICollectionViewCell的优化

在iOS 10 之前,UICollectionView上面有大量cell,当用户活动很快的时候,整个UICollectionView的卡顿会很铭心啊,为什么会造成这个问题,这里涉及到iOS系统的重用机制,当cell准备加载进入屏幕的时候,整个cell都已经完成加载,等待在屏幕外面了,也就是整整一行cell都已经加载完毕,这就是造成卡顿的主要原因,专业术语叫:掉帧。
要想让用感觉不到卡顿,我们的app必须帧率达到60帧每秒,也就是说每帧16毫秒要刷一下。

iOS 10之前UICollectionView的生命周期是这样的:

  • 1.用户滑动屏幕,屏幕外有一个cell准备加载进来,把cell从reusr队列拿出来,然后调用prepareForReuse方法,在这个方法里面,可以重置cell的状态,加载数据;

  • 2.继续滑动,就会调用cellForItemAtIndexPath方法,这个方法里面我们还可以修改cell,为进入屏幕做最后的准备工作。

  • 3.当cell马上进去屏幕的时候,就会调用willDisplayCell 方法,在这个方法里面我们呢还可以修改cell,为进入屏幕做最后的准备工作。

  • 4.执行完willDisplayCell 方法后,cell就进去屏幕了。当cell完全离开屏幕后,会调用didEndDisplayingCell方法。

iOS 10 UICollectionViewCell的生命周期是这样的:

  • 1.用户滑动屏幕,屏幕外有一个cell准备加载进来,把cell从reusr队列拿出来,然后调用prepareForReuse方法,在这里当cell还没有完全进去屏幕的时候,就已经提前调用这个方法了,对比之前的区别是之前是cell上的边缘马上进去屏幕的时候就会调用该方法,而iOS 10提前到cell还在屏幕之外就开始调用了;

  • 2.在cellForItemAtIndexPath中创建cell,填充数据,刷新状体等操作,相比之前也提前了;

  • 3.用户继续滑动的话,当cell马上就需要显示的时候,我们再调用willDisplayCell方法,原则是:何时需要显示,何时再去调用willDisplayCell方法;

  • 4.当cell完全离开屏幕以后,会调用didEndDisplayingCell方法,跟之前一样,cell会进入重用队列。在iOS 10以前,cell只能从重用队列里面取出来,再走一遍生命周期,并调用cellForItemAtIndexPath 创建或者生成一个cell。
    在iOS 10中,系统会让cell保存一段时间,也就是说当用户把cell滑出屏幕以后,如果又滑动回来,cell不用再走一遍生命周期了,只需要调用willDisplayCell方法就可以重新出现在屏幕中了。
    iOS 10中,系统是一个一个加载cell的,而以前是一行一行加载的,这样的性能可以提升很多。

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

这个是为了降低UICollectionViewCell在加载的时候所花费的时间,在iOS 10中,除了数据源协议和代理协议外,新增加了一个UICollectionViewDataSourcePrefetching协议,这个协议里面定义了两个方法:

- (void)collectionView:(UICollectionView *)collectionView prefetchItemsAtInde
xPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(10_0);
- (void)collectionView:(UICollectionView *)collectionView cancelPrefetchingFo
rItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths  NS_AVAILABLE_IOS(10_
0);

CollectionView prefetchItemsAt indexPaths这个方法是异步预加载数据的,当中的indexPaths数组是有序的,就是item接受数据的顺序;
CollectionView cancelPrefetcingForItemsAt indexPaths 这个方法是可选的,可以用来处理在滑动中取消或者降低提前加载数据的优先级。

注意:
这个协议并不能代替之前读取数据的方法,不仅仅是辅助加载数据。
Pre-Fetching预加载对UITableViewCell同样适用。

10.UIRefreshControl的使用

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


UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
    [refreshControl addTarget:self action:@selector(loadData) forControlEvent
s:UIControlEventValueChanged];
    collectionView.refreshControl = refreshControl;

以上是关于iOS 10 的适配问题的主要内容,如果未能解决你的问题,请参考以下文章

iOS 10 版本适配问题收集-b

iOS iOS10 的适配问题

iOS - 如何适配iOS10(插曲)

iOS 10 的适配问题

iOS 10 的各种适配问题

总结适配IOS10需要注意的问题