iPhone、iPadで複雑な形状をした図形内のタッチイベントを簡単に取得する方法　〜SwiftUI編〜


以前、CAShapeLayeを使う方法[iPhone、iPadで複雑な形状をした図形内のタッチイベントを簡単に取得する方法　〜CAShapeLaye〜](https://qiita.com/takaf51/items/c9409139dd7db5191b5b)を投稿しましたが、今回はSwiftUIを使ったバージョンです。
今回に限ったことではないですが、SwiftUIを使うとシンプルに、とても理解し易いコードを書くことができます。
![ezgif-5-9653f8f1f6ea.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24726/07d2f375-81e3-382f-d611-dda525925e36.gif)

# 今回のコード　〜SwiftUI〜
```swift:SwiftUIUIPathBySwiftUI
struct ContentView: View {
    @State var tapped: Bool = false
    var body: some View {
        ZStack {
            SomeShape()
            .stroke(Color.blue, lineWidth: 5)
            SomeShape()
            .fill(tapped ? Color.red : Color.white)
            .tapAction {
                self.tapped.toggle()
            }
        }
    }
}

struct SomeShape: Shape{
    func path(in rect: CGRect) -> Path {
        return Path(getPath())
    }
}
```
# 以前のコード　〜CAShapeLaye〜
```swift:UIBezierPathByCAShapeLaye
class TouchableVectorImageView:UIView   {
    private let shapeLayer = CAShapeLayer()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupView()    {
        shapeLayer.path = getPath()
        shapeLayer.fillColor = UIColor.white.cgColor
        shapeLayer.lineWidth = 2.0
        shapeLayer.strokeColor = UIColor.blue.cgColor
        self.layer.addSublayer(shapeLayer)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first
        let point = touch!.location(in: self)
        shapeLayer.fillColor = shapeLayer.path!.contains(point) ? UIColor.red.cgColor : UIColor.white.cgColor
        
    }
}

v.view.addSubview(TouchableVectorImageView(frame: CGRect(x: 50, y: 50, width: 200, height: 200)))
v.view.backgroundColor = UIColor.gray
```
# アニメーションの追加
`withAnimation`を追加するだけ。超簡単、お手軽！！！
![ezgif.com-crop.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24726/4651055b-77a1-723b-2b57-99c812e73c14.gif)


```swift:SwiftUIUIPathBySwiftUIWithAnimation
struct ContentView: View {
    @State var tapped: Bool = false
    var body: some View {
        ZStack {
            SomeShape()
            .stroke(Color.blue, lineWidth: 5)
            SomeShape()
                .fill(tapped ? gradientByDegrees(degrees: -90) : gradientByDegrees(degrees: 90))
            .tapAction {
                withAnimation(.basic(duration:0.5)) {
                    self.tapped.toggle()
                }
            }
        }.padding()
    }
}

struct SomeShape: Shape{
    func path(in rect: CGRect) -> Path {
        return Path(getPath())
    }
}

func gradientByDegrees(degrees:Double) -> AngularGradient {
    let spectrum = Gradient(colors: [.red, .yellow, .green, .blue, .purple, .red])
    return AngularGradient(gradient: spectrum, center: .center, angle: .degrees(degrees))
}
```

# まとめ
下記にコード全文を載せておきますので、Playgroudにコピー、実行すれば試せます。（Xcode11が必要です。）
SwiftUIは本当に素晴らしい！！

```swift:UIBezierPath
import SwiftUI
import PlaygroundSupport

struct ContentView: View {
    @State var tapped: Bool = false
    var body: some View {
        ZStack {
            SomeShape()
            .stroke(Color.blue, lineWidth: 5)
            SomeShape()
            .fill(tapped ? Color.red : Color.white)
            .tapAction {
                self.tapped.toggle()
            }
        }
    }
}

struct SomeShape: Shape{
    func path(in rect: CGRect) -> Path {
        return Path(getPath())
    }
}

func getPath()  -> CGPath {
    //Start: Created by PaintCode
    let bezierPath = UIBezierPath()
    bezierPath.move(to: CGPoint(x: 13.08, y: 129.49))
    bezierPath.addLine(to: CGPoint(x: 1.58, y: 117.99))
    bezierPath.addLine(to: CGPoint(x: 14.58, y: 104.99))
    bezierPath.addLine(to: CGPoint(x: 0.71, y: 91.11))
    bezierPath.addLine(to: CGPoint(x: 27.71, y: 64.1))
    bezierPath.addLine(to: CGPoint(x: 27.71, y: 53.7))
    bezierPath.addLine(to: CGPoint(x: 49.97, y: 31.45))
    bezierPath.addLine(to: CGPoint(x: 49.97, y: 23.61))
    bezierPath.addLine(to: CGPoint(x: 73.08, y: 0.5))
    bezierPath.addLine(to: CGPoint(x: 101.97, y: 0.5))
    bezierPath.addLine(to: CGPoint(x: 110.6, y: 9.13))
    bezierPath.addLine(to: CGPoint(x: 125.95, y: 9.13))
    bezierPath.addLine(to: CGPoint(x: 155.8, y: 39.71))
    bezierPath.addLine(to: CGPoint(x: 155.8, y: 52.35))
    bezierPath.addLine(to: CGPoint(x: 138.37, y: 69.78))
    bezierPath.addLine(to: CGPoint(x: 97.72, y: 29.13))
    bezierPath.addLine(to: CGPoint(x: 61.51, y: 65.34))
    bezierPath.addLine(to: CGPoint(x: 68.66, y: 72.49))
    bezierPath.addLine(to: CGPoint(x: 33.66, y: 107.49))
    bezierPath.addLine(to: CGPoint(x: 47.91, y: 121.74))
    bezierPath.addLine(to: CGPoint(x: 40.16, y: 129.49))
    bezierPath.addLine(to: CGPoint(x: 13.08, y: 129.49))
    bezierPath.close()
    //END
    return bezierPath.cgPath
}


PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView())

```