diff --git a/GradientSlider3/GradientSlider/AppDelegate.swift b/GradientSlider3/GradientSlider/AppDelegate.swift
new file mode 100644
index 0000000..2bf4f14
--- /dev/null
+++ b/GradientSlider3/GradientSlider/AppDelegate.swift
@@ -0,0 +1,47 @@
+//
+// AppDelegate.swift
+// GradientSlider
+//
+// Created by Jonathan Hull on 8/5/15.
+// Copyright © 2015 Jonathan Hull. All rights reserved.
+//
+// Updated to Swift 3.2
+
+import UIKit
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+ var window: UIWindow?
+
+
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
+ // Override point for customization after application launch.
+ return true
+ }
+
+ func applicationWillResignActive(_ application: UIApplication) {
+ // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+ // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+ }
+
+ func applicationDidEnterBackground(_ application: UIApplication) {
+ // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+ // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+ }
+
+ func applicationWillEnterForeground(_ application: UIApplication) {
+ // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+ }
+
+ func applicationDidBecomeActive(_ application: UIApplication) {
+ // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+ }
+
+ func applicationWillTerminate(_ application: UIApplication) {
+ // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+ }
+
+
+}
+
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/AppIcon.appiconset/Contents.json b/GradientSlider3/GradientSlider/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..b8236c6
--- /dev/null
+++ b/GradientSlider3/GradientSlider/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,48 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/Contents.json b/GradientSlider3/GradientSlider/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/GradientSlider3/GradientSlider/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/Contents.json b/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/Contents.json
new file mode 100644
index 0000000..54761a7
--- /dev/null
+++ b/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "drop.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "drop@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "drop@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/drop.png b/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/drop.png
new file mode 100644
index 0000000..9e3dd2c
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/drop.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/drop@2x.png b/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/drop@2x.png
new file mode 100644
index 0000000..4158841
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/drop@2x.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/drop@3x.png b/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/drop@3x.png
new file mode 100644
index 0000000..b1c963f
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/drop.imageset/drop@3x.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/Contents.json b/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/Contents.json
new file mode 100644
index 0000000..19ea450
--- /dev/null
+++ b/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "moon.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "moon@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "moon@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/moon.png b/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/moon.png
new file mode 100644
index 0000000..b087c1b
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/moon.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/moon@2x.png b/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/moon@2x.png
new file mode 100644
index 0000000..302861d
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/moon@2x.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/moon@3x.png b/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/moon@3x.png
new file mode 100644
index 0000000..6521f1e
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/moon.imageset/moon@3x.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/Contents.json b/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/Contents.json
new file mode 100644
index 0000000..f18a75b
--- /dev/null
+++ b/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "smallSun.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "smallSun@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "smallSun@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun.png b/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun.png
new file mode 100644
index 0000000..8b276f0
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@2x.png b/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@2x.png
new file mode 100644
index 0000000..d0b3157
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@2x.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@3x.png b/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@3x.png
new file mode 100644
index 0000000..35154f6
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@3x.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/Contents.json b/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/Contents.json
new file mode 100644
index 0000000..1efe4d4
--- /dev/null
+++ b/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "sun.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "sun@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "sun@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/sun.png b/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/sun.png
new file mode 100644
index 0000000..379e9be
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/sun.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/sun@2x.png b/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/sun@2x.png
new file mode 100644
index 0000000..59f3eb1
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/sun@2x.png differ
diff --git a/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/sun@3x.png b/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/sun@3x.png
new file mode 100644
index 0000000..460d881
Binary files /dev/null and b/GradientSlider3/GradientSlider/Assets.xcassets/sun.imageset/sun@3x.png differ
diff --git a/GradientSlider3/GradientSlider/Base.lproj/LaunchScreen.storyboard b/GradientSlider3/GradientSlider/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f1851b0
--- /dev/null
+++ b/GradientSlider3/GradientSlider/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GradientSlider3/GradientSlider/Base.lproj/Main.storyboard b/GradientSlider3/GradientSlider/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..ef1df4e
--- /dev/null
+++ b/GradientSlider3/GradientSlider/Base.lproj/Main.storyboard
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GradientSlider3/GradientSlider/GradientSlider3.swift b/GradientSlider3/GradientSlider/GradientSlider3.swift
new file mode 100644
index 0000000..014bd12
--- /dev/null
+++ b/GradientSlider3/GradientSlider/GradientSlider3.swift
@@ -0,0 +1,431 @@
+//
+// GradientSlider.swift
+// GradientSlider
+//
+// Created by Jonathan Hull on 8/5/15.
+// Copyright © 2015 Jonathan Hull. All rights reserved.
+//
+// Updated to Swift 3.2
+
+import UIKit
+
+@IBDesignable class GradientSlider: UIControl {
+
+ static var defaultThickness:CGFloat = 2.0
+ static var defaultThumbSize:CGFloat = 28.0
+
+ //MARK: Properties
+ @IBInspectable var hasRainbow:Bool = false {didSet{updateTrackColors()}}//Uses saturation & lightness from minColor
+ @IBInspectable var minColor:UIColor = UIColor.blue {didSet{updateTrackColors()}}
+ @IBInspectable var maxColor:UIColor = UIColor.orange {didSet{updateTrackColors()}}
+
+ @IBInspectable var value: CGFloat {
+ get{return _value}
+ set{setValue(newValue, animated:true)}
+ }
+
+ func setValue(_ value:CGFloat, animated:Bool = true) {
+ _value = max(min(value,self.maximumValue),self.minimumValue)
+ updateThumbPosition(animated: animated)
+ }
+
+ @IBInspectable var minimumValue: CGFloat = 0.0 // default 0.0. the current value may change if outside new min value
+ @IBInspectable var maximumValue: CGFloat = 1.0 // default 1.0. the current value may change if outside new max value
+
+ @IBInspectable var minimumValueImage: UIImage? = nil { // default is nil. image that appears to left of control (e.g. speaker off)
+ didSet{
+ if let img = minimumValueImage {
+ let imgLayer = _minTrackImageLayer ?? {
+ let l = CALayer()
+ l.anchorPoint = CGPoint(x: 0.0, y: 0.5)
+ self.layer.addSublayer(l)
+ return l
+ }()
+ imgLayer.contents = img.cgImage
+ imgLayer.bounds = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
+ _minTrackImageLayer = imgLayer
+
+ }else{
+ _minTrackImageLayer?.removeFromSuperlayer()
+ _minTrackImageLayer = nil
+ }
+ self.layer.needsLayout()
+ }
+ }
+ @IBInspectable var maximumValueImage: UIImage? = nil { // default is nil. image that appears to right of control (e.g. speaker max)
+ didSet{
+ if let img = maximumValueImage {
+ let imgLayer = _maxTrackImageLayer ?? {
+ let l = CALayer()
+ l.anchorPoint = CGPoint(x: 1.0, y: 0.5)
+ self.layer.addSublayer(l)
+ return l
+ }()
+ imgLayer.contents = img.cgImage
+ imgLayer.bounds = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
+ _maxTrackImageLayer = imgLayer
+
+ }else{
+ _maxTrackImageLayer?.removeFromSuperlayer()
+ _maxTrackImageLayer = nil
+ }
+ self.layer.needsLayout()
+ }
+ }
+
+ var continuous: Bool = true // if set, value change events are generated any time the value changes due to dragging. default = YES
+
+ var actionBlock:(GradientSlider,CGFloat)->() = {slider,newValue in }
+
+ @IBInspectable var thickness:CGFloat = defaultThickness {
+ didSet{
+ _trackLayer.cornerRadius = thickness / 2.0
+ self.layer.setNeedsLayout()
+ }
+ }
+
+ var trackBorderColor:UIColor? {
+ set{
+ _trackLayer.borderColor = newValue?.cgColor
+ }
+ get{
+ if let color = _trackLayer.borderColor {
+ return UIColor(cgColor: color)
+ }
+ return nil
+ }
+ }
+
+ var trackBorderWidth:CGFloat {
+ set{
+ _trackLayer.borderWidth = newValue
+ }
+ get{
+ return _trackLayer.borderWidth
+ }
+ }
+
+ var thumbSize:CGFloat = defaultThumbSize {
+ didSet{
+ _thumbLayer.cornerRadius = thumbSize / 2.0
+ _thumbLayer.bounds = CGRect(x: 0, y: 0, width: thumbSize, height: thumbSize)
+ self.invalidateIntrinsicContentSize()
+ }
+ }
+
+ @IBInspectable var thumbIcon:UIImage? = nil {
+ didSet{
+ _thumbIconLayer.contents = thumbIcon?.cgImage
+ }
+ }
+
+ var thumbColor:UIColor {
+ get {
+ if let color = _thumbIconLayer.backgroundColor {
+ return UIColor(cgColor: color)
+ }
+ return UIColor.white
+ }
+ set {
+ _thumbIconLayer.backgroundColor = newValue.cgColor
+ thumbIcon = nil
+ }
+ }
+
+ //MARK: - Convienience Colors
+
+ func setGradientForHueWithSaturation(_ saturation:CGFloat,brightness:CGFloat){
+ minColor = UIColor(hue: 0.0, saturation: saturation, brightness: brightness, alpha: 1.0)
+ hasRainbow = true
+ }
+
+ func setGradientForSaturationWithHue(_ hue:CGFloat,brightness:CGFloat){
+ hasRainbow = false
+ minColor = UIColor(hue: hue, saturation: 0.0, brightness: brightness, alpha: 1.0)
+ maxColor = UIColor(hue: hue, saturation: 1.0, brightness: brightness, alpha: 1.0)
+ }
+
+ func setGradientForBrightnessWithHue(_ hue:CGFloat,saturation:CGFloat){
+ hasRainbow = false
+ minColor = UIColor.black
+ maxColor = UIColor(hue: hue, saturation: saturation, brightness: 1.0, alpha: 1.0)
+ }
+
+ func setGradientForRedWithGreen(_ green:CGFloat,blue:CGFloat){
+ hasRainbow = false
+ minColor = UIColor(red: 0.0, green: green, blue: blue, alpha: 1.0)
+ maxColor = UIColor(red: 1.0, green: green, blue: blue, alpha: 1.0)
+ }
+
+ func setGradientForGreenWithRed(_ red:CGFloat,blue:CGFloat){
+ hasRainbow = false
+ minColor = UIColor(red: red, green: 0.0, blue: blue, alpha: 1.0)
+ maxColor = UIColor(red: red, green: 1.0, blue: blue, alpha: 1.0)
+ }
+
+ func setGradientForBlueWithRed(_ red:CGFloat,green:CGFloat){
+ hasRainbow = false
+ minColor = UIColor(red: red, green: green, blue: 0.0, alpha: 1.0)
+ maxColor = UIColor(red: red, green: green, blue: 1.0, alpha: 1.0)
+ }
+
+ func setGradientForGrayscale(){
+ hasRainbow = false
+ minColor = UIColor.black
+ maxColor = UIColor.white
+ }
+
+
+ //MARK: - Private Properties
+
+ fileprivate var _value:CGFloat = 0.0 // default 0.0. this value will be pinned to min/max
+
+ fileprivate var _thumbLayer:CALayer = {
+ let thumb = CALayer()
+ thumb.cornerRadius = defaultThumbSize/2.0
+ thumb.bounds = CGRect(x: 0, y: 0, width: defaultThumbSize, height: defaultThumbSize)
+ thumb.backgroundColor = UIColor.white.cgColor
+ thumb.shadowColor = UIColor.black.cgColor
+ thumb.shadowOffset = CGSize(width: 0.0, height: 2.5)
+ thumb.shadowRadius = 2.0
+ thumb.shadowOpacity = 0.25
+ thumb.borderColor = UIColor.black.withAlphaComponent(0.15).cgColor
+ thumb.borderWidth = 0.5
+ return thumb
+ }()
+
+ fileprivate var _trackLayer:CAGradientLayer = {
+ let track = CAGradientLayer()
+ track.cornerRadius = defaultThickness / 2.0
+ track.startPoint = CGPoint(x: 0.0, y: 0.5)
+ track.endPoint = CGPoint(x: 1.0, y: 0.5)
+ track.locations = [0.0,1.0]
+ track.colors = [UIColor.blue.cgColor,UIColor.orange.cgColor]
+ track.borderColor = UIColor.black.cgColor
+ return track
+ }()
+
+ fileprivate var _minTrackImageLayer:CALayer? = nil
+ fileprivate var _maxTrackImageLayer:CALayer? = nil
+
+ fileprivate var _thumbIconLayer:CALayer = {
+ let size = defaultThumbSize - 4
+ let iconLayer = CALayer()
+ iconLayer.cornerRadius = size/2.0
+ iconLayer.bounds = CGRect(x: 0, y: 0, width: size, height: size)
+ iconLayer.backgroundColor = UIColor.white.cgColor
+ return iconLayer
+ }()
+
+ //MARK: - Init
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ commonSetup()
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ super.init(coder: aDecoder)
+
+ minColor = aDecoder.decodeObject(forKey: "minColor") as? UIColor ?? UIColor.lightGray
+ maxColor = aDecoder.decodeObject(forKey: "maxColor") as? UIColor ?? UIColor.darkGray
+
+ value = aDecoder.decodeObject(forKey: "value") as? CGFloat ?? 0.0
+ minimumValue = aDecoder.decodeObject(forKey: "minimumValue") as? CGFloat ?? 0.0
+ maximumValue = aDecoder.decodeObject(forKey: "maximumValue") as? CGFloat ?? 1.0
+
+ minimumValueImage = aDecoder.decodeObject(forKey: "minimumValueImage") as? UIImage
+ maximumValueImage = aDecoder.decodeObject(forKey: "maximumValueImage") as? UIImage
+
+ thickness = aDecoder.decodeObject(forKey: "thickness") as? CGFloat ?? 2.0
+
+ thumbIcon = aDecoder.decodeObject(forKey: "thumbIcon") as? UIImage
+
+ commonSetup()
+ }
+
+ override func encode(with aCoder: NSCoder) {
+ super.encode(with: aCoder)
+
+ aCoder.encode(minColor, forKey: "minColor")
+ aCoder.encode(maxColor, forKey: "maxColor")
+
+ aCoder.encode(value, forKey: "value")
+ aCoder.encode(minimumValue, forKey: "minimumValue")
+ aCoder.encode(maximumValue, forKey: "maximumValue")
+
+ aCoder.encode(minimumValueImage, forKey: "minimumValueImage")
+ aCoder.encode(maximumValueImage, forKey: "maximumValueImage")
+
+ aCoder.encode(thickness, forKey: "thickness")
+
+ aCoder.encode(thumbIcon, forKey: "thumbIcon")
+
+ }
+
+ fileprivate func commonSetup() {
+ self.layer.delegate = self
+ self.layer.addSublayer(_trackLayer)
+ self.layer.addSublayer(_thumbLayer)
+ _thumbLayer.addSublayer(_thumbIconLayer)
+ }
+
+ //MARK: - Layout
+
+ override var intrinsicContentSize:CGSize {
+ return CGSize(width: UIViewNoIntrinsicMetric, height: thumbSize)
+ }
+
+ override var alignmentRectInsets : UIEdgeInsets {
+ return UIEdgeInsetsMake(4.0, 2.0, 4.0, 2.0)
+ }
+
+ override func layoutSublayers(of layer: CALayer) {
+// super.layoutSublayersOfLayer(layer)
+
+ if layer != self.layer {return}
+
+ var w = self.bounds.width
+ let h = self.bounds.height
+ var left:CGFloat = 2.0
+
+ if let minImgLayer = _minTrackImageLayer {
+ minImgLayer.position = CGPoint(x: 0.0, y: h/2.0)
+ left = minImgLayer.bounds.width + 13.0
+ }
+ w -= left
+
+ if let maxImgLayer = _maxTrackImageLayer {
+ maxImgLayer.position = CGPoint(x: self.bounds.width, y: h/2.0)
+ w -= (maxImgLayer.bounds.width + 13.0)
+ }else{
+ w -= 2.0
+ }
+
+ _trackLayer.bounds = CGRect(x: 0, y: 0, width: w, height: thickness)
+ _trackLayer.position = CGPoint(x: w/2.0 + left, y: h/2.0)
+
+ let halfSize = thumbSize/2.0
+ var layerSize = thumbSize - 4.0
+ if let icon = thumbIcon {
+ layerSize = min(max(icon.size.height,icon.size.width),layerSize)
+ _thumbIconLayer.cornerRadius = 0.0
+ _thumbIconLayer.backgroundColor = UIColor.clear.cgColor
+ }else{
+ _thumbIconLayer.cornerRadius = layerSize/2.0
+ }
+ _thumbIconLayer.position = CGPoint(x: halfSize, y: halfSize)
+ _thumbIconLayer.bounds = CGRect(x: 0, y: 0, width: layerSize, height: layerSize)
+
+
+ updateThumbPosition(animated: false)
+ }
+
+
+
+ //MARK: - Touch Tracking
+
+ override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
+ let pt = touch.location(in: self)
+
+ let center = _thumbLayer.position
+ let diameter = max(thumbSize,44.0)
+ let r = CGRect(x: center.x - diameter/2.0, y: center.y - diameter/2.0, width: diameter, height: diameter)
+ if r.contains(pt){
+ sendActions(for: UIControlEvents.touchDown)
+ return true
+ }
+ return false
+ }
+
+ override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
+ let pt = touch.location(in: self)
+ let newValue = valueForLocation(pt)
+ setValue(newValue, animated: false)
+ if(continuous){
+ sendActions(for: UIControlEvents.valueChanged)
+ actionBlock(self,newValue)
+ }
+ return true
+ }
+
+ override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
+ if let pt = touch?.location(in: self){
+ let newValue = valueForLocation(pt)
+ setValue(newValue, animated: false)
+ }
+ actionBlock(self,_value)
+ sendActions(for: [UIControlEvents.valueChanged, UIControlEvents.touchUpInside])
+
+ }
+
+ //MARK: - Private Functions
+
+ fileprivate func updateThumbPosition(animated:Bool){
+ let diff = maximumValue - minimumValue
+ let perc = CGFloat((value - minimumValue) / diff)
+
+ let halfHeight = self.bounds.height / 2.0
+ let trackWidth = _trackLayer.bounds.width - thumbSize
+ let left = _trackLayer.position.x - trackWidth/2.0
+
+ if !animated{
+ CATransaction.begin() //Move the thumb position without animations
+ CATransaction.setValue(true, forKey: kCATransactionDisableActions)
+ _thumbLayer.position = CGPoint(x: left + (trackWidth * perc), y: halfHeight)
+ CATransaction.commit()
+ }else{
+ _thumbLayer.position = CGPoint(x: left + (trackWidth * perc), y: halfHeight)
+ }
+ }
+
+ fileprivate func valueForLocation(_ point:CGPoint)->CGFloat {
+
+ var left = self.bounds.origin.x
+ var w = self.bounds.width
+ if let minImgLayer = _minTrackImageLayer {
+ let amt = minImgLayer.bounds.width + 13.0
+ w -= amt
+ left += amt
+ }else{
+ w -= 2.0
+ left += 2.0
+ }
+
+ if let maxImgLayer = _maxTrackImageLayer {
+ w -= (maxImgLayer.bounds.width + 13.0)
+ }else{
+ w -= 2.0
+ }
+
+ let diff = CGFloat(self.maximumValue - self.minimumValue)
+
+ let perc = max(min((point.x - left) / w ,1.0), 0.0)
+
+ return (perc * diff) + CGFloat(self.minimumValue)
+ }
+
+ fileprivate func updateTrackColors() {
+ if !hasRainbow {
+ _trackLayer.colors = [minColor.cgColor,maxColor.cgColor]
+ _trackLayer.locations = [0.0,1.0]
+ return
+ }
+ //Otherwise make a rainbow with the saturation & lightness of the min color
+ var h:CGFloat = 0.0
+ var s:CGFloat = 0.0
+ var l:CGFloat = 0.0
+ var a:CGFloat = 1.0
+
+ minColor.getHue(&h, saturation: &s, brightness: &l, alpha: &a)
+
+ let cnt = 40
+ let step:CGFloat = 1.0 / CGFloat(cnt)
+ let locations:[CGFloat] = (0...cnt).map({ i in return (step * CGFloat(i))})
+ _trackLayer.colors = locations.map({return UIColor(hue: $0, saturation: s, brightness: l, alpha: a).cgColor})
+ _trackLayer.locations = locations as [NSNumber]
+ }
+}
+
+
+
diff --git a/GradientSlider3/GradientSlider/Info.plist b/GradientSlider3/GradientSlider/Info.plist
new file mode 100644
index 0000000..caf9621
--- /dev/null
+++ b/GradientSlider3/GradientSlider/Info.plist
@@ -0,0 +1,42 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ GradientSlider3
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/GradientSlider3/GradientSlider/ViewController.swift b/GradientSlider3/GradientSlider/ViewController.swift
new file mode 100644
index 0000000..4951478
--- /dev/null
+++ b/GradientSlider3/GradientSlider/ViewController.swift
@@ -0,0 +1,39 @@
+//
+// ViewController.swift
+// GradientSlider
+//
+// Created by Jonathan Hull on 8/5/15.
+// Copyright © 2015 Jonathan Hull. All rights reserved.
+//
+// Updated to Swift 3.2
+
+import UIKit
+
+class ViewController: UIViewController {
+
+ @IBOutlet weak var hueSlider: GradientSlider!
+ @IBOutlet weak var brightnessSlider: GradientSlider!
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ // Do any additional setup after loading the view, typically from a nib.
+
+ hueSlider.actionBlock = {slider,newValue in
+ CATransaction.begin()
+ CATransaction.setValue(true, forKey: kCATransactionDisableActions)
+ self.brightnessSlider.maxColor = UIColor(hue: newValue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
+ slider.thumbColor = UIColor(hue: newValue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
+ CATransaction.commit()
+ }
+
+ hueSlider.thumbColor = UIColor(hue: 0.5, saturation: 1.0, brightness: 1.0, alpha: 1.0)
+ }
+
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+
+}
+
diff --git a/GradientSlider3/GradientSlider3.xcodeproj/project.pbxproj b/GradientSlider3/GradientSlider3.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..7fa01b0
--- /dev/null
+++ b/GradientSlider3/GradientSlider3.xcodeproj/project.pbxproj
@@ -0,0 +1,307 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ FC1C417D1B730A410083C062 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1C417C1B730A410083C062 /* AppDelegate.swift */; };
+ FC1C417F1B730A410083C062 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1C417E1B730A410083C062 /* ViewController.swift */; };
+ FC1C41821B730A410083C062 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FC1C41801B730A410083C062 /* Main.storyboard */; };
+ FC1C41841B730A410083C062 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FC1C41831B730A410083C062 /* Assets.xcassets */; };
+ FC1C41871B730A410083C062 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FC1C41851B730A410083C062 /* LaunchScreen.storyboard */; };
+ FC1C418F1B730E530083C062 /* GradientSlider3.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1C418E1B730E530083C062 /* GradientSlider3.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ FC1C41791B730A410083C062 /* GradientSlider3.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GradientSlider3.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC1C417C1B730A410083C062 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ FC1C417E1B730A410083C062 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
+ FC1C41811B730A410083C062 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ FC1C41831B730A410083C062 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ FC1C41861B730A410083C062 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ FC1C41881B730A410083C062 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ FC1C418E1B730E530083C062 /* GradientSlider3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientSlider3.swift; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ FC1C41761B730A410083C062 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ FC1C41701B730A410083C062 = {
+ isa = PBXGroup;
+ children = (
+ FC1C417B1B730A410083C062 /* GradientSlider3 */,
+ FC1C417A1B730A410083C062 /* Products */,
+ );
+ sourceTree = "";
+ };
+ FC1C417A1B730A410083C062 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ FC1C41791B730A410083C062 /* GradientSlider3.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ FC1C417B1B730A410083C062 /* GradientSlider3 */ = {
+ isa = PBXGroup;
+ children = (
+ FC1C417C1B730A410083C062 /* AppDelegate.swift */,
+ FC1C417E1B730A410083C062 /* ViewController.swift */,
+ FC1C41801B730A410083C062 /* Main.storyboard */,
+ FC1C418E1B730E530083C062 /* GradientSlider3.swift */,
+ FC1C41831B730A410083C062 /* Assets.xcassets */,
+ FC1C41851B730A410083C062 /* LaunchScreen.storyboard */,
+ FC1C41881B730A410083C062 /* Info.plist */,
+ );
+ name = GradientSlider3;
+ path = GradientSlider;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ FC1C41781B730A410083C062 /* GradientSlider3 */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC1C418B1B730A410083C062 /* Build configuration list for PBXNativeTarget "GradientSlider3" */;
+ buildPhases = (
+ FC1C41751B730A410083C062 /* Sources */,
+ FC1C41761B730A410083C062 /* Frameworks */,
+ FC1C41771B730A410083C062 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = GradientSlider3;
+ productName = GradientSlider;
+ productReference = FC1C41791B730A410083C062 /* GradientSlider3.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ FC1C41711B730A410083C062 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0830;
+ ORGANIZATIONNAME = "Jonathan Hull";
+ TargetAttributes = {
+ FC1C41781B730A410083C062 = {
+ CreatedOnToolsVersion = 7.0;
+ DevelopmentTeam = 29Y384GTLV;
+ LastSwiftMigration = 0830;
+ };
+ };
+ };
+ buildConfigurationList = FC1C41741B730A410083C062 /* Build configuration list for PBXProject "GradientSlider3" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = FC1C41701B730A410083C062;
+ productRefGroup = FC1C417A1B730A410083C062 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ FC1C41781B730A410083C062 /* GradientSlider3 */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ FC1C41771B730A410083C062 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC1C41871B730A410083C062 /* LaunchScreen.storyboard in Resources */,
+ FC1C41841B730A410083C062 /* Assets.xcassets in Resources */,
+ FC1C41821B730A410083C062 /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ FC1C41751B730A410083C062 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC1C417F1B730A410083C062 /* ViewController.swift in Sources */,
+ FC1C418F1B730E530083C062 /* GradientSlider3.swift in Sources */,
+ FC1C417D1B730A410083C062 /* AppDelegate.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ FC1C41801B730A410083C062 /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ FC1C41811B730A410083C062 /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ FC1C41851B730A410083C062 /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ FC1C41861B730A410083C062 /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ FC1C41891B730A410083C062 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ FC1C418A1B730A410083C062 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ FC1C418C1B730A410083C062 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ DEVELOPMENT_TEAM = 29Y384GTLV;
+ INFOPLIST_FILE = GradientSlider/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.Brad.GradientSlider3;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 3.0;
+ };
+ name = Debug;
+ };
+ FC1C418D1B730A410083C062 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ DEVELOPMENT_TEAM = 29Y384GTLV;
+ INFOPLIST_FILE = GradientSlider/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.Brad.GradientSlider3;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 3.0;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ FC1C41741B730A410083C062 /* Build configuration list for PBXProject "GradientSlider3" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC1C41891B730A410083C062 /* Debug */,
+ FC1C418A1B730A410083C062 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC1C418B1B730A410083C062 /* Build configuration list for PBXNativeTarget "GradientSlider3" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC1C418C1B730A410083C062 /* Debug */,
+ FC1C418D1B730A410083C062 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = FC1C41711B730A410083C062 /* Project object */;
+}
diff --git a/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..3da9c67
--- /dev/null
+++ b/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/xcshareddata/GradientSlider.xccheckout b/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/xcshareddata/GradientSlider.xccheckout
new file mode 100644
index 0000000..56b8062
--- /dev/null
+++ b/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/xcshareddata/GradientSlider.xccheckout
@@ -0,0 +1,41 @@
+
+
+
+
+ IDESourceControlProjectFavoriteDictionaryKey
+
+ IDESourceControlProjectIdentifier
+ 58D24B13-25F1-469C-B6C0-C0D2BE294047
+ IDESourceControlProjectName
+ GradientSlider
+ IDESourceControlProjectOriginsDictionary
+
+ 340883B9DF0F7567157D9FDF3AA826EDD0DE698F
+ https://github.com/jonhull/GradientSlider.git
+
+ IDESourceControlProjectPath
+ GradientSlider.xcodeproj
+ IDESourceControlProjectRelativeInstallPathDictionary
+
+ 340883B9DF0F7567157D9FDF3AA826EDD0DE698F
+ ../..
+
+ IDESourceControlProjectURL
+ https://github.com/jonhull/GradientSlider.git
+ IDESourceControlProjectVersion
+ 111
+ IDESourceControlProjectWCCIdentifier
+ 340883B9DF0F7567157D9FDF3AA826EDD0DE698F
+ IDESourceControlProjectWCConfigurations
+
+
+ IDESourceControlRepositoryExtensionIdentifierKey
+ public.vcs.git
+ IDESourceControlWCCIdentifierKey
+ 340883B9DF0F7567157D9FDF3AA826EDD0DE698F
+ IDESourceControlWCCName
+ GradientSlider
+
+
+
+
diff --git a/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/xcuserdata/brad.xcuserdatad/UserInterfaceState.xcuserstate b/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/xcuserdata/brad.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..7740ea9
Binary files /dev/null and b/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/xcuserdata/brad.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/xcuserdata/jon.xcuserdatad/UserInterfaceState.xcuserstate b/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/xcuserdata/jon.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..f8625e6
Binary files /dev/null and b/GradientSlider3/GradientSlider3.xcodeproj/project.xcworkspace/xcuserdata/jon.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/GradientSlider.xcscheme b/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/GradientSlider.xcscheme
new file mode 100644
index 0000000..adc568e
--- /dev/null
+++ b/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/GradientSlider.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/xcschememanagement.plist b/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..73853ce
--- /dev/null
+++ b/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ SchemeUserState
+
+ GradientSlider.xcscheme
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ FC1C41781B730A410083C062
+
+ primary
+
+
+
+
+
diff --git a/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/jon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/jon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000..fe2b454
--- /dev/null
+++ b/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/jon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,5 @@
+
+
+
diff --git a/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/GradientSlider.xcscheme b/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/GradientSlider.xcscheme
new file mode 100644
index 0000000..9f2d009
--- /dev/null
+++ b/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/GradientSlider.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/xcschememanagement.plist b/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..73853ce
--- /dev/null
+++ b/GradientSlider3/GradientSlider3.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ SchemeUserState
+
+ GradientSlider.xcscheme
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ FC1C41781B730A410083C062
+
+ primary
+
+
+
+
+
diff --git a/GradientSlider3/LICENSE.txt b/GradientSlider3/LICENSE.txt
new file mode 100644
index 0000000..5c45511
--- /dev/null
+++ b/GradientSlider3/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Jonathan Hull
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/GradientSlider3/README.md b/GradientSlider3/README.md
new file mode 100644
index 0000000..9bf9087
--- /dev/null
+++ b/GradientSlider3/README.md
@@ -0,0 +1,133 @@
+# GradientSlider
+
+
+GradientSlider is a UIControl subclass which is similar to UISlider, but with a linear gradient coloring the slider’s track. Useful for creating color pickers. It is written in Swift 2 and requires it.
+
+**Features**
+- Easily define a gradient by setting the min & max (i.e. left & right) colors
+- Rainbow gradients (with customizable saturation & brightness) for making Hue selection sliders
+- IBDesignable/Inspectable for direct use within Interface Builder
+- Add an icon or color to the slider thumb
+- Min/Max images (similar to UISlider)
+- Both Target/Action and Block-based callbacks
+- Customizable track thickness
+- Customizable track border and thumb size
+- Looks like UISlider by default, so they can be intermixed in the UI
+
+## Installation
+Drag the “GradientSlider.swift” file into your Xcode project
+
+## Usage via Interface Builder
+Drag a custom view into your storyboard and change it’s class to “GradientSlider”. Use the attributes inspector to optionally set the slider’s basic properties:
+- min/max color
+- hasRainbow (Note: the min color’s saturation & brightness are used)
+- value
+- minimum/maximum value
+- minimum/maximum images
+- thickness
+- thumbIcon
+
+## Setting the Track Gradient Programmatically
+### Min/Max Color
+The track displays a linear gradient with the minColor corresponding to the minimumValue and the maxColor corresponding to the maximumValue.
+
+ slider.minValue = UIColor.blueColor()
+ slider.maxValue = UIColor.orangeColor()
+
+
+
+### Rainbow
+When the `hasRainbow` property is set to true, the track displays a rainbow gradient which moves around the color wheel (starting and ending with red). The saturation & brightness are both pulled from the color in `minValue`.
+
+ slider.minValue = UIColor.blueColor() //This has full saturation & brightness
+ slider.hasRainbow = true
+
+
+
+### Convenience Methods
+Since one of the primary uses of the gradient sliders is to create color pickers, a few convenience methods have been provided for updating the colors.
+
+**HSB**
+
+`setGradientForHueWithSaturation(saturation:CGFloat,brightness:CGFloat)` This method sets the track to a rainbow gradient with the given saturation and brightness. This is useful for choosing a hue in the context of the current saturation and brightness settings.
+
+`setGradientForSaturationWithHue(hue:CGFloat,brightness:CGFloat)` This method sets the track to a gradient varying from grey to fully saturated with the hue and brightness provided. This is useful for choosing a saturation value in the context of the current hue and brightness settings.
+
+`setGradientForBrightnessWithHue(hue:CGFloat,saturation:CGFloat)` This method sets the track to a gradient varying from black to full brightness with the hue and saturation provided. This is useful for choosing a brightness value in the context of the current hue and saturation settings.
+
+**RGB**
+
+`setGradientForRedWithGreen(green:CGFloat, blue:CGFloat)` This method sets the track to a gradient with varying red for the given green & blue values.
+
+`setGradientForGreenWithRed(red:CGFloat, blue:CGFloat)` This method sets the track to a gradient with varying green for the given red & blue values.
+
+`setGradientForBlueWithRed(red:CGFloat, green:CGFloat)` This method sets the track to a gradient with varying blue for the given red & green values.
+
+**Grayscale**
+
+`setGradientForGrayscale()` This method sets the track to a gradient from black to white.
+
+## Responding to User Interaction
+### Target/Action
+As a UIControl subclass, the traditional target/action approach is fully supported. The slider will send TouchDown, ValueChanged, and TouchUpInside actions. If the `continuous` property is set to true (which it is by default), then the slider will send ValueChanged actions as the slider changes, otherwise a ValueChanged action is only sent when the user releases the slider.
+
+### ActionBlock
+The slider will also call it’s actionBlock when its value is changed. If the `continuous` property is set to true (which it is by default), then the slider will call its actionBlock as the slider changes, otherwise it will only call it once when the slider is released.
+The `actionBlock` property takes a closure which takes 2 parameters: The slider being changed and the new value, and has no return value.
+It’s signature is: `(GradientSlider,CGFloat)->()`
+
+Here is an example of a hue slider which updates it’s own `thumbColor` to the value it is pointing to and updates sliders representing saturation & brightness to reflect it’s new value:
+
+ //There are 3 @IBOutlets: hueSlide,satSlide, and brightSlide
+
+ hueSlide.actionBlock = { slider, value in
+ let curSat = satSlide.value
+ let curBright = brightSlide.value
+
+ //First disable animations so we get instantaneous updates
+ CATransaction.begin()
+ CATransaction.setValue(true, forKey: kCATransactionDisableActions)
+
+ //Reflect the new hue in the saturation slider
+ satSlide.setGradientForSaturationWithHue(value, brightness:curBright)
+
+ //Reflect the new hue in the brightness slider
+ brightSlide.setGradientForBrightnessWithHue(value, saturation:curSat)
+
+ //Update hueSlider's thumb color to match our new value
+ slider.thumbColor = UIColor(hue: value, saturation: curSat, brightness: curBright, alpha: 1.0)
+ // (Note: We use the slider variable passed instead of 'hueSlide' to avoid retain cycles)
+
+ CATransaction.commit()
+ }
+
+Images showing the effect of dragging the hue slider
+
+
+
+
+## Customizing the Track
+Besides the background gradient, the track is customizable in several ways using the following properties:
+- `thickness:CGFloat` This sets the track’s thickness. The default value is 2pts
+- `borderColor:UIColor` This sets the color of the track’s border. The default value is black.
+- `borderWidth:CGFloat` This sets the width of the track’s border. The default value is 0 (i.e. no border).
+- `minimumValueImage:UIImage?` This places an image to the left of the track. Setting it to nil removes any image which is there. The default value is nil (i.e. no image).
+- `maximumValueImage:UIImage?` This places an image to the right of the track. Setting it to nil removes any image which is there. The default value is nil (i.e. no image).
+
+## Customizing the Thumb
+The thumb can be resized, given a custom color or an icon (it can not have both a color and an icon at the same time). To customize the thumb, use the following properties:
+- `thumbSize:CGFloat` This sets the diameter of the thumb. Note: Resizing the thumb may change the slider’s intrisicContentSize. The default is 28.0 pts.
+- `thumbColor:UIColor` This sets the color displayed by the thumb. When the thumb has an associated icon, the color is automatically set to clearColor, and setting a new color removes the icon. The default value is White.
+- `thumbIcon:UIImage?` This sets the image to display inside the slider’s thumb. Setting an image automatically removes the thumbColor, and setting a color removes the icon. The default value is nil (i.e. no image)
+
+## License
+The MIT License (MIT)
+
+Copyright (c) 2015 Jonathan Hull
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/GradientSlider4/GradientSlider/AppDelegate.swift b/GradientSlider4/GradientSlider/AppDelegate.swift
new file mode 100644
index 0000000..8236618
--- /dev/null
+++ b/GradientSlider4/GradientSlider/AppDelegate.swift
@@ -0,0 +1,47 @@
+//
+// AppDelegate.swift
+// GradientSlider
+//
+// Created by Jonathan Hull on 8/5/15.
+// Copyright © 2015 Jonathan Hull. All rights reserved.
+//
+// Updated to Swift 4.1
+
+import UIKit
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+ var window: UIWindow?
+
+
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
+ // Override point for customization after application launch.
+ return true
+ }
+
+ func applicationWillResignActive(_ application: UIApplication) {
+ // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+ // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+ }
+
+ func applicationDidEnterBackground(_ application: UIApplication) {
+ // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+ // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+ }
+
+ func applicationWillEnterForeground(_ application: UIApplication) {
+ // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+ }
+
+ func applicationDidBecomeActive(_ application: UIApplication) {
+ // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+ }
+
+ func applicationWillTerminate(_ application: UIApplication) {
+ // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+ }
+
+
+}
+
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/AppIcon.appiconset/Contents.json b/GradientSlider4/GradientSlider/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..b8236c6
--- /dev/null
+++ b/GradientSlider4/GradientSlider/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,48 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/Contents.json b/GradientSlider4/GradientSlider/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/GradientSlider4/GradientSlider/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/Contents.json b/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/Contents.json
new file mode 100644
index 0000000..54761a7
--- /dev/null
+++ b/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "drop.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "drop@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "drop@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/drop.png b/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/drop.png
new file mode 100644
index 0000000..9e3dd2c
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/drop.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/drop@2x.png b/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/drop@2x.png
new file mode 100644
index 0000000..4158841
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/drop@2x.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/drop@3x.png b/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/drop@3x.png
new file mode 100644
index 0000000..b1c963f
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/drop.imageset/drop@3x.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/Contents.json b/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/Contents.json
new file mode 100644
index 0000000..19ea450
--- /dev/null
+++ b/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "moon.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "moon@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "moon@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/moon.png b/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/moon.png
new file mode 100644
index 0000000..b087c1b
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/moon.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/moon@2x.png b/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/moon@2x.png
new file mode 100644
index 0000000..302861d
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/moon@2x.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/moon@3x.png b/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/moon@3x.png
new file mode 100644
index 0000000..6521f1e
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/moon.imageset/moon@3x.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/Contents.json b/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/Contents.json
new file mode 100644
index 0000000..f18a75b
--- /dev/null
+++ b/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "smallSun.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "smallSun@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "smallSun@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun.png b/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun.png
new file mode 100644
index 0000000..8b276f0
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@2x.png b/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@2x.png
new file mode 100644
index 0000000..d0b3157
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@2x.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@3x.png b/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@3x.png
new file mode 100644
index 0000000..35154f6
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/smallSun.imageset/smallSun@3x.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/Contents.json b/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/Contents.json
new file mode 100644
index 0000000..1efe4d4
--- /dev/null
+++ b/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "sun.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "sun@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "sun@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/sun.png b/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/sun.png
new file mode 100644
index 0000000..379e9be
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/sun.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/sun@2x.png b/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/sun@2x.png
new file mode 100644
index 0000000..59f3eb1
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/sun@2x.png differ
diff --git a/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/sun@3x.png b/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/sun@3x.png
new file mode 100644
index 0000000..460d881
Binary files /dev/null and b/GradientSlider4/GradientSlider/Assets.xcassets/sun.imageset/sun@3x.png differ
diff --git a/GradientSlider4/GradientSlider/Base.lproj/LaunchScreen.storyboard b/GradientSlider4/GradientSlider/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f1851b0
--- /dev/null
+++ b/GradientSlider4/GradientSlider/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GradientSlider4/GradientSlider/Base.lproj/Main.storyboard b/GradientSlider4/GradientSlider/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..ef1df4e
--- /dev/null
+++ b/GradientSlider4/GradientSlider/Base.lproj/Main.storyboard
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GradientSlider4/GradientSlider/GradientSlider4.swift b/GradientSlider4/GradientSlider/GradientSlider4.swift
new file mode 100644
index 0000000..7b8c6fd
--- /dev/null
+++ b/GradientSlider4/GradientSlider/GradientSlider4.swift
@@ -0,0 +1,431 @@
+//
+// GradientSlider.swift
+// GradientSlider
+//
+// Created by Jonathan Hull on 8/5/15.
+// Copyright © 2015 Jonathan Hull. All rights reserved.
+//
+// Updated to Swift 4.1
+
+import UIKit
+
+@IBDesignable class GradientSlider: UIControl {
+
+ static var defaultThickness:CGFloat = 2.0
+ static var defaultThumbSize:CGFloat = 28.0
+
+ //MARK: Properties
+ @IBInspectable var hasRainbow:Bool = false {didSet{updateTrackColors()}}//Uses saturation & lightness from minColor
+ @IBInspectable var minColor:UIColor = UIColor.blue {didSet{updateTrackColors()}}
+ @IBInspectable var maxColor:UIColor = UIColor.orange {didSet{updateTrackColors()}}
+
+ @IBInspectable var value: CGFloat {
+ get{return _value}
+ set{setValue(newValue, animated:true)}
+ }
+
+ func setValue(_ value:CGFloat, animated:Bool = true) {
+ _value = max(min(value,self.maximumValue),self.minimumValue)
+ updateThumbPosition(animated: animated)
+ }
+
+ @IBInspectable var minimumValue: CGFloat = 0.0 // default 0.0. the current value may change if outside new min value
+ @IBInspectable var maximumValue: CGFloat = 1.0 // default 1.0. the current value may change if outside new max value
+
+ @IBInspectable var minimumValueImage: UIImage? = nil { // default is nil. image that appears to left of control (e.g. speaker off)
+ didSet{
+ if let img = minimumValueImage {
+ let imgLayer = _minTrackImageLayer ?? {
+ let l = CALayer()
+ l.anchorPoint = CGPoint(x: 0.0, y: 0.5)
+ self.layer.addSublayer(l)
+ return l
+ }()
+ imgLayer.contents = img.cgImage
+ imgLayer.bounds = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
+ _minTrackImageLayer = imgLayer
+
+ }else{
+ _minTrackImageLayer?.removeFromSuperlayer()
+ _minTrackImageLayer = nil
+ }
+ self.layer.needsLayout()
+ }
+ }
+ @IBInspectable var maximumValueImage: UIImage? = nil { // default is nil. image that appears to right of control (e.g. speaker max)
+ didSet{
+ if let img = maximumValueImage {
+ let imgLayer = _maxTrackImageLayer ?? {
+ let l = CALayer()
+ l.anchorPoint = CGPoint(x: 1.0, y: 0.5)
+ self.layer.addSublayer(l)
+ return l
+ }()
+ imgLayer.contents = img.cgImage
+ imgLayer.bounds = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
+ _maxTrackImageLayer = imgLayer
+
+ }else{
+ _maxTrackImageLayer?.removeFromSuperlayer()
+ _maxTrackImageLayer = nil
+ }
+ self.layer.needsLayout()
+ }
+ }
+
+ var continuous: Bool = true // if set, value change events are generated any time the value changes due to dragging. default = YES
+
+ var actionBlock:(GradientSlider,CGFloat)->() = {slider,newValue in }
+
+ @IBInspectable var thickness:CGFloat = defaultThickness {
+ didSet{
+ _trackLayer.cornerRadius = thickness / 2.0
+ self.layer.setNeedsLayout()
+ }
+ }
+
+ var trackBorderColor:UIColor? {
+ set{
+ _trackLayer.borderColor = newValue?.cgColor
+ }
+ get{
+ if let color = _trackLayer.borderColor {
+ return UIColor(cgColor: color)
+ }
+ return nil
+ }
+ }
+
+ var trackBorderWidth:CGFloat {
+ set{
+ _trackLayer.borderWidth = newValue
+ }
+ get{
+ return _trackLayer.borderWidth
+ }
+ }
+
+ var thumbSize:CGFloat = defaultThumbSize {
+ didSet{
+ _thumbLayer.cornerRadius = thumbSize / 2.0
+ _thumbLayer.bounds = CGRect(x: 0, y: 0, width: thumbSize, height: thumbSize)
+ self.invalidateIntrinsicContentSize()
+ }
+ }
+
+ @IBInspectable var thumbIcon:UIImage? = nil {
+ didSet{
+ _thumbIconLayer.contents = thumbIcon?.cgImage
+ }
+ }
+
+ var thumbColor:UIColor {
+ get {
+ if let color = _thumbIconLayer.backgroundColor {
+ return UIColor(cgColor: color)
+ }
+ return UIColor.white
+ }
+ set {
+ _thumbIconLayer.backgroundColor = newValue.cgColor
+ thumbIcon = nil
+ }
+ }
+
+ //MARK: - Convienience Colors
+
+ func setGradientForHueWithSaturation(_ saturation:CGFloat,brightness:CGFloat){
+ minColor = UIColor(hue: 0.0, saturation: saturation, brightness: brightness, alpha: 1.0)
+ hasRainbow = true
+ }
+
+ func setGradientForSaturationWithHue(_ hue:CGFloat,brightness:CGFloat){
+ hasRainbow = false
+ minColor = UIColor(hue: hue, saturation: 0.0, brightness: brightness, alpha: 1.0)
+ maxColor = UIColor(hue: hue, saturation: 1.0, brightness: brightness, alpha: 1.0)
+ }
+
+ func setGradientForBrightnessWithHue(_ hue:CGFloat,saturation:CGFloat){
+ hasRainbow = false
+ minColor = UIColor.black
+ maxColor = UIColor(hue: hue, saturation: saturation, brightness: 1.0, alpha: 1.0)
+ }
+
+ func setGradientForRedWithGreen(_ green:CGFloat,blue:CGFloat){
+ hasRainbow = false
+ minColor = UIColor(red: 0.0, green: green, blue: blue, alpha: 1.0)
+ maxColor = UIColor(red: 1.0, green: green, blue: blue, alpha: 1.0)
+ }
+
+ func setGradientForGreenWithRed(_ red:CGFloat,blue:CGFloat){
+ hasRainbow = false
+ minColor = UIColor(red: red, green: 0.0, blue: blue, alpha: 1.0)
+ maxColor = UIColor(red: red, green: 1.0, blue: blue, alpha: 1.0)
+ }
+
+ func setGradientForBlueWithRed(_ red:CGFloat,green:CGFloat){
+ hasRainbow = false
+ minColor = UIColor(red: red, green: green, blue: 0.0, alpha: 1.0)
+ maxColor = UIColor(red: red, green: green, blue: 1.0, alpha: 1.0)
+ }
+
+ func setGradientForGrayscale(){
+ hasRainbow = false
+ minColor = UIColor.black
+ maxColor = UIColor.white
+ }
+
+
+ //MARK: - Private Properties
+
+ fileprivate var _value:CGFloat = 0.0 // default 0.0. this value will be pinned to min/max
+
+ fileprivate var _thumbLayer:CALayer = {
+ let thumb = CALayer()
+ thumb.cornerRadius = defaultThumbSize/2.0
+ thumb.bounds = CGRect(x: 0, y: 0, width: defaultThumbSize, height: defaultThumbSize)
+ thumb.backgroundColor = UIColor.white.cgColor
+ thumb.shadowColor = UIColor.black.cgColor
+ thumb.shadowOffset = CGSize(width: 0.0, height: 2.5)
+ thumb.shadowRadius = 2.0
+ thumb.shadowOpacity = 0.25
+ thumb.borderColor = UIColor.black.withAlphaComponent(0.15).cgColor
+ thumb.borderWidth = 0.5
+ return thumb
+ }()
+
+ fileprivate var _trackLayer:CAGradientLayer = {
+ let track = CAGradientLayer()
+ track.cornerRadius = defaultThickness / 2.0
+ track.startPoint = CGPoint(x: 0.0, y: 0.5)
+ track.endPoint = CGPoint(x: 1.0, y: 0.5)
+ track.locations = [0.0,1.0]
+ track.colors = [UIColor.blue.cgColor,UIColor.orange.cgColor]
+ track.borderColor = UIColor.black.cgColor
+ return track
+ }()
+
+ fileprivate var _minTrackImageLayer:CALayer? = nil
+ fileprivate var _maxTrackImageLayer:CALayer? = nil
+
+ fileprivate var _thumbIconLayer:CALayer = {
+ let size = defaultThumbSize - 4
+ let iconLayer = CALayer()
+ iconLayer.cornerRadius = size/2.0
+ iconLayer.bounds = CGRect(x: 0, y: 0, width: size, height: size)
+ iconLayer.backgroundColor = UIColor.white.cgColor
+ return iconLayer
+ }()
+
+ //MARK: - Init
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ commonSetup()
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ super.init(coder: aDecoder)
+
+ minColor = aDecoder.decodeObject(forKey: "minColor") as? UIColor ?? UIColor.lightGray
+ maxColor = aDecoder.decodeObject(forKey: "maxColor") as? UIColor ?? UIColor.darkGray
+
+ value = aDecoder.decodeObject(forKey: "value") as? CGFloat ?? 0.0
+ minimumValue = aDecoder.decodeObject(forKey: "minimumValue") as? CGFloat ?? 0.0
+ maximumValue = aDecoder.decodeObject(forKey: "maximumValue") as? CGFloat ?? 1.0
+
+ minimumValueImage = aDecoder.decodeObject(forKey: "minimumValueImage") as? UIImage
+ maximumValueImage = aDecoder.decodeObject(forKey: "maximumValueImage") as? UIImage
+
+ thickness = aDecoder.decodeObject(forKey: "thickness") as? CGFloat ?? 2.0
+
+ thumbIcon = aDecoder.decodeObject(forKey: "thumbIcon") as? UIImage
+
+ commonSetup()
+ }
+
+ override func encode(with aCoder: NSCoder) {
+ super.encode(with: aCoder)
+
+ aCoder.encode(minColor, forKey: "minColor")
+ aCoder.encode(maxColor, forKey: "maxColor")
+
+ aCoder.encode(value, forKey: "value")
+ aCoder.encode(minimumValue, forKey: "minimumValue")
+ aCoder.encode(maximumValue, forKey: "maximumValue")
+
+ aCoder.encode(minimumValueImage, forKey: "minimumValueImage")
+ aCoder.encode(maximumValueImage, forKey: "maximumValueImage")
+
+ aCoder.encode(thickness, forKey: "thickness")
+
+ aCoder.encode(thumbIcon, forKey: "thumbIcon")
+
+ }
+
+ fileprivate func commonSetup() {
+ self.layer.delegate = self
+ self.layer.addSublayer(_trackLayer)
+ self.layer.addSublayer(_thumbLayer)
+ _thumbLayer.addSublayer(_thumbIconLayer)
+ }
+
+ //MARK: - Layout
+
+ override var intrinsicContentSize:CGSize {
+ return CGSize(width: UIViewNoIntrinsicMetric, height: thumbSize)
+ }
+
+ override var alignmentRectInsets : UIEdgeInsets {
+ return UIEdgeInsetsMake(4.0, 2.0, 4.0, 2.0)
+ }
+
+ override func layoutSublayers(of layer: CALayer) {
+// super.layoutSublayersOfLayer(layer)
+
+ if layer != self.layer {return}
+
+ var w = self.bounds.width
+ let h = self.bounds.height
+ var left:CGFloat = 2.0
+
+ if let minImgLayer = _minTrackImageLayer {
+ minImgLayer.position = CGPoint(x: 0.0, y: h/2.0)
+ left = minImgLayer.bounds.width + 13.0
+ }
+ w -= left
+
+ if let maxImgLayer = _maxTrackImageLayer {
+ maxImgLayer.position = CGPoint(x: self.bounds.width, y: h/2.0)
+ w -= (maxImgLayer.bounds.width + 13.0)
+ }else{
+ w -= 2.0
+ }
+
+ _trackLayer.bounds = CGRect(x: 0, y: 0, width: w, height: thickness)
+ _trackLayer.position = CGPoint(x: w/2.0 + left, y: h/2.0)
+
+ let halfSize = thumbSize/2.0
+ var layerSize = thumbSize - 4.0
+ if let icon = thumbIcon {
+ layerSize = min(max(icon.size.height,icon.size.width),layerSize)
+ _thumbIconLayer.cornerRadius = 0.0
+ _thumbIconLayer.backgroundColor = UIColor.clear.cgColor
+ }else{
+ _thumbIconLayer.cornerRadius = layerSize/2.0
+ }
+ _thumbIconLayer.position = CGPoint(x: halfSize, y: halfSize)
+ _thumbIconLayer.bounds = CGRect(x: 0, y: 0, width: layerSize, height: layerSize)
+
+
+ updateThumbPosition(animated: false)
+ }
+
+
+
+ //MARK: - Touch Tracking
+
+ override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
+ let pt = touch.location(in: self)
+
+ let center = _thumbLayer.position
+ let diameter = max(thumbSize,44.0)
+ let r = CGRect(x: center.x - diameter/2.0, y: center.y - diameter/2.0, width: diameter, height: diameter)
+ if r.contains(pt){
+ sendActions(for: UIControlEvents.touchDown)
+ return true
+ }
+ return false
+ }
+
+ override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
+ let pt = touch.location(in: self)
+ let newValue = valueForLocation(pt)
+ setValue(newValue, animated: false)
+ if(continuous){
+ sendActions(for: UIControlEvents.valueChanged)
+ actionBlock(self,newValue)
+ }
+ return true
+ }
+
+ override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
+ if let pt = touch?.location(in: self){
+ let newValue = valueForLocation(pt)
+ setValue(newValue, animated: false)
+ }
+ actionBlock(self,_value)
+ sendActions(for: [UIControlEvents.valueChanged, UIControlEvents.touchUpInside])
+
+ }
+
+ //MARK: - Private Functions
+
+ fileprivate func updateThumbPosition(animated:Bool){
+ let diff = maximumValue - minimumValue
+ let perc = CGFloat((value - minimumValue) / diff)
+
+ let halfHeight = self.bounds.height / 2.0
+ let trackWidth = _trackLayer.bounds.width - thumbSize
+ let left = _trackLayer.position.x - trackWidth/2.0
+
+ if !animated{
+ CATransaction.begin() //Move the thumb position without animations
+ CATransaction.setValue(true, forKey: kCATransactionDisableActions)
+ _thumbLayer.position = CGPoint(x: left + (trackWidth * perc), y: halfHeight)
+ CATransaction.commit()
+ }else{
+ _thumbLayer.position = CGPoint(x: left + (trackWidth * perc), y: halfHeight)
+ }
+ }
+
+ fileprivate func valueForLocation(_ point:CGPoint)->CGFloat {
+
+ var left = self.bounds.origin.x
+ var w = self.bounds.width
+ if let minImgLayer = _minTrackImageLayer {
+ let amt = minImgLayer.bounds.width + 13.0
+ w -= amt
+ left += amt
+ }else{
+ w -= 2.0
+ left += 2.0
+ }
+
+ if let maxImgLayer = _maxTrackImageLayer {
+ w -= (maxImgLayer.bounds.width + 13.0)
+ }else{
+ w -= 2.0
+ }
+
+ let diff = CGFloat(self.maximumValue - self.minimumValue)
+
+ let perc = max(min((point.x - left) / w ,1.0), 0.0)
+
+ return (perc * diff) + CGFloat(self.minimumValue)
+ }
+
+ fileprivate func updateTrackColors() {
+ if !hasRainbow {
+ _trackLayer.colors = [minColor.cgColor,maxColor.cgColor]
+ _trackLayer.locations = [0.0,1.0]
+ return
+ }
+ //Otherwise make a rainbow with the saturation & lightness of the min color
+ var h:CGFloat = 0.0
+ var s:CGFloat = 0.0
+ var l:CGFloat = 0.0
+ var a:CGFloat = 1.0
+
+ minColor.getHue(&h, saturation: &s, brightness: &l, alpha: &a)
+
+ let cnt = 40
+ let step:CGFloat = 1.0 / CGFloat(cnt)
+ let locations:[CGFloat] = (0...cnt).map({ i in return (step * CGFloat(i))})
+ _trackLayer.colors = locations.map({return UIColor(hue: $0, saturation: s, brightness: l, alpha: a).cgColor})
+ _trackLayer.locations = locations as [NSNumber]
+ }
+}
+
+
+
diff --git a/GradientSlider4/GradientSlider/Info.plist b/GradientSlider4/GradientSlider/Info.plist
new file mode 100644
index 0000000..465b1b9
--- /dev/null
+++ b/GradientSlider4/GradientSlider/Info.plist
@@ -0,0 +1,42 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ GradientSlider4
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/GradientSlider4/GradientSlider/ViewController.swift b/GradientSlider4/GradientSlider/ViewController.swift
new file mode 100644
index 0000000..9c32169
--- /dev/null
+++ b/GradientSlider4/GradientSlider/ViewController.swift
@@ -0,0 +1,39 @@
+//
+// ViewController.swift
+// GradientSlider
+//
+// Created by Jonathan Hull on 8/5/15.
+// Copyright © 2015 Jonathan Hull. All rights reserved.
+//
+// Updated to Swift 4.1
+
+import UIKit
+
+class ViewController: UIViewController {
+
+ @IBOutlet weak var hueSlider: GradientSlider!
+ @IBOutlet weak var brightnessSlider: GradientSlider!
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ // Do any additional setup after loading the view, typically from a nib.
+
+ hueSlider.actionBlock = {slider,newValue in
+ CATransaction.begin()
+ CATransaction.setValue(true, forKey: kCATransactionDisableActions)
+ self.brightnessSlider.maxColor = UIColor(hue: newValue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
+ slider.thumbColor = UIColor(hue: newValue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
+ CATransaction.commit()
+ }
+
+ hueSlider.thumbColor = UIColor(hue: 0.5, saturation: 1.0, brightness: 1.0, alpha: 1.0)
+ }
+
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+
+}
+
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/project.pbxproj b/GradientSlider4/GradientSlider4.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..5c77e9e
--- /dev/null
+++ b/GradientSlider4/GradientSlider4.xcodeproj/project.pbxproj
@@ -0,0 +1,327 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ FC1C417D1B730A410083C062 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1C417C1B730A410083C062 /* AppDelegate.swift */; };
+ FC1C417F1B730A410083C062 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1C417E1B730A410083C062 /* ViewController.swift */; };
+ FC1C41821B730A410083C062 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FC1C41801B730A410083C062 /* Main.storyboard */; };
+ FC1C41841B730A410083C062 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FC1C41831B730A410083C062 /* Assets.xcassets */; };
+ FC1C41871B730A410083C062 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FC1C41851B730A410083C062 /* LaunchScreen.storyboard */; };
+ FC1C418F1B730E530083C062 /* GradientSlider4.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1C418E1B730E530083C062 /* GradientSlider4.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ FC1C41791B730A410083C062 /* GradientSlider4.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GradientSlider4.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC1C417C1B730A410083C062 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ FC1C417E1B730A410083C062 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
+ FC1C41811B730A410083C062 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ FC1C41831B730A410083C062 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ FC1C41861B730A410083C062 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ FC1C41881B730A410083C062 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ FC1C418E1B730E530083C062 /* GradientSlider4.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientSlider4.swift; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ FC1C41761B730A410083C062 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ FC1C41701B730A410083C062 = {
+ isa = PBXGroup;
+ children = (
+ FC1C417B1B730A410083C062 /* GradientSlider4 */,
+ FC1C417A1B730A410083C062 /* Products */,
+ );
+ sourceTree = "";
+ };
+ FC1C417A1B730A410083C062 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ FC1C41791B730A410083C062 /* GradientSlider4.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ FC1C417B1B730A410083C062 /* GradientSlider4 */ = {
+ isa = PBXGroup;
+ children = (
+ FC1C417C1B730A410083C062 /* AppDelegate.swift */,
+ FC1C417E1B730A410083C062 /* ViewController.swift */,
+ FC1C41801B730A410083C062 /* Main.storyboard */,
+ FC1C418E1B730E530083C062 /* GradientSlider4.swift */,
+ FC1C41831B730A410083C062 /* Assets.xcassets */,
+ FC1C41851B730A410083C062 /* LaunchScreen.storyboard */,
+ FC1C41881B730A410083C062 /* Info.plist */,
+ );
+ name = GradientSlider4;
+ path = GradientSlider;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ FC1C41781B730A410083C062 /* GradientSlider4 */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC1C418B1B730A410083C062 /* Build configuration list for PBXNativeTarget "GradientSlider4" */;
+ buildPhases = (
+ FC1C41751B730A410083C062 /* Sources */,
+ FC1C41761B730A410083C062 /* Frameworks */,
+ FC1C41771B730A410083C062 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = GradientSlider4;
+ productName = GradientSlider;
+ productReference = FC1C41791B730A410083C062 /* GradientSlider4.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ FC1C41711B730A410083C062 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0930;
+ ORGANIZATIONNAME = "Jonathan Hull";
+ TargetAttributes = {
+ FC1C41781B730A410083C062 = {
+ CreatedOnToolsVersion = 7.0;
+ DevelopmentTeam = 29Y384GTLV;
+ LastSwiftMigration = 0930;
+ };
+ };
+ };
+ buildConfigurationList = FC1C41741B730A410083C062 /* Build configuration list for PBXProject "GradientSlider4" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = FC1C41701B730A410083C062;
+ productRefGroup = FC1C417A1B730A410083C062 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ FC1C41781B730A410083C062 /* GradientSlider4 */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ FC1C41771B730A410083C062 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC1C41871B730A410083C062 /* LaunchScreen.storyboard in Resources */,
+ FC1C41841B730A410083C062 /* Assets.xcassets in Resources */,
+ FC1C41821B730A410083C062 /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ FC1C41751B730A410083C062 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC1C417F1B730A410083C062 /* ViewController.swift in Sources */,
+ FC1C418F1B730E530083C062 /* GradientSlider4.swift in Sources */,
+ FC1C417D1B730A410083C062 /* AppDelegate.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ FC1C41801B730A410083C062 /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ FC1C41811B730A410083C062 /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ FC1C41851B730A410083C062 /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ FC1C41861B730A410083C062 /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ FC1C41891B730A410083C062 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ FC1C418A1B730A410083C062 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ FC1C418C1B730A410083C062 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ DEVELOPMENT_TEAM = 29Y384GTLV;
+ INFOPLIST_FILE = GradientSlider/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.3;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.Brad.GradientSlider4;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_SWIFT3_OBJC_INFERENCE = On;
+ SWIFT_VERSION = 4.0;
+ };
+ name = Debug;
+ };
+ FC1C418D1B730A410083C062 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ DEVELOPMENT_TEAM = 29Y384GTLV;
+ INFOPLIST_FILE = GradientSlider/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.3;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.Brad.GradientSlider4;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_SWIFT3_OBJC_INFERENCE = On;
+ SWIFT_VERSION = 4.0;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ FC1C41741B730A410083C062 /* Build configuration list for PBXProject "GradientSlider4" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC1C41891B730A410083C062 /* Debug */,
+ FC1C418A1B730A410083C062 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC1C418B1B730A410083C062 /* Build configuration list for PBXNativeTarget "GradientSlider4" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC1C418C1B730A410083C062 /* Debug */,
+ FC1C418D1B730A410083C062 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = FC1C41711B730A410083C062 /* Project object */;
+}
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..c5d4ba9
--- /dev/null
+++ b/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcshareddata/GradientSlider.xccheckout b/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcshareddata/GradientSlider.xccheckout
new file mode 100644
index 0000000..56b8062
--- /dev/null
+++ b/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcshareddata/GradientSlider.xccheckout
@@ -0,0 +1,41 @@
+
+
+
+
+ IDESourceControlProjectFavoriteDictionaryKey
+
+ IDESourceControlProjectIdentifier
+ 58D24B13-25F1-469C-B6C0-C0D2BE294047
+ IDESourceControlProjectName
+ GradientSlider
+ IDESourceControlProjectOriginsDictionary
+
+ 340883B9DF0F7567157D9FDF3AA826EDD0DE698F
+ https://github.com/jonhull/GradientSlider.git
+
+ IDESourceControlProjectPath
+ GradientSlider.xcodeproj
+ IDESourceControlProjectRelativeInstallPathDictionary
+
+ 340883B9DF0F7567157D9FDF3AA826EDD0DE698F
+ ../..
+
+ IDESourceControlProjectURL
+ https://github.com/jonhull/GradientSlider.git
+ IDESourceControlProjectVersion
+ 111
+ IDESourceControlProjectWCCIdentifier
+ 340883B9DF0F7567157D9FDF3AA826EDD0DE698F
+ IDESourceControlProjectWCConfigurations
+
+
+ IDESourceControlRepositoryExtensionIdentifierKey
+ public.vcs.git
+ IDESourceControlWCCIdentifierKey
+ 340883B9DF0F7567157D9FDF3AA826EDD0DE698F
+ IDESourceControlWCCName
+ GradientSlider
+
+
+
+
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcuserdata/brad.xcuserdatad/UserInterfaceState.xcuserstate b/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcuserdata/brad.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..7ded160
Binary files /dev/null and b/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcuserdata/brad.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcuserdata/jon.xcuserdatad/UserInterfaceState.xcuserstate b/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcuserdata/jon.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..f8625e6
Binary files /dev/null and b/GradientSlider4/GradientSlider4.xcodeproj/project.xcworkspace/xcuserdata/jon.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/GradientSlider.xcscheme b/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/GradientSlider.xcscheme
new file mode 100644
index 0000000..0cf1aac
--- /dev/null
+++ b/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/GradientSlider.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/xcschememanagement.plist b/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..73853ce
--- /dev/null
+++ b/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/brad.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ SchemeUserState
+
+ GradientSlider.xcscheme
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ FC1C41781B730A410083C062
+
+ primary
+
+
+
+
+
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/jon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/jon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000..fe2b454
--- /dev/null
+++ b/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/jon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,5 @@
+
+
+
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/GradientSlider.xcscheme b/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/GradientSlider.xcscheme
new file mode 100644
index 0000000..9f2d009
--- /dev/null
+++ b/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/GradientSlider.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/xcschememanagement.plist b/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..73853ce
--- /dev/null
+++ b/GradientSlider4/GradientSlider4.xcodeproj/xcuserdata/jon.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ SchemeUserState
+
+ GradientSlider.xcscheme
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ FC1C41781B730A410083C062
+
+ primary
+
+
+
+
+
diff --git a/GradientSlider4/LICENSE.txt b/GradientSlider4/LICENSE.txt
new file mode 100644
index 0000000..5c45511
--- /dev/null
+++ b/GradientSlider4/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Jonathan Hull
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/GradientSlider4/README.md b/GradientSlider4/README.md
new file mode 100644
index 0000000..9bf9087
--- /dev/null
+++ b/GradientSlider4/README.md
@@ -0,0 +1,133 @@
+# GradientSlider
+
+
+GradientSlider is a UIControl subclass which is similar to UISlider, but with a linear gradient coloring the slider’s track. Useful for creating color pickers. It is written in Swift 2 and requires it.
+
+**Features**
+- Easily define a gradient by setting the min & max (i.e. left & right) colors
+- Rainbow gradients (with customizable saturation & brightness) for making Hue selection sliders
+- IBDesignable/Inspectable for direct use within Interface Builder
+- Add an icon or color to the slider thumb
+- Min/Max images (similar to UISlider)
+- Both Target/Action and Block-based callbacks
+- Customizable track thickness
+- Customizable track border and thumb size
+- Looks like UISlider by default, so they can be intermixed in the UI
+
+## Installation
+Drag the “GradientSlider.swift” file into your Xcode project
+
+## Usage via Interface Builder
+Drag a custom view into your storyboard and change it’s class to “GradientSlider”. Use the attributes inspector to optionally set the slider’s basic properties:
+- min/max color
+- hasRainbow (Note: the min color’s saturation & brightness are used)
+- value
+- minimum/maximum value
+- minimum/maximum images
+- thickness
+- thumbIcon
+
+## Setting the Track Gradient Programmatically
+### Min/Max Color
+The track displays a linear gradient with the minColor corresponding to the minimumValue and the maxColor corresponding to the maximumValue.
+
+ slider.minValue = UIColor.blueColor()
+ slider.maxValue = UIColor.orangeColor()
+
+
+
+### Rainbow
+When the `hasRainbow` property is set to true, the track displays a rainbow gradient which moves around the color wheel (starting and ending with red). The saturation & brightness are both pulled from the color in `minValue`.
+
+ slider.minValue = UIColor.blueColor() //This has full saturation & brightness
+ slider.hasRainbow = true
+
+
+
+### Convenience Methods
+Since one of the primary uses of the gradient sliders is to create color pickers, a few convenience methods have been provided for updating the colors.
+
+**HSB**
+
+`setGradientForHueWithSaturation(saturation:CGFloat,brightness:CGFloat)` This method sets the track to a rainbow gradient with the given saturation and brightness. This is useful for choosing a hue in the context of the current saturation and brightness settings.
+
+`setGradientForSaturationWithHue(hue:CGFloat,brightness:CGFloat)` This method sets the track to a gradient varying from grey to fully saturated with the hue and brightness provided. This is useful for choosing a saturation value in the context of the current hue and brightness settings.
+
+`setGradientForBrightnessWithHue(hue:CGFloat,saturation:CGFloat)` This method sets the track to a gradient varying from black to full brightness with the hue and saturation provided. This is useful for choosing a brightness value in the context of the current hue and saturation settings.
+
+**RGB**
+
+`setGradientForRedWithGreen(green:CGFloat, blue:CGFloat)` This method sets the track to a gradient with varying red for the given green & blue values.
+
+`setGradientForGreenWithRed(red:CGFloat, blue:CGFloat)` This method sets the track to a gradient with varying green for the given red & blue values.
+
+`setGradientForBlueWithRed(red:CGFloat, green:CGFloat)` This method sets the track to a gradient with varying blue for the given red & green values.
+
+**Grayscale**
+
+`setGradientForGrayscale()` This method sets the track to a gradient from black to white.
+
+## Responding to User Interaction
+### Target/Action
+As a UIControl subclass, the traditional target/action approach is fully supported. The slider will send TouchDown, ValueChanged, and TouchUpInside actions. If the `continuous` property is set to true (which it is by default), then the slider will send ValueChanged actions as the slider changes, otherwise a ValueChanged action is only sent when the user releases the slider.
+
+### ActionBlock
+The slider will also call it’s actionBlock when its value is changed. If the `continuous` property is set to true (which it is by default), then the slider will call its actionBlock as the slider changes, otherwise it will only call it once when the slider is released.
+The `actionBlock` property takes a closure which takes 2 parameters: The slider being changed and the new value, and has no return value.
+It’s signature is: `(GradientSlider,CGFloat)->()`
+
+Here is an example of a hue slider which updates it’s own `thumbColor` to the value it is pointing to and updates sliders representing saturation & brightness to reflect it’s new value:
+
+ //There are 3 @IBOutlets: hueSlide,satSlide, and brightSlide
+
+ hueSlide.actionBlock = { slider, value in
+ let curSat = satSlide.value
+ let curBright = brightSlide.value
+
+ //First disable animations so we get instantaneous updates
+ CATransaction.begin()
+ CATransaction.setValue(true, forKey: kCATransactionDisableActions)
+
+ //Reflect the new hue in the saturation slider
+ satSlide.setGradientForSaturationWithHue(value, brightness:curBright)
+
+ //Reflect the new hue in the brightness slider
+ brightSlide.setGradientForBrightnessWithHue(value, saturation:curSat)
+
+ //Update hueSlider's thumb color to match our new value
+ slider.thumbColor = UIColor(hue: value, saturation: curSat, brightness: curBright, alpha: 1.0)
+ // (Note: We use the slider variable passed instead of 'hueSlide' to avoid retain cycles)
+
+ CATransaction.commit()
+ }
+
+Images showing the effect of dragging the hue slider
+
+
+
+
+## Customizing the Track
+Besides the background gradient, the track is customizable in several ways using the following properties:
+- `thickness:CGFloat` This sets the track’s thickness. The default value is 2pts
+- `borderColor:UIColor` This sets the color of the track’s border. The default value is black.
+- `borderWidth:CGFloat` This sets the width of the track’s border. The default value is 0 (i.e. no border).
+- `minimumValueImage:UIImage?` This places an image to the left of the track. Setting it to nil removes any image which is there. The default value is nil (i.e. no image).
+- `maximumValueImage:UIImage?` This places an image to the right of the track. Setting it to nil removes any image which is there. The default value is nil (i.e. no image).
+
+## Customizing the Thumb
+The thumb can be resized, given a custom color or an icon (it can not have both a color and an icon at the same time). To customize the thumb, use the following properties:
+- `thumbSize:CGFloat` This sets the diameter of the thumb. Note: Resizing the thumb may change the slider’s intrisicContentSize. The default is 28.0 pts.
+- `thumbColor:UIColor` This sets the color displayed by the thumb. When the thumb has an associated icon, the color is automatically set to clearColor, and setting a new color removes the icon. The default value is White.
+- `thumbIcon:UIImage?` This sets the image to display inside the slider’s thumb. Setting an image automatically removes the thumbColor, and setting a color removes the icon. The default value is nil (i.e. no image)
+
+## License
+The MIT License (MIT)
+
+Copyright (c) 2015 Jonathan Hull
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file