当支持iOS 12和13时,AppDelegate和SceneDelegate是什么?

26

我需要支持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
}

如果我不这样做,在一个版本中我会得到黑屏,但是如果我在 HomeViewController viewDidLoad方法中执行并打印,我可以看到它被调用了两次。

我更新了我的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
}

请参见 https://dev59.com/VVMH5IYBdhLWcg3wzzEe#58208876 - rmaddy
如果你支持iOS 12,完全可以跳过SceneDelegate,没有必要使用它。 - Claus Jørgensen
1
请注意,如果您计划构建现代 Catalyst 应用程序,则可能需要采用场景委托。像 macOS 上的分段栏这样的功能需要您采用场景委托。 - The_Lucifer
3个回答

32

您需要复制代码,但需要确保它仅在正确的系统上运行。在iOS 13中,您不希望应用程序委托 didFinishLaunching 中的代码运行,因此请使用可用性检查来防止它。

同样地,使用可用性来隐藏iOS 12中的窗口场景内容。

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

请注意,在处理其他重复问题时,如应用程序激活等,会更加简单,因为如果您支持窗口场景,则不会在 iOS 12 上调用应用委托方法。因此,问题仅限于一种情况,即在启动时需要执行窗口/根视图控制器操作(例如,无故事板)。


哦,我明白了,那么可能是在我的 didFinishLaunchingWithOptions 上使用类似 @available(iOS, obsoleted: 13) 的东西? - Teddy K
请看我的更新,我添加了一个检查,在iOS12中可以工作,但在iOS13中仍然会调用两次。感谢您的帮助。 - Teddy K
说“guard #available(iOS 12, *) else { return true }”是愚蠢的,因为这与你想要的相反。现在代码在iOS 12和iOS 13中都可以运行,但排除了需要它的iOS 11!你需要做的是相反的:在iOS 13中立即返回true,否则继续进行。 - matt
可下载的项目在这里 https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/bk2ch01p004appWithoutStoryboard2/bk2ch01p004appWithoutStoryboard - matt
为什么不干脆把 SceneDelegate 给移除掉呢? - Borut Tomazin

7

Xcode 11.*和Swift 5.*

按照以下步骤操作,然后您的代码将在iOS 12和iOS 13上正常工作 -

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

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


7

这对我很起作用。

由于SceneDelegate类仅适用于iOS 13及以上版本,我们必须告诉编译器只在iOS 13及以上版本中包含该类。为此,在SceneDelegate类声明的上方添加"@available(iOS 13.0, *)"行,如下所示:

import UIKit

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

在AppDelegate.swift中提供了一些可用的方法。

接下来,AppDelegate.swift中添加了两个新的方法,仅支持iOS 13及以上版本。我们将在它们顶部添加相同的@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.
}

将窗口添加回AppDelegate

如果你现在构建和运行你的应用程序,你会得到一个黑色的屏幕,因为没有初始化UIWindow。

iOS 12及更早版本始终在AppDelegate.swft的顶部位置有一个变量var window:UIWindow?。iOS 13已将此变量移动到SceneDelegate.swift中,现在我们将把此变量添加回AppDelegate。

import UIKit

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

现在在iOS 12设备上构建并运行您的应用程序,它就可以工作了!

我猜苹果真的希望iOS开发者采用并专注于iOS 13,在Xcode中默认设置下他们不介意破坏对iOS 12和早期版本的支持。

如果每次手动执行这些步骤让你感到困难,您也可以在Apple的开发者下载门户网站(需要使用您的Apple ID登录)中下载Xcode 10.3,使用它创建一个新的Xcode项目,然后使用Xcode 11进行编辑。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接