Единые окна в 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прошло, не должно открываться новое окно. Однако мне это не удалось.

Другие вопросы по тегам