I have this little sample App which creates multiple Windows of my SwiftUI MacOS app.
Is it possible:
- to have a list of all open windows in MainView?
- to close a single window from MainView?
- to send Message to a single window from MainView?
@main
struct MultiWindowApp: App {
@State var gvm = GlobalViewModel()
var body: some Scene {
WindowGroup {
MainView()
.environmentObject(gvm)
}
WindowGroup("Secondary") {
SecondaryView(bgColor: .blue)
.environmentObject(gvm)
}
.handlesExternalEvents(matching: Set(arrayLiteral: "*"))
}
}
struct MainView: View {
@Environment(\.openURL) var openURL
@EnvironmentObject var vm : GlobalViewModel
var body: some View {
VStack {
Text("MainView")
Button("Open Secondary") {
if let url = URL(string: "OpenNewWindowApp://bla") {
openURL(url)
}
//List of all open Windows
// Button to close a single window
// Button to set color of a single window to red
}
}
.padding()
}
}
struct SecondaryView: View {
var bgColor : Color
@EnvironmentObject var vm : GlobalViewModel
var body: some View {
VStack{
Spacer()
Text("Viewer")
Text("ViewModel: \(vm.name)")
Button("Set VM"){
vm.name = "Tom"
}
Spacer()
}
.background(bgColor)
.frame(minWidth: 300, minHeight: 300, idealHeight: 400, maxHeight: .infinity, alignment: .center )
}
}
class GlobalViewModel :ObservableObject {
@Published var name = "Frank"
}
It is possible that there's a more SwiftUI-centric way to do this. If there's not yet, I certainly hope Apple adds some better window management stuff for the Mac side of things -- right now, everything seems like a bit of a hack.
Here's what I came up with:
I'm using a trick from https://lostmoa.com/blog/ReadingTheCurrentWindowInANewSwiftUILifecycleApp/ to get a reference to the
NSWindow
. That gets stored in the view model in a set. Later, to access things like closing the windows, etc. I reference the windows bywindowNumber
.When a window appears, it adds itself to the view model's window list. Then, when the view model gets a
windowWillClose
call as the delegate, it removes it from the list.Setting the background color is done via the
backgroundColors
property on the view model. If there's not one set, it uses the passed-in background color property. There are tons of different ways you could choose to architect this bit.