在Xcode 11 創(chuàng)建的工程,運(yùn)行設(shè)備選擇 iOS 13.0 以下的設(shè)備,運(yùn)行應(yīng)用時會出現(xiàn)黑屏現(xiàn)象。
原因:
- Xcode 11 默認(rèn)是會創(chuàng)建通過 UIScene 管理多個 UIWindow 的應(yīng)用,工程中除了 AppDelegate 外會多一個 SceneDelegate
- AppDelegate和SceneDelegate這是iPadOS帶來的新的多窗口支持的結(jié)果,并且有效地將應(yīng)用程序委托的工作分成兩部分。
也就是說在我們用多窗口開發(fā)iPadOS中,從iOS 13開始,您的應(yīng)用代表應(yīng)該:
1.設(shè)置應(yīng)用程序期間所需的任何數(shù)據(jù)。
2.響應(yīng)任何專注于應(yīng)用的事件,例如與您共享的文件。
3.注冊外部服務(wù),例如推送通知。
4.配置您的初始場景。
相比之下,在iOS 13中的新頂級對象是一個UIWindowScene,場景代表可以處理應(yīng)用程序用戶界面的一個實(shí)例。因此,如果用戶創(chuàng)建了兩個顯示您的應(yīng)用程序的窗口,則您有兩個場景,均由同一個應(yīng)用程序委托支持。
這些場景旨在彼此獨(dú)立工作。因此,您的應(yīng)用程序不再移動到后臺,而是單個場景執(zhí)行 - 用戶可以將一個移動到后臺,同時保持另一個打開。
我們可以看下info.plist文件和工程項目文件的變化如圖:
適配方案一
如果我們不開發(fā)iPadOS多窗口APP,SceneDelegate窗口管理我們可以不需要直接刪掉就好了。
- 刪除掉info.plist中Application Scene Manifest選項,同時,文件SceneDelegate可刪除可不刪
- 在AppDelegate注釋掉這兩個方法。
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
- Appdelegate新增windows屬性
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//如果是用默認(rèn)的storyboard,下面的代碼可以不寫
// window = UIWindow.init()
// window?.frame = UIScreen.main.bounds
// window?.makeKeyAndVisible()
// window?.rootViewController = UIStoryboard.init(name: "Main", bundle: nil).instantiateInitialViewController()
return true
}
做完這些就跟以前一樣啦。
適配方案二
即要用iOS 13中新的SceneDelegate,又可以在iOS 13一下的設(shè)備中完美運(yùn)行。那就添加版本判斷,利用@available
步驟:
- SceneDelegate中添加@available(iOS 13, *)
import UIKit
@available(iOS 13, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
//在類的頭部@available(iOS 13, *)添加即可
..........
}
- AppDelegate中同樣聲明window屬性
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 13, *) {
}else{
window = UIWindow.init()
window?.frame = UIScreen.main.bounds
window?.makeKeyAndVisible()
window?.rootViewController = UIStoryboard.init(name: "Main", bundle: nil).instantiateInitialViewController()
}
return true
}
AppDelegate中兩個關(guān)于Scene的類也添加版本控制,Swift中可以用擴(kuò)展單獨(dú)拎出來,如下:
@available(iOS 13.0, *)
extension AppDelegate {
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
}
切記:這種方式,AppDelegate中的有關(guān)程序的一下狀態(tài)的方法,iOS 13設(shè)備是不會走的,iOS13一下的是會收到事件回調(diào)的。13以上的設(shè)備會走SceneDelegate對應(yīng)的方法
func applicationWillResignActive(_ application: UIApplication) {
}
.....
.....
.....