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

MacOS Swift应用中NSButton的@objc点击事件未触发求助

Mac SwiftUI中NSViewController内NSButton点击无响应问题解决

问题场景

Mac非Catalyst应用中,通过NSViewRepresentable在SwiftUI里集成NSViewController后,其中的NSButton点击无法触发绑定的closeButtonPressed方法。环境:macOS 12.6.3,Xcode 14.2。

最小复现代码

App入口代码

import SwiftUI

@main
struct MinimalReproducibleExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

核心视图代码

import Cocoa
import SwiftUI

struct ViewControllerWrapper: NSViewRepresentable {
    
    func makeNSView(context: Context) -> NSView {
        return ViewController().view
    }
    
    func updateNSView(_ nsView: NSView, context: Context) {
        // 空实现
    }
}

class ViewController: NSViewController {
    
    let closeButton = NSButton(title: "X", target: nil, action: nil)
    
    override func loadView() {
        self.view = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 300))
        self.view.wantsLayer = true
        self.view.layer?.backgroundColor = NSColor.black.cgColor
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.wantsLayer = true
        view.layer?.backgroundColor = NSColor.cyan.cgColor
        
        closeButton.target = self
        closeButton.action = #selector(closeButtonPressed)
        closeButton.frame = CGRect(x: 100, y: 100, width: 50, height: 30)
        closeButton.bezelColor = NSColor.gray
        view.addSubview(closeButton)
    }
    
    @objc func closeButtonPressed() {
        print("Close button pressed")
    }
}

struct ContentView: View {
    
    private var viewControllerWrapper: ViewControllerWrapper
    
    init(){
        self.viewControllerWrapper = ViewControllerWrapper()
    }
    
    var body: some View {
        VStack {
            viewControllerWrapper
        }
    }
}

问题原因

ViewControllerWrapper的makeNSView方法中,ViewController()是临时创建的对象,方法执行完毕后该实例会被ARC自动释放。NSButton的target指向已销毁的ViewController实例,导致点击事件无法找到响应对象。

解决方案

只需让ViewController实例被ViewControllerWrapper持有,保证其生命周期与Wrapper一致即可,以下提供两种实现方式:

方式1:直接在Wrapper中持有ViewController实例

修改ViewControllerWrapper,将ViewController作为属性持有:

struct ViewControllerWrapper: NSViewRepresentable {
    // 持有ViewController实例,保证生命周期
    private let viewController = ViewController()
    
    func makeNSView(context: Context) -> NSView {
        return viewController.view
    }
    
    func updateNSView(_ nsView: NSView, context: Context) {
        // 空实现
    }
}

方式2:通过Coordinator管理ViewController

利用NSViewRepresentable的Coordinator来持有ViewController:

struct ViewControllerWrapper: NSViewRepresentable {
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    
    func makeNSView(context: Context) -> NSView {
        return context.coordinator.viewController.view
    }
    
    func updateNSView(_ nsView: NSView, context: Context) {
        // 空实现
    }
    
    // 通过Coordinator持有ViewController
    class Coordinator {
        let viewController = ViewController()
    }
}

修改后运行,点击NSButton即可正常触发closeButtonPressed方法,控制台会输出Close button pressed。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 13:10:25