iOS 13 適配要點總結(jié)

iOS 13 支持適配的機型

iPhone 11碍舍、iPhone 11 Pro既忆、iPhone 11 Pro Max

iPhone X驱负、iPhone XR嗦玖、iPhone XS、iPhone XS Max

iPhone 8跃脊、iPhone 8 Plus

iPhone 7宇挫、iPhone 7 Plus

iPhone 6s、iPhone 6s Plus

iPhone SE

iPod touch (第七代)

后續(xù)發(fā)布的新版本 iPhone

適配時間節(jié)點要求

2019 年 11 月 5 日

蘋果在當天正式棄用?Xcode 11.2?版本酪术,需要更新至?Xcode 11.2.1?或之后的版本進行打包提審器瘪。

2020 年 6 月 30 日

蘋果在 2020.03.26 延遲了適配的時間節(jié)點,可見?Deadline for App Updates has Been Extended

所有提交到?AppStore?的新應(yīng)用和應(yīng)用更新必須使用?iOS 13?的 SDK 進行編譯打包绘雁。并支持所有對應(yīng)所有設(shè)備的屏幕尺寸橡疼,詳請可見?Submit Your iPhone Apps to the App Store?以及?Submit Your iPad Apps to the App Store?

所有應(yīng)用必須使用?Xcode?storyboard?提供支持所有屏幕尺寸的啟動界面庐舟,詳請可見?Building Adaptive User Interfaces for iPhone and iPad;

現(xiàn)有的應(yīng)用和應(yīng)用中欣除,更新符合蘋果審核規(guī)則中關(guān)于接入?Sign In With Apple?功能要求的必須進行接入 ,具體要求可以見下文內(nèi)容挪略。

所有 AppWatch 應(yīng)用必須使用?watchOS 6?或之后版本的 SDK 進行編譯打包历帚。

Kids category 中的應(yīng)用必須符合審核指南中的 guideline 1.3 、guideline 5.1.4杠娱。

使用 HTML 5 的應(yīng)用必須符合審核指南的 guideline 4.7 sections 4, 5, and 6挽牢。

2020 年 4 月

新應(yīng)用必須使用?WKWebView?代替?UIWebView,詳請可見?Updating Apps that Use Web Views摊求;

2020 年 12 月

應(yīng)用更新必須使用?WKWebView?代替?UIWebView禽拔,詳請可見?Updating Apps that Use Web Views

新特性適配

1. Dark Mode

iOS 13 推出暗黑模式睹簇,UIKit?提供新的系統(tǒng)顏色和 api 來適配不同顏色模式奏赘,xcassets?對素材適配也做了調(diào)整,具體適配可見:?Implementing Dark Mode on iOS太惠。

Dark Mode 不是必須適配磨淌,但前提是你需要確保應(yīng)用在切換主題后不會影響到用戶使用(比如說文字和背景顏色相同可能會影響使用)。如果不打算適配 Dark Mode凿渊,可以直接在?Info.plist?中添加一欄:User Interface Style?:?Light梁只,即可在應(yīng)用內(nèi)禁用暗黑模式。

另外埃脏,即使設(shè)置了顏色方案搪锣,申請權(quán)限的系統(tǒng)彈窗還是會依據(jù)系統(tǒng)的顏色進行顯示,自己創(chuàng)建的?UIAlertController?就不會彩掐。

2. Sign In with Apple

在 iOS 13 中蘋果推出一種在 App 和網(wǎng)站上快速构舟、便捷登錄的方式:?Sign In With Apple。這是 iOS 13 新增的功能堵幽,因此需要使用 Xcode 11 進行開發(fā)狗超。關(guān)于應(yīng)用是否要求接入此登錄方式弹澎,蘋果在?App Store 應(yīng)用審核指南?中提到:

Apps that exclusively use a third-party or social login service (such as Facebook Login, Google Sign-In, Sign in with Twitter, Sign In with LinkedIn, Login with Amazon, or WeChat Login) to set up or authenticate the user’s primary account with the app must also offer Sign in with Apple as an equivalent option.

如果你的應(yīng)用使用了第三方或社交賬號登錄服務(wù)(如Facebook、Google努咐、Twitter苦蒿、LinkedIn、Amazon渗稍、微信等)來設(shè)置或驗證用戶的主賬號佩迟,就必須把?Sign In With Apple?作為同等的選項添加到應(yīng)用上。如果是下面這些類型的應(yīng)用則不需要添加:

僅僅使用公司內(nèi)部賬號來注冊和登錄的應(yīng)用竿屹;

