swiftmacosswiftuipopovercolor-picker

Show ColorPicker in popover on macOS


We can present the color picker in swiftUI easily.

ColorPicker("", selection: $selectedColor)

However, in my macOS app it looks ridiculous as it opens as a standalone view and is disconnected from the app.

What I would like to achieve is by tapping a button, color picker opens in popover, the same way as it opens in the Preview app.


Solution

  • You might be looking for a NSColorWell with the minimal style.

    It looks like this:

    enter image description here

    Here I have wrapped this in a NSViewRepresentable:

    struct MyColorPicker: NSViewRepresentable {
        @Binding var color: Color
        
        func makeNSView(context: Context) -> NSColorWell {
            let colorWell = NSColorWell(style: .minimal)
            colorWell.color = NSColor(color)
            
            context.coordinator.startObservingColorChange(of: colorWell)
            return colorWell
        }
        
        func updateNSView(_ nsView: NSColorWell, context: Context) {
            nsView.color = NSColor(color)
            context.coordinator.colorDidChange = {
                color = Color(nsColor: $0)
            }
        }
        
        func makeCoordinator() -> Coordinator {
            Coordinator()
        }
        
        @MainActor
        class Coordinator: NSObject {
            var colorDidChange: ((NSColor) -> Void)?
            
            private var cancellable: AnyCancellable?
            
            func startObservingColorChange(of colorWell: NSColorWell) {
                cancellable = colorWell.publisher(for: \.color).sink { [weak self] in
                    self?.colorDidChange?($0)
                }
            }
        }
    }
    

    Usage:

    struct ContentView: View {
        @State var color: Color = .white
        var body: some View {
            VStack {
                color
                MyColorPicker(color: $color).fixedSize()
            }
            .padding()
        }
    }