Last active
April 21, 2023 09:48
-
-
Save daniloc/b31612b315dc05f73e8d77e271196b5e to your computer and use it in GitHub Desktop.
A simple color picker with enum for easy storage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct ColorPicker: View { | |
enum Option: Int, CaseIterable { | |
case none, | |
red, | |
orange, | |
yellow, | |
green, | |
teal, | |
blue, | |
purple | |
func color() -> Color { | |
switch self { | |
case .none: | |
return Color.primary | |
case .red: | |
return Color.red | |
case .orange: | |
return Color.orange | |
case .yellow: | |
return Color.yellow | |
case .green: | |
return Color.green | |
case .blue: | |
return Color.blue | |
case .purple: | |
return Color(.systemIndigo) | |
case .teal: | |
return Color(.systemTeal) | |
} | |
} | |
} | |
@Binding var selection: Option | |
//MARK: - Style Details | |
var background: some View { | |
RoundedRectangle(cornerRadius: 25) | |
.fill(Color(.systemGray5)) | |
} | |
func backgroundForOption(_ option: Option) -> Color { | |
if option == selection { | |
return Color(.systemBackground) | |
} else { | |
return .clear | |
} | |
} | |
func sizeForOption(_ option: Option) -> CGFloat { | |
if option == selection { | |
return 22.0 | |
} else { | |
return 18.0 | |
} | |
} | |
let selectionRingWidth: CGFloat = 40.0 | |
let containerHeight: CGFloat = 50.0 | |
func imageNameForOption(_ option: Option) -> String { | |
if option != selection { | |
return "circle.fill" | |
} else { | |
return "smallcircle.fill.circle.fill" | |
} | |
} | |
//MARK: - Views | |
func imageForOption(_ option: Option) -> some View { | |
Image(systemName: self.imageNameForOption(option)) | |
.onTapGesture { | |
self.selection = option | |
} | |
.font(.system(size: self.sizeForOption(option))) | |
.foregroundColor(option.color()) | |
.frame(width: self.selectionRingWidth, height: self.selectionRingWidth, alignment: .center) | |
.cornerRadius(containerHeight / 2) | |
.frame(maxWidth: .infinity, maxHeight: .infinity) | |
} | |
var body: some View { | |
ZStack(alignment: .ZAlignment) { | |
Circle() | |
.fill(Color(.systemBackground)) | |
.frame(width: self.selectionRingWidth, height: self.selectionRingWidth, alignment: .center) | |
.alignmentGuide(HorizontalAlignment(Alignment.CustomAlignment.self), computeValue: { d in d[HorizontalAlignment.center] }) | |
.animation(.spring()) | |
HStack { | |
ForEach(Option.allCases, id: \.self) { option in | |
Group { | |
if option == self.selection { | |
self.imageForOption(option) | |
.alignmentGuide(HorizontalAlignment(Alignment.CustomAlignment.self), computeValue: { d in d[HorizontalAlignment.center] }) | |
} else { | |
self.imageForOption(option) | |
} | |
} | |
} | |
} | |
} | |
.padding(4) | |
.frame(maxWidth: .infinity, minHeight: self.containerHeight, maxHeight: self.containerHeight, alignment: .center) | |
.background(background) | |
} | |
} | |
extension Alignment { | |
//adapted from: https://swiftui-lab.com/alignment-guides/ | |
enum CustomAlignment : AlignmentID { | |
static func defaultValue(in d: ViewDimensions) -> CGFloat { | |
return d[.bottom] | |
} | |
} | |
static let ZAlignment = Alignment(horizontal: HorizontalAlignment(CustomAlignment.self), vertical: .center) | |
} | |
struct ColorPicker_Previews: PreviewProvider { | |
//via: https://stackoverflow.com/a/59626213/150181 | |
static var previews: some View { | |
PreviewWrapper() | |
} | |
struct PreviewWrapper: View { | |
@State(initialValue: .none) var option: ColorPicker.Option | |
var body: some View { | |
ColorPicker(selection: $option) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Simple SwiftUI color picker with dark mode support. Uses an enum to make selection storage in Core Data, etc, easy.
Usage: