问题
I have a problem getting the current UIScene
when multiple scenes are connected.
Specifically, my app (iOS 13) supports multiple windows on the iPad. When I have two windows side by side and I access the apps connected scenes (with UIApplication.shared.connectedScenes
), all of them have the activationState
set to .foregroundActive
. But I am actually only interacting with one of them.
I found some code here on stackoverflow to retrieve the current key window (can't remember who it was that posted it) but as you can see it will always return one of the two windows.
let keyWindow: UIWindow? = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
Am I missing some method to determine the last window that was interacted with or are the multiple .foregroundActive
states a bug?
Thanks!
回答1:
I found the solution to my problem by refactoring my code so that now I access the current UIScene
s window through the window
attribute of any view or view controller. This way I don't need the keyWindow
anymore.
I think it's probably correct that bot windows open next to each other have the activationState
.foregroundActive
In case anyone needs it, these are the two extensions that helped me get to to current window from anywhere in the app:
extension UIViewController {
var window: UIWindow? {
return view.window
}
}
extension UIScene {
var window: UIWindow? {
return (delegate as? SceneDelegate)?.window
}
}
But the problem still stands if you don't have access to a UIView
, UIViewController
or UIScene
. For example inside a data model with no reference to a view or scene. Then I don't see a way the consistently accessing the correct window the user is interacting with.
回答2:
I use this extension in my project
// MARK: - Scene Delegate helpers
@available(iOS 13.0, *)
extension UIApplication {
static var firstScene: UIScene? {
UIApplication.shared.connectedScenes.first
}
static var firstSceneDelegate: SceneDelegate? {
UIApplication.firstScene?.delegate as? SceneDelegate
}
static var firstWindowScene: UIWindowScene? {
UIApplication.firstSceneDelegate?.windowScene
}
}
// MARK: - Window Scene sugar
@available(iOS 13.0, *)
protocol WindowSceneDependable {
var windowScene: UIWindowScene? { get }
var window: UIWindow? { get }
}
@available(iOS 13.0, *)
extension WindowSceneDependable {
var windowScene: UIWindowScene? {
window?.windowScene
}
}
@available(iOS 13.0, *)
extension UIScene: WindowSceneDependable { }
@available(iOS 13.0, *)
extension SceneDelegate: WindowSceneDependable { }
@available(iOS 13.0, *)
extension UIViewController: WindowSceneDependable { }
// MARK: - Window sugar
@available(iOS 13.0, *)
extension UIScene {
var window: UIWindow? {
(delegate as? SceneDelegate)?.window
}
}
extension UIViewController {
var window: UIWindow? {
view.window
}
}
And then I can do this when I have to present something:
func present(from navigation: UINavigationController) {
self.navigation = navigation
if #available(iOS 13.0, *) {
guard let currentWindowScene = navigation.windowScene else { return }
myPresenter.present(in: currentWindowScene)
} else {
myPresenter.present()
}
}
Using this I can get a window from almost anywhere. But the thing is that it's not possible if I have no access to view-related objects at all. For example, in my auth service I have to use this bullshit:
func authorize() {
if #available(iOS 13.0, *) {
guard let currentWindowScene = UIApplication.firstWindowScene else { return }
presenter.present(in: currentWindowScene)
} else {
presenter.present()
}
}
This does not look correct for me, and it works only for the first windowScene. It's better not to do this just like I did, you have to elaborate on this solution.
回答3:
This solution gives you the last interacted-with UIScene
without relying on anything but using a UIWindow
subclass for all your scenes.
class Window: UIWindow
{
static var lastActive: Window?
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
{
Self.lastActive = self
return super.hitTest(point, with: event)
}
}
Now you can access Window.lastActive
and its associated windowScene
(UIWindowScene
/UIScene
).
来源:https://stackoverflow.com/questions/57679284/getting-currently-focused-uiscene-when-multiple-connected-scenes-are-active-in-f