Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

pisces/AnimatedTransitionKit

Open more actions menu

Repository files navigation

AnimatedTransitionKit

Swift Objective-c Version License Platform Carthage Compatible

  • It's the very simple library to apply transitioning to between scene and other scene

Features

  • Simple integration of custom transitioning for UIViewController and UINavigationController
  • Provides various types of custom transitioning
  • Support percent driven interactive transtions
  • Expandable

Example

   

Transition types for UIViewController

  • Move
  • DragDrop
  • Fade
  • Zoom

Transition types for UINavigationController

  • Move

Gestures

  • Pan
  • Pinch

Import

Objective-C

#import <AnimatedTransitionKit/AnimatedTransitionKit.h>

Swift

import AnimatedTransitionKit

🔥Using AnimatedTransition

Using ZoomTransition Simply

Using single transition for dismission with pan gesture

import AnimatedTransitionKit

let secondVC = SecondViewController()
secondVC.transition = ZoomTransition()
present(secondVC, animated: true, completion: nil)

Using ZoomTransition Deeply

Using pair transitions for presenting and dismission both with pinch gesture

import AnimatedTransitionKit

final class ZoomTransitionFirstViewController: UIViewController {
    
    // MARK: - Lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = "First View"

        // View binding with any transition id
        button.transitionItem.id = "zoomTarget"
        
        zoomTransition.prepareAppearance(from: self)
    }

    // MARK: - Private

    private lazy var zoomTransition = ZoomTransition()

    @IBOutlet private weak var button: UIButton!
    
    @IBAction private func clicked() {
        present(createSecondVC(), animated: true, completion: nil)
    }
}

extension ZoomTransitionFirstViewController: InteractiveTransitionDataSource {
    func viewController(forAppearing interactor: AbstractInteractiveTransition) -> UIViewController? {
        createSecondVC()
    }

    private func createSecondVC() -> UIViewController {
        let rootVC = UIStoryboard(name: "ZoomTransition", bundle: nil).instantiateViewController(withIdentifier: "SecondScene")
        let navigationController = UINavigationController(rootViewController: rootVC)
        navigationController.transition = zoomTransition
        return navigationController
    }
}

final class ZoomTransitionSecondViewController: UIViewController {
    
    // MARK: - Lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Second View"
        navigationItem.setLeftBarButton(UIBarButtonItem(title: "Close", style: .plain, target: self, action: #selector(close)), animated: false)
        
        // View binding with matched transition id
        targetView.transitionItem.id = "zoomTarget"
    }
    
    // MARK: - Internal
    
    @IBOutlet private(set) weak var targetView: UIView!
    
    // MARK: - Private
    
    @objc private func close() {
        dismiss(animated: true, completion: nil)
    }
}

Using MoveTransition Simply

Using single transition for dismission with pan gesture

import AnimatedTransitionKit

let secondVC = SecondViewController()
secondVC.transition = MoveTransition()
present(secondVC, animated: true, completion: nil)

Using MoveTransition Deeply

Using pair transitions for presenting and dismission both with pan gesture

import AnimatedTransitionKit

final class MoveTransitionFirstViewController: UIViewController {
    
    // MARK: - Lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "First View"
        moveTransition.prepareAppearance(from: self)
    }

    // MARK: - Private

    private lazy var moveTransition = MoveTransition()

    @IBAction private func clicked() {
        present(createSecondVC(), animated: true, completion: nil)
    }
}

extension MoveTransitionFirstViewController: InteractiveTransitionDataSource {
    func viewController(forAppearing interactor: AbstractInteractiveTransition) -> UIViewController? {
        createSecondVC()
    }

    private func createSecondVC() -> UIViewController {
        let rootVC = MoveTransitionSecondViewController(nibName: "MoveTransitionSecondView", bundle: .main)
        let navigationController = UINavigationController(rootViewController: rootVC)
        navigationController.transition = moveTransition
        return navigationController
    }
}

final class MoveTransitionSecondViewController: UITableViewController {
    
    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Second View"
        navigationItem.setLeftBarButton(UIBarButtonItem(title: "Close", style: .plain, target: self, action: #selector(close)), animated: false)
        navigationController?.transition?.disappearenceInteractor?.drivingScrollView = tableView
    }
    
    // MARK: - Overridden: UITableViewController
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 50
    }
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let identifier = "UITableViewCell"
        var cell = tableView.dequeueReusableCell(withIdentifier: identifier)
        if cell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: identifier)
        }
        return cell!
    }
    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        cell.textLabel?.text = "\(indexPath.row + 1)"
    }
    
    // MARK: - Private
    
    @objc private func close() {
        dismiss(animated: true)
    }
}

// MARK: - InteractiveTransitionDelegate

extension MoveTransitionSecondViewController: InteractiveTransitionDelegate {
    func shouldTransition(_ interactor: AbstractInteractiveTransition) -> Bool {
        navigationController?.viewControllers.count == 1
    }
}

Using DragDropTransition

import AnimatedTransitionKit

