Skip to content

jaywcjlove/ColorSelector

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ColorSelector

A SwiftUI color picker component library for macOS, designed to replace the default ColorPicker component. In addition, I’ve created another component library, Colorful, which offers a different user experience with a distinct style.

ColorSelector for macOS/iOS

✦ My macOS/iOS application ✦

Vidwall Mousio Hint Mousio Musicer Audioer FileSentinel FocusCursor Videoer KeyClicker DayBar Iconed RightMenu Master Quick RSS Quick RSS Web Serve Copybook Generator DevTutor for SwiftUI RegexMate Time Passage Iconize Folder Textsound Saver Create Custom Symbols DevHub Resume Revise Palette Genius Symbol Scribe

Welcome to download DevTutor, a cheat sheet app designed to help developers quickly build excellent applications using SwiftUI.

DevTutor for SwiftUI AppStore

Usage

import ColorSelector

struct ContentView: View {
    @State var color: Color? = .red
    @State var colorClear: Color? = .clear
    @State var nsColor: NSColor? = NSColor.red
    
    var body: some View {
        ColorSelector("Color", selection: $color)
        ColorSelector(selection: $colorClear)
        ColorSelector(nsColor: $nsColor, arrowEdge: .top)
        ColorSelector(selection: $color) {
            Text("Color Picker")
        }
    }
}

Using the swatchColors environment value, developers can customize the color list in the color selector, replacing the default color options.

struct ContentView: View {
    @State var color: Color? = .red
    
    var body: some View {
        ColorSelector(selection: $color)
            .environment(\.swatchColors, [
                NSColor(hue: 0.999, saturation: 0.857, brightness: 0.878, alpha: 1.0),
                NSColor(hue: 0.066, saturation: 1.000, brightness: 0.980, alpha: 1.0),
                NSColor(hue: 0.121, saturation: 0.976, brightness: 0.969, alpha: 1.0),
            ])
        ///     
        ColorSelector(selection: $colorClear, arrowEdge: .top)
    }
}

By setting the cornerSize (corner radius) and pointSize (point size) environment values, the corner radius and point size can be dynamically adjusted.

struct ContentView: View {
    @State var color: Color? = .red
    @State var cornerRadius: CGFloat = 6
    @State var pointSize: CGSize = .init(width: 12, height: 12)
    
    var body: some View {
        ColorSelector(selection: $color)
            .environment(\.cornerSize, cornerRadius)
            .environment(\.pointSize, pointSize)
    }
}

arrowEdge: The edge of the attachmentAnchor that defines the location of the popover’s arrow. The default is nil, which results in the system allowing any arrow edge.

import ColorSelector

struct ContentView: View {
    @State var colorClear: Color? = .clear
    
    var body: some View {
        ColorSelector(selection: $colorClear, arrowEdge: .bottom)
    }
}

Use the showsAlpha modifier to control the visibility of the alpha (opacity) slider in the picker panel.

import ColorSelector

struct ContentView: View {
    @State var color: Color? = .clear
    
    var body: some View {
        ColorSelector(selection: $color)
            .showsAlpha(false)
    }
}

Customize button size using the controlSize modifier

ColorSelector(selection: $color)
    .controlSize(.regular)

ColorSelector Modifier

The colorSelectorPopover modifier provides an easy way to add color picker functionality to any view. The modifier attaches a popover that shows when triggered externally.

struct ContentView: View {
    @State private var backgroundColor: Color? = .blue
    @State private var showColorPicker = false
    
    var body: some View {
        Text("Background Color")
            .padding()
            .background(backgroundColor ?? .clear)
            .onTapGesture {
                showColorPicker = true
            }
            .colorSelectorPopover(
                selection: $backgroundColor,
                isPresented: $showColorPicker
            )
    }
}

Basic usage with external popover control:

@State private var textColor: Color? = .primary
@State private var showTextPicker = false

