隨著iPhone 11的發(fā)布惭载,iOS 13適配也提上了日程来惧,剛好最近在做項(xiàng)目適配,順便總結(jié)一下:
首先升級(jí)Xcode11摹迷,iOS13版本,因?yàn)閄code11出現(xiàn)了一些新的API郊供。廢話不多說(shuō)峡碉,直接上菜:
一、私有KVC
在iOS13中驮审,不在允許通過(guò)KVC的方式去訪問(wèn)私有屬性鲫寄,需要通過(guò)其他方式去修改吉执;之前如果使用到,會(huì)造成崩潰地来。
在網(wǎng)上看到有人說(shuō) 私有KVC崩潰與系統(tǒng)版本無(wú)關(guān)戳玫,與Xcode版本有關(guān),Xcode11編譯會(huì)崩潰未斑。
我個(gè)人覺(jué)得這個(gè)說(shuō)法是錯(cuò)的咕宿;通過(guò)我的驗(yàn)證,我認(rèn)為私有KVC在Xcode11---iOS12以下的系統(tǒng)不會(huì)崩潰蜡秽,Xcode11---iOS13的會(huì)崩潰府阀;大家也可以自己驗(yàn)證一下。
目前我找到的會(huì)觸發(fā) KVC 訪問(wèn)權(quán)限異常崩潰的方法有:
- UITabBarButton -> _info
- UITextField -> _placeholderLabel
- _UIBarBackground -> _shadowView
- _UIBarBackground -> _backgroundEffectView
- UISearchBar -> _cancelButtonText
- UISearchBar -> _cancelButton
- UISearchBar -> _searchField
目前我項(xiàng)目中芽突,主要用到UITextField
和UISearchBar
的私有屬性肌似,現(xiàn)在我就拿這兩個(gè)分析一下:
1、UITextFiled 修改根據(jù)kvc提示文字的大小和顏色诉瓦,
這樣寫:
[self.onePwdField setValue:color_cccccc forKeyPath:@"_placeholderLabel.textColor"];
在Xcode11--iOS13會(huì)直接崩潰,修改方法為:
self.onePwdField.attributedPlaceholder = [self placeholder:@"請(qǐng)輸入原來(lái)的密碼"];
-(NSMutableAttributedString *)placeholder:(NSString *)text{
if (text.length == 0) {
return nil;
}
NSMutableAttributedString *att = [[NSMutableAttributedString alloc]initWithString:text attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15],NSForegroundColorAttributeName:color_cccccc}];
return att;
}
2力细、獲取UISearchBar的textField
在iOS13之前睬澡,我們通過(guò)"_searchField"
來(lái)獲取UISearchTextField來(lái)修改一些屬性。
UITextField *searchFiled = [self valueForKey:@"_searchField"];
在iOS13中,繼續(xù)這樣會(huì)崩潰眠蚂,修改方法為:
UITextField *searchFiled;
if(@available(iOS 13.0, *)) {
searchFiled = self.searchTextField;
}else{
searchFiled = [self valueForKey:@"_searchField"];
}
3煞聪、UISearchBar 黑線處理導(dǎo)致崩潰
iOS13之前為了處理搜索框的黑線問(wèn)題,通常會(huì)遍歷 searchBar 的 subViews逝慧,找到并刪除 UISearchBarBackground
昔脯。
在 iOS13 中這么做會(huì)導(dǎo)致 UI 渲染失敗,然后直接崩潰笛臣,崩潰信息如下:
Terminating app due to uncaught exception'NSInternalInconsistencyException', reason: 'Missing or detached view for search bar layout'
修改方法為:設(shè)置 UISearchBarBackground 的 layer.contents 為 nil
for (UIView *view in _searchBar.subviews.lastObject.subviews) {
if ([view isKindOfClass:NSClassFromString(@"UISearchBarBackground")]) {
view.layer.contents = nil;
break;
}
}
4云稚、 設(shè)置UISearchBar 的searchTextField.attributedPlaceholder無(wú)效問(wèn)題。
在 iOS13中需要把設(shè)置的代碼寫在viewDidAppear,親測(cè)可以.
5沈堡、獲取狀態(tài)欄視圖静陈,以下兩種方法在iOS13導(dǎo)致崩潰。
UIView *statusBar = [[UIApplication sharedApplication] valueForKey:@"statusBar"];
或:
UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
改為:
if(@available(iOS 13.0, *)) {
}else{
UIView *statusBar = [[UIApplication sharedApplication] valueForKey:@"statusBar"];
statusBar.transform = CGAffineTransformIdentity;
}
二诞丽、presentViewController的問(wèn)題(模態(tài)彈出默認(rèn)樣式改變)
在 iOS 13鲸拥,使用 presentViewController 方式打開(kāi)視圖,會(huì)跟導(dǎo)航欄有部分視覺(jué)差僧免,這里就不上圖了刑赶,可以自行試一下。
原因是:蘋果將 UIViewController
的 modalPresentationStyle
屬性的默認(rèn)值改成了新加的一個(gè)枚舉值 UIModalPresentationAutomatic
懂衩,對(duì)于多數(shù) UIViewController
撞叨,此值會(huì)映射成 UIModalPresentationPageSheet
金踪。
解決辦法: 可以在vc present之前設(shè)置
modalPresentationStyle 為 UIModalPresentationFullScreen
另外,present的vc用戶下拉可以
dissmiss
控制器谒所,如果不想要這效果热康,可以這樣設(shè)置
/*當(dāng)該屬性為 false 時(shí),用戶下拉可以 dismiss 控制器劣领,為 true 時(shí)姐军,下拉不可以 dismiss控制器*/
xxVC.isModalInPresentation = true;
- 還有一點(diǎn)需要注意尖淘,原來(lái)以UIModalPresentationFullScreen樣式彈出頁(yè)面奕锌,那么這個(gè)頁(yè)面彈出 ViewController 會(huì)依次調(diào)viewWillDisappear和 viewDidDisappear。然后在這個(gè)頁(yè)面被 dismiss 的時(shí)候村生,將他彈出的那個(gè) ViewController 的 viewWillAppear 和 viewDidAppear會(huì)被依次調(diào)用惊暴。然而使用默認(rèn)的視差效果彈出頁(yè)面,將他彈出的那個(gè) ViewController 并不會(huì)調(diào)用這些方法趁桃,原先寫在這四個(gè)函數(shù)中的代碼以后都有可能會(huì)存在問(wèn)題辽话。
三、藍(lán)牙權(quán)限更新
在 iOS 13 中卫病,蘋果將原來(lái)藍(lán)牙申請(qǐng)權(quán)限用的 NSBluetoothPeripheralUsageDescription 字段油啤,替換為 NSBluetoothAlwaysUsageDescription 字段。
四蟀苛、廢棄UIWebview 改用 WKWebView
iOS13 開(kāi)始蘋果將 UIWebview 列為過(guò)期API(支持iOS2.0-iOS12)益咬。 目前提交蘋果應(yīng)用市場(chǎng)(App Store)會(huì)發(fā)送郵件提示你在下一次提交時(shí)將應(yīng)用中UIWebView 的 api 移除。
暫時(shí)沒(méi)有強(qiáng)制使用WKWebView帜平,但是在iOS13開(kāi)始UIWebView已是廢棄的API幽告,以后更高的版本中防止出現(xiàn)問(wèn)題,盡早移除是上上之策裆甩。
五冗锁、UISegmentedControl 默認(rèn)樣式改變。
默認(rèn)樣式變?yōu)?白底黑字嗤栓,如果設(shè)置修改過(guò)顏色的話蒿讥,頁(yè)面需要修改。
六抛腕、WKWebView 中測(cè)量頁(yè)面內(nèi)容高度的方式變更
iOS 13以前 document.body.scrollHeight iOS 13中 document.documentElement.scrollHeight 兩者相差55 應(yīng)該是瀏覽器定義高度變了芋绸。
七、關(guān)于暗黑模式和切換
1担敌、暗黑模式是iOS13的一大亮點(diǎn)摔敛,下面來(lái)看看模式切換的設(shè)置
(1) 切換 修改當(dāng)前 UIViewController 或 UIView的模式。只要設(shè)置了控制器為暗黑模式全封,那么它子view也會(huì)對(duì)應(yīng)的修改马昙。
即:只會(huì)影響當(dāng)前的視圖桃犬,不會(huì)影響前面的 controller 和后續(xù) present 的 controller。
if (@available(iOS 13.0, *)) {
self.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;//UIUserInterfaceStyleLight
} else {
// Fallback on earlier versions
}
注意啦??行楞,但是當(dāng)我們?cè)?window 上設(shè)置 overrideUserInterfaceStyle 的時(shí)候攒暇,就會(huì)影響 window 下所有的 controller, view,包括后續(xù)推出的 controller子房。
(2) 獲取當(dāng)前的模式
if (@available(iOS 12.0, *)) {
if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark ){
// Dark
NSLog(@"是dark模式形用、。证杭。田度。");
} else if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleLight) {
// Light
NSLog(@"是light模式、解愤。镇饺。。");
} else {
//unspecified
NSLog(@"是unspecified模式送讲、奸笤。。哼鬓。");
}
}
(3) 監(jiān)聽(tīng)模式的變化
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
// trait發(fā)生了改變
if (@available(iOS 13.0, *)) {
if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
// 執(zhí)行操作
}
} else {
// Fallback on earlier versions
}
}
2揭保、適配暗黑模式
(1)將同一個(gè)資源,創(chuàng)建出兩種模式的樣式魄宏。系統(tǒng)根據(jù)當(dāng)前選擇的樣式,自動(dòng)獲取該樣式的資源
(2)每次系統(tǒng)更新樣式時(shí)存筏,應(yīng)用會(huì)調(diào)用當(dāng)前所有存在的元素調(diào)用對(duì)應(yīng)的一些重新方法宠互,進(jìn)行重繪視圖,可以在對(duì)應(yīng)的方法做相應(yīng)的改動(dòng)椭坚。
(3)資源文件適配
- 1.創(chuàng)建一個(gè)Assets文件(或在現(xiàn)有的Assets文件中)
詳情請(qǐng)看:iOS中創(chuàng)建多個(gè)Assets.xcassets文件 - 2.新建一個(gè)圖片資源文件(或者顏色資源文件予跌、或者其他資源文件)
- 3.選中該資源文件, 打開(kāi) Xcode ->View ->Inspectors ->Show Attributes Inspectors (或者Option+Command+4)視圖善茎,將Apperances 選項(xiàng) 改為Any券册,Dark
- 4.執(zhí)行完第三步,資源文件將會(huì)有多個(gè)容器框垂涯,分別為 Any Apperance 和 Dark Apperance. Any Apperance 應(yīng)用于默認(rèn)情況(Unspecified)與高亮情況(Light)烁焙, Dark Apperance 應(yīng)用于暗黑模式(Dark)
- 5.代碼默認(rèn)執(zhí)行時(shí),就可以正常通過(guò)名字使用了耕赘,系統(tǒng)會(huì)根據(jù)當(dāng)前模式自動(dòng)獲取對(duì)應(yīng)的資源文件
注意啦??骄蝇,同一工程內(nèi)多個(gè)Assets文件在打包后,就會(huì)生成一個(gè)Assets.car 文件操骡,所以要保證Assets內(nèi)資源文件的名字不能相同.
-
步驟如圖:資源文件適配暗黑模式.png
3九火、全局關(guān)閉黑暗模式
-
方式一 配置plist文件: 在Info.plist 文件中赚窃,添加UIUserInterfaceStyle key 名字為 User Interface Style 值為String,將UIUserInterfaceStyle key 的值設(shè)置為 Light岔激。
全局關(guān)閉黑暗模式.png
在開(kāi)發(fā)中勒极,如果用的系統(tǒng)控件(如cell、tableview的背景色)未設(shè)置背景色(或者為透明)虑鼎,則進(jìn)入暗黑模式后辱匿,控件背景色變?yōu)楹谏?/p>
可以每一個(gè)頁(yè)面設(shè)置,當(dāng)然也可以整體設(shè)置震叙, 一般我們的APP都是在一個(gè)window下的掀鹅,那就整體設(shè)置APP里的window
- 方式二 :代碼關(guān)閉黑暗模式 強(qiáng)制關(guān)閉暗黑模式
#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if(@available(iOS 13.0,*)){
self.window.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}
#endif
4、單個(gè)界面不遵循暗黑模式
- UIViewController與UIView 都新增一個(gè)屬性 overrideUserInterfaceStyle
- 將 overrideUserInterfaceStyle 設(shè)置為對(duì)應(yīng)的模式媒楼,則強(qiáng)制限制該元素與其子元素以設(shè)置的模式進(jìn)行展示乐尊,不跟隨系統(tǒng)模式改變進(jìn)行改變
1、設(shè)置 ViewController 的該屬性划址, 將會(huì)影響視圖控制器的視圖和子視圖控制器采用該樣式扔嵌。
2、設(shè)置 View 的該屬性夺颤, 將會(huì)影響視圖及其所有子視圖采用該樣式痢缎。
3、設(shè)置 Window 的該屬性世澜, 將會(huì)影響窗口中的所有內(nèi)容都采用樣式独旷,包括根視圖控制器和在該窗口中顯示內(nèi)容的所有演示控制器(UIPresentationController)
更詳細(xì)的暗黑模式設(shè)置請(qǐng)看:iOS適配暗黑模式
八、即將廢棄的 LaunchImage(iOS 7.0–13.0)
從 iOS 8 的時(shí)候寥裂,蘋果就引入了 LaunchScreen.storyboard嵌洼,我們可以設(shè)置 LaunchScreen來(lái)作為啟動(dòng)頁(yè)。當(dāng)然封恰,現(xiàn)在你還可以使用LaunchImage來(lái)設(shè)置啟動(dòng)圖麻养。不過(guò)使用LaunchImage的話,要求我們必須提供各種屏幕尺寸的啟動(dòng)圖诺舔,來(lái)適配各種設(shè)備鳖昌,隨著蘋果設(shè)備尺寸越來(lái)越多,這種方式顯然不夠 Flexible低飒。而使用 LaunchScreen的話许昨,情況會(huì)變的很簡(jiǎn)單, LaunchScreen是支持AutoLayout+SizeClass的褥赊,所以適配各種屏幕都不在話下车要。
注意啦??,從2020年4月開(kāi)始崭倘,所有使? iOS13 SDK 的 App 將必須提供 LaunchScreen.storyboard翼岁,LaunchImage即將退出歷史舞臺(tái)类垫,否則將無(wú)法提交到 App Store 進(jìn)行審批。
九琅坡、StatusBar 與之前版本不同
目前狀態(tài)欄也增加了一種模式悉患,由之前的兩種,變成了三種, 其中default由之前的黑色內(nèi)容榆俺,變成了會(huì)根據(jù)系統(tǒng)模式售躁,自動(dòng)選擇當(dāng)前展示lightContent還是darkContent。
十茴晋、iOS13 獲取window適配
UIWindow* window = nil;
if (@available(iOS 13.0, *)) {
for (UIWindowScene* windowScene in [UIApplication sharedApplication].connectedScenes)
{
if (windowScene.activationState == UISceneActivationStateForegroundActive)
{
window = windowScene.windows.firstObject;
break;
}
}
}else{
window = [UIApplication sharedApplication].keyWindow;
}