Search code examples
swiftuiswiftui-view

Custom shape registers taps outside drawn path


I created a custom Arc shape in SwiftUI, but when I add an onTapGesture modifier to it, it registers taps outside the area I drew for the Arc (it registers taps anywhere within the rect used to draw the Arc). How can I make sure the taps only register within the drawn path of the Arc, not the entire rect?

Here's the Arc shape I drew:

struct Arc: Shape {
  let angle: Angle
  let thickness: CGFloat
  let clockwise: Bool
  
  func path(in rect: CGRect) -> Path {
    var path = Path()
    
    let innerArcEndPoint = CGPoint(x: rect.maxX - thickness, y: 0)
    let innerArcStartPoint = CGPoint(
      x: innerArcEndPoint.x * cos(CGFloat(angle.radians)),
      y: innerArcEndPoint.x * sin(CGFloat(angle.radians)))
    
    path.move(to: innerArcStartPoint)
    path.addArc(center: rect.origin, radius: innerArcEndPoint.x, startAngle: angle, endAngle: Angle.zero, clockwise: !clockwise)
    path.addLine(to: CGPoint(x: rect.maxX, y: 0))
    path.addArc(center: rect.origin, radius: rect.maxX, startAngle: Angle.zero, endAngle: angle, clockwise: clockwise)
    path.closeSubpath()
    
    return path
  }
}

Here's me using the Arc with an onTapGesture modifier:

struct TestTappingArc: View {
    var body: some View {
        Arc(angle: Angle(degrees: 45), thickness: 20, clockwise: false)
          .foregroundColor(.blue) // The only area I want to be tappable
          .background(Color.orange) // The area I DON'T want to be tappable (but currently is tappable)
          .frame(width: 100, height: 100)
          .onTapGesture {
            print("hello")
          }
    }
}

And here's what it looks like on screen:

enter image description here


Solution

  • Thanks Asperi for that answer--that does fix the problem. However, I realized an even simpler solution is just to move the background to after the onTapGesture modifier, like this:

    Arc(angle: Angle(degrees: 45), thickness: 20, clockwise: false)
      .foregroundColor(.blue)
      .frame(width: 100, height: 100)
      .onTapGesture {
        print("hello")
      }
      .background(Color.orange)