SwiftUI显示AppKit视图的NSViewRepresentable协议
SwiftUI显示AppKit视图的NSViewRepresentable协议

SwiftUI显示AppKit视图的NSViewRepresentable协议

NSViewRepresentable 是 SwiftUI 中的一个协议,用来将 macOS 的 AppKit 视图(NSView)嵌入到 SwiftUI 界面中。

NSViewRepresentable 可以在 SwiftUI 中“包装”一个 NSView,从而在 SwiftUI 界面中使用任意 AppKit 控件,比如:NSColorWell、NSTextView、NSScrollView、NSVisualEffectView,甚至自定义的 NSView 子类。

协议结构

protocol NSViewRepresentable {
    associatedtype NSViewType: NSView

    func makeNSView(context: Context) -> NSViewType
    func updateNSView(_ nsView: NSViewType, context: Context)
}

在SwiftUI视图中,需要创建一个遵循NSViewRepresentable协议的结构,并且实现makeNSView和updateNSView方法:

struct NSSomeView: NSViewRepresentable {
    func makeNSView(context: Context) -> 返回的NS类型{
        创建 NS 视图/控件
        return NS 视图/控件
    }

    func updateNSView(_ nsView: NS类型, context: Context) {
        // 更新 NSView 时会调用
    }
}

其中,makeNSView需要返回对应的NSViewType类型的视图/控件。

基本用法

创建一个遵循NSViewRepresentable协议的结构。

import SwiftUI
import AppKit

struct ColorWellView: NSViewRepresentable {
    func makeNSView(context: Context) -> NSColorWell {
        let colorWell = NSColorWell()
        colorWell.color = .systemRed
        return colorWell
    }

    func updateNSView(_ nsView: NSColorWell, context: Context) {
        // 更新 NSView 时会调用,例如绑定颜色状态
    }
}

在SwiftUI中显示该NSView视图:

struct ContentView: View {
    var body: some View {
        VStack {
            ColorWellView()
                .frame(width: 160,height:100)
                .cornerRadius(10)
                .shadow(radius: 3)
        }
        .frame(width: 200,height: 140)
    }
}

生命周期

1、makeNSView(context:):创建 NSView 实例(只调用一次)。

2、updateNSView(_:context:):SwiftUI 状态变化时更新 view 的属性。

3、Context 参数:提供如 Coordinator、环境信息等。

使用场景

1、支持Binding扩展

struct ColorWellRepresentable: NSViewRepresentable {
    
    @Binding var selectedColor: NSColor
    
    func makeNSView(context: Context) -> NSColorWell {
        let colorWell = NSColorWell()
        colorWell.color = selectedColor
        colorWell.target = context.coordinator
        colorWell.action = #selector(Coordinator.colorChanged(_:))
        return colorWell
    }
    
    func updateNSView(_ nsView: NSColorWell, context: Context) {
        if nsView.color != selectedColor {
            nsView.color = selectedColor
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(color: $selectedColor)
    }
    
    class Coordinator {
        var selectedColor: Binding<NSColor>
        
        init(color: Binding<NSColor>) {
            self.selectedColor = color
        }
        
        @objc func colorChanged(_ sender: NSColorWell) {
            selectedColor.wrappedValue = sender.color
        }
    }
}

在SwiftUI中,将变量传递给ColorWellRepresentable。

struct ContentView: View {
    @State private var selectedColor: NSColor = .systemBlue
    var body: some View {
        VStack {
            Text("颜色选择器")
                .foregroundColor(Color(selectedColor))
                .padding()
            
            ColorWellRepresentable(selectedColor: $selectedColor)
                .frame(width: 60, height: 30)
        }
        .frame(width: 300,height: 200)
    }
}

知识点

1、context是什么?

在 SwiftUI 中,当实现一个 NSViewRepresentable(或 UIViewRepresentable)时,makeNSView(context:) 和 updateNSView(_:context:) 这两个函数会传入一个参数:

func makeNSView(context: Context) -> NSSplitView

这个 context 是 SwiftUI 传给结构体,包含了协调器(Coordinator)和环境信息。

struct Context {
    var coordinator: Coordinator
    var environment: EnvironmentValues
    ...
}

它是 SwiftUI 提供的桥梁,作用包括:

1、context.coordinator:提供自定义的 Coordinator 对象(用于代理、事件、共享状态)。

2、context.environment:当前 SwiftUI 的环境(如颜色模式、字体大小)。

也就是说,可以通过 context.coordinator 访问到Coordinator 实例。

func makeCoordinator() -> Coordinator {
    Coordinator(color: $selectedColor)  // 通过 context.coordinator 访问到该实例
}

2、Coordinator是什么?

Coordinator 是 NSViewRepresentable 提供的机制,用于在 SwiftUI 和 AppKit 之间保存状态和处理桥接逻辑(比如 delegate)。

通过实现这个方法来提供一个 Coordinator 实例:

func makeCoordinator() -> Coordinator

SwiftUI 会:

1、自动调用 makeCoordinator(),创建 Coordinator。

2、自动保留它的引用(直到视图销毁)。

3、把它作为 context.coordinator 使用。

例如,在Coordinator创建对象,设置强引用:

class Coordinator {
    let delegate = MySplitDelegate() // 被强引用住
}

然后在 makeNSView 中设置 delegate:

splitView.delegate = context.coordinator.delegate

这样 delegate 会和 Coordinator 一起被 SwiftUI 持久保存。

可以进一步阅读《SwiftUI桥接协议事件回调Coordinator》。

总结

NSViewRepresentable 是 SwiftUI 与 AppKit(NSView)之间的桥梁,在 SwiftUI 中复用任何 macOS 原生控件或自定义视图。

相关文章

1、SwiftUI、AppKit和UIKit之间的桥接控制器:https://fangjunyu.com/2025/06/30/swiftui%e3%80%81appkit%e5%92%8cuikit%e4%b9%8b%e9%97%b4%e7%9a%84%e6%a1%a5%e6%8e%a5%e6%8e%a7%e5%88%b6%e5%99%a8/

2、UIKit显示SwiftUI的桥接控制器UIHostingController:https://fangjunyu.com/2025/06/30/uikit%e6%98%be%e7%a4%baswiftui%e7%9a%84%e4%be%a8%e6%8e%a5%e6%8e%a7%e5%88%b6%e5%99%a8uihostingcontroller/

3、macOS显示SwiftUI的桥接控制器NSHostingController:https://fangjunyu.com/2025/06/30/macos%e6%98%be%e7%a4%baswiftui%e7%9a%84%e6%a1%a5%e6%8e%a5%e6%8e%a7%e5%88%b6%e5%99%a8nshostingcontroller/

4、SwiftUI桥接协议事件回调Coordinator:https://fangjunyu.com/2025/07/03/swiftui%e6%a1%a5%e6%8e%a5%e5%8d%8f%e8%ae%ae%e4%ba%8b%e4%bb%b6%e5%9b%9e%e8%b0%83coordinator/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注