今日のゴール

  • UIImageViewを点滅させて一定時間後に止める move.gif

やったこと

  • 放置型育成ゲームの戦闘画面を作っている中で、キャラが攻撃するよ!ってのを視覚的に表すために行動するキャラを点滅させる必要性が出てきたよ
  • 単純に点滅させるだけなら下のようにUIView.animationでalpha変えて点滅させるのを繰り返せばいいんだけど、どうやって止めればいいかわからなかったよ
UIViewController.swift
~~~省略~~~
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        UIView.animate(withDuration: 0.1, delay: 0.0, options: .repeat, animations: {
            self.imageView.alpha = 0.0
        }, completion: nil)
    }
~~~省略~~

方針を決めるよ

  • 点滅を実現するにあたって下記のように方針を決めたよ
    • 点滅は0.1秒間にalpha値を上げ下げすることで実現させる
    • 0.3秒後(点滅が3回繰り返されたら)に点滅を止める

countを使って止める

  • こんな感じでcount変数使って止められないか検証したよ
ViewController.swift
    @IBOutlet weak var imageView: UIImageView!
    private var count = 0
    override func viewDidLoad() {
        super.viewDidLoad()

        UIView.animate(withDuration: 0.1, delay: 0.0, options: .repeat, animations: {
            if self.count < 4 {
                self.imageView.alpha = 0.0
            }
        }) { (finished) in
            self.count += 1
        }
        // Do any additional setup after loading the view, typically from a nib.
    }
  • ところがそもそもanimationsの中が1回しか呼ばれなくてこの方法では断念したよ・・・

Timerを使って時限的に止める

  • 0.3秒後に発火するTimerを作って止められないか検証したよ
ViewController.swift
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        UIView.animate(withDuration: 1.0, delay: 0.0, options: .repeat, animations: {
            self.imageView.alpha = 0.0
        }, completion: nil)

        Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(stopFlashing), userInfo: nil, repeats: false)

    }

    @objc func stopFlashing() {
        self.imageView.alpha = 1.0
        self.imageView.layer.removeAllAnimations()
    }
  • とりあえずこれで目標は達成できたよ

ダメコードみが深い:innocent:

  • なんというかちょっとダサいのでもうちょっとうまく書けないか調べてたらこんな書き方ができるとわかったよ
ViewController.swift
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        UIView.animate(withDuration: 1.0, delay: 0.0, options: .repeat, animations: {
            self.imageView.alpha = 0.0
        }, completion: nil)

        Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { (_) in
            self.imageView.alpha = 1.0
        }
    }

割とすっきり!

もう拡張しちゃおう!

  • やりたいことができたので、UIImageViewだけじゃなくてUIView全体に拡張しちゃいました
  • これでUIImageViewだけじゃなく、UIViewを継承しているサブクラスでも点滅が実現できるはず
  • ついでに点滅が終わった後に処理を書きたい場合が出てきたので、クロージャーを追加して「点滅アニメーションが終わった後に何か処理をする」というのを実現させてみたよ
UIView+Flashing.swift
extension UIView {
    func flash(withDuration: TimeInterval, delay: TimeInterval = 0.0, interval: TimeInterval, after:(() -> Void)? = nil) {
        UIView.animate(withDuration: withDuration, delay: delay, options: .repeat, animations: {
            self.alpha = 0.0
        }, completion: nil)

        Timer.scheduledTimer(withTimeInterval: interval, repeats: false) { (_) in
            self.alpha = 1.0
            after?()
        }
    }
}
ViewController.swift
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 拡張メソッドのflashを呼び出すだけ
        imageView.flash(withDuration: 0.1, timeInterval: 0.3) {
            // アニメーションが終わった後にやりたい処理を書く
            print("終わったよ")
        }
    }

結果

_人人人人人人人人人_
> 割と使いやすい <
 ̄Y^Y^Y^Y^Y^Y^Y^Y ̄

最後に

(最近さぼりがちですが)放置型育成ゲームを作ってます:metal:
よければそちらも見ていってください:pray:
https://qiita.com/nasteng/items/49551d74267809ccf2fc