Change radius for bezierPath
Ширина попрежнему остается на всех размерах view одинаковой. Нужно чтобы дуги меняли свой размер как будто им добавили ограничения.
@IBDesignable class ArcView: UIView {
typealias ArcAction = () -> Void
struct ArcInfo {
var outlinePath: UIBezierPath
var action: ArcAction
}
private var arcInfos: [ArcInfo]!
let bgShapeLayer = CAShapeLayer()
var redRadius: Float?
var greenRadius: Float?
private var pathLineWidth: CGFloat {
return frame.size.width / 15.0
}
private var normalArcRadius: CGFloat {
return frame.size.width / 2 - pathLineWidth * 1.5
}
private var tappedArcRadius: CGFloat {
return frame.size.width / 2 - pathLineWidth / 2
}
required init?(coder: NSCoder) {
super.init(coder: coder)
let recognizer = UITapGestureRecognizer(target: self, action: #selector(tap(_ :)))
addGestureRecognizer(recognizer)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override func draw(_ rect: CGRect) {
let fullCircle = CGFloat.pi * 2
let arcAngle = fullCircle * 1.5 / 6
var lastArcAngle = CGFloat.pi / 4.0 + CGFloat.pi //-CGFloat.pi
// background
let backPath = UIBezierPath(arcCenter: CGPoint(x: rect.width/2, y: rect.height/2), radius: 55.0, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
#colorLiteral(red: 0.9931474328, green: 0.9932896495, blue: 0.9931163192, alpha: 1).setStroke()
backPath.lineWidth = 2.3
backPath.stroke()
#colorLiteral(red: 0.9212146401, green: 0.9490351081, blue: 0.9671724439, alpha: 1).setFill()
backPath.fill()
arcInfos = []
// Red Arc
func redArc( action: @escaping ArcAction) {
let path = UIBezierPath(arcCenter: CGPoint(x: rect.width/2, y: rect.height/2), radius: 46, startAngle: lastArcAngle, endAngle: lastArcAngle + arcAngle, clockwise: true)
#colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1).setStroke()
path.lineWidth = 10
path.stroke()
lastArcAngle += arcAngle
// separators
#colorLiteral(red: 0.927436769, green: 0.9490308166, blue: 0.967099607, alpha: 1).setStroke()
let outlinePath = hitTestPath(for: path)
outlinePath.lineWidth = 3
outlinePath.stroke()
arcInfos.append(ArcInfo(outlinePath: outlinePath, action: action))
}
//Green Arc
func greenArc( action: @escaping ArcAction) {
let path = UIBezierPath(arcCenter: CGPoint(x: rect.width/2, y: rect.height/2), radius: CGFloat(greenRadius ?? 56), startAngle: lastArcAngle, endAngle: lastArcAngle + arcAngle, clockwise: true)
#colorLiteral(red: 0.2443430722, green: 0.800511539, blue: 0.4006313086, alpha: 1).setStroke()
path.lineWidth = 10
path.stroke()
lastArcAngle += arcAngle
// separators
#colorLiteral(red: 0.927436769, green: 0.9490308166, blue: 0.967099607, alpha: 1).setStroke()
let outlinePath = hitTestPath(for: path)
outlinePath.lineWidth = 3
outlinePath.stroke()
arcInfos.append(ArcInfo(outlinePath: outlinePath, action: action))
}
//Add Arc
greenArc {
self.redRadius = 46
self.greenRadius = 56
}
redArc {
self.redRadius = 56
self.greenRadius = 46
}
}
@objc func tap(_ recognizer: UITapGestureRecognizer) {
let location = recognizer.location(in: self)
if let hitPath = (arcInfos.first { $0.outlinePath.contains(location) }) {
hitPath.action()
setNeedsDisplay()
//print(hitPath)
}
}
func hitTestPath(for path: UIBezierPath) -> UIBezierPath {
let pathCopy = path.cgPath.copy(strokingWithWidth: 15, lineCap: .butt, lineJoin: .miter, miterLimit: 0)
return UIBezierPath(cgPath: pathCopy)
}
}
protocol circleViewDelegate: NSObjectProtocol {
func selectedType(_ type: typeCircle)
}
Ответы (1 шт):
Автор решения: schmidt9
→ Ссылка
Я взял за основу предыдущий ответ и переписал его, получилось следующее
class ArcsView: UIView {
typealias ArcAction = () -> Void
class ArcInfo {
var normalPath: UIBezierPath
var tappedPath: UIBezierPath
var currentPath: UIBezierPath {
isTapped ? tappedPath : normalPath
}
var outlinePath: UIBezierPath {
hitTestPath(for: isTapped ? tappedPath : normalPath)
}
var strokeColor: UIColor
var action: ArcAction
var isTapped = false
init(normalPath: UIBezierPath, tappedPath: UIBezierPath, strokeColor: UIColor, action: @escaping ArcAction) {
self.normalPath = normalPath
self.tappedPath = tappedPath
self.strokeColor = strokeColor
self.action = action
}
private func hitTestPath(for path: UIBezierPath) -> UIBezierPath {
let pathCopy = path.cgPath.copy(strokingWithWidth: 20, lineCap: .butt, lineJoin: .miter, miterLimit: 0)
return UIBezierPath(cgPath: pathCopy)
}
}
private var pathLineWidth: CGFloat {
frame.size.width / 15.0
}
private var normalArcRadius: CGFloat {
frame.size.width / 2 - pathLineWidth * 1.5
}
private var tappedArcRadius: CGFloat {
frame.size.width / 2 - pathLineWidth / 2
}
private var arcInfos: [ArcInfo]!
required init?(coder: NSCoder) {
super.init(coder: coder)
let recognizer = UITapGestureRecognizer(target: self, action: #selector(tap(_ :)))
addGestureRecognizer(recognizer)
initArcs()
}
override func draw(_ rect: CGRect) {
arcInfos.forEach { info in
info.strokeColor.set()
info.currentPath.stroke()
}
}
private func initArcs() {
arcInfos = []
var lastArcAngle = -CGFloat.pi
func addArc(color: UIColor, percentage: CGFloat, index: Int, action: @escaping ArcAction) {
let fullCircle = CGFloat.pi * 2
let arcAngle = fullCircle * percentage
let normalPath = UIBezierPath(arcCenter: CGPoint(x: frame.width / 2, y: frame.height / 2), radius: normalArcRadius, startAngle: lastArcAngle, endAngle: lastArcAngle + arcAngle, clockwise: true)
normalPath.lineWidth = pathLineWidth
let tappedPath = UIBezierPath(arcCenter: CGPoint(x: frame.width / 2, y: frame.height / 2), radius: tappedArcRadius, startAngle: lastArcAngle, endAngle: lastArcAngle + arcAngle, clockwise: true)
tappedPath.lineWidth = pathLineWidth
let arcInfo = ArcInfo(normalPath: normalPath,
tappedPath: tappedPath,
strokeColor: color,
action: action)
arcInfos.append(arcInfo)
lastArcAngle += arcAngle
}
addArc(color: .red, percentage: 1.5 / 6.0, index: 1) {
print("action 1")
}
addArc(color: .green, percentage: 1.5 / 6.0, index: 2) {
print("action 2")
}
addArc(color: .blue, percentage: 1.5 / 6.0, index: 3) {
print("action 3")
}
addArc(color: .purple, percentage: 1.5 / 6.0,index: 4) {
print("action 4")
}
}
@objc func tap(_ recognizer: UITapGestureRecognizer) {
let location = recognizer.location(in: self)
if let hitPath = (arcInfos.first { $0.outlinePath.contains(location) }) {
arcInfos.forEach { $0.isTapped = ($0 === hitPath) }
hitPath.action()
setNeedsDisplay()
}
}
}