Skip to content

Instantly share code, notes, and snippets.

@SAllen0400
Created February 7, 2017 02:42
Show Gist options
  • Save SAllen0400/303c3a49142eeace8e57d269943e048d to your computer and use it in GitHub Desktop.
Save SAllen0400/303c3a49142eeace8e57d269943e048d to your computer and use it in GitHub Desktop.
Custom Slider
import Foundation
class BMSliderControl: UIControl {
var minimumValue: Double = 0.0 {
didSet {
updateLayerFrames()
}
}
var maximumValue: Double = 1.0 {
didSet {
updateLayerFrames()
}
}
var upperValue: Double = 0.5 {
didSet {
updateLayerFrames()
}
}
var lowerValue: Double = 0.0 {
didSet {
updateLayerFrames()
}
}
var trackTintColor: UIColor = Colors.darkGrey {
didSet {
trackLayer.setNeedsDisplay()
}
}
var trackHighlightTintColor: UIColor = Colors.brightOrange {
didSet {
trackLayer.setNeedsDisplay()
}
}
var thumbTintColor: UIColor = UIColor.whiteColor() {
didSet {
upperThumbLayer.setNeedsDisplay()
}
}
var curvaceousness: CGFloat = 1.0 {
didSet {
trackLayer.setNeedsDisplay()
upperThumbLayer.setNeedsDisplay()
}
}
var thumbWidth: CGFloat {
return CGFloat(bounds.height)
}
var previousLocation = CGPoint()
let trackLayer = BMSliderTrackLayer()
let upperThumbLayer = BMSliderThumbLayer()
override init(frame: CGRect) {
super.init(frame: frame)
trackLayer.slider = self
trackLayer.contentsScale = UIScreen.mainScreen().scale
layer.addSublayer(trackLayer)
upperThumbLayer.slider = self
upperThumbLayer.contentsScale = UIScreen.mainScreen().scale
layer.addSublayer(upperThumbLayer)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func updateLayerFrames() {
CATransaction.begin()
CATransaction.setDisableActions(true)
trackLayer.frame = bounds
trackLayer.setNeedsDisplay()
let upperThumbCenter = CGFloat(positionForValue(upperValue))
upperThumbLayer.frame = CGRect(x: upperThumbCenter - thumbWidth / 2.0, y: 0.0, width: thumbWidth, height: thumbWidth)
upperThumbLayer.setNeedsDisplay()
CATransaction.commit()
}
func positionForValue(value: Double) -> Double {
return Double(bounds.width - thumbWidth) * (value - minimumValue) /
(maximumValue - minimumValue) + Double(thumbWidth / 2.0)
}
override var frame: CGRect {
didSet {
updateLayerFrames()
}
}
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
previousLocation = touch.locationInView(self)
if upperThumbLayer.frame.contains(previousLocation) {
upperThumbLayer.highlighted = true
}
return upperThumbLayer.highlighted
}
func boundValue(value: Double, toLowerValue lowerValue: Double, upperValue: Double) -> Double {
return min(max(value, lowerValue), upperValue)
}
override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
let location = touch.locationInView(self)
let deltaLocation = Double(location.x - previousLocation.x)
let deltaValue = (maximumValue - minimumValue) * deltaLocation / Double(bounds.width - thumbWidth)
previousLocation = location
if upperThumbLayer.highlighted {
upperValue += deltaValue
upperValue = boundValue(upperValue, toLowerValue: lowerValue, upperValue: maximumValue)
}
sendActionsForControlEvents(.ValueChanged)
return true
}
override func endTrackingWithTouch(touch: UITouch?, withEvent event: UIEvent?) {
upperThumbLayer.highlighted = false
}
}
class BMSliderThumbLayer: CALayer {
var highlighted: Bool = false {
didSet {
setNeedsDisplay()
}
}
weak var slider: BMSliderControl?
override func drawInContext(ctx: CGContext) {
if let slider = slider {
let thumbFrame = bounds.insetBy(dx: 3.0, dy: 3.0)
let cornerRadius = thumbFrame.height * slider.curvaceousness / 2.0
let thumbPath = UIBezierPath(roundedRect: thumbFrame, cornerRadius: cornerRadius)
// Fill - with a subtle shadow
let shadowColor = Colors.veryDarkGrey
CGContextSetShadowWithColor(ctx, CGSize(width: 0.0, height: 2), 3, shadowColor.CGColor)
CGContextSetFillColorWithColor(ctx, slider.thumbTintColor.CGColor)
CGContextAddPath(ctx, thumbPath.CGPath)
CGContextFillPath(ctx)
// Outline
CGContextSetStrokeColorWithColor(ctx, shadowColor.CGColor)
CGContextSetLineWidth(ctx, 0.5)
CGContextAddPath(ctx, thumbPath.CGPath)
CGContextStrokePath(ctx)
if highlighted {
CGContextSetFillColorWithColor(ctx, UIColor(white: 0.0, alpha: 0.1).CGColor)
CGContextAddPath(ctx, thumbPath.CGPath)
CGContextFillPath(ctx)
}
}
}
}
class BMSliderTrackLayer: CALayer {
weak var slider: BMSliderControl?
override func drawInContext(ctx: CGContext) {
if let slider = slider {
let cornerRadius = bounds.height * slider.curvaceousness / 2.0
let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
CGContextAddPath(ctx, path.CGPath)
// Fill the track
CGContextSetFillColorWithColor(ctx, slider.trackTintColor.CGColor)
CGContextAddPath(ctx, path.CGPath)
CGContextFillPath(ctx)
// Fill the highlighted range
CGContextSetFillColorWithColor(ctx, slider.trackHighlightTintColor.CGColor)
let upperValuePosition = CGFloat(slider.positionForValue(slider.upperValue))
let rect = CGRect(x: 0, y: 0.0, width: upperValuePosition + 20, height: bounds.height)
let highlightedRect = UIBezierPath(roundedRect: rect, cornerRadius: rect.size.height/2)
CGContextAddPath(ctx, highlightedRect.CGPath)
CGContextFillPath(ctx)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment