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)載請注明出處仅醇。