修复 UISlider swift 上的两种颜色

Posted

技术标签:

【中文标题】修复 UISlider swift 上的两种颜色【英文标题】:Fix two colours on UISlider swift 【发布时间】:2021-03-18 08:46:24 【问题描述】:

Slider with two different colors

我们如何制作具有两种固定颜色的滑块?即使滑块移动,颜色也不会改变。此外,滑块拇指应该能够超过这两种颜色中的任何一种。我应该能够定义第一部分的长度。

    func createSlider(slider:UISlider) 
    let frame = CGRect(x: 0.0, y: 0.0, width: slider.bounds.width, height: 4)
    let tgl = CAGradientLayer()
    tgl.frame = frame
    tgl.colors = [UIColor.gray.cgColor,UIColor.red.cgColor]
    tgl.endPoint = CGPoint(x: 0.4, y:  1.0)
    tgl.startPoint = CGPoint(x: 0.0, y:  1.0)
    UIGraphicsBeginImageContextWithOptions(tgl.frame.size, false, 0.0)
    tgl.render(in: UIGraphicsGetCurrentContext()!)
    
    let backgroundImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    slider.setMaximumTrackImage(backgroundImage?.resizableImage(withCapInsets:.zero),  for: .normal)
    slider.setMinimumTrackImage(backgroundImage?.resizableImage(withCapInsets:.zero),  for: .normal)

我已经试过了。但这确实给了我想要实现的目标。

【问题讨论】:

您好,欢迎 Xeb。 - 你试过什么了?这似乎是一个有趣的问题。如果您可以显示到目前为止的代码以及哪些代码不起作用,这将有助于人们为您提供帮助。 如果您只想要颜色,为什么要设置轨道图像?你试过minimumTrackTintColor/maximumTrackTintColor 是的,已经尝试过了,它不适合我想要在这里实现的目标。我想制作一个有两种固定颜色的滑块。即使滑块移动,颜色也不应该改变。另外,我应该能够定义第一部分的长度。见附图。 “我已经尝试过这个。但是这个doseent给了我我想要达到的目标。”有什么区别?您当前的代码有什么问题?您的问题是否与 backgroundImage 无关? 见附图 【参考方案1】:

这是一种方法...

将最小和最大轨道图像设置为“空”图像 添加左右子视图以充当“假”轨道图像 在每个侧面子视图上添加不同颜色的子图层 当您滑动拇指时,更新图层的框架

这是一些示例代码...您可能需要做一些调整:

