Color Kit

repository·master·Indexed 42 days ago

https://github.com/kieranb662/swiftui-color-kit

A SwiftUI library for creating customizable color picking experiences. It provides color space pickers for RGB, HSB, CMYK, and Grayscale, utilizing the ColorToken data object as a single source of truth. The kit includes specialized components such as saturation/brightness trackpads, hue sliders, and dedicated pickers for Linear, Radial, and Angular gradients, all of which support custom styling via dedicated style protocols.

Tokens
5K
Snippets
8
Records
10
Agent score
3%

What's inside Color Kit

  1. Understand the ColorToken data object

    master

    ColorToken is the central data object used to store color values across all pickers. It acts as a single source of truth for various color formulations (RGB, HSB, CMYK, Grayscale).

    Key properties include:

    • color: A computed property that returns a SwiftUI Color generated from the token's values.
    • name: A String identifier (defaults to "New").
    • colorFormulation: An enum describing the active color space.
    • alpha: A Double (0 to 1) representing opacity.
    • RGB values: red, green, blue (0 to 1).
    • HSB values: hue, saturation, brightness (0 to 1).
    • CMYK values: cyan, magenta, yellow, keyBlack (0 to 1).
    • Grayscale: white (0 to 1).

    Refer to the source code for all available initializers.

    import ColorKit
    
    // Example usage concept:
    // let token = ColorToken(name: "My Color")
    // let color = token.color
  2. Use Color Pickers and Sliders

    master

    Color Kit provides several pre-styled components for color manipulation. Most components are styled versions of sliders from the Sliders library and work by binding to a ColorToken.

    Available Pickers

    • RGB / RGBA: Uses RGBColorPicker (composed of 3 styled sliders).
    • HSB / HSBA: Uses HSB-based controls.
    • CMYK: Uses CMYKSliders.
    • Gray Scale: Uses grayscale-specific controls.
    • All In One: Comprehensive pickers.

    Specialized Components

    • Alpha Slider: A styled slider specifically for adjusting the alpha property of a ColorToken.
    • RGB Sliders: A set of three sliders where each slider's track contains a gradient representing the color at that specific channel's limit.
    • Saturation Brightness TrackPad: A 2D control using composited linear gradients for 2D color selection.
    • Hue Slider: A slider with a background gradient representing the full hue spectrum.
  3. Install Color Kit via Swift Package Manager

    master

    To add Color Kit to your existing Xcode project, follow these steps:

    1. Copy the repository URL from GitHub.
    2. In Xcode, navigate to File -> Swift Packages -> Add Package Dependencies.
    3. Paste the URL into the search box.
    4. Specify the minimum version as 1.0.2.
    5. Once added, include import ColorKit at the top of the Swift file where you intend to use the pickers.

    Alternatively, you can clone the example project to explore different implementations and custom styles.

  4. Use the AngularGradientPicker

    master

    The AngularGradientPicker is a component view used to create and style an AngularGradient.

    Sub-components:

    • Gradient: The view containing the angular gradient.
    • Center Thumb: A draggable view representing the location of the gradient's center.
    • Start Handle: A draggable view representing the start location of the gradient.
    • End Handle: A draggable view representing the end location of the gradient.
    • AngularStop: A draggable view representing a gradient stop, constrained between the current stop and the end angle locations.

    Requirement: You must create a GradientManager as an ObservedObject and inject it into the environment using .environmentObject() for the picker (or its parent view) to function.

    @ObservedObject var gradientManager = GradientManager()
    
    AngularGradientPicker()
        .environmentObject(gradientManager)
  5. Use the LinearGradientPicker

    master

    The LinearGradientPicker is a component view used to create and style a LinearGradient.

    Sub-components:

    • Gradient: The view containing the linear gradient.
    • Start Handle: A draggable view for the gradient's start location.
    • End Handle: A draggable view for the gradient's end location.
    • LinearStop: A draggable view representing a gradient stop, constrained between the start and end handles.

    Requirement: You must create a GradientManager as an ObservedObject and inject it into the environment using .environmentObject() for the picker (or its parent view) to function.

    // Ensure you have a GradientManager instance
    @ObservedObject var gradientManager = GradientManager()
    
    // Apply it to the view hierarchy
    LinearGradientPicker()
        .environmentObject(gradientManager)
  6. Style the LinearGradientPicker

    master

    To customize the appearance of the LinearGradientPicker, create a struct that conforms to the LinearGradientPickerStyle protocol. You must implement four methods: makeGradient, makeStartHandle, makeEndHandle, and makeStop.

    After defining your style, apply it using the .linearGradientPickerStyle() modifier.

    struct MyPickerStyle: LinearGradientPickerStyle {
        func makeGradient(gradient: LinearGradient) -> some View {
            RoundedRectangle(cornerRadius: 5)
                .fill(gradient)
                .overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.white))
        }
    
        func makeStartHandle(configuration: GradientHandleConfiguration) -> some View {
            Capsule()
                .foregroundColor(Color.white)
                .frame(width: 25, height: 75)
                .rotationEffect(configuration.angle + Angle(degrees: 90))
                .animation(.none)
                .shadow(radius: 3)
                .opacity(configuration.isHidden ? 0 : 1)
        }
    
        func makeEndHandle(configuration: GradientHandleConfiguration) -> some View {
            Capsule()
                .foregroundColor(Color.white)
                .frame(width: 25, height: 75)
                .rotationEffect(configuration.angle + Angle(degrees: 90))
                .animation(.none)
                .shadow(radius: 3)
                .opacity(configuration.isHidden ? 0 : 1)
        }
    
        func makeStop(configuration: GradientStopConfiguration) -> some View {
            Capsule()
                .foregroundColor(configuration.color)
                .frame(width: 20, height: 55)
                .overlay(Capsule().stroke(configuration.isSelected ? Color.yellow : Color.white))
                .rotationEffect(configuration.angle + Angle(degrees: 90))
                .animation(.none)
                .shadow(color: configuration.isSelected ? Color.white : Color.black, radius: 3)
                .opacity(configuration.isHidden ? 0 : 1)
        }
    }
    
    // Usage
    LinearGradientPicker()
        .environmentObject(gradientManager)
        .linearGradientPickerStyle(MyPickerStyle())
  7. Use the RadialGradientPicker

    master

    The RadialGradientPicker is a component view used to create and style a RadialGradient.

    Sub-components:

    • Gradient: The view containing the radial gradient.
    • Center Thumb: A draggable view representing the center of the gradient.
    • StartHandle: A draggable circle representing the start radius.
    • EndHandle: A draggable circle representing the end radius.
    • RadialStop: A draggable view contained to the gradient bar, representing the unit location of the stop.
    • Gradient Bar: A slider-like container filled with a linear gradient created with the gradient stops.

    Requirement: You must create a GradientManager as an ObservedObject and inject it into the environment using .environmentObject() for the picker (or its parent view) to function.

    @ObservedObject var gradientManager = GradientManager()
    
    RadialGradientPicker()
        .environmentObject(gradientManager)
  8. Style the RadialGradientPicker

    master

    To customize the appearance of the RadialGradientPicker, create a struct that conforms to the RadialGradientPickerStyle protocol. You must implement six methods: makeGradient, makeCenter, makeStartHandle, makeEndHandle, makeStop, and makeBar.

    After defining your style, apply it using the .radialGradientPickerStyle() modifier.

    struct MyPickerStyle: RadialGradientPickerStyle {
        func makeGradient(gradient: RadialGradient) -> some View {
            RoundedRectangle(cornerRadius: 5)
                .fill(gradient)
                .overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.white))
        }
    
        func makeCenter(configuration: GradientCenterConfiguration) -> some View {
            Circle().fill(configuration.isActive ? Color.yellow : Color.white)
                .frame(width: 35, height: 35)
                .opacity(configuration.isHidden ? 0 : 1)
                .animation(.easeIn)
        }
    
        func makeStartHandle(configuration: GradientHandleConfiguration) -> some View {
            Circle()
                .stroke(Color.white.opacity(0.001), style: StrokeStyle(lineWidth: 10))
                .overlay(Circle().stroke(Color.black, style: StrokeStyle(lineWidth: 1, dash: [10, 5])))
                .opacity(configuration.isHidden ? 0 : 1)
                .animation(.easeIn)
        }
    
        func makeEndHandle(configuration: GradientHandleConfiguration) -> some View {
            Circle()
                .stroke(Color.white.opacity(0.001), style: StrokeStyle(lineWidth: 10))
                .overlay(Circle().stroke(Color.white, style: StrokeStyle(lineWidth: 1, dash: [10, 5])))
                .opacity(configuration.isHidden ? 0 : 1)
                .animation(.easeIn)
        }
    
        func makeStop(configuration: GradientStopConfiguration) -> some View {
            Group {
                if !configuration.isHidden {
                    RoundedRectangle(cornerRadius: 5)
                        .foregroundColor(configuration.color)
                        .frame(width: 25, height: 45)
                        .overlay(RoundedRectangle(cornerRadius: 5).stroke(configuration.isSelected ? Color.yellow : Color.white))
                        .shadow(color: configuration.isSelected ? Color.white : Color.black, radius: 3)
                        .transition(AnyTransition.opacity)
                        .animation(Animation.easeOut)
                }
            }
        }
    
        func makeBar(configuration: RadialGradientBarConfiguration) -> some View {
            Group {
                if !configuration.isHidden {
                    RoundedRectangle(cornerRadius: 5)
                        .fill(LinearGradient(gradient: configuration.gradient, startPoint: .leading, endPoint: .trailing))
                        .overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.white))
                        .transition(AnyTransition.move(edge: .leading))
                        .animation(Animation.easeOut)
                }
            }
        }
    }
    
    // Usage
    RadialGradientPicker()
        .environmentObject(gradientManager)
        .radialGradientPickerStyle(MyPickerStyle())
  9. Style the AngularGradientPicker

    master

    To customize the appearance of the AngularGradientPicker, create a struct that conforms to the AngularGradientPickerStyle protocol. You must implement four methods: makeGradient, makeCenter, makeStartHandle, makeEndHandle, and makeStop.

    After defining your style, apply it using the .angularGradientPickerStyle() modifier.

    struct MyPickerStyle: AngularGradientPickerStyle {
        func makeGradient(gradient: AngularGradient) -> some View {
            RoundedRectangle(cornerRadius: 5)
                .fill(gradient)
                .overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.white))
        }
    
        func makeCenter(configuration: GradientCenterConfiguration) -> some View {
            Circle().fill(configuration.isActive ? Color.yellow : Color.white)
                .frame(width: 35, height: 35)
                .opacity(configuration.isHidden ? 0 : 1)
        }
    
        func makeStartHandle(configuration: GradientHandleConfiguration) -> some View {
            Capsule()
                .foregroundColor(Color.white)
                .frame(width: 30, height: 75)
                .rotationEffect(configuration.angle)
                .animation(.none)
                .shadow(radius: 3)
                .opacity(configuration.isHidden ? 0 : 1)
        }
    
        func makeEndHandle(configuration: GradientHandleConfiguration) -> some View {
            Capsule()
                .foregroundColor(Color.white)
                .frame(width: 30, height: 75)
                .rotationEffect(configuration.angle)
                .animation(.none)
                .shadow(radius: 3)
                .opacity(configuration.isHidden ? 0 : 1)
        }
    
        func makeStop(configuration: GradientStopConfiguration) -> some View {
            Group {
                if !configuration.isHidden {
                    Circle()
                        .foregroundColor(configuration.color)
                        .frame(width: 25, height: 45)
                        .overlay(Circle().stroke(configuration.isSelected ? Color.yellow : Color.white))
                        .shadow(color: configuration.isSelected ? Color.white : Color.black, radius: 3)
                        .transition(AnyTransition.opacity)
                        .animation(Animation.easeOut)
                }
            }
        }
    }
    
    // Usage
    AngularGradientPicker()
        .environmentObject(gradientManager)
        .angularGradientPickerStyle(MyPickerStyle())
  10. Reference: ColorToken properties

    master

    The following properties are available on a ColorToken instance to manage color state:

    id: UUID
    dateCreated: Date
    name: String
    colorFormulation: ColorFormulation
    rgbColorSpace: RGBColorSpace
    white: Double
    red: Double
    green: Double
    blue: Double
    hue: Double
    saturation: Double
    brightness: Double
    cyan: Double
    magenta: Double
    yellow: Double
    keyBlack: Double
    alpha: Double