1. 程式人生 > >iOS 10 的各種適配問題

iOS 10 的各種適配問題

一、證書管理

用Xcode8開啟工程後,比較明顯的就是下圖了,這個是蘋果的新特性,可以幫助我們自動管理證書。建議大家勾選這個Automatically manage signing(Ps.但是在beat2版本我用的時候,完全不可以,GM版本竟然神奇的又好了。)


QQ20160913-8.png-96.9kB

下面我來說說可能會出現的問題:

1.Xcode未設定開發者賬號情況下的截圖


QQ20160913-0.png-38.5kB


解決辦法是:大家在Xcode的偏好設定中,新增蘋果賬號,即可。

2.裝置機器未新增進開發者的Device情況下的截圖


QQ20160913-2.png-33.7kB


解決辦法是:大家在官網將裝置新增進開發機後,陪下描述檔案重新下個描述檔案即可。

3.正常情況:Xcode配置登入開發者賬號後的圖片,耐心等待即可。


QQ20160913-1.png-25.1kB


等待完成之後的圖


QQ20160913-3.png-27kB

二、Xib檔案的注意事項

使用Xcode8開啟xib檔案後,會出現下圖的提示。


QQ20160913-9.png-41.7kB


大家選擇Choose Device即可。
之後大家會發現佈局啊,frame亂了,只需要更新一下frame即可。如下圖


QQ20160913-11.png-113.2kB
  • 注意:如果按上面的步驟操作後,在用Xcode7開啟Xib會報一下錯誤,

QQ20160913-12.png-32.3kB
  • 解決辦法:需要刪除Xib裡面
    <capability
    name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    這句話,以及把< document >中的toolsVersion和< plugIn >中的version改成你正常的xib檔案中的值
    ,不過不建議這麼做,在Xcode8出來後,希望大家都快速上手,全員更新。這就跟Xcode5到Xcode6一樣,有變動,但是還是要儘早學習,儘快適應喲!

三、程式碼及Api注意

使用Xcode8之後,有些程式碼可能就編譯不過去了,具體我就說說我碰到的問題。
1.UIWebView的代理方法:
**注意要刪除NSError前面的 nullable,否則報錯。

- (void)webView:(UIWebView *)webView didFailLoadWithError:(nullable NSError *)error
{
    [self hideHud];
}

四、程式碼註釋不能用的解決辦法

這個是因為蘋果解決xcode ghost,把外掛遮蔽了。
解決方法
開啟終端,命令執行: sudo /usr/libexec/xpccachectl
然後必須重啟電腦後生效

注意:Xcode8內建了開啟註釋的功能,位置在這裡


QQ20160914-3.png

快捷鍵的設定在這裡


QQ20160914-2.png

貌似Xcode8取消了三方外掛的功能,具體可以查閱下Xcode8 Source Editor

五、許可權以及相關設定

注意,新增的時候,末尾不要有空格
我們需要開啟info.plist檔案新增相應許可權的說明,否則程式在iOS10上會出現崩潰。
具體如下圖:


QQ20160914-0.png

麥克風許可權:Privacy - Microphone Usage Description 是否允許此App使用你的麥克風?
相機許可權: Privacy - Camera Usage Description 是否允許此App使用你的相機?
相簿許可權: Privacy - Photo Library Usage Description 是否允許此App訪問你的媒體資料庫?通訊錄許可權: Privacy - Contacts Usage Description 是否允許此App訪問你的通訊錄?
藍芽許可權:Privacy - Bluetooth Peripheral Usage Description 是否許允此App使用藍芽?

語音轉文字許可權:Privacy - Speech Recognition Usage Description 是否允許此App使用語音識別?
日曆許可權:Privacy - Calendars Usage Description 是否允許此App使用日曆?

定位許可權:Privacy - Location When In Use Usage Description 我們需要通過您的地理位置資訊獲取您周邊的相關資料
定位許可權: Privacy - Location Always Usage Description 我們需要通過您的地理位置資訊獲取您周邊的相關資料
定位的需要這麼寫,防止上架被拒。

六、字型變大,原有frame需要適配

經有的朋友提醒,發現程式內原來2個字的寬度是24,現在2個字需要27的寬度來顯示了。。
希望有解決辦法的朋友,評論告我一下耶,謝謝啦

七、推送

如下圖的部分,不要忘記開啟。所有的推送平臺,不管是極光還是什麼的,要想收到推送,這個是必須開啟的喲✌️


QQ20160914-4.png

之後就應該可以收到推送了。另外,極光推送也推出新版本了,大家也可以更新下。

PS.蘋果這次對推送做了很大的變化,希望大家多查閱查閱,處理推送的代理方法也變化了。

// 推送的代理
[<UNUserNotificationCenterDelegate>]

iOS10收到通知不再是在
[application: didReceiveRemoteNotification:]方法去處理, iOS10推出新的代理方法,接收和處理各類通知(本地或者遠端)

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { //應用在前臺收到通知 NSLog(@"========%@", notification);}- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler { //點選通知進入應用 NSLog(@"response:%@", response);}

稍後我會更新文章,對推送做一個詳細的講解。

8.遮蔽雜亂無章的bug

更新Xcode8之後,新建立工程,都會列印一堆莫名其妙看不懂的Log.
如這些

subsystem: com.apple.UIKit, category: HIDEventFiltered, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1,

遮蔽的方法如下:
Xcode8裡邊 Edit Scheme-> Run -> Arguments, 在Environment Variables裡邊新增
OS_ACTIVITY_MODE = Disable


QQ20160914-8.png

如果寫了之後還是列印log,請重新勾選對勾,就可以解決了

9.系統判斷方法失效

在你的專案中,當需要判斷系統版本的話,不要使用下面的方法:

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

它會永遠返回NO,substringToIndex:1在iOS 10 會被檢測成 iOS 1了,
應該使用下面的這些方法:
Objective-C 中這樣寫:

#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

或者使用:

if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){.majorVersion = 9, .minorVersion = 1, .patchVersion = 0}]) { NSLog(@"Hello from > iOS 9.1");}
if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){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 versions than iOS 9}

