我正在将Core Data和Cloud Kit一起使用,因此必须在应用程序启动期间检查iCloud用户状态。如果出现问题,我想向用户发出一个对话框UIApplication.shared.keyWindow?.rootViewController?.present(...),直到现在我还是这样做。
在Xcode 11 beta 4中,现在有一条新的弃用消息,告诉我:
iOS 13.0中已弃用“ keyWindow”:不应将其用于支持多个场景的应用程序,因为它会返回所有已连接场景的关键窗口
我该如何显示对话框?
在我们都习惯之前,AppDelegate我们创建UIWindow并rootViewController为此设置的方法Window。有时,当我们需要自定义警报时,我们可以创建一个新的警报UIWindow。
现在 Xcode 会根据这个场景自动创建UIScene和创建UIWindow。我将不胜感激,如果有人能详细解释如何在现实生活中使用这个场景,什么是他们的主要受益于仅使用UIWindow在AppDelegate
因为这篇文章没有解释太多
这有点长,但并不琐碎,需要花很多时间来证明这个问题。
我试图弄清楚如何将一个小示例应用程序从iOS 12更新到iOS13。该示例应用程序不使用任何故事板(除了启动屏幕)。这是一个简单的应用程序,显示了一个带有由计时器更新的标签的视图控制器。它使用状态恢复,因此计数器从中断处开始。我希望能够支持iOS 12和iOS13。在iOS 13中,我想更新到新的场景架构。
在iOS 12下,该应用程序可以正常运行。在全新安装中,计数器从0开始并上升。将应用程序置于后台,然后重新启动应用程序,然后计数器从停止处继续计数。状态恢复全部正常。
现在,我正在尝试使用场景在iOS 13下运行该功能。我遇到的问题是找出初始化场景窗口并将导航控制器和主视图控制器还原到场景的正确方法。
我已经阅读了尽可能多的Apple文档,这些文档与状态恢复和场景有关。我看过与窗口和场景相关的WWDC视频(212-在iPad上引入多个Windows,258-为多个Windows架构应用)。但我似乎缺少将所有内容组合在一起的作品。
当我在iOS 13下运行该应用程序时,所有预期的委托方法(AppDelegate和SceneDelegate)都被调用。状态恢复正在恢复导航控制器和主视图控制器,但rootViewController由于所有UI状态恢复都在AppDelegate中,因此我不知道如何设置场景窗口的状态。
似乎也有一些与NSUserTask应该使用的相关的东西,但我无法说明问题。
缺少的部分似乎是在willConnectTo方法中SceneDelegate。我敢肯定,我还需要一些变化stateRestorationActivity的SceneDelegate。中可能还需要进行更改AppDelegate。我怀疑其中的任何内容都ViewController需要更改。
要复制我的工作,请使用Single View App模板使用Xcode 11(当前为Beta 4)创建一个新的iOS项目。将部署目标设置为iOS 11或12。
删除主故事板。在Info.plist中删除对Main的两个引用(一个位于顶层,另一个位于Application Scene Manifest的深处。按如下所示更新3个swift文件。
AppDelegate.swift:
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
print("AppDelegate willFinishLaunchingWithOptions")
// This probably shouldn't be run under iOS 13? …Run Code Online (Sandbox Code Playgroud) 我正在将适用于 iPadOS 的 iOS13 应用程序转换为 SceneDelegate(多窗口)。
如何从当前的 SceneDelegate 获取当前的 UIWindow?
我知道 a 可以使用UIView.windowor访问当前场景UIViewController.view.window,但是我有一个非 UI 类(AppDelegate),我需要在其中获取窗口(iOS12 之前的 keyWindow)以在所有内容的顶部显示小吃吧。
我曾经这样做,[UIApplication sharedApplication].keyWindow但现在,当然,这是错误的。
UIScene当连接多个场景时,我无法获取电流。
具体来说,我的应用程序(iOS 13)支持 iPad 上的多个窗口。当我有两个并排的窗口并访问应用程序连接的场景时(使用UIApplication.shared.connectedScenes),它们都activationState设置为.foregroundActive. 但我实际上只与其中之一互动。
我在 stackoverflow 上找到了一些代码来检索当前的关键窗口(不记得是谁发布的),但正如您所见,它总是会返回两个窗口之一。
let keyWindow: UIWindow? = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
Run Code Online (Sandbox Code Playgroud)
我是否缺少某些方法来确定与之交互的最后一个窗口,或者多个.foregroundActive状态是否是错误?
谢谢!
scene(_ scene: UIScene, continue userActivity: NSUserActivity)在用户单击通用链接后启动应用程序时,不会调用方法。
当用户单击通用链接后,已启动的应用程序再次打开时,它工作正常。示例代码:
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let incomingURL = userActivity.webpageURL,
let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
let path = components.path else {
return
}
let params = components.queryItems ?? [URLQueryItem]()
print("path = \(path)")
print("params = \(params)")
}
Run Code Online (Sandbox Code Playgroud)
我尝试使用application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration,但是当用户单击链接时它永远不会被调用:
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
if let scene = …Run Code Online (Sandbox Code Playgroud) iOS 具有以下调用方法,因此您可以通过通知中心或设置应用程序向用户提供自定义通知设置:
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {
// Open up a Settings view controller within your app or something
}
Run Code Online (Sandbox Code Playgroud)
在其中,您将打开一些应用内设置视图控制器。
在 iOS 12 上这很容易,找到活动窗口,找到 rootViewController 并将设置屏幕扔到它上面。
在 iOS 13 上你(可以)有多个窗口,所以我假设这个方法可以在其中任何一个上调用?
假设您有 5 个应用程序窗口在后台打开,然后您打开“设置”应用程序并请求应用程序的通知设置。它使用哪个窗口?我如何知道要将设置屏幕添加到哪个窗口/视图控制器?我不想随机选择一个并且不小心将它添加到背景中的一个而不是打开的那个。
UNNotificationResponse另一方面,确实有一种targetScene方法,因此如果您的应用程序是从通知中打开的,您就可以知道要使用哪个窗口,但这种方法似乎缺乏它。
我应该遍历应用程序可用的所有窗口场景并在前台找到一个吗?
我想知道为什么:
UIApplication.shared.windows.first { $0.isKeyWindow }
Run Code Online (Sandbox Code Playgroud)
在模拟器和设备上始终返回 nil。您可以通过在任何视图控制器中的 viewDidLoad 中放置断点并将对象打印到控制台来确认这一点。
po UIApplication.shared.windows.first { $0.isKeyWindow }
Run Code Online (Sandbox Code Playgroud)
我获得“keyWindow”的唯一方法是获取
UIApplication.shared.windows.first
Run Code Online (Sandbox Code Playgroud)
但没有明确提及这是关键。如果 iPad 拆分视图上有我的应用程序的多个实例怎么办?
有任何想法吗?
我的想法是,如果只有一个窗口,则默认情况下它是关键窗口,如果您在 iPad 上运行多个窗口,则当前正在交互的窗口将成为关键窗口,但是,我还没有测试过这一点。
如果我编译到 iOS 12 设备(不使用 UIScene)和 AirPlay Mirror 到我的 Apple TV,应用程序会按预期镜像到电视。
在 iOS 13 设备上,它似乎将其视为外部显示器,在其中格式化以适合屏幕(但我无法控制它)。
我更喜欢只是镜像它的旧功能。
如何在 iOS 13 上完成镜像?我在文档中挖掘:
application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration
Run Code Online (Sandbox Code Playgroud)
并且在UISceneConfiguration那里有一个role属性(UISceneSession.Role.windowExternalDisplay当我尝试 AirPlay Mirror 时它有)但它似乎没有任何价值,如UISceneSession.Role.windowMirror.
我要添加一个“在新窗口中打开”上下文菜单项,该菜单项会在new中打开我的应用程序文档之一UIScene。当然,我只想在实际上支持多个场景的设备上显示该菜单项。
目前,我只是在检查设备是否是使用旧设备的iPad:
if UIDevice.current.userInterfaceIdiom == .pad
Run Code Online (Sandbox Code Playgroud)
有没有更精确的方法来检查设备是否支持此特定功能?