iOS12、iOS11、iOS10、iOS9常見適配
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為 UIViewController
和 UIView
增加了兩個新的屬性 safeAreaInsets
和 safeAreaLayoutGuide
-
safeAreaInsets
適用於手動計算. -
safeAreaLayoutGuide
適用於自動佈局.
UIViewController中新增: - (void)viewSafeAreaInsetsDidChange; UIView中新增: - (void)viewSafeAreaInsetsDidChange; 複製程式碼
在 Storyboard
使用 Safe Area
最低只支援 iOS9
, iOS8
的使用者就要放棄了

當 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

- 給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。 複製程式碼
三、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的的優化
-
在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] 複製程式碼