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
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.
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)
}