要求用戶使用現(xiàn)有的教育或企業(yè)賬號進行登錄的教育报强、企業(yè)或商務(wù)類型的應(yīng)用;

使用政府或業(yè)界支持的公民身份識別系統(tǒng)或電子標識對用戶進行身份驗證的應(yīng)用羔沙;

特定第三方服務(wù)的應(yīng)用躺涝,用戶需要直接登錄其郵箱厨钻、社交媒體或其他第三方帳戶才能訪問其內(nèi)容扼雏。

另外需要注意,關(guān)于何時要求接入?Sign In With Apple夯膀,蘋果在?News and Updates?中提到:

Starting today, new apps submitted to the App Store must follow these guidelines. Existing apps and app updates must follow them by April 2020.

2019 年 9 月 12 日 起诗充,提交到 App Store 的新應(yīng)用必須按照應(yīng)用審核指南中的標準進行接入;現(xiàn)有應(yīng)用和應(yīng)用更新必須也在 2020 年 4 月前完成接入诱建。

API 適配

1.?私有方法 KVC 可能導(dǎo)致崩潰

在 iOS 13 中部分方法屬性不允許使用?valueForKey蝴蜓、setValue:forKey:?來獲取或者設(shè)置私有屬性,具體表現(xiàn)為在運行時會直接崩潰俺猿,并提示以下崩潰信息:

*** Terminating app due to uncaught exception'NSGenericException', reason:'Access to UISearchBar's _searchField ivar is prohibited. This is an application bug'復(fù)制代碼

解決方案

目前整理的會導(dǎo)致崩潰的私有 api 和對應(yīng)替代方案如下茎匠,感謝?@君賞?的反饋,也歡迎各位大佬補充和指正 :

// 崩潰 apiUITextField *textField = [searchBar valueForKey:@"_searchField"];// 替代方案 1押袍,使用 iOS 13 的新屬性 searchTextFieldsearchBar.searchTextField.placeholder = @"search";// 替代方案 2诵冒,遍歷獲取指定類型的屬性- (UIView *)findViewWithClassName:(NSString *)classNameinView:(UIView *)view{? ? Class specificView = NSClassFromString(className);if([view isKindOfClass:specificView]) {returnview;? ? }if(view.subviews.count > 0) {for(UIView *subViewinview.subviews) {? ? ? ? ? ? UIView *targetView = [self findViewWithClassName:classNameinView:subView];if(targetView != nil) {returntargetView;? ? ? ? ? ? }? ? ? ? }? ? }returnnil;}// 調(diào)用方法 UITextField *textField = [self findViewWithClassName:@"UITextField"inView:_searchBar];復(fù)制代碼

// 崩潰 api[searchBarsetValue:@"取消"forKey:@"_cancelButtonText"];// 替代方案,用同上的方法找到子類中 UIButton 類型的屬性谊惭,然后設(shè)置其標題UIButton *cancelButton = [self findViewWithClassName:NSStringFromClass([UIButton class])inView:searchBar];[cancelButtonsetTitle:@"取消"forState:UIControlStateNormal];復(fù)制代碼

// 崩潰 api汽馋。獲取 _placeholderLabel 不會崩潰,但是獲取 _placeholderLabel 里的屬性就會[textFieldsetValue:[UIColor blueColor]forKeyPath:@"_placeholderLabel.textColor"];[textFieldsetValue:[UIFont systemFontOfSize:20]forKeyPath:@"_placeholderLabel.font"];// 替代方案 1圈盔,去掉下劃線豹芯,訪問 placeholderLabel[textFieldsetValue:[UIColor blueColor]forKeyPath:@"placeholderLabel.textColor"];[textFieldsetValue:[UIFont systemFontOfSize:20]forKeyPath:@"placeholderLabel.font"];// 替代方案 2textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"輸入"attributes:@{? ? NSForegroundColorAttributeName: [UIColor blueColor],? ? NSFontAttributeName: [UIFont systemFontOfSize:20]}];復(fù)制代碼

2. 推送的 deviceToken 獲取到的格式發(fā)生變化

原本可以直接將?NSData?類型的?deviceToken?轉(zhuǎn)換成?NSString?字符串,然后替換掉多余的符號即可:

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {? ? NSString *token = [deviceToken description];for(NSString *symbolin@[@" ", @"<", @">", @"-"]) {? ? ? ? token = [token stringByReplacingOccurrencesOfString:symbol withString:@""];? ? }? ? NSLog(@"deviceToken:%@", token);}復(fù)制代碼

