原文同步發佈在:devhy.com/33-xcode11-…ios
一年一適配,今年又來了。
今年總體上問題不大,我司的App 沒有出現編譯報錯問題。git
首先,用 Xcode10 編譯的 App 在 iOS 13 上使用甚至幾乎完美😀
然而,用 Xcode11 編譯的 App 再在 iOS 13 上跑,就有些問題了😂github
具體碰到的問題以下:shell
iOS 13 多了一個新的枚舉類型 UIModalPresentationAutomatic
,且是modalPresentationStyle
的默認值。xcode
UIModalPresentationAutomatic
實際是表現是在 <iOS 13 的設備上被映射成UIModalPresentationFullScreen
,在 >=iOS 13的設備上被映射成UIModalPresentationPageSheet
。ruby
我這邊的設計師表示,新樣式不錯,能夠不用改😆。bash
不過, PageSheet
與 FullScreen
對比 有個須要注意的地方,控制器的生命週期有點區別:markdown
以 控制器A
、控制器B
舉例:app
控制器A
present 控制器B
控制器A
不會調用 viewWillDisappear
以及 viewDidDisappear
控制器B
dismiss 時 控制器A
不會調用 viewWillAppear
以及 viewDidAppear
那麼若是有些業務邏輯會在控制器A
的生命週期裏作的話,就須要考慮其餘方式實現,或者改回UIModalPresentationFullScreen
ide
若是須要改爲本來全屏的樣式,能夠處理Controller:
modalPresentationStyle
值modalPresentationStyle
值modalPresentationStyle
的get方法擴展 看上面gif,用戶是能夠經過手勢下拉關閉被present出來的控制器的,那若是我須要禁止他下來要怎麼實現呢?
能夠參考 disabling_pulling_down_a_sheet 的Demo
設置presentationController.delegate
代理對象,實現 UIAdaptivePresentationControllerDelegate
協議方法
@interface XXViewController () <UIAdaptivePresentationControllerDelegate> @property (nonatomic, assign) BOOL allowBack; @end @implementation XXViewController - (void)viewDidLoad { [super viewDidLoad]; // ... // 若是vc被navigation套了一層就要取navigation的presentationController self.navigationController.presentationController.delegate = self; } // 使用UIAdaptivePresentationControllerDelegate 協議方法控制可否下拉dismiss // 或者不實現這個協議,設置self.modalInPresentation(YES-不容許下拉關閉,NO-能夠下拉關閉) - (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presentationController { return self.allowBack; } // 若是 self.modalInPresentation=YES 或者 // 上面協議方法return NO,用戶再下拉就會在這收到回調 - (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)presentationController { // 若是self.allowBack == NO,會回調到這裏 // 能夠在這裏作一些事情而後再考慮讓 self.allowBack=YES 或 // 則讓 self.modalInPresentation=NO 便可讓頁面能夠下拉返回 } @end 複製代碼
以下方式,直接給 textfield.leftView
賦值一個 UILabel
對象,他的寬高會被 sizeToFit
,而不是建立時的值。
// left view label UILabel *phoneLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 63, 50)]; phoneLabel.text = @"手機號"; phoneLabel.font = [UIFont systemFontOfSize:16]; // set textfield left view self.textfieldName.leftView = phoneLabel; 複製代碼
如所看到,實際 leftview 的width爲59,height爲19:
經過監聽 leftView
的 frame
變化,發現是 layoutSubview
以後變化的。 最終仍是給UILabel多套了一個UIView來解決
// label UILabel *phoneLabel = [[UILabel alloc] init]; phoneLabel.text = @"手機號"; phoneLabel.font = [UIFont systemFontOfSize:16]; [phoneLabel sizeToFit]; phoneLabel.centerY = 50/2.f; // left view UIView *leftView = [[UIView alloc] initWithFrame:(CGRect){0, 0, 63, 50}]; [leftView addSubview:phoneLabel]; // set textfield left view self.textfieldName.leftView = leftView; 複製代碼
打開有UISearchBar
的頁面發現Crash了,看到控制檯輸出提示:
// 獲取_searchField
UITextField *sField = [self.searchbar valueForKey:@"_searchField"];
// crash log
*** Terminating app due to uncaught exception 'NSGenericException', reason:
'Access to UISearchBar's _searchField ivar is prohibited.
This is an application bug'
複製代碼
看起來是禁止訪問私有屬性了。
用 Xcode 10 編譯的 App 在 iOS 13 上能正常使用,那麼就是 Xcode 11 作了限制訪問私有屬性的一些處理了。
偶然發現 iOS 13 中增長了UISearchTextField
類,且暴露了searchTextField
。
// UISearchTextField.h UIKIT_CLASS_AVAILABLE_IOS_ONLY(13.0) @interface UISearchTextField : UITextField // ... @end @interface UISearchBar (UITokenSearch) @property (nonatomic, readonly) UISearchTextField *searchTextField; @end 複製代碼
可是僅在 iOS 13 以上系統支持,仍是暫時用遍歷view的方式去作了😂
點擊導航欄返回的時候Crash了,控制檯輸出提示:
Теrmіnаtіng арр due to uncaught exception' NSInternalInconsistencyException' , reason : ' Override of -navigationBar : shouldPopItem: returned YES after manually popping a view controller ( navigat ionController=<MHCRNavgationController : 0x106039400>) ' 複製代碼
由於咱們工程裏,基本上全部的 Controller
是繼承基類 BaseViewController
並實現- (BOOL)naviBack:
方法,用於實如今用戶點擊返回和策劃返回時,一些不能返回的特殊處理。 其根本原理是經過實現 UINavgationBar
的代理方法 - (BOOL)navigationBar:shouldPopItem:
來作的控制:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item { // 默承認以返回 BOOL canGoBack = YES; // BaseViewController定義協議方法判斷可否能點擊返回上一層 UIViewController *vc = self.viewControllers.lastObject; if ([vc isKindOfClass:BaseViewController.class]) { canGoBack = [(BaseViewController *)vc naviBack:nil]; } if (canGoBack) { [self popViewControllerAnimated:YES]; } return canGoBack; } 複製代碼
可是我實現的時候有 Return YES
啊!想了想,試着 註釋 了[self popViewControllerAnimated:YES]
,發現沒有崩潰了。 可是在iOS 12上,會發現控制器沒有回到上一層,如圖,只有navbar回到上一層了:
好吧,那隻能判斷一下版本解決這個問題了,修改方式:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item { // 判斷 iOS 版本低於13 BOOL bellow13 = !@available(iOS 13.0, *); // 默承認以返回 BOOL canGoBack = YES; // BaseViewController定義協議方法判斷可否能點擊返回上一層 UIViewController *vc = self.viewControllers.lastObject; if ([vc isKindOfClass:BaseViewController.class]) { canGoBack = [(BaseViewController *)vc naviBack:nil]; } if (canGoBack && bellow13) { // 若是低於13且能夠返回,就執行popViewController [self popViewControllerAnimated:YES]; } return canGoBack; } 複製代碼
WWDC 19 直播的時候看到夜間模式,老實說挺開心的,直到我用 Xcode 11 開始作適配,媽耶!x N
注意:使用 Xcode 10 編譯的 App 依然是日間模式,不會產生效果!!!
初步掃了一下出現的問題以下圖,大體狀況是:沒有設置背景色的系統控件會被設置成黑色,部分控件是tintColor沒設置的話也會被改。
因爲Assets裏的Color配置是 iOS 12 以上才能使用的,因此若是沒有作全局主題色設計且須要支持 iOS 12 如下設備,改起來會比較噁心。
對此現象,找設計師溝通。設計師表示,暫時沒有精力作夜間模式規劃。
設計師問:可否強制隻日間模式?
答:能。配置方式有兩種,單頁面配置
和 全局配置
。
單頁配置
將須要配置的 UIViewControler
對象的 overrideUserInterfaceStyle
屬性設置成 UIUserInterfaceStyleLight
或者 UIUserInterfaceStyleDark
以強制是某個頁面顯示爲 淺/深色模式
全局配置
在工程的 Info.plist
的中,增長/修改 UIUserInterfaceStyle
爲 UIUserInterfaceStyleLight
或 UIUserInterfaceStyleDark
。
2019.07.26 補充: 早上恰好看到知識小集發了篇 適配 Dark Mode 的文章,看了下挺好的,推薦閱讀。
推 Cocoapods 私有庫校驗的時候,收到一個報錯:
- ERROR | [iOS] unknown: Encountered an unknown error (Could not find aiossimulator
(valid values: ). Ensure that Xcode -> Window -> Devices has at least
oneiossimulator listed or otherwise add one.) during validation.
複製代碼
直覺告訴我是 cocoapods 的問題,去其 github 倉庫的 issues 搜了一下,沒有搜到 xcode 11 相關的,而後去 google 了一下,看到了這個問題:
cocoapods-cant-find-simulators-pod-repo-push-fails
裏面有一條
After installing Xcode 11, pod lib lint is broken again because of fourflusher - The "availability" key from
xcrun simctl list -j
is now a boolean "isAvailable"
可是這個fourflusher的路徑我找不到,可能本身的ruby環境不太同樣,那麼gem會不會支持反查一個工具的位置呢?
經過 gem help
看了下使用說明,發現能夠用gem help commands
查找全部gem的指令。 其中就有一個which
指令,描述上說是用來找庫文件位置的。
# 使用gem which來查找位置 gem which fourflusher 複製代碼
果真出現了:
/Users/HY/.rvm/gems/ruby-2.4.1/gems/fourflusher-2.2.0/lib/fourflusher.rb
複製代碼
對應的知道 fourflusher
的位置,那麼接下來就能夠修改了。
/Users/HY/.rvm/gems/ruby-2.4.1/gems/fourflusher-2.2.0/lib/fourflusher/
find.rb
device['availability'] == '(available)'
device['isAvailable'] == true
便可修改結果如圖便可: