具有清晰背景的 UISlider 拇指图像显示其后面的轨道

Max*_*Sim 3 objective-c uikit uislider ios

如何设置清晰的拇指背景UISlider以使轨道在拇指后面不可见?

当我使用setThumbImage:forState:并传递部分透明图像时,滑块的最小轨道和最大轨道图像/颜色在拇指图像后面可见:

拇指后面可见轨迹

我希望它看起来像在 iOS 相机应用程序中,其中“透明”拇指图像圆圈剪辑轨道背景颜色:

iOS 相机滑块

all*_*enh 5

长话短说

简而言之,没有简单的 API 可以UISlider重现这种行为。

前提条件:根据问题,我只是演示如何使拇指显示透明而不显示轨道。我并不是试图准确地重新创建相机的滑块。

理想的解决方案

理想的解决方案是创建最小和最大轨道图像,利用上限插入来防止轨道的一部分被填充。我说这是理想的,因为它很简单并且相对性能较高。不幸的是,UISlider有两件事阻止了我们使用这个方法。

  1. 最大轨道图像实际上与整个轨道的宽度相同,但它包含在已clipsToBounds设置为 true 的视图中。最大轨道的超级视图已调整大小并剪辑最大轨道,以确保它在拇指的前侧永远不可见。

    禁用clipsToBounds整个层次结构可以让我们在视图调试器中看到这一点。

在此输入图像描述

  1. 最小轨道的大小UIImageView已调整,但当调用时,setMinimumTrackImageUISlider实际上会创建一个不同的可调整大小的图像。这个问题需要一段时间才能弄清楚,但可以通过检查内存地址来证明。

    在此输入图像描述

替代解决方案:更改图像以匹配值

根据我从初步调查中学到的知识,我们可以随着值的变化生成图像。这不是最有效的方法,因为它需要一遍又一遍地创建许多图像。但是,它符合原始问题的参数。请注意,您只能使用“连续”滑块有效地做到这一点,因为它依赖于值更新。从好的方面来说,这可以很容易地扩展以绘制更有趣的轨道图像(就像相机中的图像)。

这是结果,我把拇指做得很大,这样你就可以很容易地看到它是透明的。

在此输入图像描述

这是课程:

class TransparentThumbSlider: UISlider {

    let thumbWidth: CGFloat = 40

    override init(frame: CGRect) {
        super.init(frame: frame)

        configure()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        configure()
    }

    func configure() {
        setThumbImage(TransparentThumbSlider.thumbImage(diameter: thumbWidth, color: .blue), for: .normal)

        addTarget(self, action: #selector(updateTrackImages), for: .valueChanged)

        updateTrackImages()
    }

    static func thumbImage(diameter: CGFloat, color: UIColor) -> UIImage {
        let strokeWidth: CGFloat = 4
        let halfStrokeWidth = strokeWidth / 2
        let totalRect = CGRect(x: 0, y: 0, width: diameter, height: diameter)
        let strokeRect = CGRect(x: halfStrokeWidth,
                                y: halfStrokeWidth,
                                width: diameter - strokeWidth,
                                height: diameter - strokeWidth)

        let renderer = UIGraphicsImageRenderer(size: totalRect.size)
        let image = renderer.image { context in
            context.cgContext.setStrokeColor(color.cgColor)
            context.cgContext.setLineWidth(strokeWidth)
            context.cgContext.strokeEllipse(in: strokeRect)
        }

        return image
    }

    static func trackImage(colored color: UIColor,
                           thumbWidth: CGFloat,
                           trackWidth: CGFloat,
                           value: CGFloat,
                           flipped: Bool) -> UIImage {

        let fraction = flipped ? 1 - value : value
        let adjustableWidth = (trackWidth - thumbWidth)
        let halfThumbWidth = thumbWidth / 2
        let fudgeFactor: CGFloat = 2

        var totalRect: CGRect

        var coloredRect: CGRect
        if flipped {
            totalRect = CGRect(x: 0, y: 0, width: trackWidth, height: 2)
            coloredRect = CGRect(
                x: adjustableWidth * value + thumbWidth - fudgeFactor,
                y: 0,
                width: adjustableWidth * fraction + (halfThumbWidth - fudgeFactor),
                height: 2)
        } else {
            totalRect = CGRect(x: 0, y: 0, width: adjustableWidth * fraction + halfThumbWidth, height: 2)
            coloredRect = CGRect(x: 0, y: 0, width: adjustableWidth * fraction, height: 2)
        }

        let renderer = UIGraphicsImageRenderer(size: totalRect.size)
        let image = renderer.image { context in
            context.cgContext.setFillColor(color.cgColor)
            context.fill(coloredRect)
        }

        return image
    }

    @objc func updateTrackImages() {

        let trackWidth = trackRect(forBounds: bounds).width

        let minimumImage = TransparentThumbSlider.trackImage(
            colored: .green,
            thumbWidth: thumbWidth,
            trackWidth: trackWidth,
            value: CGFloat(value),
            flipped: false)
        setMinimumTrackImage(minimumImage, for: .normal)

        let maximumImage = TransparentThumbSlider.trackImage(
            colored: .yellow,
            thumbWidth: thumbWidth,
            trackWidth: trackWidth,
            value: CGFloat(value),
            flipped: true)
        setMaximumTrackImage(maximumImage, for: .normal)
    }

    var previousBounds: CGRect = .zero

    override func layoutSubviews() {
        super.layoutSubviews()

        // Prevent layout loop
        if previousBounds != bounds {
            previousBounds = bounds
            updateTrackImages()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我想说的是(a)你在这件事上付出了额外的努力,(b)你应该得到赏金。 (2认同)