Button("Choose Text Color") {
    showTextPicker = true
}
.colorSelectorPopover(
    selection: $textColor,
    isPresented: $showTextPicker,
    showsAlpha: false
)

Using NSColor binding:

@State private var shadowColor: NSColor? = NSColor.gray
@State private var showShadowPicker = false

Text("Shadow Color")
    .onTapGesture { showShadowPicker = true }
    .colorSelectorPopover(
        nsColor: $shadowColor,
        isPresented: $showShadowPicker,
        arrowEdge: .top
    )

Available parameters:

  • selection: A binding to the selected Color
  • nsColor: A binding to the selected NSColor (alternative to selection)
  • isPresented: A binding to control popover visibility
  • arrowEdge: The edge where the popover arrow appears (default: system decides)
  • showsAlpha: Whether to show alpha controls (default: true)

ColorSelectorButton

The ColorSelectorButton is a standalone button component that displays the current selected color and can trigger a popover when tapped. This component provides the visual color representation without the popover functionality built-in.

struct ContentView: View {
    @State private var selectedColor: Color? = .blue
    @State private var showColorPicker = false
    @State private var buttonSize: ControlSize = .regular
    
    var body: some View {
        VStack(spacing: 20) {
            // Color selector button
            ColorSelectorButton(
                popover: $showColorPicker,
                selection: $selectedColor,
                controlSize: $buttonSize
            )
            
            // Display current color
            Text("Selected: \(selectedColor?.description ?? "None")")
        }
        .colorSelectorPopover(
            selection: $selectedColor,
            isPresented: $showColorPicker
        )
    }
}

Customize button size:

@State private var buttonSize: ControlSize = .large

ColorSelectorButton(
    popover: $showColorPicker,
    selection: $selectedColor,
    controlSize: $buttonSize
)

Available control sizes: .mini, .small, .regular, .large, .extraLarge

AlphaSlider

struct ContentView: View {
    @State var alpha: CGFloat = 1.0
    var saturation: CGFloat = 1.0
    var brightness: CGFloat = 1.0
    var hue: CGFloat = 0.0
    
    var body: some View {
        AlphaSlider(
            alpha: $alpha,
            hue: hue,
            saturation: saturation,
            brightness: brightness
        )
    }
}

ColorSampler

struct ContentView: View {
    @State var color: Color = Color.red
    var cornerRadius: CGFloat = 6
    var pointSize: CGSize = .init(width: 8, height: 8)
    
    var body: some View {
        ColorSampler(
            color: $color,
            cornerRadius: cornerRadius,
            pointSize: pointSize
        )
    }
}

You can customize the width and height of the rectangle using the rectSize modifier.

#Preview {
    @Previewable @State var color: Color? = Color.blue
    ColorSampler(color: $color)
        .rectSize(23)
}

HueSlider

struct ContentView: View {
    @State var hue: CGFloat = 1
    
    var body: some View {
        HueSlider(hue: $hue)
    }
}

Saturation

struct ContentView: View {
    @State var saturation: CGFloat = 1.0
    @State var brightness: CGFloat = 1.0
    var hue: CGFloat = 0.0
    
    var body: some View {
        Saturation(
            saturation: $saturation,
            brightness: $brightness,
            hue: hue,
            cornerRadius: 16
        )
    }
}

Swatch

struct ContentView: View {
    @State var color: NSColor = NSColor.white
    
    var body: some View {
        Swatch()
        Swatch(nsColor: color) { value in
            color = value
        }
    }
}

Sketch

struct ContentView: View {
    @State var saturation: CGFloat = 1.0
    @State var brightness: CGFloat = 1.0
    @State var hue: CGFloat = 0.0
    @State var alpha: CGFloat = 1.0
    
    var body: some View {
        Sketch(
            hue: $hue,
            saturation: $saturation,
            brightness: $brightness,
            alpha: $alpha
        )
        .frame(width: 180, height: 230)
    }
}

License

Licensed under the MIT License.