Search code examples
swiftuikitcore-graphicsuislider

How to make CustomSlider with Curved Line around Thumb?


I am trying to achieve this kind of slider to draw a line around the thumb in Swift an UIKit. This is what I am trying to achieve. Anyone can guide how can I achieve this or any CocoaPods I can use?

This is what I have achieved so far.

This is my code I have written to draw the curve:

private func drawCurvedLine() {
        
    guard let context = UIGraphicsGetCurrentContext() else { return }
        
    let trackRect = self.trackRect(forBounds: bounds)
    let thumbRect = self.thumbRect(forBounds: bounds, trackRect: trackRect, value: value)
        
    let startX = trackRect.minX - 5
    let endX = trackRect.maxX + 5
    let thumbCenterX = thumbRect.midX
    let thumbCenterY = thumbRect.minY + 10 // Start of the thumb
    let curveHeight: CGFloat = 20
    let radius: CGFloat = 20 // Radius for the curve around the thumb
        
    context.setStrokeColor(UIColor.black.cgColor)
    context.setLineWidth(1.5)
    // context.setLineCap(.round)
        
    // Line before the curve
    context.move(to: CGPoint(x: startX, y: thumbCenterY))
    context.addLine(to: CGPoint(x: thumbCenterX - radius - 5, y: thumbCenterY))
        
    // Curved line above the thumb
    context.addCurve(to: CGPoint(x: thumbCenterX + radius + 5, y: thumbCenterY),
                         control1: CGPoint(x: thumbCenterX - radius, y: thumbCenterY - curveHeight),
                         control2: CGPoint(x: thumbCenterX + radius, y: thumbCenterY - curveHeight))
        
    // Line after the curve
    context.addLine(to: CGPoint(x: endX, y: thumbCenterY))
    context.strokePath()
}

Solution

  • First imagine 4 line segments formed by 5 points, A to E, like this:

    enter image description here

    If you draw arcs that are tangent to each pair of these lines, you'd get something very similar to the desired result.

    You can draw arcs like that with addArc(tangent1End:tangent2End:radius:). Just pass in the pairs of points, each pair at a time, i.e. B, C, C, D, D, E.

    Assuming rect is the CGRect in which we are drawing,

    guard let context = UIGraphicsGetCurrentContext() else { return }
    context.setStrokeColor(UIColor.black.cgColor)
    context.setLineWidth(1.5)
    
    // Try playing around with the parameters below :)
    
    // this is the vertical distance between point C and the line BD
    let curveHeight = 35.0
    
    // this is the lenght of the line BD
    let triangleWidth = 100.0
    
    // radius of the arcs
    let radius = 40.0
    
    // the y coordinate of the points A, B, D, E
    let y = rect.midY
    
    // the slider's position along the x axis as a percentage
    let percentage = 0.7
    
    let pointA = CGPoint(x: rect.minX, y: y)
    let pointB = CGPoint(x: (rect.width - triangleWidth) * percentage, y: y)
    let pointC = CGPoint(x: (rect.width - triangleWidth) * percentage + triangleWidth / 2, y: y - curveHeight)
    let pointD = CGPoint(x: (rect.width - triangleWidth) * percentage + triangleWidth, y: y)
    let pointE = CGPoint(x: rect.maxX, y: y)
    
    context.move(to: pointA)
    context.addArc(tangent1End: pointB, tangent2End: pointC, radius: radius)
    context.addArc(tangent1End: pointC, tangent2End: pointD, radius: radius)
    context.addArc(tangent1End: pointD, tangent2End: pointE, radius: radius)
    context.addLine(to: pointE)
    context.strokePath()
    

    The output looks like this:

    enter image description here