gpt4 book ai didi

swift - 如何在 macOS 的 SwiftUI 中使用菜单命令实现多窗口?

转载 作者:行者123 更新时间:2023-12-04 07:25:11 29 4
gpt4 key购买 nike

情况
实现一个多窗口应用程序,其中每个窗口都有自己的状态。
示例
这是一个展示问题的示例( on github ):

import SwiftUI

@main
struct multi_window_menuApp: App {

var body: some Scene {
WindowGroup {
ContentView()
}.commands {
MenuCommands()
}
}
}

struct ContentView: View {
@StateObject var viewModel: ViewModel = ViewModel()

var body: some View {
TextField("", text: $viewModel.inputText)
.disabled(true)
.padding()
}
}

public class ViewModel: ObservableObject {

@Published var inputText: String = "" {
didSet {
print("content was updated...")
}
}
}
问题
我们应该如何以编程方式找出当前选择的 View 是什么,以便我们可以在菜单命令即将完成时更新状态并更新 View 模型中的状态?
import Foundation
import SwiftUI
import Combine

struct MenuCommands: Commands {

var body: some Commands {
CommandGroup(after: CommandGroupPlacement.newItem, addition: {
Divider()
Button(action: {
let dialog = NSOpenPanel();

dialog.title = "Choose a file";
dialog.showsResizeIndicator = true;
dialog.showsHiddenFiles = false;
dialog.allowsMultipleSelection = false;
dialog.canChooseDirectories = false;

if (dialog.runModal() == NSApplication.ModalResponse.OK) {
let result = dialog.url
if (result != nil) {
let path: String = result!.path
do {
let string = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8)
print(string)
// how to get access to the currently active view model to update the inputText variable?
// viewModel.inputText = string
}
catch {
print("Error \(error)")
}
}
} else {
return
}
}, label: {
Text("Open File")
})
.keyboardShortcut("O", modifiers: .command)
})
}
}
可能有助于解决此问题的链接:
  • http://www.gfrigerio.com/build-a-macos-app-with-swiftui/
  • https://troz.net/post/2021/swiftui_mac_menus/
  • https://onmyway133.com/posts/how-to-manage-windowgroup-in-swiftui-for-macos/
  • 最佳答案

    有用的链接:

  • How to access NSWindow from @main App using only SwiftUI?
  • How to access own window within SwiftUI view?
  • https://lostmoa.com/blog/ReadingTheCurrentWindowInANewSwiftUILifecycleApp/

  • (这是我能够想出的,如果有人有更好的想法/方法,请分享)
    这个想法是创建一个共享的“全局” View 模型来跟踪打开的窗口和 View 模型。每个 NSWindow有一个唯一的属性 windowNumber .当一个窗口变为事件(键)时,它会通过 windowNumber 查找 View 模型。并将其设置为 activeViewModel .
    import SwiftUI

    class GlobalViewModel : NSObject, ObservableObject {

    // all currently opened windows
    @Published var windows = Set<NSWindow>()

    // all view models that belong to currently opened windows
    @Published var viewModels : [Int:ViewModel] = [:]

    // currently active aka selected aka key window
    @Published var activeWindow: NSWindow?

    // currently active view model for the active window
    @Published var activeViewModel: ViewModel?

    func addWindow(window: NSWindow) {
    window.delegate = self
    windows.insert(window)
    }

    // associates a window number with a view model
    func addViewModel(_ viewModel: ViewModel, forWindowNumber windowNumber: Int) {
    viewModels[windowNumber] = viewModel
    }
    }
    然后,对窗口上的每个更改使用react(当它关闭时以及当它变成事件的又名关键窗口时):
    import SwiftUI

    extension GlobalViewModel : NSWindowDelegate {
    func windowWillClose(_ notification: Notification) {
    if let window = notification.object as? NSWindow {
    windows.remove(window)
    viewModels.removeValue(forKey: window.windowNumber)
    print("Open Windows", windows)
    print("Open Models", viewModels)
    }
    }
    func windowDidBecomeKey(_ notification: Notification) {
    if let window = notification.object as? NSWindow {
    print("Activating Window", window.windowNumber)
    activeWindow = window
    activeViewModel = viewModels[window.windowNumber]
    }
    }
    }
    提供一种查找与当前 View 关联的窗口的方法:
    import SwiftUI

    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) {}
    }
    这是使用当前窗口和 viewModel 更新全局 View 模型的 View :
    import SwiftUI

    struct ContentView: View {
    @EnvironmentObject var globalViewModel : GlobalViewModel

    @StateObject var viewModel: ViewModel = ViewModel()

    var body: some View {
    HostingWindowFinder { window in
    if let window = window {
    self.globalViewModel.addWindow(window: window)
    print("New Window", window.windowNumber)
    self.globalViewModel.addViewModel(self.viewModel, forWindowNumber: window.windowNumber)
    }
    }

    TextField("", text: $viewModel.inputText)
    .disabled(true)
    .padding()
    }
    }
    然后我们需要创建全局 View 模型并将其发送到 View 和命令:
    import SwiftUI

    @main
    struct multi_window_menuApp: App {

    @State var globalViewModel = GlobalViewModel()

    var body: some Scene {
    WindowGroup {
    ContentView()
    .environmentObject(self.globalViewModel)
    }
    .commands {
    MenuCommands(globalViewModel: self.globalViewModel)
    }

    Settings {
    VStack {
    Text("My Settingsview")
    }
    }
    }
    }
    以下是命令的外观,因此它们可以访问当前选择的/事件的 viewModel:
    import Foundation
    import SwiftUI
    import Combine

    struct MenuCommands: Commands {
    var globalViewModel: GlobalViewModel

    var body: some Commands {
    CommandGroup(after: CommandGroupPlacement.newItem, addition: {
    Divider()
    Button(action: {
    let dialog = NSOpenPanel();

    dialog.title = "Choose a file";
    dialog.showsResizeIndicator = true;
    dialog.showsHiddenFiles = false;
    dialog.allowsMultipleSelection = false;
    dialog.canChooseDirectories = false;

    if (dialog.runModal() == NSApplication.ModalResponse.OK) {
    let result = dialog.url
    if (result != nil) {
    let path: String = result!.path
    do {
    let string = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8)
    print("Active Window", self.globalViewModel.activeWindow?.windowNumber)
    self.globalViewModel.activeViewModel?.inputText = string
    }
    catch {
    print("Error \(error)")
    }
    }
    } else {
    return
    }
    }, label: {
    Text("Open File")
    })
    .keyboardShortcut("O", modifiers: [.command])
    })
    }
    }
    在这个 github 项目下所有更新和运行: https://github.com/ondrej-kvasnovsky/swiftui-multi-window-menu

    关于swift - 如何在 macOS 的 SwiftUI 中使用菜单命令实现多窗口?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68242439/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com