Получение текущей фокусированной UIScene, когда на переднем плане активны несколько связанных сцен
У меня проблема с получением тока UIScene
когда несколько сцен связаны.
В частности, мое приложение (iOS 13) поддерживает несколько окон на iPad. Когда у меня есть два окна рядом, и я получаю доступ к приложениям, связанным сценами (с UIApplication.shared.connectedScenes
), все они имеют activationState
установлен в .foregroundActive
, Но на самом деле я взаимодействую только с одним из них.
Я нашел некоторый код здесь в stackru для извлечения текущего ключевого окна (не могу вспомнить, кто это опубликовал), но, как вы можете видеть, оно всегда будет возвращать одно из двух окон.
let keyWindow: UIWindow? = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
Я пропустил какой-то метод, чтобы определить последнее окно, с которым взаимодействовали, или несколько .foregroundActive
констатирует ошибку?
Спасибо!
4 ответа
Несмотря на то что
UIApplication.shared.keyWindow
устарел после iOS 13.0 Apple, кажется, этот атрибут может помочь вам найти активный, когда несколько сцен на переднем плане с помощью:
UIApplication.shared.keyWindow?.windowScene
Я нашел решение своей проблемы путем рефакторинга своего кода, чтобы теперь я получил доступ к текущему UIScene
окно через window
атрибут любого представления или контроллера представления. Таким образом, мне не нужно keyWindow
больше.
Я думаю, что это правильно, что окна ботов, открытые рядом друг с другом, имеют activationState
.foregroundActive
В случае, если кому-то это нужно, вот два расширения, которые помогли мне добраться до текущего окна из любой точки приложения:
extension UIViewController {
var window: UIWindow? {
return view.window
}
}
extension UIScene {
var window: UIWindow? {
return (delegate as? SceneDelegate)?.window
}
}
Но проблема остается, если у вас нет доступа к UIView
, UIViewController
или же UIScene
, Например, внутри модели данных без ссылки на представление или сцену. Тогда я не вижу способа постоянного доступа к правильному окну, с которым взаимодействует пользователь.
Я использую это расширение в своем проекте
// 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
}
}
И тогда я могу это сделать, когда мне нужно что-то представить:
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()
}
}
Используя это, я могу получить окно практически из любого места. Но дело в том, что это невозможно, если у меня вообще нет доступа к объектам, связанным с просмотром. Например, в моей службе авторизации я должен использовать эту чушь:
func authorize() {
if #available(iOS 13.0, *) {
guard let currentWindowScene = UIApplication.firstWindowScene else { return }
presenter.present(in: currentWindowScene)
} else {
presenter.present()
}
}
Мне это не кажется правильным и работает только для первого windowScene. Лучше не делать этого, как я, вы должны уточнить это решение.
Это решение дает вам последнее взаимодействие с UIScene
не полагаясь ни на что, кроме использования UIWindow
подкласс для всех ваших сцен.
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)
}
}
Теперь вы можете получить доступ Window.lastActive
и связанные с ним windowScene
(UIWindowScene
/UIScene
).