ios - 创建一个高度增加的图层 - 快速
问题描述
我想制作一个高度增加的自定义滑块,即它的高度从 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)
解决方案
由于最小和最大(左侧和右侧)“跟踪”图像会拉伸,因此您可能无法使用默认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
}
}
}
推荐阅读
- angular - 仅当 ngModel 的值为 true 时才选中复选框
- c# - C# Linq-to-SQL:查找最多三个级别的子级
- dart - 带有 int 项目的 DropdownButton 不起作用,它不选择新值
- apache - 将 Docker 容器应用程序与现有的 Apache Web 服务器连接
- java - 为什么分离的实体被视为短暂的?
- android - 发布 APK 不适用于 API 6.0 及更低版本
- django - 在 django oauth 工具包中引发异常
- c++11 - 错误:在将函数指针转发到线程时,没有匹配的构造函数用于初始化“std::thread”问题
- python - 使用 django 发送电子邮件
- android - JsonSyntaxException:com.google.gson.stream.MalformedJsonException:第 1 行的转义序列无效