You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在AppKit生命周期Mac应用中用SwiftUI管理NSWindow工具栏的方案

在AppKit生命周期应用中用SwiftUI管理NSWindow工具栏

核心问题解答:可以实现

直接使用NSHostingController而非NSHostingView承载SwiftUI视图,就能让SwiftUI的.toolbar修饰符生效。因为SwiftUI的工具栏配置是通过视图控制器层级传递给窗口的,仅用NSHostingView会缺少这一层的处理逻辑。

具体实现步骤

  • 创建包含.toolbar修饰符的SwiftUI视图
  • 实例化NSHostingController并将SwiftUI视图设为根视图
  • 手动创建NSWindow,将其contentViewController设置为该NSHostingController

代码示例

// 带工具栏的SwiftUI视图
struct SwiftUIToolbarContentView: View {
    @State private var isActionEnabled = true
    
    var body: some View {
        VStack {
            Text("SwiftUI Content with Native Toolbar")
                .padding()
        }
        .toolbar {
            // 主操作区工具栏项
            ToolbarItem(placement: .primaryAction) {
                Button("Perform Action") {
                    print("Action triggered")
                }
                .disabled(!isActionEnabled)
            }
            // 导航区工具栏项
            ToolbarItem(placement: .navigation) {
                Toggle(isOn: $isActionEnabled) {
                    Text("Enable Action")
                }
            }
        }
    }
}

// 在AppKit代码中创建窗口
func setupSwiftUIToolbarWindow() {
    // 创建HostingController
    let hostingVC = NSHostingController(rootView: SwiftUIToolbarContentView())
    
    // 创建NSWindow
    let window = NSWindow(
        contentRect: NSRect(x: 200, y: 200, width: 600, height: 400),
        styleMask: [.titled, .closable, .miniaturizable, .resizable],
        backing: .buffered,
        defer: false
    )
    
    // 关联HostingController到窗口
    window.contentViewController = hostingVC
    window.title = "SwiftUI Toolbar Window"
    window.center()
    window.makeKeyAndOrderFront(nil)
}

替代方案(若上述方法不适用)

1. 无需重构应用,共存SwiftUI管理的窗口

上面的实现本身就支持在AppKit生命周期应用中,按需创建完全由SwiftUI管理的窗口(包括工具栏),和原有AppKit窗口共存,不需要修改整个应用的生命周期。只需在合适的时机(比如菜单点击、按钮触发)调用上述创建窗口的方法即可。

2. 简化AppKit工具栏与SwiftUI状态的同步

使用ObservableObject作为共享状态容器,让SwiftUI视图和AppKit工具栏代码共用同一状态实例,通过@Published属性自动同步状态变更:

代码示例

// 共享状态容器
class ToolbarSharedState: ObservableObject {
    @Published var isButtonEnabled = true
}

// SwiftUI视图中使用状态
struct ContentView: View {
    @StateObject private var sharedState = ToolbarSharedState()
    
    var body: some View {
        Text("SwiftUI Content")
            .onAppear {
                // 将状态传递给AppKit工具栏
                if let window = NSApp.windows.first(where: { $0.title == "AppKit Toolbar Window" }) {
                    if let customToolbar = window.toolbar as? CustomAppKitToolbar {
                        customToolbar.sharedState = sharedState
                    }
                }
            }
    }
}

// 自定义AppKit工具栏
class CustomAppKitToolbar: NSToolbar {
    weak var sharedState: ToolbarSharedState? {
        didSet {
            // 监听状态变更,更新工具栏按钮
            sharedState?.$isButtonEnabled
                .receive(on: DispatchQueue.main)
                .sink { [weak self] isEnabled in
                    self?.updateButtonState(isEnabled: isEnabled)
                }
                .store(in: &cancellables)
        }
    }
    
    private var cancellables = Set<AnyCancellable>()
    
    override init(identifier: NSToolbar.Identifier) {
        super.init(identifier: identifier)
        setupToolbarItems()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupToolbarItems()
    }
    
    private func setupToolbarItems() {
        let actionItem = NSToolbarItem(itemIdentifier: .actionButton)
        actionItem.label = "Action"
        actionItem.image = NSImage(systemSymbolName: "gear", accessibilityDescription: nil)
        actionItem.target = self
        actionItem.action = #selector(actionButtonTapped)
        self.items = [actionItem]
    }
    
    private func updateButtonState(isEnabled: Bool) {
        if let actionItem = items.first(where: { $0.itemIdentifier == .actionButton }) {
            actionItem.isEnabled = isEnabled
        }
    }
    
    @objc private func actionButtonTapped() {
        sharedState?.isButtonEnabled.toggle()
    }
}

// 扩展工具栏标识符
extension NSToolbarItem.Identifier {
    static let actionButton = NSToolbarItem.Identifier("com.example.actionButton")
}

内容的提问来源于stack exchange,提问作者zaphoyd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 18:46:11