Добавить тап по UIBezierPath

Нарисовал круг из 4-х дуг. Как я могу добавить тап по каждой из них?

Вот как я добавлял дуги

 override func draw(_ rect: CGRect) {

        var lastArcAngle = -CGFloat.pi

        func addArc(color: UIColor, percentage: CGFloat, index: Int) {

            let fullCircle = CGFloat.pi * 2
            let arcAngle = fullCircle * percentage
            let path = UIBezierPath(arcCenter: CGPoint(x: rect.width/2, y: rect.height/2), radius: 50, startAngle: lastArcAngle, endAngle: lastArcAngle + arcAngle, clockwise: true)

            color.setStroke()
            path.lineWidth = 20
            path.stroke()
            lastArcAngle += arcAngle
        }
       addArc(color: .red, percentage: 1.5 / 6.0, index: 1)
       addArc(color: .green, percentage: 1.5 / 6.0, index: 2)
       addArc(color: .blue, percentage: 1.5 / 6.0, index: 3)
       addArc(color: .purple, percentage: 1.5 / 6.0,index: 4)
    }

Ответы (1 шт):

Автор решения: schmidt9

Можно добавить для каждого сегмента контурные пути и проверять, попадает ли в них точка

import UIKit

class ArcsView: UIView {

    private var hitTestPaths = [UIBezierPath]()

    required init?(coder: NSCoder) {
        super.init(coder: coder)

        let recognizer = UITapGestureRecognizer(target: self, action: #selector(tap(_ :)))
        addGestureRecognizer(recognizer)
    }

    override func draw(_ rect: CGRect) {

        hitTestPaths = []

        var lastArcAngle = -CGFloat.pi

        func addArc(color: UIColor, percentage: CGFloat, index: Int) {

            let fullCircle = CGFloat.pi * 2
            let arcAngle = fullCircle * percentage
            let path = UIBezierPath(arcCenter: CGPoint(x: rect.width/2, y: rect.height/2), radius: 50, startAngle: lastArcAngle, endAngle: lastArcAngle + arcAngle, clockwise: true)

            color.setStroke()
            path.lineWidth = 20
            path.stroke()
            lastArcAngle += arcAngle

            // рисуем для проверки
            UIColor.black.setStroke()
            let outlinePath = hitTestPath(for: path)
            outlinePath.stroke()

            hitTestPaths.append(outlinePath)
        }

        addArc(color: .red, percentage: 1.5 / 6.0, index: 1)
        addArc(color: .green, percentage: 1.5 / 6.0, index: 2)
        addArc(color: .blue, percentage: 1.5 / 6.0, index: 3)
        addArc(color: .purple, percentage: 1.5 / 6.0,index: 4)
    }

    @objc func tap(_ recognizer: UITapGestureRecognizer) {
        let location = recognizer.location(in: self)

        if let hitPath = (hitTestPaths.first { $0.contains(location) }) {
            print("hit \(hitPath)")
        }
    }

    func hitTestPath(for path: UIBezierPath) -> UIBezierPath {
        let pathCopy = path.cgPath.copy(strokingWithWidth: 20, lineCap: .butt, lineJoin: .miter, miterLimit: 0)
        return UIBezierPath(cgPath: pathCopy)
    }

}

Дополнение

Если к тапам нужно привязать действия, то это можно сделать например как показан ниже - добавить структуру для хранения связанных данных

import UIKit

class ArcsView: UIView {

    typealias ArcAction = () -> Void

    struct ArcInfo {
        var outlinePath: UIBezierPath
        var action: ArcAction
    }

    private var arcInfos: [ArcInfo]!

    required init?(coder: NSCoder) {
        super.init(coder: coder)

        let recognizer = UITapGestureRecognizer(target: self, action: #selector(tap(_ :)))
        addGestureRecognizer(recognizer)
    }

    override func draw(_ rect: CGRect) {

        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 path = UIBezierPath(arcCenter: CGPoint(x: rect.width/2, y: rect.height/2), radius: 50, startAngle: lastArcAngle, endAngle: lastArcAngle + arcAngle, clockwise: true)

            color.setStroke()
            path.lineWidth = 20
            path.stroke()
            lastArcAngle += arcAngle

            // рисуем для проверки
            UIColor.black.setStroke()
            let outlinePath = hitTestPath(for: path)
            outlinePath.stroke()

            arcInfos.append(ArcInfo(outlinePath: outlinePath, action: action))
        }

        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) }) {
            hitPath.action()
        }
    }

    func hitTestPath(for path: UIBezierPath) -> UIBezierPath {
        let pathCopy = path.cgPath.copy(strokingWithWidth: 20, lineCap: .butt, lineJoin: .miter, miterLimit: 0)
        return UIBezierPath(cgPath: pathCopy)
    }

}
→ Ссылка