在 iOS 13 及更高版本中,AppDelegate
和 SceneDelegate
的连接并不是通过直接调用,而是通过配置和系统框架的机制来实现的。以下是它们如何协同工作的详细说明:
1. Info.plist
配置
#
在 iOS 13 及更高版本中,Info.plist
文件中有一个关键的配置项,UIApplicationSceneManifest
,它定义了应用的场景支持。这是系统用来决定是否使用 SceneDelegate
的地方。
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
UISceneDelegateClassName
: 这个键指定了场景的委托类,即SceneDelegate
。系统在需要创建一个新的场景时,会根据这个配置来实例化SceneDelegate
。
2. 系统框架的机制 #
UIApplication
: 当应用启动时,UIApplication
会根据Info.plist
中的UIApplicationSceneManifest
配置来决定是否使用场景支持。如果配置了场景支持,UIApplication
会自动管理SceneDelegate
的生命周期。UISceneSession
:AppDelegate
中的application(_:configurationForConnecting:options:)
方法返回一个UISceneConfiguration
,这个配置告诉系统使用哪个SceneDelegate
来管理场景。
3. 代码执行流程 #
- 应用启动: 系统调用
AppDelegate
的application(_:didFinishLaunchingWithOptions:)
方法进行全局初始化。 - 场景创建: 如果
Info.plist
中配置了场景支持,系统会调用AppDelegate
的application(_:configurationForConnecting:options:)
方法来获取场景配置。 - 场景连接: 根据场景配置,系统实例化
SceneDelegate
,并调用其scene(_:willConnectTo:options:)
方法来设置场景的 UI。
总结 #
AppDelegate
和 SceneDelegate
的连接是通过 Info.plist
中的配置和系统框架的机制来实现的,而不是通过显式的代码调用。这种设计使得应用可以更好地支持多任务和多窗口环境。
细节需要继续参考 #
UISceneDelegate
https://developer.apple.com/documentation/uikit/uiscenedelegate#
Specifying the scenes your app supports
https://developer.apple.com/documentation/uikit/specifying-the-scenes-your-app-supports
示例项目:
https://github.com/shankarmadeshvaran/SwiftUI_Tasks
配置如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
配置在 Info.plist 的原因 #
在 iOS 13 及更高版本中,多窗口支持与 SceneDelegate
密切相关,但并不是每个窗口都有一个独立的 SceneDelegate
类。相反,SceneDelegate
类的实例是与每个窗口(或场景)相关联的。以下是它们之间的关系和工作机制:
多窗口与 SceneDelegate 的关系 #
- 场景(Scene):
- 在 iOS 中,场景代表应用的一个实例或用户界面。每个场景可以对应一个窗口,尤其是在 iPad 上支持多窗口的情况下。
- SceneDelegate 实例:
- 每个场景都有一个对应的
SceneDelegate
实例。SceneDelegate
负责管理该场景的生命周期事件,如进入前台、进入后台、断开连接等。
- 共享 SceneDelegate 类:
- 虽然每个场景有一个
SceneDelegate
实例,但它们通常共享同一个SceneDelegate
类。也就是说,开发者只需定义一个SceneDelegate
类,系统会为每个场景创建该类的一个实例。
工作机制 #
场景配置:
在
Info.plist
中,通过UIApplicationSceneManifest
配置场景支持。UISceneConfiguration
指定了SceneDelegate
类。场景创建:
当用户在 iPad 上打开应用的一个新窗口时,系统会创建一个新的场景,并实例化一个
SceneDelegate
。场景管理:
SceneDelegate
的每个实例管理其对应场景的生命周期事件。开发者可以在这些事件中执行特定的逻辑,如保存数据、更新 UI 等。
代码示例 #
假设你有一个 SceneDelegate
类:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// 初始化窗口和根视图
}
func sceneDidBecomeActive(_ scene: UIScene) {
// 场景变为活跃状态
}
func sceneWillResignActive(_ scene: UIScene) {
// 场景将进入非活跃状态
}
}
当用户在 iPad 上打开应用的多个窗口时,系统会为每个窗口创建一个 SceneDelegate
实例。每个实例独立管理其窗口的生命周期。
总结 #
- 一个
SceneDelegate
类: 通常只需要一个SceneDelegate
类。 - 多个
SceneDelegate
实例: 每个窗口(场景)有一个SceneDelegate
实例。 - 独立管理: 每个实例独立管理其对应场景的生命周期。
这种设计使得应用可以更好地支持多任务和多窗口环境,提供更灵活和强大的用户体验。