为何canActivateForTargetContentIdentifierPredicate为false时场景仍被激活?
以下代码来自苹果WWDC 2019 Session 212:《iPad多窗口介绍》
apple WWDC 2019 session 212: Introducing Multiple Windows on iPad
let mainSceneTargetContentIdentifier = "com.apple.gallery.mainIdentifier" var window: UIWindow? // MARK: - UIWindowSceneDelegate func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { print("willConnectTo") //3 if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity { if !configure(window: window, with: userActivity) { Swift.debugPrint("Failed to restore from \(userActivity)") } } // The 'window' property will automatically be loaded with the storyboard's initial view controller. // Set the activation predicates, which operate on the 'targetContentIdentifier'. let conditions = scene.activationConditions let prefsPredicate = NSPredicate(format: "self == %@", mainSceneTargetContentIdentifier) // The main predicate, which expresses to the system what kind of content the scene can display. conditions.canActivateForTargetContentIdentifierPredicate = prefsPredicate // The secondary predicate, which expresses to the system that this scene is especially interested in a particular kind of content. conditions.prefersToActivateForTargetContentIdentifierPredicate = prefsPredicate }
当前targetContentIdentifier的值为com.apple.gallery.openDetail,而mainSceneTargetContentIdentifier的值为com.apple.gallery.mainIdentifier,因此NSPredicate(format: "self == %@", mainSceneTargetContentIdentifier)的对比结果应该是false,但场景仍被激活。甚至直接初始化NSPredicate(value:false)赋值给场景条件,场景依然会被激活。根据文档描述,只有当谓词返回true时场景才应被激活,请问这是什么原因?
这是因为场景激活的规则并非仅由canActivateForTargetContentIdentifierPredicate决定,核心原因如下:
初始场景的强制激活逻辑
应用启动创建第一个场景时,系统会直接触发激活流程,不会严格校验canActivateForTargetContentIdentifierPredicate。这个谓词的作用范围是后续的场景创建/激活请求(比如通过UIApplication.requestSceneSessionActivation触发的场景激活、系统自动的场景恢复),而非应用首次启动的初始场景。状态恢复的优先级高于谓词校验
如果场景是通过session.stateRestorationActivity触发的状态恢复流程,系统会优先完成状态恢复,此时canActivateForTargetContentIdentifierPredicate的校验会被跳过。你代码中已经处理了状态恢复逻辑,这是场景被激活的直接触发点。谓词生效的时机滞后
在willConnectTo方法中设置谓词时,场景已经处于激活流程中,这个设置无法影响当前的激活行为,仅会对后续的场景激活请求生效。
要验证谓词的实际作用,可以尝试:
- 先启动应用创建主场景,再通过携带
com.apple.gallery.openDetail的NSUserActivity请求激活新场景,此时系统会因谓词不匹配而拒绝创建/激活该场景。 - 在场景完全激活后(比如
sceneDidBecomeActive方法中)设置谓词,再测试后续的激活请求。
内容的提问来源于stack exchange,提问作者Alyafai

