├── img ├── color.gif └── grad.gif ├── LICENSE.txt ├── README.md └── JCGGColorSlider.swift /img/color.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacquesCedric/JCGGColorSlider/HEAD/img/color.gif -------------------------------------------------------------------------------- /img/grad.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacquesCedric/JCGGColorSlider/HEAD/img/grad.gif -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | ----------- 3 | 4 | Copyright (c) 2019 Jacob Gold 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JCGGColorSlider ![Swift 4.2](https://img.shields.io/badge/swift-5-brightgreen.svg) [![MIT license](https://img.shields.io/badge/license-MIT-green.svg)](http://perso.crans.org/besson/LICENSE.html) 2 | > A colorful subclass of NSSlider. 3 | 4 | An NSSlider subclass. Supports colored sliders, including gradients. Can match knob color to that of location on the slider. 5 | 6 | 7 | 8 | 9 | ## Installation 10 | Clone the source and copy `JCGGColorSlider.swift` to your project. 11 | 12 | ## Use 13 | Simply assign an NSSlider to subclass `JCGGColorSlider`. Slider can be configured with the following options: 14 | 15 | * barColor - The color of the `NSSlider` bar 16 | * barGradient - Overrides `barColor`, assign a gradient to the slider 17 | * knobColor - Color of the knob 18 | * knobColorFromLocation - overrides `knobColor`, setting the knob's color to that of it's location on the slider bar 19 | * bezelMargin - effects the thickness of the slider bar 20 | 21 | ### Example - Rainbow gradient 22 | 23 | 24 | 1. Create an NSSlider and assign its subclass to `JCGGColorSlider` 25 | 2. Create a rainbow gradient 26 | 3. Assign gradient to NSSlider 27 | 4. Set knob to match color 28 | 29 | ``` 30 | // 1 31 | let exampleSlider = JCGGSlider.init(frame: example) 32 | 33 | // 2 34 | let rainbow = NSGradient.init(colors: Array(0...10).map{ NSColor.init(calibratedHue: CGFloat($0) / 10, saturation: 1.0, brightness: 1.0, alpha: 1.0) })! 35 | 36 | // 3 37 | exampleSlider.barGradient = rainbow 38 | 39 | // 4 40 | exampleSlider.knobColorFromLocation = true 41 | ``` 42 | 43 | ## License 44 | `JCGGColorSlider` is available under the MIT license. See the LICENSE file for more details. -------------------------------------------------------------------------------- /JCGGColorSlider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JCGGSliderCell.swift 3 | // 4 | // Created by Jacob Gold on 19/3/19. 5 | // Copyright © 2019 Jacob Gold. All rights reserved. 6 | // MIT license 7 | // 8 | 9 | import Cocoa 10 | 11 | 12 | @IBDesignable public class JCGGColorSlider: NSSlider { 13 | var barBackgroundColor: NSColor = NSColor.darkGray 14 | 15 | public var barGradient: NSGradient = NSGradient.init(colors: [NSColor.systemBlue])! 16 | // Rainbow example 17 | // var barGradient = NSGradient.init(colors: Array(0...10).map{ NSColor.init(calibratedHue: CGFloat($0) / 10, saturation: 1.0, brightness: 1.0, alpha: 1.0) })! 18 | 19 | // Can't do complex gradients with inspectable, so this will do. 20 | @IBInspectable public var barColor: NSColor { 21 | set(newValue) { 22 | barGradient = NSGradient.init(colors: [newValue])! 23 | self.needsDisplay = true 24 | } 25 | get { 26 | return barGradient.interpolatedColor(atLocation: 1.0) 27 | } 28 | } 29 | 30 | // The color of the knob 31 | public var knobColor: NSColor = NSColor.white 32 | 33 | // Should the knob color relate to its position on the slider 34 | fileprivate var _knobColorFromLocation: Bool = true 35 | @IBInspectable public var knobColorFromLocation: Bool { 36 | set(newValue) { 37 | _knobColorFromLocation = newValue 38 | } 39 | get { 40 | return _knobColorFromLocation 41 | } 42 | } 43 | 44 | // Determines the margins on the bar component. 45 | public let bezelMargin: CGFloat = 8 46 | 47 | 48 | 49 | override public func draw(_ dirtyRect: NSRect) { 50 | if (self.isVertical) { 51 | // Bar styling 52 | barBackgroundColor.setFill() 53 | let bezelFrame = bounds.insetBy(dx: bezelMargin, dy: bezelMargin / 2) 54 | let bar = NSBezierPath(roundedRect: bezelFrame, xRadius: bezelFrame.width * 0.5, yRadius: bezelFrame.width * 0.5) 55 | bar.fill() 56 | barGradient.draw(in: bar, angle: -90.0) 57 | 58 | let innerRect = bounds.insetBy(dx: 0, dy: bounds.width / 2) 59 | 60 | // Knob config 61 | let knobY: CGFloat 62 | if maxValue - minValue == 0 { 63 | knobY = innerRect.maxY 64 | } else { 65 | knobY = innerRect.maxY - CGFloat((doubleValue - minValue) / maxValue) * innerRect.height 66 | } 67 | 68 | // Knob shadow 69 | let shadowPath = NSBezierPath(ovalIn: NSRect(x: 0, y: (knobY - bounds.width * 0.5), width: bounds.width, height: bounds.width).insetBy(dx: 1.0, dy: 1.5)) 70 | NSColor.init(white: 0.3, alpha: 0.3).setFill() 71 | shadowPath.fill() 72 | 73 | // Knob iteself 74 | let knobPath = NSBezierPath(ovalIn: NSRect(x: 0, y: (knobY - bounds.width * 0.5), width: bounds.width, height: bounds.width).insetBy(dx: 2, dy: 2)) 75 | knobColor.setFill() 76 | knobPath.fill() 77 | 78 | 79 | // Knob color from location, if enabled 80 | if (_knobColorFromLocation) { 81 | let amount:CGFloat = CGFloat(floatValue / Float(maxValue)) 82 | knobColor = barGradient.interpolatedColor(atLocation: amount) 83 | knobColor.setFill() 84 | knobPath.fill() 85 | } 86 | } 87 | else { 88 | // Bar styling 89 | barBackgroundColor.setFill() 90 | let bezelFrame = bounds.insetBy(dx: bezelMargin / 2, dy: bezelMargin) 91 | let bar = NSBezierPath(roundedRect: bezelFrame, xRadius: bezelFrame.height * 0.5, yRadius: bezelFrame.height * 0.5) 92 | bar.fill() 93 | barGradient.draw(in: bar, angle: 0.0) 94 | 95 | let innerRect = bounds.insetBy(dx: bounds.height / 2, dy: 0) 96 | 97 | // Knob config 98 | 99 | let knobX: CGFloat 100 | if maxValue - minValue == 0 { 101 | knobX = innerRect.minX 102 | } else { 103 | knobX = innerRect.minX + CGFloat((doubleValue - minValue) / maxValue) * innerRect.width 104 | } 105 | 106 | // Knob shadow 107 | let shadowPath = NSBezierPath(ovalIn: NSRect(x: (knobX - bounds.height * 0.5), y: 0, width: bounds.height, height: bounds.height).insetBy(dx: 1.5, dy: 1.0)) 108 | NSColor.init(white: 0.3, alpha: 0.3).setFill() 109 | shadowPath.fill() 110 | 111 | // Knob iteself 112 | let knobPath = NSBezierPath(ovalIn: NSRect(x: knobX - bounds.height * 0.5, y: 0, width: bounds.height, height: bounds.height).insetBy(dx: 2, dy: 2)) 113 | knobColor.setFill() 114 | knobPath.fill() 115 | 116 | // Knob color from location, if enabled 117 | if (_knobColorFromLocation) { 118 | let amount:CGFloat = CGFloat(floatValue / Float(maxValue)) 119 | knobColor = barGradient.interpolatedColor(atLocation: amount) 120 | knobColor.setFill() 121 | knobPath.fill() 122 | } 123 | } 124 | 125 | } 126 | } 127 | --------------------------------------------------------------------------------