Единые окна в VisionOS
Кажется, чтоWindow
недоступен в VisionOS, толькоWindowGroup
. Если я хочу иметь окно только с одним экземпляром (скажем, меню или подобное), как я могу гарантировать, что каждый раз, когда я вызываюopenWindow
, появляется то же самое окно, а новое не создается?
2 ответа
Я подозреваю, что это сделано намеренно, чтобы позволить пользователям «находить» окна, которые они потеряли. Похоже, что будет легко потерять что-то в среде, и, возможно, с точки зрения UX будет иметь смысл позволить пользователю иметь несколько наборов элементов управления в своей среде.
Если вы хотите контролировать открытие только одного экземпляра WindowGroup, то разумным способом будет сначала закрыть его, прежде чем открывать, поскольку это вернет его туда, куда смотрит пользователь.
@Environment(\.openWindow) private var openWindow
@Environment(\.dismissWindow) private var dismissWindow
Button("open") {
dismissWindow(id: "targetView")
openWindow(id: "targetView")
}
В противном случае отслеживание жизненного цикла окна с использованием фазы сцены в сочетании с.onDisappear
отслеживать, когда представление закрывается, более сложно, но тоже работает.controlActiveState
был бы предпочтительным методом, но он недоступен в VisionOS (на данный момент).
У меня есть довольно быстрое и грязное решение, которое помогло мне. У меня не было другой идеи, кроме как просто кэшировать идентификаторы Windows в каком-то глобальном каталоге и проверять их при каждом открытии нового окна.
Например, внутри вашегоApp
соответствующая структура:
@main
struct MyApp: App {
static var openendWindowsIDs: Set<String> = []
//...
}
Затем, где бы вы ни открыли это новое окно, вы можете проверить его.Set
(Ниже приведен лишь пример, приводящий к некоторому шаблону, поэтому вы, вероятно, каким-то образом абстрагируете его)
Button {
let windowID = "myWindowsUniqueId"
if !MyApp.openendWindowsIDs.contains(windowID) {
openWindow(id: windowID)
}
MyApp.openendWindows.insert(windowID)
}
(Или даже не показывайте эту кнопку или что-либо еще, вызывающее окно, предварительно проверив это)
Также не забудьте удалить идентификатор окна там, где это имеет смысл, например, в главном представлении того окна, которое вы закрываете:
.onDisappear(perform: {
MyApp.openendWindows.remove("myWindowsUniqueId")
})
Что упомянуто в документации, но у меня не сработало:
Документация дляWindowGroup
упоминает:
Если окно с привязкой к тому же значению, которое вы передаете действию openWindow, уже появляется в пользовательском интерфейсе, система выводит существующее окно на передний план, а не открывает новое окно. У меня до сих пор всегда появляются новые экземпляры. Вот почему я сейчас использую приведенный выше пример.
Вместе с этим примером кода:
WindowGroup(for: Message.ID.self) { $messageID in
MessageDetail(messageID: messageID)
}
Как я это понял: Если вы позвонитеopenWindow
с тем же (здесь)messageID
прошло, не должно открываться новое окно. Однако мне это не удалось.