有時候會缺少一些常量,NSFoundationVersionNumber是在NSObjCRuntime.h中定義的,作為Xcode7.3.1的一部分,我們設定常熟範圍從iPhone OS 2到#define NSFoundationVersionNumber_iOS_8_4 1144.17,在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(majorVersion: 10, minorVersion: 0, patchVersion: 0)) { 
         // 程式碼塊
}

或者使用

if #available(iOS 10.0, *) { 
         // 程式碼塊
} else { 
         // 程式碼塊
}

10.UIColor的問題

官方文件中說:大多數core開頭的圖形框架和AVFoundation都提高了對擴充套件畫素和寬色域色彩空間的支援.通過圖形堆疊擴充套件這種方式比以往支援廣色域的顯示裝置更加容易。現在對UIKit擴充套件可以在sRGB的色彩空間下工作,效能更好,也可以在更廣泛的色域來搭配sRGB顏色.如果你的專案中是通過低級別的api自己實現圖形處理的,建議使用sRGB,也就是說在專案中使用了RGB轉化顏色的建議轉換為使用sRGB,在UIColor類中新增了兩個api:

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

11.真彩色的顯示

真彩色的顯示會根據光感應器來自動的調節達到特定環境下顯示與效能的平衡效果,如果需要這個功能的話,可以在info.plist裡配置(在Source Code模式下):

<key>UIWhitePointAdaptivityStyle</key>

它有五種取值,分別是:

<string>UIWhitePointAdaptivityStyleStandard</string> // 標準模式
<string>UIWhitePointAdaptivityStyleReading</string> // 閱讀模式
<string>UIWhitePointAdaptivityStylePhoto</string> // 圖片模式
<string>UIWhitePointAdaptivityStyleVideo</string> // 視訊模式
<string>UIWhitePointAdaptivityStyleStandard</string> // 遊戲模式

也就是說如果你的專案是閱讀類的,就選擇UIWhitePointAdaptivityStyleReading這個模式,五種模式的顯示效果是從上往下遞減,也就是說如果你的專案是圖片處理類的,你選擇的是閱讀模式,給選擇太好的效果會影響效能.

12.ATS的問題

1.在iOS 9的時候,預設非HTTS的網路是被禁止的,我們可以在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, 建議儘快停用SHA13DES演算法;

13.UIStatusBar的問題:

在iOS10中,如果還使用以前設定UIStatusBar型別或者控制隱藏還是顯示的方法,會報警告,方法過期,如下圖:


UIStatusBar的警告.png


上面方法到 iOS 10 不能使用了,要想修改UIStatusBar的樣式或者狀態使用下圖中所示的屬性或方法:

@property(nonatomic, readonly) UIStatusBarStyle preferredStatusBarStyle NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED; // Defaults to UIStatusBarStyleDefault
@property(nonatomic, readonly) BOOL prefersStatusBarHidden NS_AVAILABLE_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_PROHIBITED; // 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 affects changes to prefersStatusBarHidden.
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED; // Defaults to UIStatusBarAnimationFade

14.UITextField

在iOS 10 中,UITextField新增了textContentType欄位,是UITextContentType型別,它是一個列舉,作用是可以指定輸入框的型別,以便系統可以分析出使用者的語義.是電話型別就建議一些電話,是地址型別就建議一些地址.可以在#import <UIKit/UITextInputTraits.h>檔案中,檢視textContentType欄位,有以下可以選擇的型別:

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 UITextContentTypeOrganizationName          NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeLocation                  NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeFullStreetAddress         NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeStreetAddressLine1        NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeStreetAddressLine2        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 UITextContentTypeAddressCityAndState       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 UITextContentTypeTelephoneNumber           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 UITextContentTypeCreditCardNumber          NS_AVAILABLE_IOS(10_0);

15.UserNotifications(使用者通知)

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>

16.UICollectionViewCell的的優化

在iOS 10 之前,UICollectionView上面如果有大量cell,當用戶活動很快的時候,整個UICollectionView的卡頓會很明顯,為什麼會造成這樣的問題,這裡涉及到了iOS 系統的重用機制,當cell準備載入進螢幕的時候,整個cell都已經載入完成,等待在螢幕外面了,也就是整整一行cell都已經載入完畢,這就是造成卡頓的主要原因,專業術語叫做:掉幀.
要想讓使用者感覺不到卡頓,我們的app必須幀率達到60幀/秒,也就是說每幀16毫秒要重新整理一次.

iOS 10 之前UICollectionViewCell的生命週期是這樣的:
  • 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 prefetchItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(10_0);

- (void)collectionView:(UICollectionView *)collectionView cancelPrefetchingForItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths  NS_AVAILABLE_IOS(10_0);

ColletionView prefetchItemsAt indexPaths這個方法是非同步預載入資料的,當中的indexPaths陣列是有序的,就是item接收資料的順序;
CollectionView cancelPrefetcingForItemsAt indexPaths這個方法是可選的,可以用來處理在滑動中取消或者降低提前載入資料的優先順序.
注意:這個協議並不能代替之前讀取資料的方法,僅僅是輔助載入資料.
Pre-Fetching預載入對UITableViewCell同樣適用.

17. UIRefreshControl的使用

在iOS 10 中, UIRefreshControl可以直接在UICollectionView和UITableView中使用,並且脫離了UITableViewController.現在RefreshControl是UIScrollView的一個屬性.
使用方法:

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