[Swift5]Viewをアニメーションさせる


#1.はじめに
久しぶりの投稿です。
作成するアプリをリッチにするために、アニメーションを覚えてみたいと思い、調べてみました。

#2.全体のソースコード
ソースコードはこんな感じです。UIButtonを作って、押したらそのボタンが動きます。コピペでいけるので試してみてください。

```Swift:animation.swift
import UIKit



class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        

        // UIButtonのインスタンスを作成する
        let button = UIButton()
        
        // ボタンを押した時に実行するメソッドを指定
        button.addTarget(self, action: #selector(upEvent(_:)), for: UIControl.Event.touchUpInside)
        // ボタンを押した時に実行するメソッドを指定
        button.addTarget(self, action: #selector(downEvent(_:)), for: UIControl.Event.touchDown)
        
        // ラベルを設定する
        button.setTitle("Enter", for: UIControl.State.normal)
        
        button.layer.borderColor = UIColor(red: 0, green: 60/255, blue: 0, alpha: 1).cgColor
        button.layer.borderWidth = 3
        button.layer.cornerRadius = 8

        // サイズを決める(自動調整)
        button.frame.size = CGSize(width: 100, height: 40)
        
        // 位置を決める(画面中央)
        button.center = self.view.center
        
        // viewに追加する
        self.view.addSubview(button)
        // Do any additional setup after loading the view.
    }


    // ボタンが押された時に呼ばれるメソッド
    @objc func downEvent(_ sender: UIButton) {
        let beforeCenter : CGPoint! = sender.center
        sender.frame.size = CGSize(width: 80, height: 30)
        sender.center = beforeCenter
    }
    
    // ボタンが押された時に呼ばれるメソッド
    @objc func upEvent(_ sender: UIButton) {
        let beforeCenter : CGPoint! = sender.center
        sender.frame.size = CGSize(width: 100, height: 40)
        sender.center = beforeCenter
        
        
        
        let animationGroup = CAAnimationGroup()
        animationGroup.duration = 0.5
        animationGroup.fillMode = CAMediaTimingFillMode.forwards
        animationGroup.isRemovedOnCompletion = false
        
        //大きさ(transform.scale)を1.5倍にする
        let animation1 = CABasicAnimation(keyPath: "transform.scale")
        animation1.fromValue = 1.0
        animation1.toValue = 1.5
        
        //透明度(opacity)を1から0にする
        let animation2 = CABasicAnimation(keyPath: "opacity")
        animation2.fromValue = 1.0
        animation2.toValue = 0.0
        
        //位置(position.x)を変更する
        let animation3 = CABasicAnimation(keyPath: "position.x")
        animation3.fromValue = sender.center.x
        animation3.toValue = sender.center.x + 50
        
        //右回りに回転する(transform.rotation.z)する
        let animation4 = CABasicAnimation(keyPath: "transform.rotation.z")
        animation4.toValue = Double.pi * 2
        
        //アンカーポイントを設定（※viewの位置も変わるため、frameを事前に変数に格納する）
        let frm = sender.frame
        sender.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
        sender.frame = frm
        
        animationGroup.animations = [animation1,animation2,animation3,animation4]
        sender.layer.add(animationGroup, forKey: nil)
    }

}

```
#3.解説
アニメーション部分に該当する以下の各部分ごとに解説していきます。
UIButtonについては割愛します。

```Swift:animation.swift
let animationGroup = CAAnimationGroup()
        animationGroup.duration = 0.5
        animationGroup.fillMode = CAMediaTimingFillMode.forwards
        animationGroup.isRemovedOnCompletion = false
        
        //大きさ(transform.scale)を1.5倍にする
        let animation1 = CABasicAnimation(keyPath: "transform.scale")
        animation1.fromValue = 1.0
        animation1.toValue = 1.5
        
        //透明度(opacity)を1から0にする
        let animation2 = CABasicAnimation(keyPath: "opacity")
        animation2.fromValue = 1.0
        animation2.toValue = 0.0
        
        //位置(position.x)を変更する
        let animation3 = CABasicAnimation(keyPath: "position.x")
        animation3.fromValue = sender.center.x
        animation3.toValue = sender.center.x + 50
        
        //右回りに回転する(transform.rotation.z)する
        let animation4 = CABasicAnimation(keyPath: "transform.rotation.z")
        animation4.toValue = Double.pi * 2
        
        //アンカーポイントを設定（※viewの位置も変わるため、frameを事前に変数に格納する）
        let frm = sender.frame
        sender.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
        sender.frame = frm
        
        animationGroup.animations = [animation1,animation2,animation3,animation4]
        sender.layer.add(animationGroup, forKey: nil)
```

##3-1.アニメーショングループの作成
わかんないなりに書いていきます。一応動きます。(一番ダメなやつ。。。)

###3-1-1.ソースコード
```Swift:animation.swift
let animationGroup = CAAnimationGroup()
        animationGroup.duration = 0.5
        animationGroup.fillMode = CAMediaTimingFillMode.forwards
        animationGroup.isRemovedOnCompletion = false
```

