Base on macOS 10.15, Xcode 11.7.
1、首先,NSColorPanel 有一个静态“单例”对象:NSColorPanel.shared。我们可以直接使用这个静态对象。这个静态对象也可以被多个组件共享,但要注意如果共享的话,获得的 color 值也是共享的。
2、但 NSColorPanel 又不纯粹的“单例”模式,它允许我们手动新建 NSColorPanel 对象,而不使用 shared 静态对象。
(手动新建对象之前,最好调用一次 NSColorPanel 的静态函数,或 shared 静态属性,确保已经创建了 shared 静态对象后,才新建对象。否则 NSColorPanel() 返回的就会是 shared 对象。)
3、点击 NSColorWell 对象唤起的 NSColorPanel,似乎就是 NSColorPanel.shared 静态对象。但是几个 NSColorWell 的 color 属性并不会互相影响!我猜测 NSColorWell 组件应该是注册了 NSColorPanel.shared.accessoryView 属性,每次收到新的 NSColorPanel.shared.color 值的时候,会判断当前的 accessoryView 是不是自己。👈 accessoryView 这个猜测被验证是错的。好奇它到底是怎么区分不同的 NSColorWell 的啊。
4、NSColorPanel 继承自 NSPanel,所以它的 isReleasedWhenClosed = false。但实际上,对于手动新建的 NSColorPanel 对象,它是会在窗口关闭后自动释放的!
5、由于 shared 是静态对象,所以 MyNSColorPanel.setPickerMask() 对 shared 对象是无效的。即 NSColorPanel.shared 对象(包括 NSColorWell 弹出的)必定是最全的颜色选择器。
实例代码:
import SwiftUI struct ColorView: View { @State var hovered: Bool = false @State var color1: NSColor = .red @State var color2: NSColor = .green @State var color3: NSColor = .blue @State var coordinator: Coordinator? = nil #if DEBUG private let deallocPrinter = DeallocPrinter(forType: String(describing: Self.self)) #endif var body: some View { VStack { Text("颜色啊") VStack(spacing: 1) { HStack { ForEach(Theme.colors, id: \.self) { color in ZStack { Rectangle() .cornerRadius(10) .foregroundColor(Color(color)) .frame(width: 30, height: 50) HStack { Text(color.isLight() ? "L" : "D") .foregroundColor(color.isLight() ? Color.black : Color.white) }.font(.body) } } } Divider() HStack { Rectangle().fill(Color(color1)).frame(width: 80, height: 30) Rectangle().fill(Color(color2)).frame(width: 80, height: 30) Rectangle().fill(Color(color3)).frame(width: 80, height: 30) } HStack { Button("Color1"){ // let _ = MyNSColorPanel.shared MyNSColorPanel.setPickerMask([.grayModeMask, .rgbModeMask, .colorListModeMask]) let cp = MyNSColorPanel() // ps: 应该把这个 cp 设置成结存储属性才对。 // 否则这样每次点击 color1 按钮都会新建一个 NSColorPanel 窗口来 =。=# // 算了,这里懒得改了。😄 log.debug("new address: \(cp)") log.debug("shared address: \(MyNSColorPanel.shared)") log.debug("isReleasedWhenClosed? \(cp.isReleasedWhenClosed)") cp.color = self.color1 cp.setTarget(self.coordinator!) cp.setAction(#selector(Coordinator.onColorChanged(sender:))) cp.orderFront(nil) }.frame(width: 80, height: 30) Button("Color2"){ let cp = NSColorPanel.shared log.debug("new address: \(cp)") log.debug("shared address: \(NSColorPanel.shared)") cp.color = self.color2 cp.orderFront(nil) }.frame(width: 80, height: 30) CustomNSColorWell(color: $color3).frame(width: 80, height: 30) } } } .onAppear(){ if self.coordinator == nil { self.coordinator = Coordinator(color: self.$color1) } } .onReceive(NotificationCenter.default.publisher(for: NSColorPanel.colorDidChangeNotification, object: NSColorPanel.shared), perform: { v in log.debug("接收到 colorpannel 消息: \(v)") self.color2 = NSColorPanel.shared.color }) } // 自定义协同器类 class Coordinator: NSObject { // 绑定 SwiftUI 中需要交互的数据 @Binding var color: NSColor // 注意!这是在构造器中传递 @Binding 属性的正确方式 init(color: Binding<NSColor>) { self._color = color } @objc func onColorChanged(sender: NSColorPanel){ log.verbose("Coordinator color1 changed: \(sender)") self.color = sender.color } } } struct ColorView_Previews: PreviewProvider { static var previews: some View { ColorView() } } struct CustomNSColorWell: NSViewRepresentable { typealias NSViewType = NSColorWell @Binding var color: NSColor func makeCoordinator() -> Coordinator { return Coordinator(color: $color) } func makeNSView(context: Context) -> NSColorWell { let colorWell = NSColorWell() colorWell.target = context.coordinator colorWell.action = #selector(Coordinator.onColorChanged(sender:)) return colorWell } func updateNSView(_ nsView: NSColorWell, context: Context) { nsView.color = color } // 自定义协同器类 class Coordinator: NSObject { // 绑定 SwiftUI 中需要交互的数据 @Binding var color: NSColor // 注意!这是在构造器中传递 @Binding 属性的正确方式 init(color: Binding<NSColor>) { self._color = color } @objc func onColorChanged(sender: NSColorWell){ log.verbose("NSColorWell color3 changed \(sender)") self.color = sender.color } } } class MyNSColorPanel: NSColorPanel { deinit { log.debug("释放啦") } }