【Swift】フェードイン・アウト機能の実装方法

##はじめに
個人アプリ作成時にフェードイン・アウト機能を実装したので、
コマンドの共有をさせていただきます。

GitHubにコードを載せていますのでぜひご覧ください。
-> https://github.com/onishi-app/FeedInOut

###環境

・Apple Swift version 5.3
・XCode version 12.3


##完成形

⚠︎４秒おきにフェードイン・アウトが始まります。
![nC9RSKQZk5ihDQ0K6HQs1610263334-1610263350.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/9f1af831-3fb3-ac33-7572-3feda80af4a1.gif)


##コード

コードの完成形は下記のようになります。

`@IBOutlet weak var button: UIButton!`で分かる通り、
StoryBoardにオブジェクトを追加しコードと連携しているので
全文コピーしてもコンパイルエラーになります。

```swift:ViewController.swift

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!
    var timer = Timer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        button.layer.cornerRadius = 15
        
        // timer実装
        timer = Timer.scheduledTimer(withTimeInterval: 4.0, repeats: true, block: { (timer) in
            self.animateView(self.button)
        })
        
    }
    
    // フェードイン・アウトメソッド
    func animateView(_ viewAnimate: UIView) {
        UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn) {
            viewAnimate.alpha = 0
        } completion: { (_) in
            UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn) {
                viewAnimate.alpha = 1
            }
        }
    }
    
    // ボタンが押された時の処理
    @IBAction func buttonAction(_ sender: Any) {
        print("ボタンが押されました。")
    }
}
```

###呼び出し方法

`self.animateView(self.button)`で
フェードイン・アウトのメソッドを呼び出しています。

```swift:ViewController.swift

// timer実装
timer = Timer.scheduledTimer(withTimeInterval: 4.0, repeats: true, block: { (timer) in
    self.animateView(self.button)
})
```

###コードの説明

**〜フェードイン・アウト〜**

フェードイン・アウトを行うためのメソッド`animateView()`を定義します。
今回は、UIKitのUIViewクラスに実装されている`animate()`メソッドを使います。

```swift:ViewController.swift

// フェードイン・アウトメソッド
func animateView(_ viewAnimate: UIView) {
    UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn) {
        viewAnimate.alpha = 0
    } completion: { (_) in
        UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn) {
            viewAnimate.alpha = 1
        }
    }
}
```

UIViewクラスの中にはいくつかのanimate( )メソッドが記述されているのですが、
今回は下記のメソッドを使用していきたいと思います。

```swift:UIKit.UIView

open class func animate(withDuration duration: TimeInterval, delay: TimeInterval, options: UIView.AnimationOptions = [], animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil)
```

**第一引数：`withDuration duration: TimeInterval`**
withDurationの値は処理を行う時にかける時間を渡します。

今回は、`withDuration: 0.5,`のように、0.5秒かけて処理を行うようにしました。

**第二引数：`delay: TimeInterval`**
delayには、遅延させる時間を渡します。

delayに１が渡されていたら、メソッドが呼ばれてから１秒後に実行されます。
今回は、`delay: 0`のように遅延処理は実装しておりません。

**第三引数：`options: UIView.AnimationOptions = []`**
optionsには、アニメーション処理を行う際の挙動を指定しています。

optionsには多くの種類があるので一部紹介します。

|  | 挙動 |
|:-:|:-:|
| repeat  | アニメーションを無期限に繰り返す  |
| autoreverse  |  繰り返す場合は、アニメーションを前後に実行します |
| curveEaseInOut  | 始めと終わりを緩める動き(デフォルト)  |
| curveEaseIn  | 始めを緩める動き  |
| curveEaseOut  | 終わりを緩める動き |
| curveLinear  | 等速  |

この他にも色々あるので気になる方は調べてみてください！

今回は、`options: .curveEaseIn`のように、
はじめの動きを少し緩やかにしています。

**第四引数：`animations: @escaping () -> Void`**
animationsには、実行する処理を記述します。

クロージャとして定義されているのでクロージャの形で書きましょう。

今回は、`viewAnimate.alpha = 0`のように、
引数で渡されてきた値のalpha（透過）を0.5秒かけて０にしています。

alpha = 0 ということは透明にするということです。

**第五引数：`completion: ((Bool) -> Void)? = nil`**
completionには、処理が終了した後の処理を記述します。

今回は、`UIView.animate( ・・・ )`のように、
処理後にまたアニメーション処理を実行しています。

２回目のアニメーション処理は、**０.５秒かけてalpha（透過）を１**にしています。
つまり、表示されている状態にしている訳です。

２回目のcompletionは、行いたい処理が無いので記述していません。



**〜Timerの実装〜**

次にTimerの実装についてです。

Foundationに実装されている**Timerクラス**を使用します。
Timerクラスでは、**一定の時間で繰り返し処理を行うことができる機能**などが備わっています。

```swift:ViewController.swift

var timer = Timer()

// timer実装
timer = Timer.scheduledTimer(withTimeInterval: 4.0, repeats: true, block: { (timer) in
    self.animateView(self.button)
})
```

`Timer.scheduledTimer( ・・・ )`が繰り返しの処理になります。

今回は、viewDidLoad( )内に記述しているので、
画面が読み込まれた時点でTimerがスタートされます。

**scheduledTimer( )メソッドについて**

**第一引数：`withTimeInterval`**
こちらは、処理が何秒後に行われるかを記述します。

**第二引数：`repeats`**
こちらは、繰り返し処理を行うかどうかを指定します。

**trueの場合は繰り返し処理を行い、falseの場合は一度だけ行います。**

今回の場合はtrueなので、timerが呼び出されてから4秒後に１回目、
１回目の処理が呼び出されたてから４秒後に２回目・・・という感じで繰り返します。

フェードイン・アウトの時間が合計で１秒あるので、
何もアニメーションが行われない時間が３秒あることになります。

**第三引数：`block`**
こちらは、行いたい処理を記述します。
クロージャなので、関数の呼び出しなどには`self`キーワードをつける必要があります。

今回は、`self.animateView(self.button)`のように
フェードイン・アウトを４秒おきに繰り返し実行しています。

##最後に

これでフェードイン・アウト機能の実装完了です。

ただ、現状一つ弱点があります。
というのも、**アニメーション中はボタンが反応しないことです。**

なので、アニメーション中の１秒間はボタンがタップできず、
**フェードインしてから次のフェードアウトまでの３秒間しかタップできません。**

もしかしたらアニメーション中に処理を行う方法もあるかもしれませんが、
ぱっと見調べた感じ分からなかったので分かり次第追記したいと思います。

とりあえずは、ボタンをビューに変えてその上に同じサイズの透明のボタンを実装し、
そちらをタップするようにしておこうと思います・・・。

ご存知の方教えて頂けますと幸いです。

楽に実装できるのでぜひ皆さん使って見てください。

以上、最後までご覧いただきありがとうございました。