在 MacOS SwiftUI 应用程序中列出和关闭 Windows
Listing and closing Windows in a MacOS SwiftUI App
我有这个小示例应用程序,它创建了多个 Windows 我的 SwiftUI MacOS 应用程序。
是否可以:
- 要在 MainView 中列出所有打开的 windows?
- 要从 MainView 关闭单个 window?
- 要从 MainView 向单个 window 发送消息?
@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"
}
可能有更以 SwiftUI 为中心的方法来执行此操作。如果还没有,我当然希望 Apple 为 Mac 方面添加一些更好的 window 管理内容——现在,一切似乎都有点乱七八糟。
这是我想出的:
@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")
List {
ForEach(Array(vm.windows), id: \.windowNumber) { window in
HStack {
Text("Window: \(window.windowNumber)")
Button("Red") {
vm.setColor(.red, forWindowNumber: window.windowNumber)
}
Button("Close") {
window.close()
}
}
}
}
Button("Open Secondary") {
if let url = URL(string: "OpenNewWindowApp://bla") {
openURL(url)
}
}
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct SecondaryView: View {
var bgColor : Color
@EnvironmentObject var vm : GlobalViewModel
@State private var windowNumber = -1
var body: some View {
VStack{
HostingWindowFinder { window in
if let window = window {
vm.addWindow(window: window)
self.windowNumber = window.windowNumber
}
}
Spacer()
Text("Viewer")
Text("ViewModel: \(vm.name)")
Button("Set VM"){
vm.name = "Tom"
}
Spacer()
}
.background(vm.backgroundColors[windowNumber] ?? bgColor)
.frame(minWidth: 300, minHeight: 300, idealHeight: 400, maxHeight: .infinity, alignment: .center )
}
}
class GlobalViewModel : NSObject, ObservableObject {
@Published var name = "Frank"
@Published var windows = Set<NSWindow>()
@Published var backgroundColors : [Int:Color] = [:]
func addWindow(window: NSWindow) {
window.delegate = self
windows.insert(window)
}
func setColor(_ color: Color, forWindowNumber windowNumber: Int) {
backgroundColors[windowNumber] = color
}
}
extension GlobalViewModel : NSWindowDelegate {
func windowWillClose(_ notification: Notification) {
if let window = notification.object as? NSWindow {
windows = windows.filter { [=10=].windowNumber != window.windowNumber }
}
}
}
struct HostingWindowFinder: NSViewRepresentable {
var callback: (NSWindow?) -> ()
func makeNSView(context: Self.Context) -> NSView {
let view = NSView()
DispatchQueue.main.async { [weak view] in
self.callback(view?.window)
}
return view
}
func updateNSView(_ nsView: NSView, context: Context) {}
}
我正在使用 https://lostmoa.com/blog/ReadingTheCurrentWindowInANewSwiftUILifecycleApp/ 中的技巧来获取对 NSWindow
的引用。它被存储在一组视图模型中。稍后,要访问关闭 windows 等内容。我通过 windowNumber
.
引用 windows
当 window 出现时,它会将自己添加到视图模型的 window 列表中。然后,当视图模型作为委托获得 windowWillClose
调用时,它会将其从列表中删除。
背景颜色的设置是通过视图模型上的 backgroundColors
属性 完成的。如果没有一组,它使用传入的背景颜色 属性。您可以选择多种不同的方式来构建这一点。
我有这个小示例应用程序,它创建了多个 Windows 我的 SwiftUI MacOS 应用程序。
是否可以:
- 要在 MainView 中列出所有打开的 windows?
- 要从 MainView 关闭单个 window?
- 要从 MainView 向单个 window 发送消息?
@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"
}
可能有更以 SwiftUI 为中心的方法来执行此操作。如果还没有,我当然希望 Apple 为 Mac 方面添加一些更好的 window 管理内容——现在,一切似乎都有点乱七八糟。
这是我想出的:
@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")
List {
ForEach(Array(vm.windows), id: \.windowNumber) { window in
HStack {
Text("Window: \(window.windowNumber)")
Button("Red") {
vm.setColor(.red, forWindowNumber: window.windowNumber)
}
Button("Close") {
window.close()
}
}
}
}
Button("Open Secondary") {
if let url = URL(string: "OpenNewWindowApp://bla") {
openURL(url)
}
}
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct SecondaryView: View {
var bgColor : Color
@EnvironmentObject var vm : GlobalViewModel
@State private var windowNumber = -1
var body: some View {
VStack{
HostingWindowFinder { window in
if let window = window {
vm.addWindow(window: window)
self.windowNumber = window.windowNumber
}
}
Spacer()
Text("Viewer")
Text("ViewModel: \(vm.name)")
Button("Set VM"){
vm.name = "Tom"
}
Spacer()
}
.background(vm.backgroundColors[windowNumber] ?? bgColor)
.frame(minWidth: 300, minHeight: 300, idealHeight: 400, maxHeight: .infinity, alignment: .center )
}
}
class GlobalViewModel : NSObject, ObservableObject {
@Published var name = "Frank"
@Published var windows = Set<NSWindow>()
@Published var backgroundColors : [Int:Color] = [:]
func addWindow(window: NSWindow) {
window.delegate = self
windows.insert(window)
}
func setColor(_ color: Color, forWindowNumber windowNumber: Int) {
backgroundColors[windowNumber] = color
}
}
extension GlobalViewModel : NSWindowDelegate {
func windowWillClose(_ notification: Notification) {
if let window = notification.object as? NSWindow {
windows = windows.filter { [=10=].windowNumber != window.windowNumber }
}
}
}
struct HostingWindowFinder: NSViewRepresentable {
var callback: (NSWindow?) -> ()
func makeNSView(context: Self.Context) -> NSView {
let view = NSView()
DispatchQueue.main.async { [weak view] in
self.callback(view?.window)
}
return view
}
func updateNSView(_ nsView: NSView, context: Context) {}
}
我正在使用 https://lostmoa.com/blog/ReadingTheCurrentWindowInANewSwiftUILifecycleApp/ 中的技巧来获取对 NSWindow
的引用。它被存储在一组视图模型中。稍后,要访问关闭 windows 等内容。我通过 windowNumber
.
当 window 出现时,它会将自己添加到视图模型的 window 列表中。然后,当视图模型作为委托获得 windowWillClose
调用时,它会将其从列表中删除。
背景颜色的设置是通过视图模型上的 backgroundColors
属性 完成的。如果没有一组,它使用传入的背景颜色 属性。您可以选择多种不同的方式来构建这一点。