1. 問題及原因
1.1 問題
????????首先聲明一下這里說的問題是針對通過代碼的方式來進入程序而不是通過Storyboard。通過Xcode11之前的版本創建項目后,將AppDelegate.m
文件中的application: didFinishLaunchingWithOptions:
方法的實現改為下面的代碼就可以通過代碼設置rootViewController
來啟動程序了。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
self.window.backgroundColor = UIColor.whiteColor;
[self.window makeKeyAndVisible];
self.window.rootViewController = [ViewController new];
return YES;
}
????????但是當我們用Xcode11創建項目通過同樣的方式來啟動時,發現程序閃退了,原因如下圖所示:
1.2 造成這個問題的原因
????????我們發現,通過Xcode11創建的工程除了有AppDelegate
這個類文件,還多出來一個SceneDelegate
類文件。多出來的這個類時用來干嘛的呢?這是iPadOS帶來的用來支持多窗口的。
????????在iOS13之前,Appdelegate
的職責全權處理App生命周期和UI生命周期。
????????而在iOS 13之后就發生了變化,UI生命周期變成由SceneDelegate
來負責,而Appdelegate
負責APP的生命周期和SceneDelegate
的生命周期。所以還是像之前那么處理的話就會出問題。
2. 解決方案一
????????如果我們項目中不需要支持多窗口,那我們就不需要用到SceneDelegate
。
????????第一步,先將項目的Info.plist
文件中的Application Scene Manifest
這一項刪掉,然后刪掉SceneDelegate .h
和SceneDelegate.m
文件(這兩個文件不刪也不會有影響)。
????????第二步,在AppDelegate .h
文件中添加@property (nonatomic , strong) UIWindow *window;
,然后將AppDelegate.m
文件中的application: didFinishLaunchingWithOptions:
方法的實現改為下面的代碼。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
self.window.backgroundColor = UIColor.whiteColor;
[self.window makeKeyAndVisible];
self.window.rootViewController = [ViewController new];
return YES;
}
????????第三步,經過前面2步操作后運行程序不會閃退了,但是發現黑屏,還需要將AppDelegate.m
中的下面2個方法刪掉。這樣就可以正常啟動程序了。
#pragma mark - UISceneSession lifecycle
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
}
3. 解決方案二
????????如果我項目中有多窗口開發相關的需求,或者需要保留SceneDelegate
相關的內容該怎么辦呢?首先我們這里不采用Storyboard
的方式來啟動,先刪掉項目中Main.storyboard
文件,然后打開項目的Info.plist
文件,刪掉下圖用紅色框起來的兩項。
????????然后在
SceneDelegate.m
文件的scene: willConnectToSession: options:
方法中設置window
和rootViewController
,代碼如下所示:
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
self.window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];
self.window.backgroundColor = UIColor.whiteColor;
[self.window makeKeyAndVisible];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]];
}
????????這樣程序就可以在iOS 13的設備中正常運行了,但是再iOS 13以下的設備上運行會黑屏,所以還需要適配iOS 13以下的系統。首先在AppDelegate .h
文件中添加@property (nonatomic , strong) UIWindow *window;
,然后將AppDelegate.m
文件中的application: didFinishLaunchingWithOptions:
方法的實現改為下面的代碼。這樣就可以在所有系統上都能正常運行了。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// @available(iOS 13.0,*)表示iOS 13.0及以上的系統,后面的*表示所有平臺
if (@available(iOS 13.0,*)) {
}else{
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = UIColor.whiteColor;
[self.window makeKeyAndVisible];
ViewController *vc = [ViewController new];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:vc];
}
return YES;
}