MacOS Swift应用中NSButton的@objc点击事件未触发求助
问题场景
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
相关产品推荐
相关产品推荐