final class DragDropTransitionFirstViewController: UIViewController {

    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = "First View"
        view.addGestureRecognizer(gestureRecognizer)
    }

    // MARK: - Private
    
    @IBOutlet private weak var imageView: UIImageView!
    
    private lazy var gestureRecognizer: UITapGestureRecognizer = { [unowned self] in
        UITapGestureRecognizer(target: self, action: #selector(tapped))
    }()
    
    @objc private func tapped() {
        let secondViewController = DragDropTransitionSecondViewController(nibName: "DragDropTransitionSecondView", bundle: .main)
        let secondNavigationController = UINavigationController(rootViewController: secondViewController)
        
        let transition = DragDropTransition()
        transition.disappearenceInteractor?.delegate = secondViewController
        
        let w = view.frame.size.width
        let statusBarHeight = UIApplication.shared.statusBarFrame.size.height
        let navigationBarHeight = navigationController!.navigationBar.frame.size.height
        let bigRect = CGRect(x: 0, y: statusBarHeight + navigationBarHeight, width: w, height: w)
        let smallRect = imageView.frame
        
        transition.presentingSource = DragDropTransitioningSource.image({ () -> UIImage? in
            self.imageView.image
        }, from: { () -> CGRect in
            smallRect
        }, to: { () -> CGRect in
            bigRect
        }, rotation: { () -> CGFloat in
            0
        }) {
            self.imageView.isHidden = true
            secondViewController.imageView.isHidden = false
        }
        
        transition.dismissionSource = DragDropTransitioningSource.image({ () -> UIImage? in
            secondViewController.imageView.image
        }, from: { () -> CGRect in
            bigRect
        }, to: { () -> CGRect in
            smallRect
        }, rotation: { () -> CGFloat in
            0
        }) {
            self.imageView.isHidden = false
        }
        
        secondNavigationController.transition = transition
        navigationController?.present(secondNavigationController, animated: true, completion: nil)
    }
}

final class DragDropTransitionSecondViewController: UIViewController {
    
    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = "Second View"
        edgesForExtendedLayout = .bottom
        imageView.isHidden = true
        
        navigationItem.setLeftBarButton(UIBarButtonItem(title: "Close", style: .plain, target: self, action: #selector(close)), animated: false)
    }
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        imageViewHeight.constant = view.frame.size.width
    }
    
    // MARK: - Internal

    @IBOutlet private(set) weak var imageView: UIImageView!
    
    // MARK: - Private

    @IBOutlet private weak var imageViewHeight: NSLayoutConstraint!
    
    @objc private func close() {
        self.dismiss(animated: true, completion: nil)
    }
}

// MARK: - InteractiveTransitionDelegate

extension DragDropTransitionSecondViewController: InteractiveTransitionDelegate {

    func didBegin(withInteractor interactor: AbstractInteractiveTransition) {
        imageView.isHidden = true
    }
    func didChange(withInteractor interactor: AbstractInteractiveTransition, percent: CGFloat) {
    }
    func didCancel(withInteractor interactor: AbstractInteractiveTransition) {
        imageView.isHidden = false
    }
    func didComplete(withInteractor interactor: AbstractInteractiveTransition) {
        imageView.isHidden = false
    }
    func interactor(_ interactor: AbstractInteractiveTransition, gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch?) -> Bool {
        return true
    }
    func interactor(_ interactor: AbstractInteractiveTransition, shouldInteract gestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

Customize AnimatedTransition

import AnimatedTransitionKit

class CustomTransition: AnimatedTransition {
    
    override func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return CustomTransitioning()
    }
    
    override func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return CustomTransitioning()
    }
}

class CustomTransitioning: AnimatedTransitioning {
    
    // Write code here for dismission
    override func animateTransition(forDismission transitionContext: UIViewControllerContextTransitioning) {
    }
    // Write code here for presenting
    override func animateTransition(forPresenting transitionContext: UIViewControllerContextTransitioning) {
    }
    // Write interative transition began code here for dismission or presenting
    override func interactionBegan(_ interactor: AbstractInteractiveTransition) {
        if self.presenting {
            // for presenting
        } else {
            // for dismission
        }
    }
    // Write interative transition changed code here for dismission or presenting
    override func interactionChanged(_ interactor: AbstractInteractiveTransition, percent: CGFloat) {
        if self.presenting {
            // for presenting
        } else {
            // for dismission
        }
    }
    // Write interative transition cacelled code here for dismission or presenting and call completion after animation finished
    override func interactionCancelled(_ interactor: AbstractInteractiveTransition, completion: (() -> Void)? = nil) {
        if self.presenting {
            // for presenting
        } else {
            // for dismission
        }
    }
    // Write interative transition completed code here for dismission or presenting and call completion after animation finished
    override func interactionCompleted(_ interactor: AbstractInteractiveTransition, completion: (() -> Void)? = nil) {
        if self.presenting {
            // for presenting
        } else {
            // for dismission
        }
    }
}

Using CustomTransition

Using simply

let secondVC = SecondViewController()
secondVC.transition = CustomTransition()
present(secondVC, animated: true, completion: nil)

Use a gesture to present the second view controller

final class FirstViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        customTransition.prepareAppearance(from: self)
    }

    private let customTransition = CustomTransition()
    
    @IBAction private func clicked() {
        present(createSecondVC(), animated: true, completion: nil)
    }
}

