最近iOSのAnimation/Transition周りを触ったので、勉強の意味も込めてQiitaに書きたいと思います。
iOSのAnimation/Transitionの実装
今回はTinder的な画面遷移を実装してみたいと思います。
実装後の状態
ソースコードはこちらにあげています。
まず、大きく分けて、画像部分のTransitionの実装と、下の三つのボタンのAnimationがあります。
プロフィール画像部分のTransitionの実装
最初に上のgifでいうメガネのおじさん(ImageView)の遷移時の動きを実装していきます。
必要なプロトコル
・ UIViewControllerAnimatedTransitioning (遷移時の動きを定義する)
・ UIViewControllerTransitioningDelegate (遷移先のcontrollerで適応するdelegate)
概要
まず、UIViewControllerAnimatedTransitioningを実装します。
UIViewControllerAnimatedTransitioning内に遷移時の動きを定義していきます。
transitionContextで遷移先/遷移元のViewControllerが取得できます。
//遷移時のアニメーションを定義
class PresentedAnimater : NSObject, UIViewControllerAnimatedTransitioning {
let duration = 0.3
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
//animationのdurationを返す
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) else { return }
let container = transitionContext.containerView
let imageView = (fromVC as! FirstViewController).copyImageView() // <-このcopyImageView()は遷移元のImageViewと同じ大きさ、位置のImageViewを返す
let destImageViewRect = (toVC as! SecondViewController).copyImageView().frame // <-このcopyImageView()は遷移先のImageViewと同じ大きさ、位置のImageViewを返す
container.addSubview(toVC.view)
container.addSubview(imageView)
toVC.view.alpha = 0.0
UIView.animate(withDuration: duration, delay: 0.01, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.1, options: [], animations: {
toVC.view.alpha = 1.0
imageView.frame = destImageViewRect
}, completion: {_ in
imageView.isHidden = true
transitionContext.completeTransition(true)
})
}
}
//Dismiss時のアニメーションを定義
class DismissedAnimater : NSObject, UIViewControllerAnimatedTransitioning {
let duration = 0.3
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
//animationのdurationを返す
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) else { return }
let container = transitionContext.containerView
let imageView = (fromVC as! SecondViewController).copyImageView()
let destImageViewRect = (toVC as! FirstViewController).copyImageView().frame
container.addSubview(toVC.view)
container.addSubview(imageView)
toVC.view.alpha = 0.0
UIView.animate(withDuration: duration, delay: 0.01, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.1, options: [], animations: {
toVC.view.alpha = 1.0
imageView.frame = destImageViewRect
}, completion: {_ in
transitionContext.completeTransition(true)
})
}
}
次に、UIViewControllerTransitioningDelegate部分を書いていきます。
extension SecondViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// 画面遷移時に呼ばれるメソッド
return PresentedAnimater()
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// Dismiss時に呼ばれるメソッド
return DismissedAnimater()
}
}
あとは、SecondViewControllerのviewDidLoadあたりで、
self.transitioningDelegate = self
としてあげれば完成です。
下の三つのボタン部分のAnimationの実装
最後に上のgifでいう三つのボタンが外側からすっと出てくるアニメーションを実装します。
こちらは先ほどのTransitionとは違って遷移後にボタンをアニメーションさせます。
概要
まずはUIButtonを継承したものを実装します。
class MovableUIButton: UIButton {
let repeatCount: Float = 1
let dumping: CGFloat = 15
let duration: Double = 1
let initialVelocity: CGFloat = 15
let fromBottomKey = "appearFromBottom"
let fromRightKey = "appearFromBottom"
let fromLeftKey = "appearFromBottom"
func appearFromBottom(){
//今回はボタンを動かすだけなので、"position"
let animation = CASpringAnimation(keyPath: "position")
//アニメーションのdurationを定義
animation.duration = duration
//アニメーションのrepeatの回数を定義
animation.repeatCount = repeatCount
//ハネ具合を定義 (この値が小さいほど伸縮します)
animation.damping = dumping
//最初の速度を定義
animation.initialVelocity = initialVelocity
//アニメーション開始点を定義
animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x, y: self.center.y + 100))
//アニメーション終了点を定義
animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x, y: self.center.y))
self.layer.add(animation, forKey: fromBottomKey)
}
func appearFromRight(){
let animation = CASpringAnimation(keyPath: "position")
animation.duration = duration
animation.repeatCount = repeatCount
animation.damping = dumping
animation.initialVelocity = initialVelocity
animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x + 100, y: self.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x, y: self.center.y))
self.layer.add(animation, forKey: fromRightKey)
}
func appearFromLeft(){
let animation = CASpringAnimation(keyPath: "position")
animation.duration = duration
animation.repeatCount = repeatCount
animation.damping = dumping
animation.initialVelocity = initialVelocity
animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 100, y: self.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x, y: self.center.y))
self.layer.add(animation, forKey: fromLeftKey)
}
}
次にStoryBoard内でUIButtonのこの部分をMovableUIButtonにセットします。
あとは、ControllerのviewDidLayoutSubviewsあたりで、
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
superlikeButton.appearFromBottom()
nopeButton.appearFromLeft()
likeButton.appearFromRight()
}
とすれば、Buttonを動かすことができます。
最後に
こちらにソースコードをあげています。