     return CGSize(width: width, height: height)
   }
   
-  @objc fileprivate func sliderTouchedUpInside(_ sender: UISlider) {
-    guard type == .continuous else {
-      return
-    }
-    makeSliderValueDiscrete()
-  }
-  
-  @objc fileprivate func sliderValueChanged(_ sender: UISlider) {
-    guard type == .stepped else {
-      return
-    }
-    makeSliderValueDiscrete()
-  }
-  
-  
-  fileprivate func makeSliderValueDiscrete() {
-    let floatValue = slider.value
-    let discreteValue = showDiscretion(value: floatValue)
-    let newValue = Int(discreteValue)
-    let valueChanged = newValue != value
-    value = newValue
-    
-    if valueChanged {
-      sendActions(for: .valueChanged)
-      
-      if doHaptic {
-        if #available(iOS 10.0, *) {
-          let hap = haptic
-          hap.notificationOccurred(.success)
-        }
-      }
-    }
-  }
-  
-  fileprivate func showDiscretion(value : Float) -> Int {
-    guard values.count > 0 else {
+  override public func layoutSubviews() {
+    super.layoutSubviews()
+    layoutTrackLine()
+    calculateLittleBallRects()
+    recalculateThumbX()
+    setNeedsDisplay()
+  }
+  
+  fileprivate func recalculateThumbX() {
+    guard trackRect.width > 0, values.count > 1 else {
+      return
+    }
+  
+    thumbX = trackRect.width / CGFloat(values.count - 1) * CGFloat(discreteValue)
+  }
+  
+  fileprivate func layoutTrackLine() {
+    let trackSize : CGSize = CGSize(width: frame.width, height: trackHeight)
+    
+    trackRect = CGRect(x: 0, y: (frame.height - trackSize.height) / 2,
+      width: trackSize.width, height: trackSize.height)
+    
+    setNeedsDisplay()
+  }
+  
+  fileprivate func calculateLittleBallRects() {
+    ballRects.removeAll()
+    let width : CGFloat = bounds.width
+    let extra = ceil(bounds.height / 2.0)
+    let y = bounds.origin.y + extra
+    let cx = width / CGFloat(values.count - 1)
+    maxTextWidthAllowed = cx
+    
+    for i in 0..<self.values.count {
+      let px : CGFloat = CGFloat(i) * cx + self.ballRadius
+      let center : CGPoint = CGPoint(x: px, y: y)
+      var rect = CGRect(x: center.x - self.ballRadius,
+        y: center.y - self.ballRadius, width: self.ballRadius * 2.0,
+        height: self.ballRadius * 2.0)
+      
+      if i == self.values.count - 1 {
+        let rectOriginX = rect.origin.x
+        let newRectOriginX = self.bounds.width - (self.ballRadius * 2)
+        let offsetX = newRectOriginX - rectOriginX
+        rect = rect.offsetBy(dx: offsetX, dy: 0.0)
+      }
+      
+      ballRects.append(rect)
+    }
+  }
+  
+  override public func touchesBegan(_ touches: Set<UITouch>,
+    with event: UIEvent?) {
+    
+    guard let location = locationOfFirstTouch(inTouches: touches) else {
+      return
+    }
+    let thumbcenter = thumbX + ballRadius
+    touchOffsetX = location.x - thumbcenter
+  }
+  
+  override public func touchesMoved(_ touches: Set<UITouch>,
+    with event: UIEvent?) {
+    
+    guard let location = locationOfFirstTouch(inTouches: touches) else {
+      return
+    }
+    
+    let desiredX = desiredThumbX(touchX: location.x)
+    moveThumbTo(x: desiredX, animationDuration: 0.0)
+  }
+  
+  override public func touchesEnded(_ touches: Set<UITouch>,
+    with event: UIEvent?) {
+    
+    guard let location = locationOfFirstTouch(inTouches: touches) else {
+      return
+    }
+    
+    let desiredX = desiredThumbX(touchX: location.x)
+    moveThumbTo(x: desiredX, animationDuration: 0.0, discrete: true)
+    touchOffsetX = 0.0
+  }
+  
+  fileprivate func desiredThumbX(touchX : CGFloat) -> CGFloat {
+    return touchX - touchOffsetX
+  }
+  
+  fileprivate func locationOfFirstTouch(inTouches touches : Set<UITouch>)
+    -> CGPoint? {
+      
+    guard let touch = touches.first else {
+      return nil
+    }
+    return touch.location(in: touch.view)
+  }
+  
+  fileprivate func showDiscretion(value : CGFloat) -> Int {
+    guard values.count > 1, value >= 0.0, trackRect.width > 0 else {
       return 0
     }
     
-    var intValue = Int(value)
-    if value - Float(intValue) > 0.5 {
+    let scaledToValueSetValue = value / trackRect.width *
+      CGFloat(values.count - 1)
+    
+    var intValue = Int(scaledToValueSetValue)
+    if scaledToValueSetValue - CGFloat(intValue) > 0.5 {
       intValue += 1
     }
     return intValue
   }
-}
-
-extension DiscreteSlider {
-  override func draw(_ rect: CGRect) {
+
+  
+  override public func draw(_ rect: CGRect) {
     super.draw(rect)
     
+    drawTrackLine()
+    guard let context = UIGraphicsGetCurrentContext() else {
+      return
+    }
+    
+    let currentThumbCenter = thumbX + selectorRadius
+    
+    for i in 0..<ballRects.count {
+      let ballRect = ballRects[i]
+      context.saveGState()
+      let ball = UIBezierPath(ovalIn: ballRect)
+      // The zeroth ball is always painted the max color
+      if ballRect.origin.x < currentThumbCenter  || i == 0 {
+        maximumTrackTintColor.setFill()
+        ball.fill()
+      } else {
+        minimumTrackTintColor.setFill()
+        ball.fill()
+      }
+      context.restoreGState()
+    }
+    
+    drawThumb()
+    drawLabels()
+  }
+  
+  fileprivate func drawLabels() {
     let tuple = getTextRectFontsAndCollisionInfo()
     let textRectFonts = tuple.0
     let collision = tuple.1

    initPhase2()
  }
  
  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    initPhase2()
  }
  
  private func initPhase2() {
    nameLabel.translatesAutoresizingMaskIntoConstraints = false
    slider.translatesAutoresizingMaskIntoConstraints = false
    
    addSubview(nameLabel)
    addSubview(slider)
    
    guard let font = getAdaptiveFont(bold: true) else {
      return
    }
    
    let nsString = NSString(string: "text string")
    let textSize = nsString.size(attributes: [NSFontAttributeName : font])
    textHeight = textSize.height
    
    NSLayoutConstraint.activate([
      nameLabel.topAnchor.constraint(equalTo: topAnchor),
      nameLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
      
      slider.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: Constants.vertPad),
      slider.leadingAnchor.constraint(equalTo: leadingAnchor),
      slider.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Constants.sliderHorzInsets),
      slider.heightAnchor.constraint(equalToConstant: Constants.sliderHeight),
      ])
    
    slider.addTarget(self, action: #selector(DiscreteSlider.sliderTouchedUpInside(_:)),
                     for: .touchUpInside)
    
    slider.addTarget(self, action: #selector(DiscreteSlider.sliderValueChanged(_:)),
                     for: .valueChanged)
  }
  
  override var intrinsicContentSize: CGSize {
    var height =  nameLabel.intrinsicContentSize.height + Constants.vertPad
    height += slider.intrinsicContentSize.height
    height += textHeight
    
    guard let font = getAdaptiveFont(bold: true) else {
      return CGSize(width: 100.0, height: height)
    }
    

        nsText.draw(in: trf.rect, withAttributes: [NSFontAttributeName : trf.font])
      }
    }
  }
  
  fileprivate func drawTrackLine() {
    trackLine.frame = trackRect
    trackLine.cornerRadius = trackRect.height / 2.0
    
    maxLine.frame = {
      var frame = trackLine.bounds
      frame.size.width = thumbX - trackRect.minX
      return frame
    }()
    
    maxLine.backgroundColor = maximumTrackTintColor.cgColor
    
    minLine.frame = {
      var frame = trackLine.bounds
      frame.size.width = trackRect.width - maxLine.frame.width
      frame.origin.x = maxLine.frame.maxX
      return frame
    }()
    
    minLine.backgroundColor = minimumTrackTintColor.cgColor
  }
  
  var selectorSize : CGSize {
    let dim = selectorRadius * 2.0 + shadowRadius * 2.0 + shadowOffset.width * 2
    return CGSize(width: dim, height: dim)
  }
  
  fileprivate func drawThumb() {
      let dim = selectorSize.width
      let rectangle = CGRect(x:thumbX, y: (frame.height - dim)/2, width: dim,
        height: dim)
      
      var selectorFrame = rectangle.insetBy(dx: shadowRadius + shadowOffset.width,
        dy: shadowRadius + shadowOffset.width)
      
      selectorFrame.origin.x = thumbX
      
      selector.frame = selectorFrame
      
      selector.backgroundColor = UIColor.white.cgColor
      
      let strokeColor = UIColor(red: 197.0 / 255.0, green: 197.0 / 255.0,
        blue: 197.0 / 255.0, alpha: 1.0)
      
      selector.borderColor = strokeColor.cgColor
      selector.borderWidth = 0.05
      selector.cornerRadius = selector.frame.width / 2.0
      selector.allowsEdgeAntialiasing = true
      
      // Shadow
      selector.shadowOffset = shadowOffset
      
      selector.shadowRadius = shadowRadius
      let shadowColor = UIColor(red: 197.0 / 255.0, green: 197.0 / 255.0,
        blue: 197.0 / 255.0, alpha: 1.0)
      selector.shadowColor = shadowColor.cgColor
      selector.shadowOpacity = 1.0
   }
  
  func moveThumbTo(x:CGFloat, animationDuration duration:TimeInterval,
    discrete : Bool = false) {
    
    let leftMost = CGFloat(0.0)
    let rightMost = frame.maxX
    
    let newDiscreteValue = showDiscretion(value: x)

    thumbX = max(leftMost, min(x, rightMost))
    
    if newDiscreteValue != discreteValue {
      discreteValue = newDiscreteValue
    }
    
    if discrete {
      assert(ballRects.count > newDiscreteValue)
      let newThumbCenterX = ballRects[discreteValue].midX
      thumbX = newThumbCenterX - selectorRadius
    }
   
    
    CATransaction.setAnimationDuration(duration)
    
    setNeedsDisplay()
  }
  
  fileprivate func paintText(text : String, textRectFont trf : TextRectFont, font: UIFont? = nil) {
