Xcode11 和 iOS13 踩坑

原文同步發佈在:devhy.com/33-xcode11-…ios

一年一適配,今年又來了。
今年總體上問題不大,我司的App 沒有出現編譯報錯問題。git

首先,用 Xcode10 編譯的 App 在 iOS 13 上使用甚至幾乎完美😀
然而,用 Xcode11 編譯的 App 再在 iOS 13 上跑,就有些問題了😂github

具體碰到的問題以下:shell

modal新樣式

iOS 13 多了一個新的枚舉類型 UIModalPresentationAutomatic,且是modalPresentationStyle的默認值。xcode

UIModalPresentationAutomatic實際是表現是在 <iOS 13 的設備上被映射成UIModalPresentationFullScreen,在 >=iOS 13的設備上被映射成UIModalPresentationPageSheetruby

我這邊的設計師表示,新樣式不錯,能夠不用改😆。bash

不過, PageSheetFullScreen 對比 有個須要注意的地方,控制器的生命週期有點區別markdown

控制器A控制器B 舉例:app

  • 控制器A present 控制器B
    控制器A 不會調用 viewWillDisappear 以及 viewDidDisappear
  • 控制器B dismiss 時
    控制器A 不會調用 viewWillAppear 以及 viewDidAppear

那麼若是有些業務邏輯會在控制器A的生命週期裏作的話,就須要考慮其餘方式實現,或者改回UIModalPresentationFullScreenide

Xcode11_002

若是須要改爲本來全屏的樣式,能夠處理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

以下方式,直接給 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:

Xcode11_003

經過監聽 leftViewframe 變化,發現是 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;
複製代碼

KVC訪問私有屬性Crash

打開有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的方式去作了😂

navigationBar:shouldPopItem:

點擊導航欄返回的時候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回到上一層了:

Xcode11_004

好吧,那隻能判斷一下版本解決這個問題了,修改方式:

- (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沒設置的話也會被改。

Xcode11_005

Xcode11_006

因爲Assets裏的Color配置是 iOS 12 以上才能使用的,因此若是沒有作全局主題色設計且須要支持 iOS 12 如下設備,改起來會比較噁心。

對此現象,找設計師溝通。設計師表示,暫時沒有精力作夜間模式規劃。

設計師問:可否強制隻日間模式?
答:能。配置方式有兩種,單頁面配置全局配置

  • 單頁配置

    將須要配置的 UIViewControler 對象的 overrideUserInterfaceStyle 屬性設置成 UIUserInterfaceStyleLight 或者 UIUserInterfaceStyleDark 以強制是某個頁面顯示爲 淺/深色模式

  • 全局配置

    在工程的 Info.plist 的中,增長/修改 UIUserInterfaceStyleUIUserInterfaceStyleLightUIUserInterfaceStyleDark

2019.07.26 補充: 早上恰好看到知識小集發了篇 適配 Dark Mode 的文章,看了下挺好的,推薦閱讀。

使用 QMUITheme 實現換膚並適配 iOS 13 Dark Mode

Pod私有庫校驗報錯找不到simulator

推 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 的位置,那麼接下來就能夠修改了。

  1. 前往 /Users/HY/.rvm/gems/ruby-2.4.1/gems/fourflusher-2.2.0/lib/fourflusher/
  2. 打開文件find.rb
  3. 查找 device['availability'] == '(available)'
  4. 增長一個判斷 device['isAvailable'] == true 便可

修改結果如圖便可:

Xcode11_006
相關文章
相關標籤/搜索