image.png
1、绘制刻度尺子的layer
class YXScaleLayer: CAShapeLayer {
var maxHeight = 40.0
var minHeight = 20.0
private(set) var dataArr: [DataEntity] = []
private let bezierPath = UIBezierPath()
private(set) var space: CGFloat = 8
override func layoutSublayers() {
super.layoutSublayers()
let y = bounds.height
for item in dataArr {
bezierPath.move(to: CGPoint(x: item.posX, y: y))
let posY = item.isLong ? (y-maxHeight) : (y-minHeight)
bezierPath.addLine(to: CGPoint(x: item.posX, y: posY))
}
self.path = bezierPath.cgPath
}
var longItems: [DataEntity] {
// dataArr.filter { $0.index % 2 == 0 }
dataArr.filter { $0.isLong }
}
}
extension YXScaleLayer {
func setData(_ arr: [Int], space: CGFloat = 20) {
self.space = space
var posX = lineWidth/2
var index = 0
for long in arr {
for _ in 0..<long {
dataArr.append(DataEntity(index: index, posX: posX))
posX += (space+lineWidth)
index += 1
}
dataArr.append(DataEntity(index: index, posX: posX, isLong: true))
posX += (space+lineWidth)
index += 1
}
var rect = frame
rect.size.width = posX - space
frame = rect
}
}
extension YXScaleLayer {
struct DataEntity {
var index: Int
var posX: CGFloat
var isLong = false
}
}
2、指示计及滑动容器逻辑
class YXRulerView: UIView {
private let contentView = UIScrollView()
private let scaleLayer = YXScaleLayer()
private let indicatorLayer = CALayer()
private let titleContentView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
scaleLayer.lineWidth = 2.0
scaleLayer.strokeColor = UIColor.lightGray.cgColor
contentView.layer.addSublayer(scaleLayer)
contentView.showsHorizontalScrollIndicator = false
contentView.delegate = self
contentView.decelerationRate = .init(rawValue: 0.01) //降低手指离开屏幕后scrollView的滚动速度
addSubview(contentView)
indicatorLayer.backgroundColor = UIColor.red.cgColor
layer.addSublayer(indicatorLayer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = bounds
let contentW = scaleLayer.frame.width
scaleLayer.frame = CGRect(x: 0, y: 0, width: contentW, height: 60)
contentView.contentSize = .init(width: contentW, height: bounds.height)
contentView.contentInset = UIEdgeInsets(top: 0, left: bounds.width/2, bottom: 0, right: bounds.width/2)
titleContentView.frame = CGRect(x: 0, y: 70, width: contentW, height: 20)
indicatorLayer.frame = CGRect(x: bounds.width/2, y: 0, width: 1, height: bounds.height)
}
func setData(_ arr: [Int]) {
scaleLayer.setData(arr)
createTitles()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.handleScrollEnd()
}
}
private func createTitles() {
titleContentView.backgroundColor = .brown.withAlphaComponent(0.3)
contentView.addSubview(titleContentView)
let longs = scaleLayer.longItems
for long in longs {
let label = UILabel()
label.text = "\(long.index)"
label.font = UIFont.systemFont(ofSize: 11)
titleContentView.addSubview(label)
label.sizeToFit()
label.center = CGPoint(x: long.posX, y: 10)
}
}
}
extension YXRulerView: UIScrollViewDelegate {
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
handleScrollEnd()
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
handleScrollEnd()
}
private func handleScrollEnd() {
let offsetX = getValuePosX(by: contentView.contentOffset.x)
let offsetY = contentView.contentOffset.y
contentView.layoutIfNeeded()
contentView.setContentOffset(.init(x: offsetX, y: offsetY), animated: false)
}
private func getValuePosX(by offsetX: CGFloat) ->CGFloat {
let insetLet = contentView.contentInset.left
let targetIndex = lround( (offsetX + insetLet) / (scaleLayer.space + scaleLayer.lineWidth)) // 四舍五
if targetIndex < 0 || targetIndex >= scaleLayer.dataArr.count {
return offsetX
}
print("=====targetIndex:\(targetIndex)")
let entity = scaleLayer.dataArr[targetIndex]
let targetX = entity.posX - insetLet - scaleLayer.lineWidth/4
return targetX
}
}
3、具体使用
let ruler = YXRulerView()
let arr = (1...9).map{ _ in Int.random }
print("arr: \(arr)")
ruler.setData(arr)
view.addSubview(ruler)
NSLayoutConstraint.activate([
ruler.centerYAnchor.constraint(equalTo: view.centerYAnchor),
ruler.centerXAnchor.constraint(equalTo: view.centerXAnchor),
ruler.widthAnchor.constraint(equalTo: view.widthAnchor),
ruler.heightAnchor.constraint(equalToConstant: 100),
])
4、单独的刻度layer绘制
let arr = (1...4).map{ _ in Int.random }
let sLayer = YXScaleLayer()
sLayer.lineWidth = 1.0
sLayer.strokeColor = UIColor.lightGray.cgColor
sLayer.frame = CGRect(x: 10, y: 100, width: 0, height: 100)
sLayer.setData(arr)
view.layer.addSublayer(sLayer)
sLayer.backgroundColor = UIColor.cyan.cgColor
5、其他
extension Int {
/// 4 ~ 9 随机数字
static var random: Int {
Int(arc4random()%6 + 4)
}
}