class XebSlider: UISlider 
    
    private var colors: [[UIColor]] = [
        [.black, .black],
        [.black, .black],
    ]

    // left and right views will become the "fake" track images
    private let leftView = UIView()
    private let rightView = UIView()
    
    // each view needs a layer to "change color"
    private let leftShape = CALayer()
    private let rightShape = CALayer()
    
    // constraints that will be updated
    private var lvWidth: NSLayoutConstraint!
    private var lvLeading: NSLayoutConstraint!
    private var rvTrailing: NSLayoutConstraint!
    
    // how wide the two "sides" should be
    private var leftPercent: CGFloat = 0
    private var rightPercent: CGFloat = 0

    // track our current width, so we don't repeat constraint updates
    //  unless our width has changed
    private var currentWidth: CGFloat = 0
    
    init(withLeftSidePercent leftWidth: CGFloat, leftColors: [UIColor], rightColors: [UIColor]) 
        super.init(frame: .zero)
        commonInit()
        leftView.backgroundColor = leftColors[1]
        rightView.backgroundColor = rightColors[1]
        leftShape.backgroundColor = leftColors[0].cgColor
        rightShape.backgroundColor = rightColors[0].cgColor
        leftPercent = leftWidth
        rightPercent = 1.0 - leftPercent
    
    override init(frame: CGRect) 
        super.init(frame: frame)
        commonInit()
    
    required init?(coder: NSCoder) 
        super.init(coder: coder)
        commonInit()
    
    func commonInit() -> Void 
        // set both track images to "empty" images
        setMinimumTrackImage(UIImage(), for: [])
        setMaximumTrackImage(UIImage(), for: [])

        // add left and right subviews
        [leftView, rightView].forEach  v in
            v.translatesAutoresizingMaskIntoConstraints = false
            v.layer.cornerRadius = 4.0
            v.layer.masksToBounds = true
            v.isUserInteractionEnabled = false
            insertSubview(v, at: 0)
        
        
        // add sublayers
        leftView.layer.addSublayer(leftShape)
        rightView.layer.addSublayer(rightShape)
        
        // create constraints whose .constant values will be modified in layoutSubviews()
        lvLeading = leftView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0)
        rvTrailing = rightView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0)
        lvWidth = leftView.widthAnchor.constraint(equalToConstant: 0.0)
        
        // avoids auto-layout complaints when the frame changes (such as on device rotation)
        lvWidth.priority = UILayoutPriority(rawValue: 999)
        
        // set constraints for subviews
        NSLayoutConstraint.activate([
            lvLeading,
            rvTrailing,
            lvWidth,
            
            leftView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0.0),
            leftView.heightAnchor.constraint(equalToConstant: 8.0),
            
            rightView.centerYAnchor.constraint(equalTo: leftView.centerYAnchor),
            rightView.heightAnchor.constraint(equalTo: leftView.heightAnchor),
            
            rightView.leadingAnchor.constraint(equalTo: leftView.trailingAnchor, constant: 1.0),
        ])
    
    
    override func layoutSubviews() 
        super.layoutSubviews()

        // we only want to do this if the bounds width has changed
        if bounds.width != currentWidth 
            let trackRect = self.trackRect(forBounds: bounds)
            lvLeading.constant = trackRect.origin.x
            rvTrailing.constant = -(bounds.width - (trackRect.origin.x + trackRect.width))
            lvWidth.constant = trackRect.width * leftPercent
        

        // get percentage of thumb position
        //  based on min and max values
        let pctValue = (self.value - self.minimumValue) / (self.maximumValue - self.minimumValue)
    
        // calculate percentage of each side that needs to be "covered"
        //  by the different color layer
        let leftVal = max(0.0, min(CGFloat(pctValue) / leftPercent, 1.0))
        let rightVal = max(0.0, min((CGFloat(pctValue) - leftPercent) / rightPercent, 1.0))

        var rLeft = leftView.bounds
        var rRight = rightView.bounds
        
        rLeft.size.width = leftView.bounds.width * leftVal
        rRight.size.width = rightView.bounds.width * rightVal
        
        // disable default layer animations
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        leftShape.frame = rLeft
        rightShape.frame = rRight
        CATransaction.commit()
    

以及一个显示其用法的控制器示例:

class ViewController: UIViewController 
    
    var slider: XebSlider!
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        let leftSideColors: [UIColor] = [
            #colorLiteral(red: 0.4796532989, green: 0.4797258377, blue: 0.4796373844, alpha: 1),
            #colorLiteral(red: 0.8382737041, green: 0.8332912326, blue: 0.8421040773, alpha: 1),
        ]
        let rightSideColors: [UIColor] = [
            #colorLiteral(red: 0.9009097219, green: 0.3499996662, blue: 0.4638580084, alpha: 1),
            #colorLiteral(red: 0.9591985345, green: 0.8522816896, blue: 0.8730568886, alpha: 1),
        ]
        let leftSideWidthPercent: CGFloat = 0.5

        slider = XebSlider(withLeftSidePercent: leftSideWidthPercent, leftColors: leftSideColors, rightColors: rightSideColors)
        
        view.addSubview(slider)
        slider.translatesAutoresizingMaskIntoConstraints = false

        // respect safe area
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([

            // constrain slider 40-pts from Top / Leading / Trailing
            slider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            slider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
            slider.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            
        ])
        
    
    
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) 
        super.viewWillTransition(to: size, with: coordinator)
        coordinator.animate(alongsideTransition:  _ in
            // to get teh custom slider to update properly
            self.slider.setNeedsLayout()
        , completion: 
            _ in
        )
    

结果:

【讨论】:

非常感谢@DonMag。它对我来说完全有用......如果我将 minimumValue 和 maximumValue 提供给滑块,那么图层不会改变颜色。你能解释一下它是如何计算的吗? @Xeb - 如果您要为滑块设置最小/最大值,请使用“值作为范围的百分比”,如下所示:let pctValue = (self.value - self.minimumValue) / (self.maximumValue - self.minimumValue) ... 然后改用pctValue self.value 在接下来的计算中。我在答案中编辑了代码来实现它。

以上是关于修复 UISlider swift 上的两种颜色的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 UITapGestureRecognizer 在 UIView 上的两种颜色之间切换

带有渐变层的 iOS UISLider

每次移动 UISlider 时删除注释 - mapView, Swift

在swift4中查找按钮的文本颜色

iPhone 的自定义 UISlider

给 UISlider 进度条圆角 [Swift]