UISlider не понятное поведение

День добрый, когда бегунок слайдера идет по progresBar в начале не идет закрашивание области за ним, после прохождения определенного этапа оно закрашивается. Такое же поведение и при приближении к концу, финальная область закрашивается сразу и после этого бегунок до нее доходит. Подскажите как решить проблему окраски начальной области

Код для слайдера

class CustomPlayerSlider: UISlider {
    @IBInspectable var trackHeight: CGFloat = 25

    @IBInspectable var thumbRadius: CGFloat = 0

    private lazy var thumbView: UIView = {
        let thumb = UIView()
        thumb.backgroundColor = .yellow//thumbTintColor
        thumb.layer.borderWidth = 0.4
        thumb.layer.borderColor = UIColor.darkGray.cgColor
        return thumb
    }()

    override func awakeFromNib() {
        super.awakeFromNib()
        let thumb = thumbImage(radius: thumbRadius)
        setThumbImage(thumb, for: .normal)
    }

    private func thumbImage(radius: CGFloat) -> UIImage {
        thumbView.frame = CGRect(x: 0, y: radius / 2, width: radius, height: radius)
        thumbView.layer.cornerRadius = radius / 2

        let renderer = UIGraphicsImageRenderer(bounds: thumbView.bounds)
        return renderer.image { rendererContext in
            thumbView.layer.render(in: rendererContext.cgContext)
        }
    }

    override func trackRect(forBounds bounds: CGRect) -> CGRect {
        let originalRect = super.trackRect(forBounds: bounds)
                return CGRect(origin: CGPoint(x: originalRect.origin.x, y: originalRect.origin.y - (trackHeight / 2)), size: CGSize(width: bounds.width - 10, height: trackHeight))
    }
}

объявление слайдера в контролере

lazy var progressSlider: CustomPlayerSlider = {
        let slider = CustomPlayerSlider()
        slider.minimumTrackTintColor = UIColor.rgbColorFor(hex: "#719FB0")
        slider.maximumTrackTintColor = .white
        slider.value = 0.0
        slider.isContinuous = true
        let image  = UIImage(named: "thumbs")
        slider.setThumbImage(image, for: .normal)
        //slider.setThumbImage(nil, for: .disabled)
        return slider
    }()

Движение слайдера в зависимости от времени плеера

self.playRecord(index: indexPath.row) { (time, isPlaying) in
        DispatchQueue.main.async {
            cell.progressSlider.value = Float(time)
      }
   }

Пустая область за thumbs

Окраска области


Ответы (1 шт):

Автор решения: schmidt9

Подтверждаю такое странное поведение слайдера при использовании свойств minimumTrackTintColor и maximumTrackTintColor и одновременном переопределении метода trackRect(forBounds:).

Например вот что выходит при использовании

    maximumTrackTintColor = .red
    minimumTrackTintColor = .green

    // ...

    override func trackRect(forBounds bounds: CGRect) -> CGRect {
        let originalRect = super.trackRect(forBounds: bounds)
        // woraround for left slider edge clipping
        let clippingAdjustment: CGFloat = 3.0
        
        return CGRect(origin: CGPoint(x: originalRect.origin.x - clippingAdjustment, y: originalRect.origin.y - thumbRadius / 2 ), size: CGSize(width: bounds.width, height: trackHeight))
    }

введите сюда описание изображения

Решение

использовать кастомные изображения для фона слайдера, полный пример:

class CustomPlayerSlider: UISlider {
    @IBInspectable var trackHeight: CGFloat = 25
    
    @IBInspectable var thumbRadius: CGFloat = 25 // FIXME: actially it's diameter, rename
    
    private lazy var thumbView: UIView = {
        let thumb = UIView()
        thumb.backgroundColor = .yellow
        thumb.layer.borderWidth = 0.4
        thumb.layer.borderColor = UIColor.darkGray.cgColor
        return thumb
    }()
    
    private lazy var minimumTrackView: UIView = {
        let thumb = UIView()
        thumb.backgroundColor = .red
        return thumb
    }()
    
    private lazy var maximumTrackView: UIView = {
        let thumb = UIView()
        thumb.backgroundColor = .green
        return thumb
    }()
    
    override func awakeFromNib() {
        super.awakeFromNib()
        let thumb = thumbImage(radius: thumbRadius)
        setThumbImage(thumb, for: .normal)
        // set custom images
        setMinimumTrackImage(minimumTrackImage(size: CGSize(width: thumbRadius, height: thumbRadius)), for: .normal)
        setMaximumTrackImage(maximumTrackImage(size: CGSize(width: thumbRadius, height: thumbRadius)), for: .normal)
        // add rounding
        layer.cornerRadius = thumbRadius / 2
        layer.masksToBounds = true
    }
    
    private func thumbImage(radius: CGFloat) -> UIImage {
        thumbView.frame = CGRect(x: 0, y: radius / 2, width: radius, height: radius)
        thumbView.layer.cornerRadius = radius / 2
        
        let renderer = UIGraphicsImageRenderer(bounds: thumbView.bounds)
        return renderer.image { rendererContext in
            thumbView.layer.render(in: rendererContext.cgContext)
        }
    }
    
    private func minimumTrackImage(size: CGSize) -> UIImage {
        minimumTrackView.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)

        let renderer = UIGraphicsImageRenderer(bounds: minimumTrackView.bounds)
        return renderer.image { rendererContext in
            minimumTrackView.layer.render(in: rendererContext.cgContext)
        }
    }
    
    private func maximumTrackImage(size: CGSize) -> UIImage {
        maximumTrackView.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)

        let renderer = UIGraphicsImageRenderer(bounds: maximumTrackView.bounds)
        return renderer.image { rendererContext in
            maximumTrackView.layer.render(in: rendererContext.cgContext)
        }
    }
    
    override func trackRect(forBounds bounds: CGRect) -> CGRect {
        let originalRect = super.trackRect(forBounds: bounds)
        // workaround for left slider edge clipping
        let clippingAdjustment: CGFloat = 3.0
        
        return CGRect(origin: CGPoint(x: originalRect.origin.x - clippingAdjustment, y: originalRect.origin.y), size: CGSize(width: bounds.width, height: trackHeight))
    }
}

введите сюда описание изображения

Дополнение

Обрезание "хваталки" слайдера (или как ее еще обозвать?) в крайних положениях происходит скорее всего из-за того, что она выравнивается по центру относительно точки перемещения, исправить это можно, ограничивая его крайние значения, которые здесь я взял приблизительно, посмотрев на вывод значений при перемещении, при желании их вероятно можно вычислять отталкиваясь от размера слайдера и хваталки

override func thumbRect(forBounds bounds: CGRect, trackRect rect: CGRect, value: Float) -> CGRect {
        print(value)
        // TODO: calculate this values instead of hardcoding
        let minValue: Float = 0.008
        let maxValue: Float = 0.9925634
        
        var value = value
        
        if value < minValue {
            value = minValue
        } else if value > maxValue {
            value = maxValue
        }
        
        return super.thumbRect(forBounds: bounds, trackRect: rect, value: value)
    }
→ Ссылка