// // MLSliderRuler.swift // MLSilderRulerView // // Created by Macro on 2018/1/25. // Copyright © 2018年 CodeMarco. All rights reserved. // import UIKit let DISTANCELEFTANDRIGHT: CGFloat = 8.0 // 标尺左右距离 let DISTANCEVALUE: CGFloat = 8.0 // 每隔刻度实际长度8个点 let DISTANCETOPANDBOTTOM: CGFloat = 5.0 // 标尺上下距离 class MLSliderRulerScrollView: UIScrollView { var maxValue: CGFloat = 2000 var minValue: CGFloat = 0 var rulerAverage: CGFloat = 1 var rulerCount: Int { return Int(maxValue - minValue) } var isShowShortSymbol = true var isSmallModel: Bool = true var binary: Int = 10 var ruleFont = UIFont.systemFont(ofSize: 12) var longSymbolColor = UIColor.hexSting(color: "#B6BCC7", alpha: 1) { didSet { longSymbol.strokeColor = longSymbolColor.cgColor } } var middleSymbolColor = UIColor.hexSting(color: "#B6BCC7", alpha: 1) { didSet { middleSymbol.strokeColor = middleSymbolColor.cgColor } } var shortSymbolColor = UIColor.hexSting(color: "#B6BCC7", alpha: 1) { didSet { shortSymbol.strokeColor = shortSymbolColor.cgColor } } fileprivate lazy var longSymbol: CAShapeLayer = { let shapeLayer = CAShapeLayer() shapeLayer.strokeColor = longSymbolColor.cgColor shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.lineWidth = 3 shapeLayer.lineCap = .round return shapeLayer }() fileprivate lazy var middleSymbol: CAShapeLayer = { let shapeLayer = CAShapeLayer() shapeLayer.strokeColor = middleSymbolColor.cgColor shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.lineWidth = 3 shapeLayer.lineCap = .round return shapeLayer }() fileprivate lazy var shortSymbol: CAShapeLayer = { let shapeLayer = CAShapeLayer() shapeLayer.strokeColor = shortSymbolColor.cgColor shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.lineWidth = 3 shapeLayer.lineCap = .round return shapeLayer }() override init(frame: CGRect) { super.init(frame: frame) setupSubviews() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func setupSubviews() { layer.addSublayer(shortSymbol) layer.addSublayer(middleSymbol) layer.addSublayer(longSymbol) backgroundColor = UIColor.white } func drawRuler() { layer.sublayers?.forEach { (sublayer) in if sublayer.isKind(of: CATextLayer.self) { sublayer.removeFromSuperlayer() } } let longPathRef = CGMutablePath() let middlePathRef = CGMutablePath() let shortPathRef = CGMutablePath() for idx in 0 ... rulerCount { let i = CGFloat(idx) let rule = CATextLayer() rule.foregroundColor = UIColor.lightGray.cgColor rule.font = ruleFont rule.fontSize = ruleFont.pointSize rule.contentsScale = UIScreen.main.scale rule.string = String(format: "%.0f", (i + minValue) * rulerAverage) let textSize = (rule.string as! NSString).size(withAttributes: [NSAttributedString.Key.font: ruleFont]) rule.bounds = CGRect(origin: CGPoint.zero, size: textSize) let xPos = DISTANCELEFTANDRIGHT + DISTANCEVALUE * i if idx % Int(binary) == 0 { longPathRef.move(to: CGPoint(x: xPos, y: DISTANCETOPANDBOTTOM + textSize.height + 5)) longPathRef.addLine(to: CGPoint(x: xPos, y: bounds.height - DISTANCETOPANDBOTTOM)) rule.position = CGPoint(x: xPos, y: 5 + textSize.height / 2) layer.addSublayer(rule) } else if idx % Int(binary / 2) == 0 { middlePathRef.move(to: CGPoint(x: xPos, y: DISTANCETOPANDBOTTOM + textSize.height + 10)) middlePathRef.addLine(to: CGPoint(x: xPos, y: bounds.height - DISTANCETOPANDBOTTOM)) } else if isShowShortSymbol { shortPathRef.move(to: CGPoint(x: xPos, y: DISTANCETOPANDBOTTOM + textSize.height + 15)) shortPathRef.addLine(to: CGPoint(x: xPos, y: bounds.height - DISTANCETOPANDBOTTOM)) } } longSymbol.path = longPathRef let longSymbolFrame = longSymbol.path?.boundingBoxOfPath ?? .zero let longSymbolCenterY = longSymbolFrame.midY middleSymbol.path = middlePathRef shortSymbol.path = shortPathRef middleSymbol.position.y += (longSymbolCenterY - (middlePathRef.boundingBoxOfPath.midY)) shortSymbol.position.y += (longSymbolCenterY - (shortPathRef.boundingBoxOfPath.midY)) if isSmallModel { contentInset = UIEdgeInsets(top: 0, left: bounds.width / 2 - DISTANCELEFTANDRIGHT, bottom: 0, right: bounds.width / 2 - DISTANCELEFTANDRIGHT) contentOffset = CGPoint(x: DISTANCEVALUE * ((currentRulerValue - minValue) / rulerAverage) - bounds.width + (bounds.width / 2 + DISTANCELEFTANDRIGHT), y: 0) } else { contentOffset = CGPoint(x: DISTANCEVALUE * ((currentRulerValue - minValue) / rulerAverage) - bounds.width / 2.0 + DISTANCELEFTANDRIGHT, y: 0) } contentSize = CGSize(width: CGFloat(rulerCount) * DISTANCEVALUE + DISTANCELEFTANDRIGHT * 2, height: bounds.height) } // Computed property to get the current ruler value based on scroll position var currentRulerValue: CGFloat { let offsetX = contentOffset.x + bounds.size.width / 2 - DISTANCELEFTANDRIGHT return (offsetX / DISTANCEVALUE) * rulerAverage + minValue } }