调整 RangeSlider 的最小值、最大值、下限值和上限值?

Adjusting RangeSlider minimum, maximum, lower and upper values?

我按照 Raywenderlich 网站 (https://www.raywenderlich.com/7595-how-to-make-a-custom-control-tutorial-a-reusable-slider) 上的教程创建了自定义范围滑块。我执行的步骤如下:

(1) 创建了一个 swift 文件并向其中添加了以下代码:

import UIKit

class RangeSlider: UIControl {

    override var frame: CGRect {

        didSet {

            updateLayerFrames()

        }

    }

    private var previousLocation = CGPoint()

    var minimumValue: CGFloat = 0 {

        didSet {

            updateLayerFrames()

        }

    }

    var maximumValue: CGFloat = 1 {

        didSet {

            updateLayerFrames()

        }

    }

    var lowerValue: CGFloat = 0.2 {

        didSet {

            updateLayerFrames()

        }

    }

    var upperValue: CGFloat = 0.8 {

        didSet {

            updateLayerFrames()

        }

    }

    var trackTintColor = UIColor(white: 0.9, alpha: 1) {

        didSet {

            trackLayer.setNeedsDisplay()

        }

    }

    var trackHighlightTintColor = UIColor(red: 0, green: 0.45, blue: 0.94, alpha: 1) {

        didSet {

            trackLayer.setNeedsDisplay()

        }

    }

    var thumbImage = #imageLiteral(resourceName: "Oval") {

        didSet {

            upperThumbImageView.image = thumbImage

            lowerThumbImageView.image = thumbImage

            updateLayerFrames()

        }

    }

    var highlightedThumbImage = #imageLiteral(resourceName: "HighlightedOval") {

        didSet {

            upperThumbImageView.highlightedImage = highlightedThumbImage

            lowerThumbImageView.highlightedImage = highlightedThumbImage

            updateLayerFrames()

        }

    }

    private let trackLayer = RangeSliderTrackLayer()

    private let lowerThumbImageView = UIImageView()

    private let upperThumbImageView = UIImageView()

    override init(frame: CGRect) {

        super.init(frame: frame)

        trackLayer.rangeSlider = self

        trackLayer.contentsScale = UIScreen.main.scale

        layer.addSublayer(trackLayer)

        lowerThumbImageView.image = thumbImage

        addSubview(lowerThumbImageView)

        upperThumbImageView.image = thumbImage

        addSubview(upperThumbImageView)

    }

    required init?(coder aDecoder: NSCoder) {

        fatalError("init(coder:) has not been implemented")

    }

    private func updateLayerFrames() {

        CATransaction.begin()

        CATransaction.setDisableActions(true)

        trackLayer.frame = bounds.insetBy(dx: 0.0, dy: bounds.height / 3)

        trackLayer.setNeedsDisplay()

        lowerThumbImageView.frame = CGRect(origin: thumbOriginForValue(lowerValue),size: thumbImage.size)

        upperThumbImageView.frame = CGRect(origin: thumbOriginForValue(upperValue),size: thumbImage.size)

        CATransaction.commit()

    }

    func positionForValue(_ value: CGFloat) -> CGFloat {

        return bounds.width * value

    }

    private func thumbOriginForValue(_ value: CGFloat) -> CGPoint {

        let x = positionForValue(value) - thumbImage.size.width / 2.0

        return CGPoint(x: x, y: (bounds.height - thumbImage.size.height) / 2.0)

    }

}

extension RangeSlider {

    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {

        previousLocation = touch.location(in: self)

        if lowerThumbImageView.frame.contains(previousLocation) {

            lowerThumbImageView.isHighlighted = true

        } else if upperThumbImageView.frame.contains(previousLocation) {

            upperThumbImageView.isHighlighted = true

        }

        return lowerThumbImageView.isHighlighted || upperThumbImageView.isHighlighted

    }