extension FirstViewController: InteractiveTransitionDataSource {
    func viewController(forAppearing interactor: AbstractInteractiveTransition) -> UIViewController? {
        createSecondVC()
    }
    
    private func createSecondVC() -> UIViewController {
        let vc = SecondViewController()
        vc.transition = customTransition
        return vc
    }
}

🔥Using AnimatedNavigationTransition

Using NavigationMoveTransition

import AnimatedTransitionKit

final class NavigationMoveTransitionFirstViewController: UIViewController {
    
    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "First View"
        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "close", style: .plain, target: self, action: #selector(close))
        navigationController?.navigationTransition = {
            $0.interactor?.dataSource = self
            return $0
        }(NavigationMoveTransition())
    }
    
    // MARK: - Private
    
    @IBAction private func clicked() {
        let vc = createSecondVC()
        navigationController?.pushViewController(vc, animated: true)
    }
    
    @objc private func close() {
        dismiss(animated: true, completion: nil)
    }
}

extension NavigationMoveTransitionFirstViewController: InteractiveTransitionDataSource {
    func viewController(forAppearing interactor: AbstractInteractiveTransition) -> UIViewController? {
        createSecondVC()
    }

    private func createSecondVC() -> UIViewController {
        NavigationMoveTransitionSecondViewController()
    }
}

final class NavigationMoveTransitionSecondViewController: UITableViewController {
    
    // MARK: - Lifecycle

    init() {
        super.init(nibName: "NavigationMoveTransitionSecondView", bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Second View"
    }
    
    // MARK: - Overridden: UITableViewController
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        50
    }
    
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        100
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let identifier = "UITableViewCell"
        var cell = tableView.dequeueReusableCell(withIdentifier: identifier)
        if cell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: identifier)
        }
        return cell!
    }
    
    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        cell.textLabel?.text = "\(indexPath.row + 1)"
    }
}

Customize AnimatedNavigationTransition

import AnimatedTransitionKit

class CustomNavigationTransition: UINavigationControllerTransition {
    override func newTransitioning() -> AnimatedNavigationTransitioning? {
        return CustomNavigationTransitioning()
    }
}

class CustomNavigationTransitioning: AnimatedNavigationTransitioning {

    // Write code here for pop without interaction
    override func animateTransition(forPop transitionContext: UIViewControllerContextTransitioning) {
    }
    // Write code here for push without interaction
    override func animateTransition(forPush transitionContext: UIViewControllerContextTransitioning) {
    }
    // Write interative transition began code here for push or pop
    override func interactionBegan(_ interactor: AbstractInteractiveTransition, transitionContext: UIViewControllerContextTransitioning) {
        if isPush {
            // for push
        } else {
            // for pop
        }
    }
    // Write interative transition changed code here for push or pop
    override func interactionChanged(_ interactor: AbstractInteractiveTransition, percent: CGFloat) {
        if isPush {
            // for push
        } else {
            // for pop
        }
    }
    // Write interative transition cacelled code here for push or pop and call completion after animation finished
    override func interactionCancelled(_ interactor: AbstractInteractiveTransition, completion: (() -> Void)? = nil) {
        if isPush {
            // for push
        } else {
            // for pop
        }
    }
    // Write interative transition completed code here for push or pop and call completion after animation finished
    override func interactionCompleted(_ interactor: AbstractInteractiveTransition, completion: (() -> Void)? = nil) {
        if isPush {
            // for push
        } else {
            // for pop
        }
    }
}

Using CustomNavigationTransition

Using simply

navigationController.navigationTransition = CustomNavigationTransition()

let secondVC = SecondViewController()
navigationController.pushViewController(secondVC, animated: true)

Use a gesture to push the second view controller

final class FirstViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationController?.navigationTransition = {
            $0.interactor?.dataSource = self
            return $0
        }(CustomNavigationTransition())
    }
    
    @IBAction private func clicked() {
        navigationController?.pushViewController(createSecondVC(), animated: true)
    }
}

extension FirstViewController: InteractiveTransitionDataSource {
    func viewController(forAppearing interactor: AbstractInteractiveTransition) -> UIViewController? {
        createSecondVC()
    }
    
    private func createSecondVC() -> UIViewController {
        SecondViewController()
    }
}

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

$ gem install cocoapods

CocoaPods 1.1.0+ is required to build AnimatedTransitionKit.

To integrate AnimatedTransitionKit into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '13.0'

target '<Your Target Name>' do
    pod 'AnimatedTransitionKit', '~> 4'
end

Then, run the following command:

$ pod install

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate AnimatedTransitionKit into your Xcode project using Carthage, specify it in your Cartfile:

github "pisces/AnimatedTransitionKit" ~> 4

Run carthage update to build the framework and drag the built AnimatedTransitionKit.framework into your Xcode project.

Requirements

iOS Deployment Target 13.0 higher

Author

Steve Kim, hh963103@gmail.com

License

AnimatedTransitionKit is available under the BSD 2-Clause license. See the LICENSE file for more info.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.