1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

iOSAdvent Calendar 2021

Day 10

UIView.animateくらい簡単にカスタムイージング

Last updated at Posted at 2021-12-09

UIKitを使ったアニメーションでカスタマイズしたイージングを使う方法がわかったので共有です。色々調べた結果、UIView.animateくらい簡単にカスタムイージングを設定することができました。

イージングとは... アニメーションの緩急のこと。これを調整することで、ぬるっとしたアニメーションなどを実現させることができます。 [世界一わかりやすい「イージング」と、その応用|ritar|note](https://note.com/ritar/n/n5e8ed0e07917)とかがわかりやすいと思います。

イージングの調整は、3次ベジェ曲線でパラメータを指定します。(他の指定方法を知らないだけかもしれません...)
3次ベジェ曲線とイージングの関係や調整はcubic-bezier.comで色々試してみると理解しやすいと思います。

UIView.animateでのイージング

iOSでのアニメーションといえばUIView.animateがとてもお手軽です。イージングは options: 引数で指定できるのですが、規定のもの(.curveLinear, .curveEaseIn, .curveEaseOut, .curveEaseInOut)しか使うことができません。

UIView.animate(withDuration: 1.0, delay: 0, options: .curveLinear) {
    linearConstraint.constant = 288
    liveView.layoutIfNeeded()
}

カスタマイズしたイージングを使おうと思うと、CoreAnimationのCAMediaTimingFunctionなどを使うことになるのかなと思っていたのですが、UIViewPropertyAnimatorを使えば、UIKitの世界だけでとても簡単に実現できました。

UIViewPropertyAnimatorでのイージング

UIViewPropertyAnimator(duration: 1.0,
                       controlPoint1: .init(x: 0.2, y: 1),
                       controlPoint2: .init(x: 1, y: 0.2)) {
    customEasingConstraint.constant = 288
    liveView.layoutIfNeeded()
}.startAnimation()

UIView.animateとそれほど変わらないコード量で実装できました 🎉
コードを解説しておくと以下の通りとなります。

UIViewPropertyAnimator()
の引数
説明
duration: アニメーション秒数。
UIView.animatewithDuration:と同じ。
controlPoint1,
controlPoint2
イージングの設定。3次ベジェ曲線の制御点を指定する。

上の例では途中で止まるような極端な例にしています。
c.f. https://cubic-bezier.com/#0,1,1,0
animations:
(上の例ではtrailingClosure)
UIView.animateanimations:と同じ。

UIViewPropertyAnimatorをインスタンス化して、 startAnimation() でアニメーションを開始できます。

Demo

  • 青:UIView.animateのoption指定なし(デフォルトで .curveEaseInOut が適用される)
  • 赤:UIView.animateのoption: .curveLinear
  • 緑:UIViewPropertyAnimator

easing.gif

アニメーションGIFなのでわかりにくいかもですが、緑のバーは途中で止まって再開するようなイージングになっています。

Demoコード

Demoに使ったコードの全体です。Playgroundで実行できる形になってますので、ぜひ動かしてみてください。

import UIKit
import PlaygroundSupport

// レイアウト準備
let frame = CGRect(x: 0, y: 0, width: 320, height: 320)
let liveView = UIView(frame: frame)
liveView.backgroundColor = .white
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .leading
stackView.spacing = 8
liveView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    stackView.leadingAnchor.constraint(equalTo: liveView.leadingAnchor,
                                       constant: 16),
    stackView.centerYAnchor.constraint(equalTo: liveView.centerYAnchor)
])
let animateView = UIView()
animateView.backgroundColor = .blue
let linearView = UIView()
linearView.backgroundColor = .red
let customEasingView = UIView()
customEasingView.backgroundColor = .green
[animateView, linearView, customEasingView].forEach {
    stackView.addArrangedSubview($0)
}
let animateConstraint = animateView.widthAnchor.constraint(equalToConstant: 0)
let linearConstraint = linearView.widthAnchor.constraint(equalToConstant: 0)
let customEasingConstraint = customEasingView.widthAnchor.constraint(equalToConstant: 0)
NSLayoutConstraint.activate([
    animateConstraint,
    animateView.heightAnchor.constraint(equalToConstant: 32),
    linearConstraint,
    linearView.heightAnchor.constraint(equalToConstant: 32),
    customEasingConstraint,
    customEasingView.heightAnchor.constraint(equalToConstant: 32)
])
liveView.layoutIfNeeded()

// アニメーション
UIView.animate(withDuration: 1.0) {
    animateConstraint.constant = 288
    liveView.layoutIfNeeded()
}
UIView.animate(withDuration: 1.0, delay: 0, options: .curveLinear) {
    linearConstraint.constant = 288
    liveView.layoutIfNeeded()
}
UIViewPropertyAnimator(duration: 1.0,
                       controlPoint1: .init(x: 0.2, y: 1),
                       controlPoint2: .init(x: 1, y: 0.2)) {
    customEasingConstraint.constant = 288
    liveView.layoutIfNeeded()
}.startAnimation()

// Playground準備
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = liveView

completionもつけれる

UIView.animateにも用意されているcompletionもつけることができます。その場合は一度変数で受ける必要があります。

let animator = UIViewPropertyAnimator(duration: 1.0,
                       controlPoint1: .init(x: 0.2, y: 1),
                       controlPoint2: .init(x: 1, y: 0.2)) {
    customEasingConstraint.constant = 288
    liveView.layoutIfNeeded()
}
animator.addCompletion { _ in
    customEasingView.backgroundColor = .black
}
animator.startAnimation()

その他にも、一時停止したり、逆再生したり、UIView.animateより複雑なことができるようです(試してない)
もっと使いこなしたい方は UIViewPropertyAnimatorを使いこなそう - Qiita こちらの記事がとても参考になると思います。

まとめ

これで気軽にイージングが試せますね💪
どなたかの助けになれば幸いです🙏

参考

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?