This article was previously published in the 2022 IT Ironman Competition - Turning Interesting Ideas into Apps with SwiftUI

Apple’s Documentation on Color Picker

https://developer.apple.com/documentation/swiftui/colorpicker

The Color Picker is one of my favorite SwiftUI components. If you were to manually create this component using UIKit, it would be time-consuming. Previously, if such a requirement arose, we generally resorted to third-party libraries. Now, with SwiftUI, this component is readily available, and it can be used on iOS 14+ versions. For selecting colors, you can use gridlines, a spectrum, or sliders, making it both beautiful and convenient.

Let’s now make a test project: a backwards clock dial with a splash of color.

color picker

color picker

color picker

Introducing ColorPickerContainerView

The ColorPicker requires a Binding parameter, designed to let the outer part (the page with the clock) pass in the color, which then updates the color of the dial externally.

struct ColorPickerContainerView: View {
  
  @Binding var dialColor: Color
  
  var body: some View {
    
    ColorPicker("Dial Color", selection: $dialColor)
      .padding()
  }
}

struct ColorPickerContainerView_Previews: PreviewProvider {
  
  @State static var previewDialColor: Color = .white
  
  static var previews: some View {
    ColorPickerContainerView(dialColor: $previewDialColor)
  }
}

color picker 4

color picker 5

Even in the preview, pressing the action button brings up the color palette.

Modifying the Dial, Allowing the Dial Color to Change

Add a dialColor to allow the color to be changed. Below the original Circle().stroke(), add another dial layer, which is the part that actually changes color.

struct ClockDialView: View {
  
  @Binding var dialColor: Color
  
  var tickLength: CGFloat = 5
  
  var body: some View {
    
    ZStack {
      /// The part that actually changes color
      Circle()
        .fill(dialColor)
        .padding(3)
      /// The outer frame
      Circle()
        .stroke()
        .padding(3)
      
      ticks
        .padding(3)
      
      HStack {
        Spacer()
        BackwardsClockNumberView()
        Spacer()
      }
    }
  }
  
  private func isLongTick(_ position: Int) -> Bool {
    return position % 5 == 0
  }
 
  var ticks: some View {

    ForEach(0..<60) { position in
      let longerTick = tickLength * 1.8

      Tick(tickLength: isLongTick(position) ? longerTick : tickLength )
        .stroke(lineWidth: 3)
        .rotationEffect(.radians(Double.pi * 2 / 60 * Double(position)))
    }
  }
}

struct ClockDialView_Previews: PreviewProvider {
  
  @State static var dialColor: Color = .green
  
  static var previews: some View {
    ClockDialView(dialColor: $dialColor, tickLength: 5)
      .frame(width: 200, height: 200, alignment: .center)
  }
}

Bringing Up the Color Selector on the Clock Page

First, add a file named ColorPickerContainer.

Next to the original wiki page button, place a settings button.

struct ClockContainerView: View {
  
  var width: CGFloat = 200
  var height: CGFloat = 200
  
  @StateObject private var clockwork: Clockwork = .init()
  
  @State private var dialColor: Color = .white
  
  @State private var isShowingGraceWikiSheet = false
  
  @State private var isShowingColorPicker = false
  
  private let graceWikiPageURL = "https://en.wikipedia.org/wiki/Grace_Hopper"
  
  private let angleUtility: AngleUtility = .init()
  
  var body: some View {
    ZStack {
      
      VStack {
        HStack {
          Spacer()
          wikiProfileButton
          settingButton
            .padding(.trailing, 5)
        }
        Spacer()
      }
      
      Group {
        ClockDialView(dialColor: $dialColor)
        HandShape(handLength: .hour)
          .fill(Color.blue)
          .rotationEffect(Angle(degrees: clockwork.hourAngle))
        HandShape(handLength: .minute)
          .fill(Color.cyan)
          .rotationEffect(Angle(degrees: clockwork.minuteAngle))
        HandShape(handLength: .second)
          .fill(Color.red)
          .rotationEffect(Angle(degrees: clockwork.secondAngle))
        Circle()
          .fill(Color.orange)
          .frame(width: 20, height: 20, alignment: .center)
      }
      .frame(width: width, height: height, alignment: .center)
    }
  }
  /// Extract the wiki profile button
  private var wikiProfileButton: some View {
    Button {
      
      isShowingGraceWikiSheet.toggle()
    } label: {
      Image(systemName: "person.crop.circle")
        .font(.system(size: 50))
        .foregroundColor(.black)
    }
    .sheet(isPresented: $isShowingGraceWikiSheet) {
      BCWebView(urlString: graceWikiPageURL)
    }
  }
  /// Extract the settings button
  private var settingButton: some View {
    
    Button {
      
      isShowingColorPicker.toggle()
    } label: {
      Image(systemName: "gearshape.circle")
        .font(.system(size: 50))
        .foregroundColor(.black)
    }
    .sheet(isPresented: $isShowingColorPicker) {
      ColorPickerContainerView(dialColor: $dialColor)
    }
  }
}

The dial color change functionality is complete.

If you want to change the color of the hands, the process is similar.

color picker

Deployed App on App Store

This clock app was later developed into a version that was published on the App Store, integrating Firebase and RevenueCat. The total size is only 5.1 MB. SwiftUI really impressed me!

https://apps.apple.com/backwards-clock/id1632935212

backward clock

https://developer.apple.com/documentation/swiftui/colorpicker

Updated: