LoginSignup
9
5

More than 5 years have passed since last update.

AutoLayoutでアニメーションさせる話

Last updated at Posted at 2018-12-18

この記事はYahoo! JAPAN 18 新卒 2 Advent Calendar 2018 18日目の記事です。
前回の記事は@jiazioさんのSRv6で8パズルを解いてみるでした。

1枚目はこちら Yahoo! JAPAN 18 新卒 Advent Calendar 2018

はじめに

最近、アプリにアニメーションとか入れてリッチにしたいなーと思ってるんですが、入社してデザイナーさんとも一緒に作業することも多く、アニメーションさせるにもAutoLayoutを使っている前提でのアニメーションなどが必要になる機会が増えてきたので、そのあたりをまとめてみたいと思います。

作るもの

アニメーションさせるにもなるべくコードを書かずに以下の様なアニメーションを実現します。
autoLayout.gif

緑のViewに合わせてグレーのViewも変化していますが、やってることは緑のViewの左側の幅を変更しているだけです。
変更の仕方にも今回は2パターン記述します。

constantを変更する

水平方向の制約については以下の様に付けています。
スクリーンショット 2018-12-17 8.01.37.png

緑のViewの左側のconstantの変化をグレーのViewのwidthで吸収するイメージです。コードで変化させるNSLayoutConstraintだけ、関連付けして値を動かせる様にします。

swift4.0

class ViewController: UIViewController {

    @IBOutlet weak var leftConstraint: NSLayoutConstraint!
    var isMoved = false

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func tappedButton(_ sender: UIButton) {
        leftConstraint.constant = isMoved ? 50 : 100
        isMoved = !isMoved
        UIView.animate(withDuration: 0.3) {
            self.view.layoutIfNeeded()
        }
    }
}

もし、constantの値をベタ書きするのが嫌な場合は@IBInspectableでstoryboard側に持たせましょう。この方がデザイナーさんに優しい。storyboardはこうなります。
スクリーンショット 2018-12-17 9.00.07.png

swift4.0

class ViewController: UIViewController {

    @IBInspectable var defaultConstant: CGFloat = 0.0
    @IBInspectable var movedConstant: CGFloat = 0.0

    @IBOutlet weak var leftConstraint: NSLayoutConstraint!
    var isMoved = false

    override func viewDidLoad() {
        super.viewDidLoad()
        leftConstraint.constant = defaultConstant
    }

    @IBAction func tappedButton(_ sender: UIButton) {
        leftConstraint.constant = isMoved ? movedConstant : defaultConstant
        isMoved = !isMoved
        UIView.animate(withDuration: 0.3) {
            self.view.layoutIfNeeded()
        }
    }
}

isActiveで制約を使い分ける

次に、2つめのやり方として NSLayoutConstraint.isActiveを変更する方法があります。
先ほどと同じ動作をさせて見ましょう。

その際に緑のViewの左の幅の制約を2つ付ける必要があります。
素直に入れると以下の様に怒られます。
スクリーンショット 2018-12-17 7.26.02.png

基本的に制約を付けると、priority = 1000となっているので、移動後に有効にしたい制約のpriorityを下げて差を付けます。他の制約との兼ね合いを見ながら調整します。ここでは900にしてます。
スクリーンショット 2018-12-17 7.45.33.png

1000より小さくすると破線になります。
スクリーンショット 2018-12-17 7.26.30.png

この状態で、以下の様に実装すると同様の動きが得られます。

swift4.0

class ViewController: UIViewController {

    @IBOutlet var defaultLeftConstraint: NSLayoutConstraint!
    @IBOutlet var movedLeftConstraint: NSLayoutConstraint!

    var isMoved = false

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func tappedButton(_ sender: UIButton) {
        defaultLeftConstraint.isActive = !isMoved
        movedLeftConstraint.isActive = isMoved
        isMoved = !isMoved

        UIView.animate(withDuration: 0.3) {
            self.view.layoutIfNeeded()
        }
    }
}

swift4.2

class ViewController: UIViewController {

    @IBOutlet var defaultLeftConstraint: NSLayoutConstraint!
    @IBOutlet var movedLeftConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func tappedButton(_ sender: UIButton) {
        defaultLeftConstraint.isActive.toggle()
        movedLeftConstraint.isActive.toggle()

        UIView.animate(withDuration: 0.3) {
            self.view.layoutIfNeeded()
        }
    }
}

注意する点として、 NSLayoutConstraintweakにしていると、isActive = falseのときに制約自体がnilになってしまうので、このやり方の場合は強参照で持たせましょう。

constantを変更する方がシンプルで簡単ですが、cellなどの利用して複雑な構成になってくるとisActiveで制約を使い分ける方法も有効になって来ます。

終わりに

サクッと触りの部分だけ書きましたが、もっと色々追求して行こうと思ってます。こんなのもあるよーなどあれば教えてくださいー!
追記 4.2からtoggle()が良いよって話があったので、それ用にもコード足しました。

9
5
0

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
9
5