在 iOS 13 中驱敲,這種方法已經(jīng)失效铁蹈,NSData類型的 deviceToken 轉(zhuǎn)換成的字符串變成了:

{length = 32, bytes = 0xd7f9fe34 69be14d1 fa51be22 329ac80d ... 5ad13017 b8ad0736 }復(fù)制代碼

解決方案

需要進行一次數(shù)據(jù)格式處理,友盟提供了一種做法众眨,可以適配新舊系統(tǒng):

#include <arpa/inet.h>- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {if(![deviceToken isKindOfClass:[NSData class]])return;? ? const unsigned *tokenBytes = [deviceToken bytes];? ? // 數(shù)據(jù)格式處理? ? NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",? ? ? ? ? ? ? ? ? ? ? ? ? ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),? ? ? ? ? ? ? ? ? ? ? ? ? ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),? ? ? ? ? ? ? ? ? ? ? ? ? ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];? ? NSLog(@"deviceToken:%@", hexToken);}復(fù)制代碼

但是注意到這種方法限定了長度握牧,而官網(wǎng)文檔對此方法的說明中提到便锨,APNs device tokens are of variable length. Do not hard-code their size.?,因此可以對數(shù)據(jù)格式處理部分進行優(yōu)化:

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {if(![deviceToken isKindOfClass:[NSData class]]) {return;? ? }? ? const unsigned char *tokenBytes = deviceToken.bytes;? ? NSInteger count = deviceToken.length;? ? ? ? // 數(shù)據(jù)格式處理? ? NSMutableString *hexToken = [NSMutableString string];for(int i = 0; i < count; ++i) {? ? ? ? [hexToken appendFormat:@"%02x", tokenBytes[i]];? ? }? ? NSLog(@"deviceToken:%@", hexToken);}復(fù)制代碼

3.?模態(tài)視圖的默認樣式發(fā)生改變

在 iOS 13我碟,使用?presentViewController?方式打開模態(tài)視圖放案,默認的如下圖所示的視差效果,通過下滑返回矫俺。

need-to-insert-img

這是因為蘋果將?UIViewController?的?modalPresentationStyle?屬性的默認值改成了新加的一個枚舉值?UIModalPresentationAutomatic吱殉,對于多數(shù)?UIViewController,此值會映射成?UIModalPresentationPageSheet厘托。

需要注意友雳,這種效果彈出來的頁面導(dǎo)航欄部分是會被砍掉的,在 storyboard 中也可以看到铅匹,頁面布局時需要注意導(dǎo)航欄的內(nèi)容不要被遮擋押赊。

need-to-insert-img

還有一點注意的是,我們原來以全屏的樣式彈出一個頁面包斑,那么將這個頁面彈出的那個 ViewController 會依次調(diào)用?viewWillDisappear?和?viewDidDisappear流礁。然后在這個頁面被 dismiss 的時候,將他彈出的那個 ViewController 的?viewWillAppear?和?viewDidAppear?會被依次調(diào)用罗丰。然而使用默認的視差效果彈出頁面神帅,將他彈出的那個 ViewController 并不會調(diào)用這些方法,原先寫在這四個函數(shù)中的代碼以后都有可能會存在問題萌抵。

解決方案

如果視差效果的樣式可以接受的話找御,就不需要修改;如果需要改回全屏顯示的界面绍填,需要手動設(shè)置彈出樣式:

- (UIModalPresentationStyle)modalPresentationStyle {returnUIModalPresentationFullScreen;}復(fù)制代碼

4. UISearchBar 黑線處理導(dǎo)致崩潰

之前為了處理搜索框的黑線問題霎桅,通常會遍歷 searchBar 的 subViews,找到并刪除?UISearchBarBackground讨永。

for(UIView *viewin_searchBar.subviews.lastObject.subviews) {if([view isKindOfClass:NSClassFromString(@"UISearchBarBackground")]) {? ? ? ? [view removeFromSuperview];break;? ? }}復(fù)制代碼

在 iOS13 中這么做會導(dǎo)致 UI 渲染失敗滔驶,然后直接崩潰,崩潰信息如下:

*** Terminating app due to uncaught exception'NSInternalInconsistencyException', reason:'Missing or detached view for search bar layout'復(fù)制代碼

解決方案

設(shè)置?UISearchBar?的背景圖片為空:

[_searchBarsetBackgroundImage:[UIImage new]];復(fù)制代碼

5. UITabBarButton 不同狀態(tài)下結(jié)構(gòu)不同

在 iOS 13 中住闯,UITabBarButton?的控件結(jié)構(gòu)會隨著其選中狀態(tài)的變化而變化瓜浸,主要體現(xiàn)為?UITabBarSwappableImageView?和?UITabBarButtonLabel?的位置變化。在選中時和以前一樣比原,是?UITabBarButton的子控件插佛。而在未選中狀態(tài)下放到了?UIVisualEffectView?的?_UIVisualEffectContentView?里面。感謝@關(guān)燈俠的提醒量窘,具體可以看下圖的對比:

need-to-insert-img

我們在自定義?UITabBar?時雇寇,通常會遍歷?UITabBarButton?的子控件獲取?UITabBarSwappableImageView,比如自定義紅點時添加到這個 ImageView 的右上角,這在 iOS 13 中可能就會導(dǎo)致異常锨侯。

解決方案

可以使用遞歸遍歷?UITabBarButton?的所有 subviews 獲取?UITabBarSwappableImageView嫩海,具體可以參照上面?私有方法 KVC 可能導(dǎo)致崩潰?章節(jié)中給出的遞歸遍歷方法。

另外需要注意囚痴,未選中狀態(tài)下叁怪,添加的紅點會和 tabBar 的圖片一樣變成灰色,這一點應(yīng)該也是因為其結(jié)構(gòu)變化造成的深滚。具體可以見下圖:

need-to-insert-img

如果想要和以前一樣未選中時也是紅色奕谭,也很簡單,把紅點添加到?UITabBarButton?上痴荐,位置再根據(jù)?UITabBarSwappableImageView?調(diào)整即可血柳。

6. UINavigationBar 設(shè)置按鈕邊距導(dǎo)致崩潰

從 iOS 11 開始,UINavigationBar?使用了自動布局生兆,左右兩邊的按鈕到屏幕之間會有 16 或 20 的邊距难捌。

need-to-insert-img

為了避免點擊到間距的空白處沒有響應(yīng),通常做法是:定義一個?UINavigationBar?子類鸦难,重寫?layoutSubviews方法根吁,在此方法里遍歷 subviews 獲取?_UINavigationBarContentView,并將其?layoutMargins?設(shè)置為?UIEdgeInsetsZero明刷。

- (void)layoutSubviews {? ? [super layoutSubviews];for(UIView *subviewinself.subviews) {if([NSStringFromClass([subview class]) containsString:@"_UINavigationBarContentView"]) {? ? ? ? ? ? subview.layoutMargins = UIEdgeInsetsZero;break;? ? ? ? }? ? }}復(fù)制代碼

然而婴栽,這種做法在 iOS 13 中會導(dǎo)致崩潰满粗,崩潰信息如下:

*** Terminating app due to uncaught exception'NSInternalInconsistencyException', reason:'Client error attempting to change layout margins of a private view'復(fù)制代碼

解決方案

使用設(shè)置 frame 的方式辈末,讓?_UINavigationBarContentView?向兩邊伸展,從而抵消兩邊的邊距映皆。

- (void)layoutSubviews {? ? [super layoutSubviews];for(UIView *subviewinself.subviews) {if([NSStringFromClass([subview class]) containsString:@"_UINavigationBarContentView"]) {if([UIDevice currentDevice].systemVersion.floatValue >= 13.0) {? ? ? ? ? ? ? ? UIEdgeInsets margins = subview.layoutMargins;? ? ? ? ? ? ? ? subview.frame = CGRectMake(-margins.left, -margins.top, margins.left + margins.right + subview.frame.size.width, margins.top + margins.bottom + subview.frame.size.height);? ? ? ? ? ? }else{? ? ? ? ? ? ? ? subview.layoutMargins = UIEdgeInsetsZero;? ? ? ? ? ? }break;? ? ? ? }? ? }}復(fù)制代碼

7. 子線程修改界面導(dǎo)致崩潰(相冊首次授權(quán)回調(diào)必現(xiàn))

在使用相冊時我們會調(diào)用?[PHPhotoLibrary requestAuthorization:]?方法獲取權(quán)限挤聘,獲取的結(jié)果會通過一個帶有?PHAuthorizationStatus?信息的 block 進行回調(diào)。

[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {? ? // 根據(jù) status 判斷不同狀態(tài)}];復(fù)制代碼

根據(jù)?@路隨心生?的反饋捅彻,在 iOS 13 中组去,如果在第一次獲取權(quán)限的回調(diào)中直接修改界面,會導(dǎo)致崩潰步淹,崩潰信息如下:

This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.*** Terminating app due to uncaught exception'NSInternalInconsistencyException', reason:'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'復(fù)制代碼

實際測試从隆,第一次授權(quán)崩潰必先,再次授權(quán)偶現(xiàn)缭裆。另外键闺,正如崩潰信息所言,不只是相冊授權(quán)回調(diào)線程澈驼,其他子線程修改界面都有一定概率導(dǎo)致崩潰辛燥,而在 iOS 13 中貌似概率更高。

解決方案

在 Xcode 中調(diào)試運行時,子線程修改界面會有紫色感嘆號標出挎塌,注意修改成回到主線程即可徘六。

8. 默認彈出樣式打開的頁面在 WKWebView 中獲取照片崩潰

由于 iOS 13 中模態(tài)視圖的默認樣式發(fā)生改變,如果以默認的?UIModalPresentationPageSheet?樣式彈出一個 ViewController榴都,并使用?WKWebView?通過 HTML 獲取系統(tǒng)照片:

[_webView loadHTMLString:@"<input accept='image/*' type='file'>"baseURL:nil];復(fù)制代碼

在點擊選擇按鈕時待锈,根據(jù)@傷心的Easyman的反饋,會出現(xiàn)崩潰嘴高,崩潰信息如下:

*** Terminating app due to uncaught exception'NSGenericException', reason:'Your application has presented a UIDocumentMenuViewController (<UIDocumentMenuViewController: 0x101226860>). In its current trait environment, the modalPresentationStyle of a UIDocumentMenuViewController with this style is UIModalPresentationPopover. You must provide location information for this popover through the view controller's popoverPresentationController. You must provide either asourceView andsourceRect or a barButtonItem.? If this information is not known when you present the view controller, you may provide itinthe UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'復(fù)制代碼

具體原因是炉擅,點擊獲取系統(tǒng)照片時,會彈出一個模態(tài)視圖的樣式為?UIModalPresentationPopover?的?UIDocumentMenuViewController阳惹,這種樣式下谍失,如果其父?UIViewController?以非全屏方式 present 的,那么就需要像 iPad 一樣指定其?sourceView?和?sourceRect莹汤,或者指定一個?barButtonItem快鱼,否則會出現(xiàn)上述崩潰。而使用?UIModalPresentationFullScreen?的方式彈出的話就不會有這個問題纲岭。

解決方案

第一種方法就是指定sourceView抹竹、sourceRect,barButtonItem?同理:

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {? ? [selfsetUIDocumentMenuViewControllerSoureViewsIfNeeded:viewControllerToPresent];? ? [super presentViewController:viewControllerToPresent animated:flag completion:completion];}- (void)setUIDocumentMenuViewControllerSoureViewsIfNeeded:(UIViewController *)viewControllerToPresent{if(@available(iOS 13, *)) {if([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone && [viewControllerToPresent isKindOfClass:UIDocumentMenuViewController.class]){? ? ? ? ? ? viewControllerToPresent.popoverPresentationController.sourceView = self.webView;? ? ? ? ? ? viewControllerToPresent.popoverPresentationController.sourceRect = CGRectMake(15, 5, 1, 1); // 具體看按鈕的位置? ? ? ? }? ? }}// 如果頂層有 UINavigationController 的話止潮,需要如下指定- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {if([self.viewControllers.lastObject isKindOfClass:WKWebViewController.class]){? ? ? ? WKWebViewController *vc = self.viewControllers.lastObject;? ? ? ? [vcsetUIDocumentMenuViewControllerSoureViewsIfNeeded:viewControllerToPresent];? ? }? ? [super presentViewController:viewControllerToPresent animated:flag completion:completion];}復(fù)制代碼

第二種方法就是使用全屏的方式彈出(實踐證明默認彈出樣式在橫屏下是全屏的不會崩)

- (UIModalPresentationStyle)modalPresentationStyle {returnUIModalPresentationFullScreen;}復(fù)制代碼

9. WKWebView 在 iPad 上默認顯示桌面版的網(wǎng)頁

在 iPadOS 上窃判,使用?WKWebView?打開網(wǎng)頁默認使用桌面版的網(wǎng)頁,抓包發(fā)現(xiàn)其默認的 UserAgent 變成了 Desktop 版本:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko)復(fù)制代碼

原因是蘋果在 iOS 13.0 中為?WKWebView?添加了切換桌面版本和手機版本的方法喇闸,其通過在一個新加的枚舉值控制:

typedef NS_ENUM(NSInteger, WKContentMode) {? ? WKContentModeRecommended,? ? WKContentModeMobile,? ? WKContentModeDesktop} API_AVAILABLE(ios(13.0));復(fù)制代碼

此枚舉默認值為?WKContentModeRecommended袄琳,在 iPhone 和 iPad mini 上映射為?WKContentModeMobile,在其他 iPad 上則為?WKContentModeDesktop燃乍,因此 iPad 上打開網(wǎng)頁默認顯示桌面版本唆樊。

解決方案

可通過?WKWebViewConfiguration?的新屬性?defaultWebpagePreferences?來設(shè)置,目前其僅包含一個?WKContentMode?類型的屬性?preferredContentMode刻蟹,默認值為?WKContentModeRecommended?可以通過改變其值來修改顯示的版本:

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];if(@available(iOS 13.0, *)) {? ? configuration.defaultWebpagePreferences.preferredContentMode = WKContentModeMobile;}復(fù)制代碼

除了在初始化時候設(shè)置外逗旁,還可以通過新的代理方法實現(xiàn)桌面版和移動版切換,蘋果給出了一個具體的例子:?Viewing Desktop or Mobile Web Content Using a Web View舆瘪,其中關(guān)鍵的方法為:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction preferences:(WKWebpagePreferences *)preferences decisionHandler:(void (^)(WKNavigationActionPolicy, WKWebpagePreferences * _Nonnull))decisionHandler {? ? preferences.preferredContentMode = WKContentModeMobile;? ? decisionHandler(WKNavigationActionPolicyAllow, preferences);}復(fù)制代碼

方法棄用

1. UIWebView 將被禁止提交審核

在 iOS 13 推出后片效,蘋果在?UIWebView?的說明上將其支持的系統(tǒng)范圍定格在了 iOS 2 ~ iOS 12。在 2019 年 12 月 23 日更新的?News and Updates中英古,蘋果給出了確切的時間節(jié)點:

The App Store will no longer accept new apps using UIWebView as of April 2020 and app updates using UIWebView as of December 2020.

2020 年 4 月開始不再接受包含 UIWebView 的新應(yīng)用提交淀衣,2020 年 12 月開始不再接受包含 UIWebView 的應(yīng)用更新提交。

解決方案

用?WKWebView?替代?UIWebView哺呜,確保所有?UIWebView?的 api 都要移除舌缤,如果需要適配 iOS 7 的可以通過?openURL?的方式在?Safari?打開箕戳。

2. 使用 UISearchDisplayController 導(dǎo)致崩潰

在 iOS 8 之前,我們在?UITableView?上添加搜索框需要使用?UISearchBar?+?UISearchDisplayController?的組合方式国撵,而在 iOS 8 之后陵吸,蘋果就已經(jīng)推出了?UISearchController?來代替這個組合方式。在 iOS 13 中介牙,如果還繼續(xù)使用?UISearchDisplayController?會直接導(dǎo)致崩潰壮虫,崩潰信息如下:

*** Terminating app due to uncaught exception'NSGenericException', reason:'UISearchDisplayController is no longer supported when linking against this version of iOS. Please migrate your application to UISearchController.'復(fù)制代碼

解決方案

使用?UISearchController?替換?UISearchBar?+?UISearchDisplayController?的組合方案。

3. MPMoviePlayerController 被棄用

在 iOS 9 之前播放視頻可以使用?MediaPlayer.framework?中的MPMoviePlayerController類來完成环础,它支持本地視頻和網(wǎng)絡(luò)視頻播放囚似。但是在 iOS 9 開始被棄用,如果在 iOS 13 中繼續(xù)使用的話會直接拋出異常:

*** Terminating app due to uncaught exception'NSInvalidArgumentException', reason:'MPMoviePlayerController is no longer available. Use AVPlayerViewController in AVKit.'復(fù)制代碼

解決方案

使用?AVFoundation?里的?AVPlayer?作為視頻播放控件线得。

工程適配

1. 藍牙權(quán)限字段更新導(dǎo)致崩潰以及提交審核失敗

在 iOS 13 中饶唤,蘋果將原來藍牙申請權(quán)限用的?NSBluetoothPeripheralUsageDescription?字段,替換為?NSBluetoothAlwaysUsageDescription?字段贯钩。

For apps with a deployment target of iOS 13 and later, use NSBluetoothAlwaysUsageDescription instead.

感謝?@dengChaoJie?的反饋募狂,如果在 iOS 13 中使用舊的權(quán)限字段獲取藍牙權(quán)限,會導(dǎo)致崩潰角雷,崩潰信息如下:

This app has crashed because it attempted to access privacy-sensitive data without a usage description.? The app's Info.plist must contain an NSBluetoothAlwaysUsageDescription key with a string value explaining to the user how the app uses this data.復(fù)制代碼

另外祸穷,如果將沒有新字段的包提交審核,將會收到包含?ITMS-90683?的郵件勺三,并提示審核不通過雷滚。

Dear Developer,

We identified one or more issues with a recent delivery for your app, "xxx". Please correct the following issues, then upload again.

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSBluetoothAlwaysUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are required to include a purpose string. If you're using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a version of their code that doesn't contain the APIs. Learn more (developer.apple.com/documentati…).

Best regards,

The App Store Team

解決方案

官網(wǎng)文檔也有說明,就是在 Info.plist 中把兩個字段都加上吗坚。

For deployment targets earlier than iOS 13, add both NSBluetoothAlwaysUsageDescription and NSBluetoothPeripheralUsageDescription to your app’s Information Property List file.

2. CNCopyCurrentNetworkInfo 使用要求更嚴格

從 iOS 12 開始祈远,CNCopyCurrentNetworkInfo?函數(shù)需要開啟?Access WiFi Information的功能后才會返回正確的值。在 iOS 13 中刻蚯,這個函數(shù)的使用要求變得更嚴格绊含,根據(jù)?CNCopyCurrentNetworkInfo?文檔說明,應(yīng)用還需要符合下列三項條件中的至少一項才能得到正確的值:

使用Core Location?的應(yīng)用炊汹, 并獲得定位服務(wù)權(quán)限。

使用NEHotspotConfiguration來配置 WiFi 網(wǎng)絡(luò)的應(yīng)用逃顶。

目前正處于啟用狀態(tài)的 VPN 應(yīng)用讨便。

蘋果作出這項改變主要為了保障用戶的安全,因為根據(jù) MAC 地址容易推算出用戶當前所處的地理位置以政。同樣霸褒,藍牙設(shè)備也具有 MAC 地址,所以蘋果也為藍牙添加了新的權(quán)限盈蛮,可見上一點废菱。

解決方案

根據(jù)應(yīng)用需求,添加三項要求其中一項∈庵幔可以選擇第一項獲取定位權(quán)限衰倦,因為添加的成本不會太大,只需要用戶允許應(yīng)用使用定位服務(wù)即可旁理。

3. LaunchImage 被棄用

iOS 8 之前我們是在LaunchImage?來設(shè)置啟動圖樊零,每當蘋果推出新的屏幕尺寸的設(shè)備,我們需要 assets 里面放入對應(yīng)的尺寸的啟動圖孽文,這是非常繁瑣的一個步驟驻襟。因此在 iOS 8 蘋果引入了?LaunchScreen,可以直接在 Storyboard 上設(shè)置啟動界面樣式芋哭,可以很方便適配各種屏幕沉衣。

需要注意的是,蘋果在?Modernizing Your UI for iOS 13?section 中提到 减牺,從2020年4月開始厢蒜,所有支持 iOS 13 的 App 必須提供?LaunchScreen.storyboard,否則將無法提交到 App Store 進行審批烹植。

need-to-insert-img

解決方案

使用?LaunchScreen.storyboard?設(shè)置啟動頁斑鸦,棄用?LaunchImage。

4. UISegmentedControl 默認樣式改變

默認樣式變?yōu)榘椎缀谧植莸瘢绻O(shè)置修改過顏色的話巷屿,頁面需要修改。

need-to-insert-img

原本設(shè)置選中顏色的?tintColor?已經(jīng)失效墩虹,新增了??selectedSegmentTintColor?屬性用以修改選中的顏色嘱巾。

5. Xcode 11 創(chuàng)建的工程在低版本設(shè)備上運行黑屏

使用 Xcode 11 創(chuàng)建的工程,運行設(shè)備選擇 iOS 13.0 以下的設(shè)備诫钓,運行應(yīng)用時會出現(xiàn)黑屏旬昭。這是因為 Xcode 11 默認是會創(chuàng)建通過?UIScene?管理多個?UIWindow?的應(yīng)用,工程中除了?AppDelegate?外會多一個?SceneDelegate:

need-to-insert-img

這是為了 iPadOS 的多進程準備的菌湃,也就是說?UIWindow?不再是?UIApplication?中管理问拘,但是舊版本根本沒有?UIScene。

解決方案

在?AppDelegate?的頭文件加上:

@property (strong, nonatomic) UIWindow *window;復(fù)制代碼

6. Xcode 11.2 編譯的工程在 iOS 13.2 之前版本的設(shè)備上會出現(xiàn)崩潰惧所。

如果工程的?storyboard?中包含?UITextView骤坐,并使用 Xcode 11.2 編譯打包,那么 App 在?iOS 13.2?之前版本的設(shè)備會出現(xiàn)崩潰下愈,崩潰信息如下:

*** Terminating app due to uncaught exception'NSInvalidUnarchiveOperationException', reason:'Could not instantiate class named _UITextLayoutView because no class named _UITextLayoutView was found; the class needs to be defined in source code or linked in from a library (ensure the class is part of the correct target)'復(fù)制代碼

這個問題是 Xcode 的鍋纽绍,蘋果在此后緊急發(fā)布 Xcode 11.2.1 版本進行修復(fù),Xcode 11.2 在 2019 年 11 月 5 日被正式棄用势似。目前如果仍然使用 Xcode 11.2 打包提審拌夏,會收到包含?ERROR?ITMS-90534?和?WARNING?ITMS-90703?的拒絕信息僧著,相同案例可見?stackoverflow

解決方案

更新至 Xcode 11.2.1 版本打包提審障簿。

SDK 適配

1. 使用?@available?導(dǎo)致舊版本 Xcode 編譯出錯

在 Xcode 11 的 SDK 工程的代碼里面使用了?@available?判斷當前系統(tǒng)版本盹愚,打出來的包放在 Xcode 10 中編譯,會出現(xiàn)一下錯誤:

Undefine symbolsforarchitecture i386:"__isPlatformVersionAtLeast", referenced from:? ? ? ? ...ld: symbol(s) not foundforarchitecture i386復(fù)制代碼

從錯誤信息來看卷谈,是?__isPlatformVersionAtLeast?方法沒有具體的實現(xiàn)杯拐,但是工程里根本沒有這個方法。實際測試無論在哪里使用@available?世蔗,并使用 Xcode 11 打包成動態(tài)庫或靜態(tài)庫端逼,把打包的庫添加到 Xcode 10 中編譯都會出現(xiàn)這個錯誤,因此可以判斷是 iOS 13 的?@available?的實現(xiàn)中使用了新的 api污淋。

解決方案

如果你的 SDK 需要適配舊版本的 Xcode顶滩,那么需要避開此方法,通過獲取系統(tǒng)版本來進行判斷:

if([UIDevice currentDevice].systemVersion.floatValue >= 13.0) {? ? ...}復(fù)制代碼

另外寸爆,在 Xcode 10 上打開 SDK 工程也應(yīng)該可以正常編譯礁鲁,這就需要加上編譯宏進行處理:

#ifndef __IPHONE_13_0#define __IPHONE_13_0 130000#endif#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0...#endif復(fù)制代碼

作者:_森宇_

鏈接:https://juejin.im/post/5d8af88ef265da5b6e0a23ac

來源:掘金

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)赁豆,非商業(yè)轉(zhuǎn)載請注明出處仅醇。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市魔种,隨后出現(xiàn)的幾起案子析二,更是在濱河造成了極大的恐慌,老刑警劉巖节预,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叶摄,死亡現(xiàn)場離奇詭異,居然都是意外死亡安拟,警方通過查閱死者的電腦和手機蛤吓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來糠赦,“玉大人会傲,你說我怎么就攤上這事∮淅猓” “怎么了唆铐?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長奔滑。 經(jīng)常有香客問我,道長顺少,這世上最難降的妖魔是什么朋其? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任王浴,我火速辦了婚禮,結(jié)果婚禮上梅猿,老公的妹妹穿的比我還像新娘氓辣。我一直安慰自己,他們只是感情好袱蚓,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布钞啸。 她就那樣靜靜地躺著,像睡著了一般喇潘。 火紅的嫁衣襯著肌膚如雪体斩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天颖低,我揣著相機與錄音絮吵,去河邊找鬼。 笑死忱屑,一個胖子當著我的面吹牛蹬敲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播莺戒,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼伴嗡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了从铲?” 一聲冷哼從身側(cè)響起瘪校,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎食店,沒想到半個月后渣淤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡吉嫩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年价认,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片自娩。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡用踩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出忙迁,到底是詐尸還是另有隱情脐彩,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布姊扔,位于F島的核電站惠奸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏恰梢。R本人自食惡果不足惜佛南,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一梗掰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗅回,春花似錦及穗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至娃豹,卻和暖如春焚虱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背培愁。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工著摔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人定续。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓谍咆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親私股。 傳聞我的和親對象是個殘疾皇子摹察,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355