Search code examples
swiftmacosswiftuiview

How to display a Swift View programmatically from MacOS


Under Xcode 15, I have developed an application for MacOS that computes data, and upon completion, I am able to plot the data to a separate window using the Window menu. In the main body of the App, I have the following code, which creates the menu entry "Plot":

var body: some Scene {
  WindowGroup("MainPage", id: "main-page") {
    GeneticGenealogyPage()
      .frame(minWidth: 750, maxWidth: 750, minHeight: 510, maxHeight: 510)
  }

  Window("Plot", id: "plot-page") {
    PlotViewPage()
      .frame(minWidth: 800, maxWidth: 800, minHeight: 750, maxHeight: 750)
  }
}

The PlotViewPage has the following structure:

import SwiftUI
import Charts

struct PlotViewPage: View {
  @Environment(\.openWindow) private var openWindow

  var body: some View {
    HStack(alignment: .center, spacing: 0) {
      Text("Cumulative Distribution Function")
      VStack {
        // ...
      }
    }
  }
}

Instead of using a menu to bring up the plot, I would like to bring it up programmatically as soon as the calculations are complete. How can I do that?

I have found approaches online that apparently work for iOS, but have not found an approach that works under MacOS.


Solution

  • If you want to open your window from a view, you can use the openWindow environment key. To do this automatically, set a flag in your model when processing is complete. With the task or onChange view modifiers you can call a closure based on changements of an ObservableObject:

    struct GeneticGenealogyPage: View {
        @Environment(\.openWindow) var openWindow
        @StateObject var plotModel: YourPlotModel // this depends on your model
    
        // your code ...
    
        var body: some View {
    
            // your view code ...
            
            // use this:
            .task(id: plotModel.processingComplete) {
                if plotModel.processingComplete {
                    openWindow(id: "plot-page")
                }
            }
            // or this:
            .onChange(of: plotModel.processingComplete) { complete in
                if complete {
                    openWindow(id: "plot-page")
                }
            }
        }
    }
    

    Use the id that you gave to your Window in the scene declaration.