LoginSignup
230
206

More than 5 years have passed since last update.

アニメーション処理が楽になるSwift拡張メソッド 〜フェードイン・フェードアウト〜

Last updated at Posted at 2015-07-09

フェードイン・フェードアウト処理は、アプリ開発していて頻出の処理だと思います。
ベタ書きでも普通に書けちゃう程度の簡単な処理なのでそうしている人が多い気がしますが、メソッド用意しておくと地味にかなり便利です。

enum FadeType: NSTimeInterval {
    case
    Normal = 0.2,
    Slow = 1.0
}

extension UIView {
   func fadeIn(type: FadeType = .Normal, completed: (() -> ())? = nil) {
        fadeIn(duration: type.rawValue, completed: completed)
    }

    /** For typical purpose, use "public func fadeIn(type: FadeType = .Normal, completed: (() -> ())? = nil)" instead of this */
   func fadeIn(duration: NSTimeInterval = FadeType.Slow.rawValue, completed: (() -> ())? = nil) {
        alpha = 0
        hidden = false
        UIView.animateWithDuration(duration,
            animations: {
                self.alpha = 1
            }) { finished in
                completed?()
        }
    }
   func fadeOut(type: FadeType = .Normal, completed: (() -> ())? = nil) {
        fadeOut(duration: type.rawValue, completed: completed)
    }
    /** For typical purpose, use "public func fadeOut(type: FadeType = .Normal, completed: (() -> ())? = nil)" instead of this */
   func fadeOut(duration: NSTimeInterval = FadeType.Slow.rawValue, completed: (() -> ())? = nil) {
        UIView.animateWithDuration(duration
            , animations: {
                self.alpha = 0
            }) { [weak self] finished in
                self?.hidden = true
                self?.alpha = 1 
                completed?()
        }
    }
}

メリット

ベタに書いていると「本当はふわっと表示させた方がベターかもだけど、面倒だからパリッと表示させちゃうこと」などもあり得ると思いますが、簡単に呼べるようにしているとそういう手抜き心を防ぎやすいです( ´・‿・`)

特に、alpha: 0の時に丁寧にhidden: trueにするのが地味に面倒に感じてました(´-ω-`)

また、 アニメーションのdurationに統一感が生まれます。 さらに、後から統一的に値を変更するのも容易です。
上の例の場合、あっさりしたところは0.2秒、じわっと切り替えたいところは1秒、と基本2通りで、どうしてもそれ以外使いたい時はduration直接指定も可能、という作りになってます。
ベタ書きだと、この管理がわりと適当になっちゃったりしがちです。特にチーム開発だと。

呼び出し

単純な呼び出し

let view = UIView()
// これだけでふわっとフェードイン
view.fadeIn(type: .Slow)
// 完了タイミングで何かしたい場合はクロージャーも記述
view.fadeIn(type: .Slow) { [weak self] in
    self?.someMethod()
}

SDWebImageと組み合わせた例

【追記】Kingfisherで次のように書くのがオススメです

imageView.kf.setImage(with: url,
                      placeholder: placeholder,
                      options: [.transition(ImageTransition.fade(1))])
  • placeholder引数は省略しても良いですが、適当な空画像を指定すると、よりキレイです
  • SDWebImageでも同様に簡単な方法があったかもしれないですが調べていません

--- 以下原文です ---

こんな感じにSDWebImageを使った処理を定義しておくと、キャッシュ済みの時は即時表示で、ダウンロードしてきた時はふわっとフェードイン表示という処理を1行で呼び出せます。

extension UIImageView {    
    func setImageSmoothly(url: NSURL, placeholderImage: UIImage?) {
        sd_setImageWithURL(url, placeholderImage: placeholderImage) { [weak self] image, error, cacheType, imageUrl in
            if error != nil {
                return
            }
            if image != nil && cacheType == .None {
                self?.fadeIn(type: FadeType.Slow)
            }
        }
    }
}

// これだけで画像をダウンロードしつつふわっとフェードイン表示できる
imageView.setImageSmoothly(url, placeholderImage: nil)

こういう共通処理はどこに書いてる?

ライブラリとして切り出すか迷うところですが、要件に合わせてオプション引数とか追加したくなるし、このあたりは自分のソースとして書いちゃった方が取り回し良く感じちゃいます。
実際、ここに載せたのも、実際のコードはもう少し複雑になってます。

Embedded Frameworkを利用

僕はこれ系はEmbedded Frameworkに定義しちゃってます。
命名は迷いつつ、UIView+Lib.swiftとしてます。
Embedded Frameworkにする場合、呼び出し可能にするために、enumとメソッドにpublic指定が必要です。
finalも付けるとベターです。
参考: Swiftのfinal・private・Whole Module Optimizationを理解しDynamic Dispatchを減らして、パフォーマンスを向上する - Qiita

メソッド名は、衝突防ぐようにプレフィックス付けた方が良いなど言われていますが、僕はライブラリとして公開とかするのでは無ければ、無くて良いかなと思っています。

Embedded Frameworkにすると名前空間分かれる言っても、特に既存クラスの拡張メソッドの場合はそれ関係無く衝突は避けられないのでやや不確実ではありますが。(importをカットすれば一応避けられるものの)

230
206
10

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
230
206