アニメーションのSyntaxSugar(糖衣構文)

以下のように時計の針を描画して、それを回すアニメーションをつけたいとする。

- 初期値が0の`hour`, `minute`のStateを定義
- レンダリング時（onAppear）に2つのStateを360にする
- 針が値によって角度が変わるように`rotationEffect`を追加

このままでは、針の角度が0度→360度に一瞬で変化するので、動いていることに気づかない。

```swift
struct ClockHands: View {
    @State var hour: Double = 0
    @State var minute: Double = 0
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 4)
                .foregroundStyle(.black)
                .frame(width: 8, height: 100)
                .overlay(RoundedRectangle(cornerRadius: 4).stroke().fill(.white))
                .offset(y: -46)
                .rotationEffect(Angle.degrees(minute))
                .shadow(radius: 5, y: 5)
            
            RoundedRectangle(cornerRadius: 4)
                .foregroundStyle(.black)
                .frame(width: 8, height: 70)
                .overlay(RoundedRectangle(cornerRadius: 4).stroke().fill(.white))
                .offset(y: -32)
                .rotationEffect(Angle.degrees(hour))
                .shadow(radius: 5, y: 5)
            
            Circle()
                .fill(.white)
                .frame(width: 3)
        }
        .onAppear {
            hour = 360
            minute = 360
        }
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/9254b1ee-a37e-a236-a5e1-459b4aedf5c3.png)

## 方法① - modifierを使う

minuteの値が0→360になるのを10秒かけてアニメーションする。
hourの値が0→360になるのを120秒かけてアニメーションする。
それぞれを長針のView、短針のViewのmodifierで指定する。
```swift
struct ClockHands: View {
    @State var hour: Double = 0
    @State var minute: Double = 0
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 4)
                .foregroundStyle(.black)
                .frame(width: 8, height: 100)
                .overlay(RoundedRectangle(cornerRadius: 4).stroke().fill(.white))
                .offset(y: -46)
                .rotationEffect(Angle.degrees(minute))
                .shadow(radius: 5, y: 5)
                .animation(.linear(duration: 10).repeatCount(12, autoreverses: false), value: minute) //追加
            
            RoundedRectangle(cornerRadius: 4)
                .foregroundStyle(.black)
                .frame(width: 8, height: 70)
                .overlay(RoundedRectangle(cornerRadius: 4).stroke().fill(.white))
                .offset(y: -32)
                .rotationEffect(Angle.degrees(hour))
                .shadow(radius: 5, y: 5)
                .animation(.linear(duration: 120), value: hour) //追加
            
            Circle()
                .fill(.white)
                .frame(width: 3)
        }
        .onAppear {
            hour = 360
            minute = 360
        }
    }
}
```
![download.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/d2ac27bd-79f0-1fa3-55fd-b7e41e23d87f.gif)


## 方法② - `withAnimation`メソッドを使う

こちらも同様のアニメーションを実現できるが、
値を代入する部分で指定する。

```swift
struct ClockHands: View {
    @State var hour: Double = 0
    @State var minute: Double = 0
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 4)
                .foregroundStyle(.black)
                .frame(width: 8, height: 100)
                .overlay(RoundedRectangle(cornerRadius: 4).stroke().fill(.white))
                .offset(y: -46)
                .rotationEffect(Angle.degrees(minute))
                .shadow(radius: 5, y: 5)
            
            RoundedRectangle(cornerRadius: 4)
                .foregroundStyle(.black)
                .frame(width: 8, height: 70)
                .overlay(RoundedRectangle(cornerRadius: 4).stroke().fill(.white))
                .offset(y: -32)
                .rotationEffect(Angle.degrees(hour))
                .shadow(radius: 5, y: 5)
            
            Circle()
                .fill(.white)
                .frame(width: 3)
        }
        .onAppear {
            withAnimation(.linear(duration: 120)) {
                hour = 360
            }
            withAnimation(.linear(duration: 10).repeatCount(12, autoreverses: false)) {
                minute = 360
            }
        }
    }
}
```
![download.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/d2ac27bd-79f0-1fa3-55fd-b7e41e23d87f.gif)