アニメーションを複数同時に実行するために、CAAnimationGroup型の変数を作成します。
後述する各アニメーションで共通する部分の設定はここで行なっていきます。

###3-1-2.時間の設定
```Swift:animation.swift
animationGroup.duration = 0.5
```
この部分で、アニメーションの長さを設定しています。
0.5とすることで、0.5秒かけてアニメーションが行われます。

###3-1-3.おまじない①
```Swift:animation.swift
animationGroup.fillMode = CAMediaTimingFillMode.forwards
```
ここはよくわからなかったのですが、各サイトでみんな入れているので入れます。
どういったときに変更するのかご存知の方がいましたらお教えください。

###3-1-4.おまじない②
```Swift:animation.swift
animationGroup.isRemovedOnCompletion = false
```

アニメーションはViewに追加することで実行されるのですが、これはアニメーションが終了したときにViewから取り除くかどうかの設定、らしいです。

私が実装するときはアニメーションは都度Viewに追加しているので、取り除こうが取り除くまいが関係ない気もしますが、各サイトに倣って取り除かないようにしています。

##3-2.各アニメーションの設定
###3-2-1.大きさを変更する
```Swift:animation.swift
//大きさ(transform.scale)を1.5倍にする
        let animation1 = CABasicAnimation(keyPath: "transform.scale")
        animation1.fromValue = 1.0
        animation1.toValue = 1.5
```
大きさを変更するアニメーションです。
徐々に大きくなるようなアニメーションが実現できます。

```Swift:animation.swift
let animation1 = CABasicAnimation(keyPath: "transform.scale")
```

ここでアニメーショングループに追加するアニメーション(CABasicAnimation)の変数を作成します。keyPathに"transform.scale"を与えてあげることで、サイズ変更のアニメーションになります。

```Swift:animation.swift
animation1.fromValue = 1.0
animation1.toValue = 1.5
```

ここで大きさを変更するアニメーションのプロパティを設定しています。
fromValue:アニメーション開始時の大きさ(元の大きさを1.0とする)
toValue:アニメーション終了時の大きさ(元の大きさを1.0とする)

わかりやすいですね。始まりと終わりだけ教えてあげればOKです。

###3-2-2.透明度を変更する
```Swift:animation.swift
        //透明度(opacity)を1から0にする
        let animation2 = CABasicAnimation(keyPath: "opacity")
        animation2.fromValue = 1.0
        animation2.toValue = 0.0
```

透明度を変えます。
「3-2-1.大きさを変更する」と違うところは、keyPathを"opacity"にしています。
これにfromValueとtoValueを設定すれば、少しずつ消えて行ったり、逆に少しずつ出現したりと行ったリッチなアニメーションが簡単に作れます。

###3-2-3.位置を変更する
```Swift:animation.swift
        //位置(position.x)を変更する
        let animation3 = CABasicAnimation(keyPath: "position.x")
        animation3.fromValue = sender.center.x
        animation3.toValue = sender.center.x + 50
```

位置を変えます。
「3-2-1.大きさを変更する」と違うところは、keyPathを"position.x"にしています。
これにfromValueとtoValueを設定することで、左右にViewを動かすことができます。
fromValueとtoValueは、座標の位置を設定してください。1に設定すると端っこになってしまうので、基本的にはViewのcenter.xから取得すると良いと思います。

また、上下に動かしたい場合はkeyPathを"position.y"に設定すればOKです。

###3-2-4.回転させる
```Swift:animation.swift
//右回りに回転する(transform.rotation.z)する
        let animation4 = CABasicAnimation(keyPath: "transform.rotation.z")
        animation4.toValue = Double.pi * 2
```

位置を変えます。
「3-2-1.大きさを変更する」と違うところは、keyPathを"transform.rotation.z"にしています。
また、プロパティはtoValueのみ設定し、円周率(Double.pi)*2でz軸上を一回転しています。半周させたい場合は「*2」は不要で、「Double.pi * 2 * 0.25」などとすることで90度回転なども可能です。

keyPathを"transform.rotation.x"や"transform.rotation.y"に設定すれば回転の軸を変えることができます。(※実際に試して感覚をつかんでみてください)

また、回転の中心となる位置は以下のようにすることで変更が可能です。

```Swift:animation.swift
        //アンカーポイントを設定（※view自体の位置も変わるため、frameを事前に変数に格納する）
        let frm = sender.frame
        sender.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
        sender.frame = frm
```

##3-3.アニメーションを実行

```Swift:animation.swift
animationGroup.animations = [animation1,animation2,animation3,animation4]
        sender.layer.add(animationGroup, forKey: nil)
```
アニメーショングループに各アニメーションを追加し、レイヤーにアニメーショングループを追加します。

#3.おわりに
テスト用なので拡大しつつフェードアウトして回りながら右に移動するという、妙な動きになっていますが、好きなように変更して良いアニメーションライフをお過ごしください。