Understanding Scene/WindowGroup in SwiftUI 2?

6.3k views Asked by At

There is several objects in SwiftUI 2 like Scene and WindowGroup as some sort of Scene.

After reading documentation and looking WWDC2020 video related to scenes I see hierarchy by the following way :

Single App => One or multiple scenes => Hierarchy of Views (or a few)

Each scene contains the root view of a view hierarchy and has a life cycle managed by the system. Each scene can be displayed with different ways depends to the platform.

  1. In case of few children of WindowGroup scene - how it's choosing the way to display them? ( vertically / horizontally )? And how to control it manually?

I'm not sure that this is controlled with HStack and VStack because of in my test project I got different result instead expected by some reason.

  1. How I can control Scene displayed? As example app have 2 scenes - each WindowGroup with 2 Views in it. How can I switch from one scene to another in the same window of macOS?

  2. How to open second scene in new window using SwiftUI?

  3. Why do we need WindowGroup at all? Isn't it a just a set of Views?

  4. How to work with them in general?

Or where I can read more details than written in documentation or WWDC 1 min video (from 2.00 to 3.05) as there is not enough information to understand the topic.

1

There are 1 answers

1
Andrew_STOP_RU_WAR_IN_UA On
  1. still have no idea how it's choosing the way to display them
  2. There is no easy way to do this. You need to create superview and change child view in it. Sth like:
    @SceneBuilder var body: some Scene {
        WindowGroup {
            NavigatorView()
        }
    }

    enum DisplayedScene {
        case Browser
        case Status(url: URL)
    }


    struct NavigatorView: View {
        @State var displayedScene = DisplayedScene.Browser

        var body: some View {
            VStack {
                switch(model.displayedScene) {
                case DisplayedScene.Browser:
                    BrowserView(model: browserViewModel, wndId: id)
                case DisplayedScene.Status(let url):
                    VStack {
                        StatusView(url: url, wndId: id)
                    
                        Button("back") { AppCore.signals.send(signal: Signal.TaoGit.Navigator.ShowBrowser(wndId: id) ) }
                    }
                default:
                    Text("ERROR")
                }
            }.transition(.identity)
            .animation(.easeInOut)
        }
    }

so all you need to change view displayed is to send signal to change displayedScene.

Another way is to do displayedScene as EnvironmentVariable. But in this case you will be able to work with ONLY ONE INSTANCE of window correctly. So this is a bad way. But can be OK for someone.

  1. How to open second scene in new window using SwiftUI? -- there are no easy way to do this. You need to use handlesExternalEvents. Sample:
import SwiftUI

@main
struct TestAppApp: App {
    var body: some Scene {
        WindowGroup {
            MainView()
        }
        .handlesExternalEvents(matching: Set(arrayLiteral: Wnd.mainView.rawValue))
        
        WindowGroup {
            HelperView()
        }
        .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") {
                    Wnd.mainView.open()
                }
                
                Button("Open Other View") {
                    Wnd.helperView.open()
                }
            }
            .padding(150)
        }
    }

    struct HelperView: View {
        var body: some View {
            HStack {
                Text("This is ") + Text("Helper View!").bold()
            }
            .padding(150)
        }
    }
}


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)
        }
    }
}
  1. Why do we need WindowGroup at all? Isn't it a just a set of Views? -- It's telling to swiftUI that exactly this view can be shown as separated Window.

  2. How to work with them in general? -- only with lot of hacks. Looks like SwiftUI is works really bad with MacOS.