【问题标题】:SwiftUI 2: the way to open view in new windowSwiftUI 2:在新窗口中打开视图的方式
【发布时间】:2020-11-04 23:29:20
【问题描述】:

假设我有一个应用程序

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 有一个 Grid,其中有很多书籍保存在一些 DB 中。

BookView 可以通过以下方式启动:

BookView(model: bookViewModel)

目标:在新的分离窗口中打开 BookView(例如单击按钮)。我该怎么做?


奖金问题: 如何从代码中打开SettingsView(model: config)


PS:NavigationLink 不是我的解决方案,因为我没有使用 NavigationView

【问题讨论】:

    标签: swift macos navigation swiftui scene


    【解决方案1】:

    我找到了这个答案,它对我来说可以打开一个新窗口:https://developer.apple.com/forums/thread/651592?answerId=651132022#651132022

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

    现在在 Project->Info->URL Types 中的 URL Schemes 字段(以及标识符字段)中输入 test 以向系统注册我们的应用程序。

    我通过编辑Info.plist 文件并在其中添加内容来实现这一点,即URL types -> URL Schemes...:

    【讨论】:

    • 非常感谢,我稍后会检查这个
    • 首先 - 这是一个有趣的解决方案,感谢它。但看起来它没用,直到我们无法将 viewModel 发送到窗口中。你知道如何将它与 viewModel 一起使用吗?
    【解决方案2】:

    尝试以下方法(只是想法 - 无法测试)

    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)
                }
            }
        }
    }
    

    【讨论】:

    • 不可能将if()直接放在WindowGroup()中,需要换行到另一个视图中;从可空模型打开初始化视图是不可能的——无论如何我都需要返回视图,但返回空视图是个坏主意——结果我会得到一个空窗口。返回 nil - 会抛出错误;它看起来像是一种使用以下方式的hacky方式(我的意思是那些注入)。但是感谢您的尝试
    • @Andrew,这是运行时错误,因为它可以在我身边编译吗?
    • 否,编译错误;但无论如何,这并不是一个很好的解决方案,因为即使它可以工作......真的更容易和更干净的代码将是在使用 AppKit 的情况下......真的没有更好的方法来在 swiftUI 中打开全新的窗口?
    • 这对我来说编译得很好,但我没有弹出任何第二个窗口。 (我使用的是 Big Sur 测试版,Xcode 12.2 测试版)
    • @RobN 在 12.4 版本 + Big Sur 11.1 上面临同样的问题
    【解决方案3】:

    我玩过 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 {
            @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)
            }
        }
    }
    

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

    【讨论】:

    • 您是否已经尝试使用深层链接来传递“详细信息”?例如。 taotao://MainView/ShowBook/BlaBla 中的图书标识符。尝试在 MainVIew 中使用 .onOpenUrl(...) 处理深层链接。
    • 不,我没有。我在我的应用程序中使用了另一个系统而不是这个。因为这对我来说仍然不够灵活。
    • 详细信息也可以发送,只要它是一个有效的url。但要小心,在 safari 中使用该链接会打开您的应用程序。所以不要相信你得到的任何意见。
    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2023-03-21
    • 2021-07-24
    • 1970-01-01
    • 1970-01-01
    • 2014-03-24
    • 1970-01-01
    相关资源
    最近更新 更多