2
1

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.

モーダル遷移のアニメーションをカスタムする

Posted at

はじめに

アプリでモーダル遷移するときにデフォルトのアニメーションがいくつか用意されていますが、それ以外のアニメーションを使いたいときは、自分でカスタムすることができるようです。
今回は、UIViewControllerAnimatedTransitioningを使って、横スワイプしたときのようなアニメーションを実装してみました。

実装イメージ

カスタムアニメーション.gif

コード

モーダルを呼び出す側のコード

BaseViewController.swift
import UIKit

class BaseViewController: UIViewController {
    
    @IBOutlet weak var buttonShow: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
    
    @IBAction func showModalView(_ sender: Any) {
        guard let modalVC = UIStoryboard(name: String(describing: CustomModalViewController.self), bundle: nil)
                .instantiateInitialViewController() as? CustomModalViewController else { return }
        
        modalVC.modalPresentationStyle = .fullScreen
        present(modalVC, animated: true)
    }
}

モーダルとして呼び出される側のコード

CustomModalViewController.swift
import UIKit

class CustomModalViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        transitioningDelegate = self
    }
    
    @IBAction func touchCloseButton(_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }
    
}

// モーダルとして呼び出す側に「UIViewControllerTransitioningDelegate」を継承させる。
// MARK: UIViewControllerTransitioningDelegate
extension CustomModalViewController: UIViewControllerTransitioningDelegate {
    
    // presentのときに呼ばれる
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return CustomAnimater(animationMode: .present)
    }

    // dismissのときに呼ばれる
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return CustomAnimater(animationMode: .dismiss)
    }
}

アニメーションを定義するクラス

CustomAnimeter.swift
class CustomAnimater: NSObject {
    
    // 開くと閉じるしかないので、enumで定義しておくと楽
    enum AnimationMode {
        case present, dismiss
    }
    private let animationMode: AnimationMode
    private let duration = 0.3
    
    init(animationMode: AnimationMode) {
        self.animationMode = animationMode
    }
}

extension CustomAnimater: UIViewControllerAnimatedTransitioning {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return duration
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if animationMode == .present {
            guard let toView = transitionContext.view(forKey: .to) else { return }
            guard let fromView = transitionContext.view(forKey: .from) else { return }
            transitionContext.containerView.addSubview(toView)
            toView.alpha = 0.0
            toView.transform = CGAffineTransform(translationX:fromView.bounds.size.width * 0.8 , y: 0)
            UIView.animate(withDuration: 0.5, delay: 0) { [weak self] in
                guard let _ = self else { return }
                toView.transform = CGAffineTransform(translationX: 0, y: 0)
                toView.alpha = 1.0
            } completion: { finish in
                toView.transform = .identity
                transitionContext.completeTransition(finish)
            }
        } else {
            guard let toView = transitionContext.view(forKey: .to) else { return }
            guard let fromView = transitionContext.view(forKey: .from) else { return }
            transitionContext.containerView.addSubview(toView)
            transitionContext.containerView.addSubview(fromView)
            fromView.alpha = 1
            UIView.animate(withDuration: 0.5, delay: 0) {
                fromView.alpha = 0.0
                fromView.transform = CGAffineTransform(translationX:-fromView.bounds.size.width * 0.8 , y: 0)
            } completion: { finish in
                // transformをもとに戻さないとバグの要因になることがあるみたい
                fromView.transform = .identity
                transitionContext.completeTransition(finish)
            }
        }
    }
}
2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?