I cannot get a custom Color Picker working :/

Hi all, I am trying to create a custom color picker for SwiftUI. I know SwiftUI already has a ColorPicker, however it does not allow me to change the appearance, i.e. It always looks like
SUI color picker

no matter what I do. The intention of my custom color picker is to allow any view as its content/label.

To do this, I thought of creating a modifier colorPicker(isPresented:selectedColor:allowsOpacity:onDismiss:) modifier which would display the system color picker when isPresented is true. That way, I can add a tao gesture to ANY view and have the picker presented. The system color picker I used comes from a UIColorPickerController which is a view controller that UIKit uses to display a color picker. So I created a UIViewControllerRepresentable conforming type, to wrap the system color picker view controller. I also added a coordinator.

However the problem is with my setup, when the color picker is triggered, it does not look as intended, i.e. (as the image shows) has a grey translucent background, with ugly left/right padding.

BAD

Please could anyone help me with this issue, and let me know what I’m doing wrong.

I have read that it is best provided as a popover (and while this works, I’m still wondering what is wrong with using the sheet my way.

Thanks a lot!

My code is here

import SwiftUI


fileprivate struct SystemColorPicker: UIViewControllerRepresentable {
    var supportsAlpha = false
    @Binding var selectedColor: Color
    
    class Coordinator: NSObject, UIColorPickerViewControllerDelegate {
        var selectedColor: Binding<Color>
        
        init(selectedColor: Binding<Color>) {
            self.selectedColor = selectedColor
        }
        
        func colorPickerViewController(_ viewController: UIColorPickerViewController, didSelect color: UIColor, continuously: Bool) {
            selectedColor.wrappedValue = Color(uiColor: color)
        }
    }
    
    func makeUIViewController(context: Context) -> some UIViewController {
        let vcp = UIColorPickerViewController()
        vcp.supportsAlpha = supportsAlpha
        vcp.delegate = context.coordinator
        
        return vcp
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(selectedColor: $selectedColor)
    }
}


fileprivate struct ColorPickerModifier: ViewModifier {
    @Binding var isPresented: Bool
    @Binding var selectedColor: Color
    var supportsOpacity: Bool = false
    var onDismiss: (() -> ())?
    
    func body(content: Content) -> some View {
        content
            .sheet(isPresented: $isPresented, onDismiss: onDismiss) {
                SystemColorPicker(supportsAlpha: supportsOpacity, selectedColor: $selectedColor)
            }
    }
}


public extension View {
    func colorPicker(isPresented: Binding<Bool>,
                     selectedColor: Binding<Color>,
                     supportsOpacity: Bool,
                     onDismiss: (() -> ())? = nil) -> some View {
        
        self.modifier(ColorPickerModifier(isPresented: isPresented, selectedColor: selectedColor, supportsOpacity: supportsOpacity, onDismiss: onDismiss))
        
    }
}


#Preview {
    @Previewable @State var col = Color.red
    @Previewable @State var presented = false
    
    RoundedRectangle(cornerRadius: 10)
        .fill(col)
        .frame(width: 50, height: 50)
        .onTapGesture {
            presented = true
        }
        .colorPicker(isPresented: $presented, selectedColor: $col, supportsOpacity: true)
}

Hi Daniel,

Is there any reason that you have not considered using the generic SwiftUI ColorPicker?

Example

struct ContentView: View {
    @State private var selectedColor: Color = .red
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
            
            SystemColourPicker(selectedColor: $selectedColor)
        }
        .padding()
    }
}

#Preview {
    ContentView()
}
import SwiftUI

struct SystemColourPicker: View {
    @Binding var selectedColor: Color

    var body: some View {
        ColorPicker(selection: $selectedColor, label: {
            Text("Color Picker")
        })
    }
}

#Preview {
    SystemColourPicker(selectedColor: .constant(.red))
}

As I mentioned, I need to create a custom appearance for it. Thanks for the reply :slight_smile:

For what reason? Deviating from default behavior, especially like a color picker can be confusing for users and create more accessibility issues