SwiftUI 2: способ открыть просмотр в новом окне
Представим, что у меня есть приложение
var storeVM = BookStoreViewModel(bla1: bla1, bla2: bla2, bla3: bla3)
@SceneBuilder var body: some Scene {
WindowGroup {
BookStoreView( model: storeVM )
}
#if os(macOS)
Settings {
SettingsView(model: config)
}
#endif
}
В BookStore есть сетка с множеством книг, сохраненных в какой-то БД.
BookView может быть инициирован следующим образом:
BookView(model: bookViewModel)
Цель: открыть BookView В НОВОМ ОТДЕЛЬНОМ ОКНЕ (например, нажав на кнопку). Как я могу это сделать?
Бонусный вопрос: как открыть SettingsView(model: config)
из кода?
PS: NavigationLink
не решение для меня, потому что я не использую NavigationView
.
4 ответа
Попробуйте что-то вроде следующего (просто идея - не могу проверить)
class BookViewModel: ObservableObject {}
class AppState: ObservableObject {
@Published var selectedBook: BookViewModel?
}
@main
struct SwiftUI2_MacApp: App {
@StateObject var appState = AppState()
@SceneBuilder
var body: some Scene {
WindowGroup { // main scene
ContentView()
.environmentObject(appState) // inject to update
// selected book
}
WindowGroup("Book Viewer") { // other scene
if appState.selectedBook != nil {
Book(model: appState.selectedBook)
}
}
}
}
Я нашел этот ответ, который помог мне открыть новое окно: https://developer.apple.com/forums/thread/651592?answerId=651132022#651132022
Я на
xcode 12.3
,
Swift 5.3
, работает Биг-Сур.
Ниже приведен пример того, как настроить так, чтобы кнопка в
ContentView
можно использовать для открытия
OtherView
окно.
@main
struct testApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
WindowGroup("OtherView") {
OtherView()
}
.handlesExternalEvents(matching: Set(arrayLiteral: "*"))
}
}
struct ContentView: View {
@Environment(\.openURL) var openURL
var body: some View {
Button("Other View") {
if let url = URL(string: "test://otherview") {
openURL(url)
}
}
}
}
struct OtherView: View {
var body: some View {
Text("Other View!")
}
}
Примечание. Обязательно следуйте инструкциям схемы URL, включенным в связанный ответ (цитируется здесь для удобства):
Теперь в Project-> Info-> Типы URL-адресов введите
test
в поле URL Schemes (а также поле идентификатора), чтобы зарегистрировать наше приложение в системе.
Я добился этого, отредактировав
Info.plist
файл и внесение туда дополнений, т.е.
URL types
->
URL Schemes
...:
Я играл с кодом из ответа Hillmark и получил несколько лучших результатов, чем его код.
Даже если этот код все еще не является ответом, это может быть кому-то полезно.
_
Обычно этот код будет бесполезен для вас, если вам нужно работать с View на основе ViewModel.
Но если вам нужно работать только с View - это хорошее решение.
@main
struct TestAppApp: App {
var body: some Scene {
WindowGroup {
MainView()
}
// magic here
.handlesExternalEvents(matching: Set(arrayLiteral: Wnd.mainView.rawValue))
WindowGroup {
HelperView()
}
// magic here
.handlesExternalEvents(matching: Set(arrayLiteral: Wnd.helperView.rawValue))
}
}
extension TestAppApp {
struct MainView: View {
@Environment(\.openURL) var openURL
var body: some View {
VStack {
Button("Open Main View") {
// magic here
Wnd.mainView.open()
}
Button("Open Other View") {
// magic here
Wnd.helperView.open()
}
}
.padding(150)
}
}
struct HelperView: View {
var body: some View {
HStack {
Text("This is ") + Text("Helper View!").bold()
}
.padding(150)
}
}
}
// magic here
enum Wnd: String, CaseIterable {
case mainView = "MainView"
case helperView = "OtherView"
func open(){
if let url = URL(string: "taotao://\(self.rawValue)") {
print("opening \(self.rawValue)")
NSWorkspace.shared.open(url)
}
}
}
Для работы с этим кодом вам необходимо:
Недавно я столкнулся с аналогичной проблемой в swiftui, где хотел создать новое окно на уровне рабочего стола, и, кажется, наконец нашел решение.
Button("Show Bookmark") {
// create a new NSPanel
let window = NSPanel(contentRect: NSRect(x: 0, y: 0, width: 500, height: 500), styleMask: [.fullSizeContentView, .closable, .resizable, .titled], backing: .buffered, defer: false)
// the styleMask can contails more, like borderless, hudWindow...
window.center()
// put your swiftui view here in rootview
window.contentView = NSHostingView(rootView: BookmarkView())
window.makeKeyAndOrderFront(nil)
// if you want to close the window after delay
DispatchQueue.main.asyncAfter(deadline: .now()) {
window.close()
}
}