SwiftUI Tutorialsから学ぶSwiftUI〜その2〜

# はじめに
本記事は、Apple公式の[SwiftUI Tutorials](https://developer.apple.com/tutorials/swiftui)を参考として内容をまとめた記事です。
英語が苦手な方や、概要をさっと理解したい方を対象に本記事を執筆しております。

注釈コメントを入れたプロジェクトファイルは[こちら(GitHub)](https://github.com/b150005/SwiftUI)

***

# Drawing Paths and Shapes
[Drawing Paths and Shapes](https://developer.apple.com/tutorials/swiftui/drawing-paths-and-shapes)

## 図形の描画
頂点の座標を定義した図形を描画する場合は`Path`を用いる。
また、表示画面(=**親ビュー**)の高さや幅を利用する場合は、併せて`GeometryReader`を利用する。

## 定義

```swift
// 始点の指定
Path#move(to p: CGPoint)
// パラメータ
// p: 始点となる2次元座標

// 現在の座標 -> 任意の座標 への線描
Path#addLine(to p: CGPoint)
// パラメータ
// p: 終点となる2次元座標

// メジェ曲線の描画
Path#addQuadCurve(to p: CGPoint, control cp: CGPoint)
// パラメータ
// p: 終点となる2次元座標
// cp: 制御点となる2次元座標
```

## サンプル
線描の様子を表す図は、以下の通り。
![線描.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673880/475e36b9-9c39-4ccd-a838-4659df871442.jpeg)

メジェ曲線の描画を表す図は、以下の通り。
![メジェ曲線.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673880/32162dd8-ec77-d633-65e2-11db7e083dfb.jpeg)

***

# Animating Views and Transitions
[Animating Views and Transitions](https://developer.apple.com/tutorials/swiftui/animating-views-and-transitions)

## 擬似アニメーション
[参考: Graphics and Rendering](https://developer.apple.com/documentation/swiftui/view-graphics-and-rendering)
トグルスイッチの役割を持たせたビューに対して、ボタンの状態に応じたアイコンの変化を演出することができる。

ただし、アニメーション効果を設定しない場合はアイコンが切り替わるだけとなるため、
動きに連続性を持たせる場合は後述する`.animation()`修飾子を用いてアニメーション効果を付与する。

```swift:アイコンの変化
// ビューの回転
View#rotationEffect(
    _ angle: Angle, 
    anchor: UnitPoint = .center
) -> some View
// パラメータ
// angle: 回転角度を表すAngleオブジェクト
// -> 通常はAngle.degrees(_ degrees: Double)メソッドで指定
// anchor: アンカーポイント(基点)

// ビューの拡大
View#scaleEffect(
    _ s: CGFloat, 
    anchor: UnitPoint = .center
) -> some View
```

また、`Animation#withAnimation()`メソッドを用いて、状態の変化に呼応してアニメーションを付与することができる。

```swift:状態の変化に呼応したアニメーション
Animation#withAnimation<Result> (
    _ animation: Animation? = .default,
    _ body: () throws -> Result
) rethrows -> Result
// パラメータ
// animation: アニメーション効果
// body: 状態を変更するメソッド(クロージャ)
```

## ビューへのアニメーション効果の追加
ビューに対して`.animation()`修飾子を付与することで、アニメーション効果を付与することができる。
また、`.animation()`修飾子は`Animation`構造体インスタンスを引数にとり、タイププロパティで用意されているものもある。

さらに、`Animation`構造体を**拡張定義(extension)**し、アニメーション効果を定義したタイプメソッドを記述することで、
アニメーションを独自に定義することができる。

SwiftUIで用意されている`Animation`のタイププロパティは、以下の通り。

| プロパティ | 内容 |
|:--|:--|
| default | ビューによって異なる |
| easeIn | 開始のみゆっくり |
| easeInOut | 開始・終了のみゆっくり |
| easeOut | 終了のみゆっくり |
| linear | 等速 |

### バネのようなアニメーション効果
バネのようなアニメーションを付与する場合は、`.animation()`修飾子の引数として、以下の`Animation`構造体のタイプメソッドを用いる。
ここで、**単振動モデル**を表す`spring()`・`interactiveSpring()`メソッドの違いは、初期値が異なるだけで、仕様は同じである。

```swift:バネのようなアニメーション
// 単振動モデル①
Animation.spring(
    response: Double = 0.55,
    dampingFraction: Double = 0.825,
    blendDuration: Double = 0
) -> Animation
// パラメータ
// response: ばねの解放時間[秒]  ※値が小さいほど動きが瞬発的になる
// dampingFraction: 粘性抵抗力  ※値が小さいほど跳ね返りが維持
// blendDuration: 現在の状態(跳ね返り速度)を維持したまま次のアニメーションに移行するまでの時間[秒]

// 単振動モデル②
Animation.interactiveSpring(
    response: Double = 0.15,
    dampingFraction: Double = 0.86,
    blendDuration: Double = 0.25
) -> Animation
// パラメータ
// response: ばねの解放時間[秒]  ※値が小さいほど動きが瞬発的になる
// dampingFraction: 粘性抵抗力  ※値が小さいほど跳ね返りが維持
// blendDuration: 現在の状態(跳ね返り速度)を維持したまま次のアニメーションに移行するまでの時間[秒]

// 減衰振動モデル
Animation.interpolatingSpring(
    mass: Double = 1.0, 
    stiffness: Double, 
    damping: Double, 
    initialVelocity: Double = 0.0
) -> Animation
// パラメータ
// mass: 荷重  ※ 値が小さいほど弾性力が小さくなる
// stiffness: ばねの剛性  ※値が小さいほど動きが大きくなる
// damping: 粘性抵抗力  ※値が小さいほど跳ね返りが維持
// initialVelocity: 初速
```

## ビュー表示の切り替えアニメーション(トランジション; transition)
ビューの表示・非表示に伴うアニメーション(=**トランジション**)を定義する場合、`View`プロトコルの`transition()`メソッドを利用する。

SwiftUIで用意される、トランジションを表す`AnyTransition`のタイププロパティは以下の4つ。

| プロパティ | 表示時 | 非表示時 |
|:--|:--|:--|
| identity | - | - |
| opacity | 不透明化 | 透明化 |
| scale | 拡大 | 縮小 |
| slide | 左からスライド | 右へスライド |

また、`AnyTransition`構造体を**拡張定義(extension)**してタイププロパティを追加することで、トランジション効果を自由に作成することができる。

### 定義

```swift:ビューのトランジション
// トランジション効果の指定
View#transition(_ t: AnyTransition) -> some View
// パラメータ
// t: AnyTransition構造体のタイププロパティ

// 任意の方向(上下左右)から移動して表示
AnyTransition.move(edge: Edge) -> AnyTransition
// パラメータ
// edge: .top(上)/.bottom(下)/.leading(左)/.trailing(右)のいずれか

// トランジション効果の組み合わせ
AnyTransition#combined(with other: AnyTransition) -> AnyTransition
// パラメータ
// other: 追加するトランジション効果

// 表示・非表示でトランジション効果の異なる複合トランジションの生成
AnyTransition.asymmetric(
    insertion: AnyTransition,
    removal: AnyTransition
) -> AnyTransition
// パラメータ
// insertion: 表示時のトランジション効果
// removal: 非表示時のトランジション効果
```