支持 iOS 12 和 13 时的 AppDelegate 和 SceneDelegate

AppDelegate and SceneDelegate when supporting iOS 12 and 13

我需要支持 iOS 12 和 iOS 13。

我应该在 AppDelegateSceneDelegate 之间复制代码吗?

例如:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    let window = UIWindow(windowScene: windowScene)

    window.rootViewController = HomeViewController()
    window.makeKeyAndVisible()

    self.window = window
}

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let window = UIWindow(frame: UIScreen.main.bounds)
    window.rootViewController = HomeViewController()
    window.makeKeyAndVisible()

    self.window = window

    return true
}

如果我不这样做,在第一个版本中我会以黑屏结束,但如果我这样做并在 HomeViewControllerviewDidLoad 方法中打印,我可以看到它被称为两次。

我更新了我的 didFinishLaunchingWithOptions,我可以在 iOS13 中看到它仍然被调用了两次。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    guard #available(iOS 12, *) else { return true }

    let window = UIWindow(frame: UIScreen.main.bounds)
    window.rootViewController = HomeViewController()
    window.makeKeyAndVisible()

    self.window = window

    return true
}

您确实需要复制代码,但您需要确保它 运行 仅在正确的系统上。在 iOS 13 中,您不希望应用程序将 didFinishLaunching 主体代码委托给 运行,因此使用可用性检查来防止它。 以同样的方式,使用可用性从 iOS 12.

隐藏 window 场景内容

这是 运行 在 iOS 12 和 iOS 13 上都正确的解决方案的基本草图:

AppDelegate.Swift

import UIKit
@UIApplicationMain
class AppDelegate : UIResponder, UIApplicationDelegate {
    var window : UIWindow?
    func application(_ application: UIApplication,
        didFinishLaunchingWithOptions 
        launchOptions: [UIApplication.LaunchOptionsKey : Any]?)
        -> Bool {
            if #available(iOS 13, *) {
                // do only pure app launch stuff, not interface stuff
            } else {
                self.window = UIWindow()
                let vc = ViewController()
                self.window!.rootViewController = vc
                self.window!.makeKeyAndVisible()
                self.window!.backgroundColor = .red
            }
            return true
    }
}

SceneDelegate.swift

import UIKit
@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window : UIWindow?
    func scene(_ scene: UIScene,
        willConnectTo session: UISceneSession,
        options connectionOptions: UIScene.ConnectionOptions) {
            if let windowScene = scene as? UIWindowScene {
                self.window = UIWindow(windowScene: windowScene) 
                let vc = ViewController()                      
                self.window!.rootViewController = vc             
                self.window!.makeKeyAndVisible()                 
                self.window!.backgroundColor = .red
            }
    }
}

ViewController.swift

import UIKit
class ViewController : UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print("view did load")
        self.view.backgroundColor = .green
    }
}

请注意,处理其他重复项(例如应用程序激活)要简单得多,因为如果您支持 window 场景,则不会在 iOS 12 上调用应用程序委托方法。所以问题仅限于这种情况,即您有 window / 根视图控制器操作要在启动时执行(例如,没有故事板)。

Xcode 11.* 和 Swift 5.*

按照下面给出的步骤,您的代码将适用于 iOS 12 和 iOS 13 -

  1. 从 info.plist 文件中删除场景清单
  2. 移除场景委托
  3. 在 AppDelegate
  4. 中添加 window 属性
  5. 从 AppDelegate 中删除与场景相关的所有方法(主要是 2 个方法)

希望这对某些人有用。快乐编码

这是我的作品。

@可用 SceneDelegate.swift

由于 SceneDelegate class 仅在 iOS 13 及更高版本上可用,我们必须告诉编译器仅包含 iOS 13 及更高版本的 class .为此,我们将在 SceneDelegate class 声明的正上方添加这一行“@available(iOS 13.0, *)”,如下所示:

import UIKit

@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
//...
}

@可用AppDelegate.swift

中的一些方法

接下来AppDelegate.swift新增了两个方法,只支持iOS13及以上。我们还将在它们之上添加相同的 @available(iOS 13.0, *) :

// AppDelegate.swift

@available(iOS 13.0, *)
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)
}

@available(iOS 13.0, *)
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.
}

将 window 添加回 AppDelegate

如果您现在构建并 运行 您的应用程序,您将看到黑屏,因为没有初始化 UIWindow。

在 iOS 12 和更早的版本中,总是有一个 var window: UIWindow?位于 AppDelegate.swft 顶部的变量。 iOS 13 已将此变量移至 SceneDelegate.swift,现在我们要将此变量添加回 AppDelegate。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
     
    var window: UIWindow?
  
    // ...
}

现在在 iOS 12 台设备上构建并 运行 您的应用程序,并且可以正常工作!

我猜 Apple 真的希望 iOS 开发人员采用并专注于 iOS 13,以至于他们不介意默认破坏对 iOS 12 及更早版本的支持Xcode.

中的设置

如果你懒得每次都手动做这些步骤,你也可以在Apple的开发者下载门户下载Xcode10.3(需要用你的Apple ID登录),新建一个Xcode 项目,然后使用 Xcode 11.

对其进行编辑