    override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {

        let location = touch.location(in: self)

        let deltaLocation = location.x - previousLocation.x

        let deltaValue = (maximumValue - minimumValue) * deltaLocation / bounds.width

        previousLocation = location

        if lowerThumbImageView.isHighlighted {

            lowerValue += deltaValue

            lowerValue = boundValue(lowerValue, toLowerValue: minimumValue, upperValue: upperValue)

        } else if upperThumbImageView.isHighlighted {

            upperValue += deltaValue

            upperValue = boundValue(upperValue, toLowerValue: lowerValue, upperValue: maximumValue)

        }

        sendActions(for: .valueChanged)

        return true

    }

    private func boundValue(_ value: CGFloat, toLowerValue lowerValue: CGFloat, upperValue: CGFloat) -> CGFloat {

        return min(max(value, lowerValue), upperValue)

    }

    override func endTracking(_ touch: UITouch?, with event: UIEvent?) {

        lowerThumbImageView.isHighlighted = false

        upperThumbImageView.isHighlighted = false

    }

}

(2) 然后,我在我的项目中创建了另一个 Swift 文件,并按照上述教程在其中添加了以下代码:

import UIKit

class RangeSliderTrackLayer: CALayer {

    weak var rangeSlider: RangeSlider?

    override func draw(in ctx: CGContext) {

        guard let slider = rangeSlider else {

            return

        }

        let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)

        ctx.addPath(path.cgPath)

        ctx.setFillColor(slider.trackTintColor.cgColor)

        ctx.fillPath()

        ctx.setFillColor(slider.trackHighlightTintColor.cgColor)

        let lowerValuePosition = slider.positionForValue(slider.lowerValue)

        let upperValuePosition = slider.positionForValue(slider.upperValue)

        let rect = CGRect(x: lowerValuePosition, y: 0, width: upperValuePosition - lowerValuePosition, height: bounds.height)

        ctx.fill(rect)

    }

}

(3) 最后一步是我在 ViewController 中创建了自定义范围滑块 class 的实例 我有兴趣在其中实现 RangeSlider:

import UIKit

class FilterDataVC: UIViewController {

    let rangeSlider = RangeSlider(frame: .zero)

    override func viewDidLoad() {

        super.viewDidLoad()

        view.addSubview(rangeSlider)

        rangeSlider.addTarget(self, action: #selector(rangeSliderValueChanged(_:)), for: .valueChanged)

    }

    override func viewDidLayoutSubviews() {

        let margin: CGFloat = 20

        let width = view.bounds.width - 2 * margin

        let height: CGFloat = 30

        rangeSlider.frame = CGRect(x: 0, y: 0, width: width, height: height)

        rangeSlider.center = view.center

    }

    @objc func rangeSliderValueChanged(_ rangeSlider: RangeSlider) {

        let values = "(\(rangeSlider.lowerValue) \(rangeSlider.upperValue))"

}

到目前为止,一切都完美无缺,我获得了与教程相同的结果。但是,显然我需要为我自己的 App 自定义 RangeSlider。因此,我尝试通过将上面步骤 01 中所述的代码部分更改为如下所示的代码来更改滑块的最小值、最大值、下部值和上部值:

var minimumValue: CGFloat = 100 {

            didSet {

                updateLayerFrames()

            }

        }

        var maximumValue: CGFloat = 1200 {

            didSet {

                updateLayerFrames()

            }

        }

        var lowerValue: CGFloat = 200 {

            didSet {

                updateLayerFrames()

            }

        }

        var upperValue: CGFloat = 800 {

            didSet {

                updateLayerFrames()

            }

        }

但是,在进行了上述修改之后,运行我的App使用了模拟器。 RangeSlider 只显示轨道,我看不到拇指。知道哪里出了问题吗?

我通过调整上面第 01 步中所述的部分代码,设法解决了我的问题,方法如下:

func positionForValue(_ value: CGFloat) -> CGFloat { 

return (bounds.width * value) / (maximumValue - minimumValue) 

}