SwiftUI 2:新窗口打开视图的方式

问题描述 投票:0回答:5

假设我有一个应用程序

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

swift macos navigation swiftui scene
5个回答
8
投票

更新:Xcode 14 / macOS 13

现在我们可以显式打开所需的窗口并为其提供标识符,并在

openWindow
环境操作中使用它。

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
        }

        Window("Book Viewer", id: "book") { // << here !!
            BookView(model: appState.selectedBook)
        }

        // also possible variant with injected model for group
        // WindowGroup("Book Viewer", id: "book", for: Book.self) {  book in // << here !!
        //   BookView(model: book)
        // }
    }
}

struct ContentView: View {
    @EnvironmentObject var appState: AppState

    @Environment(\.openWindow) private var openWindow   // << !!

    var body: some View {

        // assuming selection in grid somewhere here to
        // appState.selectedBook

        Button("Open Book") {
            openWindow(value: "book")       // << here !!

        // for group variant with injected value
        // if let book = self.selectedBook {
        //    openWindow(value: "book", book)       // << here !!
        // }
        }
    }
}

5
投票

我找到了这个答案,它在能够打开新窗口方面对我有用:https://developer.apple.com/forums/thread/651592?answerId=651132022#651132022

我正在

xcode 12.3
Swift 5.3
运行 Big Sur。

以下是如何设置的示例,以便可以使用

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 方案说明(为方便起见,在此处引用):

现在在“项目”->“信息”->“URL 类型”中,在“URL 方案”字段(以及标识符字段)中输入

test
,以向系统注册我们的应用程序。

我通过编辑

Info.plist
文件并在其中进行添加来实现此目的,即
URL types
->
URL Schemes
...:

Info.plist


4
投票

我使用了 hillmark 的答案中的代码,得到了比他的代码更好的结果。

即使此代码仍然不是答案,这可能对某人有用。

_

通常,如果您需要使用基于 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 {
        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)
        }
    }
}

要使用此代码,您需要具备以下条件:

enter image description here

enter image description here


0
投票

我最近在 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()
    }
}

0
投票

Xcode 16 中的新功能

Window("My super duper window", id: "myWnd") {
    Text("New in this version…")
}
struct ContentView: View {
    @Environment(\.openWindow) var openWindow

    var body: some View {
        Button("Show another one wnd") {
            openWindow(id: "myWnd")
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.