[英]Add ticker to snapping UISlider
我在創建自定義 UISlider 時遇到了一些問題,該 UISlider 在某些值處捕捉,並且在這些值處也有刻度線。 我有捕捉部分工作,但刻度線是一個問題。 滑塊在 12 個位置卡入,從 15-70 中每 5 個卡入。 我嘗試了多種方法,包括將 12 個子視圖添加到堆棧視圖並將其放置在滑塊上,以及計算每個子視圖之間的步長。 兩種方法都將刻度放在拇指卡住的位置。 有誰知道如何正確地做到這一點?
這是第二種方法:
let stepCount = 12
print("THE WIDTH: \(self.bounds.width)")
guard let imageWidth = self.currentThumbImage?.size.width else {return}
guard let imageWidth = self.currentThumbImage?.size.width else {return}
let stepWidth = bounds.width / CGFloat(stepCount)
for i in 0..<stepCount {
let view = UIView(frame: CGRect(x: stepWidth / 2 + stepWidth * CGFloat(i) - imageWidth / 2, y: 0, width: 1, height: 29))
view.backgroundColor = .lightGray
self.insertSubview(view, at: 0)
}
這是捕捉代碼:
@objc func valueChanged(_ sender: UISlider){
let step: Float = 5
let roundedValue = round(sender.value / step) * step
self.value = roundedValue
delegate?.sliderChanged(Int(roundedValue), sender)
}
您需要處理寬度的各種問題。
首先,看看這三個“默認” UISlider
控件。 拇指垂直偏移以便我們可以看到軌道,紅色虛線輪廓是滑塊框架:
正如我們所看到的,拇指的水平中心是不是在跟蹤矩形的末端(邊界)。
如果我們希望刻度線與拇指的水平中心對齊,我們需要根據拇指中心的原點和寬度在最小值和最大值處計算 x 位置:
下一個問題是您可能不希望軌道矩形/圖像延伸到刻度線的左側/右側。
在這種情況下,我們需要清除內置的軌道圖像並繪制我們自己的:
以下是一些您可以嘗試使用的示例代碼:
protocol TickerSliderDelegate: NSObject {
func sliderChanged(_ newValue: Int, sender: Any)
}
extension TickerSliderDelegate {
// make this delegate func optional
func sliderChanged(_ newValue: Int, sender: Any) {}
}
class TickerSlider: UISlider {
var delegate: TickerSliderDelegate?
var stepCount = 12
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
// clear min and max track images
// because we'll be drawing our own
setMinimumTrackImage(UIImage(), for: [])
setMaximumTrackImage(UIImage(), for: [])
}
override func draw(_ rect: CGRect) {
super.draw(rect)
// get the track rect
let trackR: CGRect = self.trackRect(forBounds: bounds)
// get the thumb rect at min and max values
let minThumbR: CGRect = self.thumbRect(forBounds: bounds, trackRect: trackR, value: minimumValue)
let maxThumbR: CGRect = self.thumbRect(forBounds: bounds, trackRect: trackR, value: maximumValue)
// usable width is center of thumb to center of thumb at min and max values
let usableWidth: CGFloat = maxThumbR.midX - minThumbR.midX
// Tick Height (or use desired explicit height)
let tickHeight: CGFloat = bounds.height
// "gap" between tick marks
let stepWidth: CGFloat = usableWidth / CGFloat(stepCount)
// a reusable path
var pth: UIBezierPath!
// a reusable point
var pt: CGPoint!
// new path
pth = UIBezierPath()
// left end of our track rect
pt = CGPoint(x: minThumbR.midX, y: bounds.height * 0.5)
// top of vertical tick lines
pt.y = (bounds.height - tickHeight) * 0.5
// we have to draw stepCount + 1 lines
// so use
// 0...stepCount
// not
// 0..<stepCount
for _ in 0...stepCount {
pth.move(to: pt)
pth.addLine(to: CGPoint(x: pt.x, y: pt.y + tickHeight))
pt.x += stepWidth
}
UIColor.lightGray.setStroke()
pth.stroke()
// new path
pth = UIBezierPath()
// left end of our track lines
pt = CGPoint(x: minThumbR.midX, y: bounds.height * 0.5)
// move to left end
pth.move(to: pt)
// draw the "right-side" of the track first
// it will be the full width of "our track"
pth.addLine(to: CGPoint(x: pt.x + usableWidth, y: pt.y))
pth.lineWidth = 3
UIColor.lightGray.setStroke()
pth.stroke()
// new path
pth = UIBezierPath()
// move to left end
pth.move(to: pt)
// draw the "left-side" of the track on top of the "right-side"
// at percentage width
let rng: Float = maximumValue - minimumValue
let val: Float = value - minimumValue
let pct: Float = val / rng
pth.addLine(to: CGPoint(x: pt.x + (usableWidth * CGFloat(pct)), y: pt.y))
pth.lineWidth = 3
UIColor.systemBlue.setStroke()
pth.stroke()
}
override func setValue(_ value: Float, animated: Bool) {
// don't allow value outside range of min and max values
let newVal: Float = min(max(minimumValue, value), maximumValue)
super.setValue(newVal, animated: animated)
// we need to trigger draw() when the value changes
setNeedsDisplay()
let steps: Float = Float(stepCount)
let rng: Float = maximumValue - minimumValue
// get the percentage along the track
let pct: Float = newVal / rng
// use that pct to get the rounded step position
let pos: Float = round(steps * pct)
// tell the delegate which Tick the thumb snapped to
delegate?.sliderChanged(Int(pos), sender: self)
}
override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
super.endTracking(touch, with: event)
let steps: Float = Float(stepCount)
let rng: Float = maximumValue - minimumValue
// get the percentage along the track
let pct: Float = value / rng
// use that pct to get the rounded step position
let pos: Float = round(steps * pct)
// use that pos to calculate the new percentage
let newPct: Float = (pos / steps)
let newVal: Float = minimumValue + (rng * newPct)
self.value = newVal
}
override var bounds: CGRect {
willSet {
// we need to trigger draw() when the bounds changes
setNeedsDisplay()
}
}
}
注意:這只是此任務的一種方法(僅示例代碼)。 搜索會發現您可能想要查看的許多其他方法/示例/等。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.