创建一个高度增加的图层 - swift

Create a layer with increasing height - swift

我想制作一个高度增加的自定义滑块,即它的高度从 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)

您必须像下面那样在子类中重写超类 UISlidertrackRect 方法,并且 return 您需要的边界和大小:

/**
 UISlider Subclass
 */
class CustomSlider: UISlider {

    override func trackRect(forBounds bounds: CGRect) -> CGRect {
        return CGRect(origin: #oigin#, size: CGSize(width: #width#, height: #height#))
    }
}

由于最小值和最大值(左侧和右侧)"Track" 图像拉伸,您可能无法使用默认值 UISlider.

虽然绕过它并不难。

基本上:

  • 使用您的 "rounded wedge" 形状创建自定义视图
  • 在该自定义视图上叠加 UISlider
  • "fill"滑块值变化时形状的百分比

这是想法, 覆盖它们之前:

当我们要在楔子上叠加滑块时,将滑块Min/Max轨道图像设置为清晰,它看起来像这样:

我们可以使用一个小技巧来按百分比处理 "filling" 形状:

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

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

这是一个完整的示例——没有 @IBOutlet@IBAction 连接,因此只需将视图控制器的自定义 class 设置为 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
        }
    }

}