Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UISlider with thumb center over the start and end of track

The default behaviour of UISlider is that its thumb isn't centered on start/end of track. Like below:

default UISlider

I would like to modify it's behaviour to get:

desired slider

where thumb's center can be positioned on start or end.

I have tried to cover start/end with empty UIView. Effect is that is look almost ok, but thumb has dropshadow that reveals my hack in some positions (which I can live with), however my hacky view covers a little the thumb and captures it's touch event (even when user interactions are disabled).

So I guess I would need to pack my hacky view between thumb and track. Possibly without manipulating its internal views. Any ideas?

like image 691
Fishman Avatar asked Nov 09 '16 08:11

Fishman


2 Answers

Swift 5 version for lazy ones like me :)

class CustomSlider: UISlider {

    private let trackHeight: CGFloat = 8

    override func trackRect(forBounds bounds: CGRect) -> CGRect {
        let point = CGPoint(x: bounds.minX, y: bounds.midY)
        return CGRect(origin: point, size: CGSize(width: bounds.width, height: trackHeight))
    }

    private let thumbWidth: Float = 52
    lazy var startingOffset: Float = 0 - (thumbWidth / 2)
    lazy var endingOffset: Float = thumbWidth

    override func thumbRect(forBounds bounds: CGRect, trackRect rect: CGRect, value: Float) -> CGRect {
        let xTranslation =  startingOffset + (minimumValue + endingOffset) / maximumValue * value
        return super.thumbRect(forBounds: bounds, trackRect: rect.applying(CGAffineTransform(translationX: CGFloat(xTranslation), y: 0)), value: value)
    }
}
like image 88
ergunkocak Avatar answered Nov 17 '22 00:11

ergunkocak


UISlider class has a method thumbRect(forBounds:trackRect:value:) that you can override to control the drawing rectangle for the slider’s thumb image. So assuming that this space is 5 points (might vary depending on the thumb image you are using) we can do the following:

  1. Subclass UISlider and override thumbRect(forBounds:trackRect:value:) to hook ourselves into the process
  2. Define the thumbSpace (I assumed it is 5), and from the thumbSpace we can calculate starting offset (5 points before the slider) and ending offset (5 points after the slider).
  3. Calculate the thumb translation in relation to current value, minValue and maxValue (In other words, based on the current value, we are shifting the thumb a little to cover the spaces we don't want to show)
  4. Return a shifted rectangle (after applying the x translation) to the super method to do the regular calculations

The complete code should be like the following:

class CustomSlider: UISlider {
    let defaultThumbSpace: Float = 5
    lazy var startingOffset: Float = 0 - defaultThumbSpace
    lazy var endingOffset: Float = 2 * defaultThumbSpace

    override func thumbRect(forBounds bounds: CGRect, trackRect rect: CGRect, value: Float) -> CGRect {
        let xTranslation =  startingOffset + (minimumValue + endingOffset) / maximumValue * value
        return super.thumbRect(forBounds: bounds,
                               trackRect: rect.applying(CGAffineTransform(translationX: CGFloat(xTranslation),
                                                                          y: 0)),
                               value: value)
    }
}
like image 34
Mostafa Abdellateef Avatar answered Nov 17 '22 01:11

Mostafa Abdellateef