Swiftでやる簡単なグラフアニメーション（CABasicAnimation）

# やりたいこと
Swiftでアニメーションを作ろうと思うと毎回ググるところから始まるので自分なりにメモ
今回はsin関数のグラフを軽くアニメーションしてみます

# 開発環境
Xcode: 10.3
Swift: 5.0.1

# とりあえず結論
```swift:ViewController.swift
class ViewController: UIViewController {

    var layer_frame = CGRect()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        layer_frame = CGRect(x: 0, y: view.frame.height/2 - 60, width: view.frame.width, height: 120)
    }
    
    @IBAction func onTouch(_ sender: Any) {
        // アニメーションを作成
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.duration = 1.0
        animation.fromValue = 0.0
        animation.toValue = 1.0
        
        // sinグラフを描画するレイヤー
        let layer = CAShapeLayer()
        layer.frame = layer_frame
        layer.backgroundColor = UIColor.gray.cgColor
        layer.strokeColor = UIColor.cyan.cgColor
        layer.fillColor = UIColor.gray.cgColor
        layer.lineWidth = 3.0
        layer.path = create_sin_path()
        // レイヤーにアニメーションを付加
        layer.add(animation, forKey: nil)
        
        view.layer.addSublayer(layer)
    }
    
    // sin関数のCGPathを作成
    func create_sin_path() -> CGPath {
        let div: Double = 1 / 100
        let sin_path = UIBezierPath()
        let origin = CGPoint(x: layer_frame.width/2 - CGFloat(50*Double.pi), y: layer_frame.height / 2.0)
        sin_path.move(to: origin)
        for i in 0 ... Int(2.0*Double.pi / div) {
            let point_x = div * Double(i)
            let point_y = sin(point_x)
            let point = CGPoint(x: 50*point_x + Double(origin.x), y: -50*point_y + Double(origin.y))
            sin_path.addLine(to: point)
        }
        return sin_path.cgPath
    }
}

```

#説明
## 1. CABasicAnimationでアニメーション処理を記述
アニメーションの方法はいくつかあるらしいけど、今回はCABasicAnimationを使ってみます。
以下のサイトが非常に参考になったので詳細を知りたい方はどうぞ。
[iOSアプリ開発でアニメーションするなら押さえておきたい基礎](https://qiita.com/hachinobu/items/57d4c305c907805b4a53#cabasicanimation)

今回のコードでは、CABasicAnimationでstrokeEndプロパティを変化させてます。

```swift
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = 1.0    // アニメーションの時間
animation.fromValue = 0.0   // 変化させる値の初期値
animation.toValue = 1.0     // 変化させる値の終点値
```

## 2. アニメーションさせたいグラフを描画するレイヤーを作成
ここでは、グラフを描画するレイヤーを作成します。グラフの種類にもよると思うけど、今回はUIBezierPathでグラフを作成するので、レイヤーはCAShapeLayerを使います。
UIBezierPathとCAShapeLayerの使い方は以下のサイトが参考になりました。
[Swiftで描画し、アニメーションするまでのメモ](https://qiita.com/dolfalf/items/4e094cf36104e4df5b9c)

CAShapeLayerのpathプロパティに描画したいグラフのCGPath(UIBezierPath)を設定すればOKです。また、strokeColorプロパティでは線の色を指定できます。

```swift
let layer = CAShapeLayer()
layer.frame = layer_frame                       // 位置、サイズを指定
layer.backgroundColor = UIColor.gray.cgColor    // 背景
layer.strokeColor = UIColor.cyan.cgColor        // 線の色
layer.fillColor = UIColor.gray.cgColor          // 図形（グラフ）の内側の色？（グラフを描画するならbackgroundColorと同じのほうがいいかも？）
layer.lineWidth = 3.0                           // 線の太さ
layer.path = create_sin_path()                  // 描画する図形（グラフ）
```

その他のプロパティについては公式ドキュメントで詳細が分かると思います。
[CAShapeLayer - Core Animation | Apple Developer Documentation](https://developer.apple.com/documentation/quartzcore/cashapelayer)

#### グラフの作成
`create_sin_path()`でsin関数のグラフを作成してます。細かい説明は省きますが、UIBezierPathでsin関数のx座標とy座標を線で繋げているだけです。
正直、ただの一本線でもアニメーションは確認できるのでsin関数にする必要ななかったですねw。

## 3. 作成したレイヤーにアニメーションを追加する
先ほど、作成したレイヤーにアニメーション処理を追加します。
以下のコードの部分ですね

```swift
layer.add(animation, forKey: nil)
```

最後にレイヤーを表示されているViewのlayerのsublayerとして追加すれば完成です。

```swift
view.layer.addSublayer(layer)
```

今回は、ViewController内にあるViewのlayerに追加しました。

# 結果
![ezgif-1-bd969d2f38e9.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278251/2540af57-f5de-7803-36e8-0fd7398783b4.gif)

できましたね！
今回は単純なアニメーションだったので少ないコードで書けましたが、もっと複雑なアニメーションも作れるようになりたいものですね...。

# 参考サイト
[iOSアプリ開発でアニメーションするなら押さえておきたい基礎](https://qiita.com/hachinobu/items/57d4c305c907805b4a53#cabasicanimation)
[Swiftで描画し、アニメーションするまでのメモ](https://qiita.com/dolfalf/items/4e094cf36104e4df5b9c)
[CAShapeLayer - Core Animation | Apple Developer Documentation](https://developer.apple.com/documentation/quartzcore/cashapelayer)