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()
        }
    }

}
→ Ссылка