Xcode11 创建的项目运行闪退或黑屏怎么解决?

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 .hSceneDelegate.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:方法中设置windowrootViewController,代码如下所示:

- (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;
}

推荐阅读更多精彩内容