この世でいちばんわかりやすいSwiftUIのパス描画

#この投稿は何？

SwiftUIフレームワーク上でパスを描画する方法を解説します。
下図のような「パイチャートなどを描画するために利用できる扇形」のパスを各コードを書いていきます。

![スクリーンショット 2021-06-18 23.38.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70476/f424292e-de2b-32ff-ad4a-fd5e0bc581d4.png)

##実行環境
macOS 11.4
Xcode 12.5
Swift 5.4

#ハンズオン
ここで実践するシェイプは「パスの始まりと終わり」が閉じるまで、見ることはできません。
円弧を描けると、プレビュー上で確認することができます。

###Shapeプロトコル

パスを描画するには`Shape`プロトコルに適合した構造体を定義します。
一般的なSwiftUIビューのような`View`プロトコルでないので注意してください。

```Swift:Pie構造体にShapeプロトコルを採用する
import SwiftUI

struct Pie: Shape {
    func path(in rect: CGRect) -> Path {
        // draw and return Path
    }
}
```

型に`Shape`プロトコルを採用すると、`path(in:)`メソッドの実装が要求されます。
このメソッドのボディで、描画したいパスを実装します。

###パスのインスタンスを作成する

描画される線は`Path`型のインスタンスです。
とりあえず、パスのインスタンスを作成して、そのまま返しておきます。

```Swift:path(in:)メソッドの実装
struct Pie: Shape {
    func path(in rect: CGRect) -> Path {        
        var path = Path()
        return path
    }
}
```

以降、作成したパスのインスタンスに対して、「どのような図形を描画するか」を指定していきます。

###パスの始まりを決める

画面の中央となる点を取得します。
`path(in:)`メソッドが受け取る`rect`パラメータが、そのパスが描画される画面の情報を持っています。

```Swift:path(in:)メソッドの実装
struct Pie: Shape {
    func path(in rect: CGRect) -> Path {        
        let center = CGPoint(x: rect.midX, y: rect.midY)

        var path = Path()
        return path
    }
}
```

画面の中央点がわかったら、そこを「パスの開始位置」とします。

```Swift:ペンを中心に移動する
struct Pie: Shape {
    func path(in rect: CGRect) -> Path {
        let center = CGPoint(x: rect.midX, y: rect.midY)

        var path = Path()
        path.move(to: center)
        return path
    }
}
```

`move(to:)`メソッドで、パスのインスタンスを移動します。

###直線を引く

パイの円弧が「開始される点」まで、直線を引きましょう。
パイの半径は「画面幅の半分」とします。
また、円弧の開始点は「画面の中心からX軸方向に半径分だけ移動した地点」とします。

```Swift:直線を引く
struct Pie: Shape {    
    func path(in rect: CGRect) -> Path {
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let radius = rect.width / 2
        let startPoint = CGPoint(
            x: center.x + radius,
            y: center.y
        )
    
        var path = Path()
        path.move(to: center)
        path.addLine(to: startPoint)
        return path
    }
}
```


###円弧を引く

`addArc(center:radius:startAngle:endAngle:clockwise)`メソッドを使って、円弧を引くことができます。
5つのパラメータにはそれぞれ、以下の値を指定します。

* 円弧の中心点
* 円弧の半径
* 円弧の開始角
* 円弧の終了角
* 円弧の進む方向

```swift:Pie
import SwiftUI

struct Pie: Shape {
    let startAngle = Angle(degrees: 0)
    let endAngle   = Angle(degrees: 90)
    
    func path(in rect: CGRect) -> Path {
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let radius = rect.width / 2
        let startPoint = CGPoint(
            x: center.x + radius,
            y: center.y
        )
    
        var path = Path()
        path.move(to: center)
        path.addLine(to: startPoint)
        path.addArc(center: center,
                    radius: radius,
                    startAngle: startAngle,
                    endAngle:   endAngle,
                    clockwise: false
        )
        return path
    }
}
```

円弧の向きを調整するには、パス全体の角度を変更します。

```Swift
struct Pie: Shape {
    let startAngle = Angle(degrees: 0-90)
    let endAngle   = Angle(degrees: 90-90)
    
    func path(in rect: CGRect) -> Path {
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let radius = rect.width / 2
        let startPoint = CGPoint(
            x: center.x + radius * CGFloat(cos(startAngle.radians)),
            y: center.y + radius * CGFloat(sin(startAngle.radians))
        )
    
        var path = Path()
        path.move(to: center)
        path.addLine(to: startPoint)
        path.addArc(center: center,
                    radius: radius,
                    startAngle: startAngle,
                    endAngle:   endAngle,
                    clockwise: false
        )
        return path
    }
}
```

![スクリーンショット 2021-06-19 16.48.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70476/5aa6a68d-3446-0d62-bc79-398f01ecbce4.png)

三角関数については、[こちらのnote](https://note.com/imchino/n/n64b07f9a9c97)で説明しています。