首页 > 解决方案 > 创建一个高度增加的图层 - 快速

问题描述

我想制作一个高度增加的自定义滑块,即它的高度从 4.0 开始到 6.0。

在此处输入图像描述

我已经编写了用于创建图层的代码,但我找不到以这种方式增加其高度的方法。这是我的代码:

let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
    ctx.addPath(path.cgPath)

    ctx.setFillColor(UIColor.red.cgColor)
    ctx.fillPath()

    let lowerValuePosition = slider.positionForValue(slider.lowerValue)
    let upperValuePosition = slider.positionForValue(slider.upperValue)
    let rect = CGRect(x: 0, y: 0,
                      width: (bounds.width - 36),
                      height: bounds.height)
    ctx.fill(rect)

标签: iosswiftcalayer

解决方案


由于最小和最大(左侧和右侧)“跟踪”图像会拉伸,因此您可能无法使用默认UISlider.

不过,绕过它并不太难。

基本上:

  • 使用您的“圆形楔形”形状创建自定义视图
  • UISlider在该自定义视图上覆盖 a
  • 当滑块值改变时“填充”形状的百分比

这是在覆盖它们之前的想法:

在此处输入图像描述

当我们想要在楔形块上覆盖滑块时,将滑块 Min/Max track images 设置为 clear,它看起来像这样:

在此处输入图像描述

我们可以使用一个小技巧来处理按百分比“填充”形状:

  • 使用渐变背景层
  • 用形状掩盖它
  • 将渐变颜色设置为red, red, gray, gray
  • 将颜色位置设置为[0.0, pct, pct, 1.0]

这样我们得到一个干净的边缘,而不是渐变淡入淡出。

这是一个完整的例子——没有@IBOutlet@IBAction连接,所以只需将视图控制器的自定义类设置为WedgeSliderViewController

class RoundedWedgeSliderView: UIView {

    var leftRadius: CGFloat = 4.0
    var rightRadius: CGFloat = 6.0

    // mask shape
    private var cMask = CAShapeLayer()

    var pct: Float = 0.0 {
        didSet {
            let p = pct as NSNumber
            // disable layer built-in animation so the update won't "lag"
            CATransaction.begin()
            CATransaction.setDisableActions(true)
            // update gradient locations
            gradientLayer.locations = [
                0.0, p, p, 1.0
            ]
            CATransaction.commit()
        }
    }

    // allows self.layer to be a CAGradientLayer
    override class var layerClass: AnyClass { return CAGradientLayer.self }
    private var gradientLayer: CAGradientLayer {
        return self.layer as! CAGradientLayer
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        // gradient colors will be
        //  red, red, gray, gray
        let colors = [
            UIColor.red.cgColor,
            UIColor.red.cgColor,
            UIColor(white: 0.9, alpha: 1.0).cgColor,
            UIColor(white: 0.9, alpha: 1.0).cgColor,
        ]
        gradientLayer.colors = colors
        // initial gradient color locations
        gradientLayer.locations = [
            0.0, 0.0, 0.0, 1.0
        ]
        // horizontal gradient
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        let r = bounds
        // define the "Rounded Wedge" shape
        let leftCenter = CGPoint(x: r.minX + leftRadius, y: r.midY)
        let rightCenter = CGPoint(x: r.maxX - rightRadius, y: r.midY)
        let bez = UIBezierPath()
        bez.addArc(withCenter: leftCenter, radius: leftRadius, startAngle: .pi * 0.5, endAngle: .pi * 1.5, clockwise: true)
        bez.addArc(withCenter: rightCenter, radius: rightRadius, startAngle: .pi * 1.5, endAngle: .pi * 0.5, clockwise: true)
        bez.close()
        // set the mask layer's path
        cMask.path = bez.cgPath
        // mask self's layer
        layer.mask = cMask
    }

}

class WedgeSliderViewController: UIViewController {

    let mySliderView = RoundedWedgeSliderView()
    let theSlider = UISlider()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(mySliderView)
        view.addSubview(theSlider)

        mySliderView.translatesAutoresizingMaskIntoConstraints = false
        theSlider.translatesAutoresizingMaskIntoConstraints = false

        // respect safe area
        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([

            // constrain slider 100-pts from top, 40-pts on each side
            theSlider.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0),
            theSlider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            theSlider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),

            // constrain mySliderView width to the slider width minus 16-pts
            //  (so we have 8-pt "padding" on each side for the thumb to cover)
            mySliderView.widthAnchor.constraint(equalTo: theSlider.widthAnchor, constant: -16.0),
            // constrain mySliderView to same height as the slider, centered X & Y
            mySliderView.heightAnchor.constraint(equalTo: theSlider.heightAnchor),
            mySliderView.centerXAnchor.constraint(equalTo: theSlider.centerXAnchor),
            mySliderView.centerYAnchor.constraint(equalTo: theSlider.centerYAnchor),

        ])

        // set left- and right-side "track" images to empty images
        theSlider.setMinimumTrackImage(UIImage(), for: .normal)
        theSlider.setMaximumTrackImage(UIImage(), for: .normal)

        // add target for the slider
        theSlider.addTarget(self, action: #selector(self.sliderValueChanged(_:)), for: .valueChanged)

        // set intitial values
        theSlider.value = 0.0
        mySliderView.pct = 0.0

        // end-radii of mySliderView defaults to 4.0 and 6.0
        //  un-comment next line to see the difference
        //mySliderView.rightRadius = 10.0

    }

    @objc func sliderValueChanged(_ sender: Any) {
        if let s = sender as? UISlider {
            // update mySliderView when the slider changes
            mySliderView.pct = s.value
        }
    }

}

推荐阅读