-1

How can a simple cross dissolve animation be written in the tab controller class that performs when switching between tabs.

Here is an example of the app showing this kind of animation.

I am fairly new to custom transition animations I understand that it has to go within didSelect

override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {

}

enter image description here

UPDATE

There seems to be some confusion on my part:

I've created a new Swift File called TabBarClass.swift

Inside of I added the following code:

    class MyFadeTransition: NSObject, UIViewControllerAnimatedTransitioning {
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            if let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to) {
                toVC.view.frame = fromVC.view.frame
                toVC.view.alpha = 0
                fromVC.view.alpha = 1
                transitionContext.containerView.addSubview(fromVC.view)
                transitionContext.containerView.addSubview(toVC.view)

                UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
                    toVC.view.alpha = 1
                }) { (finished) in
                    transitionContext.completeTransition(finished)
                }
            }
        }

        func animationEnded(_ transitionCompleted: Bool) {
            // no-op
        }

        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 0.3
        }
    }


**And in the AppDelegate I added:**

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            let fade = MyFadeTransition()
            return fade
        }

What is the issue?

UPDATE 2

This is how my AppDelegate is written:

import UIKit

@UIApplicationMain


class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var tabBarController: UITabBarController!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {


        let tab = window!.rootViewController as! UITabBarController
        tab.delegate = self


        return true
    }



    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(_ application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }


}
extension AppDelegate: UITabBarControllerDelegate {
    func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        let fade = MyFadeTransition()

        return fade
    }
}
  • Effectively a duplicate of e.g. https://stackoverflow.com/questions/54842069/view-transitions-when-tabbatcontroller-selectedindex-changed-programmatically. It's a different animation but that's irrelevant. – matt May 29 '19 at 05:39

1 Answers1

2

Make use of the UITabBarControllerDelegate method:

func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?

The bulk of the work is in the class that conforms to UIViewControllerAnimatedTransitioning.

Here is an implementation that crossfades the two controllers.

MyFadeTransition.swift:

import UIKit

class MyFadeTransition: NSObject, UIViewControllerAnimatedTransitioning {
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to) {
            toVC.view.frame = fromVC.view.frame
            toVC.view.alpha = 0
            fromVC.view.alpha = 1
            transitionContext.containerView.addSubview(fromVC.view)
            transitionContext.containerView.addSubview(toVC.view)

            UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
                toVC.view.alpha = 1
            }) { (finished) in
                transitionContext.completeTransition(finished)
            }
        }
    }

    func animationEnded(_ transitionCompleted: Bool) {
        // no-op
    }

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }
}

Something in your code needs to be the tab bar controller's delegate. Set that and then implement the delegate method in that class.

func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    let fade = MyFadeTransition()
    return fade
}

Note that this same MyFadeTransition class can be used with a UINavigationController. The UINavigationControllerDelegate has the same basic method so you can reuse the transition class for both tab bar controllers and nav controllers.

Assuming that your tab bar controller is your app's root view controller, you can add the following code to the didFinishLaunchingWithOptions of your AppDelegate:

let tab = window!.rootViewController as! UITabBarController
tab.delegate = self

Then add:

extension AppDelegate: UITabBarControllerDelegate {
    func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        let fade = MyFadeTransition()

        return fade
    }
}

to AppDelegate.swift.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • I appreciate your help rmaddy, I'm a little confused I am using storyboards so I tried to assign the MyFadeTransition class to the tab view controller but it does not show up –  May 29 '19 at 14:59
  • You don't add the `MyFadeTransition` class to the tab controller. Just add that class to your project. You need some class to be the tab view controller's `delegate`. This could be the `AppDelegate` class or any other appropriate class. Whichever class you choose, set that class to be the tab bar controller's `delegate` property. And then put the delegate method (shown above) in that class. – rmaddy May 29 '19 at 15:02
  • Please see the update I posted, I don't understand what I am doing incorrectly, for the delegate, I add the above (last) code to the AppDelegate file and the class to a new separate view controller. But it still does not work –  May 29 '19 at 16:27
  • Do not add a `TabBarClass.swift`. Add `MyFadeTransition.swift`. The only piece you are missing is setting the `delegate` property of the tab bar controller to your AppDelegate. See my updated answer. – rmaddy May 29 '19 at 16:33
  • I keep getting the error Redundant conformance of `'AppDelegate' to protocol 'UITabBarControllerDelegate'` and `Invalid redeclaration of 'tabBarController(_:animationControllerForTransitionFrom:to:)'` for extension AppDelegate. I put it in AppDelegate.swift like advised –  May 29 '19 at 17:20
  • I posted an update of how my app delegate is written –  May 29 '19 at 17:23
  • Make sure you are not adding the tab bar delegate method twice in your AppDelegate. – rmaddy May 29 '19 at 17:34
  • I am not, please see the Update 2 I posted. That is all in my delegate –  May 29 '19 at 17:50
  • The code you posted in Update 2 can't result in the error you mention. – rmaddy May 29 '19 at 17:55
  • It is a bit odd, but that is exactly the code that is causing it. –  May 29 '19 at 18:16