How to animate Tab tab with CrossDissolve slide transition?

I am trying to create a transition effect on UITabBarController with something similar to a Facebook application. I managed to get a “scroll effect” that works on a tab switch, but I can't figure out how cross dissolving (or at least it doesn't work).

Here is my current code:

 import UIKit class ScrollingTabBarControllerDelegate: NSObject, UITabBarControllerDelegate { func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return ScrollingTransitionAnimator(tabBarController: tabBarController, lastIndex: tabBarController.selectedIndex) } } class ScrollingTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning { weak var transitionContext: UIViewControllerContextTransitioning? var tabBarController: UITabBarController! var lastIndex = 0 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.2 } init(tabBarController: UITabBarController, lastIndex: Int) { self.tabBarController = tabBarController self.lastIndex = lastIndex } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { self.transitionContext = transitionContext let containerView = transitionContext.containerView let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) containerView.addSubview(toViewController!.view) var viewWidth = toViewController!.view.bounds.width if tabBarController.selectedIndex < lastIndex { viewWidth = -viewWidth } toViewController!.view.transform = CGAffineTransform(translationX: viewWidth, y: 0) UIView.animate(withDuration: self.transitionDuration(using: (self.transitionContext)), delay: 0.0, usingSpringWithDamping: 1.2, initialSpringVelocity: 2.5, options: .transitionCrossDissolve, animations: { toViewController!.view.transform = CGAffineTransform.identity fromViewController!.view.transform = CGAffineTransform(translationX: -viewWidth, y: 0) }, completion: { _ in self.transitionContext?.completeTransition(!self.transitionContext!.transitionWasCancelled) fromViewController!.view.transform = CGAffineTransform.identity }) } } 

It would be great if someone knew how to get this to work, trying for many days without progress ...: /

edit: I got the cross-dissolution work by replacing the UIView.animate block with:

 UIView.transition(with: containerView, duration: 0.2, options: .transitionCrossDissolve, animations: { toViewController!.view.transform = CGAffineTransform.identity fromViewController!.view.transform = CGAffineTransform(translationX: -viewWidth, y: 0) }, completion: { _ in self.transitionContext?.completeTransition(!self.transitionContext!.transitionWasCancelled) fromViewController!.view.transform = CGAffineTransform.identity }) 

However, the animation is really late and not used :(

edit 2: For people trying to use these snippets, be sure to include the delegate for the UITabBarController , otherwise nothing will happen.

edit 3: I found the Swift library that does exactly what I was looking for: https://github.com/Interactive-Studio/TransitionableTab

+11
source share
3 answers

There is an easier way to do this. Add the following code to the tab bar delegate:

We work on Swift 2, 3 and 4

 class MySubclassedTabBarController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() delegate = self } } extension MySubclassedTabBarController: UITabBarControllerDelegate { func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { guard let fromView = selectedViewController?.view, let toView = viewController.view else { return false // Make sure you want this as false } if fromView != toView { UIView.transition(from: fromView, to: toView, duration: 0.3, options: [.transitionCrossDissolve], completion: nil) } return true } } 

EDIT (23/23/18) As this answer becomes popular, I updated the code to remove force deployments, which is bad practice, and added a defensive statement.

EDIT (7/11/18) @ AlbertoGarcía is right. If you double-tap the tab bar icon, you’ll see a blank screen. So I added an extra check

+49
source

If you want to use UIViewControllerAnimatedTransitioning to do something more custom than UIView.transition , take a look at this point .

enter image description here

 // MyTabController.swift import UIKit class MyTabBarController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() delegate = self } } extension MyTabBarController: UITabBarControllerDelegate { func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return MyTransition(viewControllers: tabBarController.viewControllers) } } class MyTransition: NSObject, UIViewControllerAnimatedTransitioning { let viewControllers: [UIViewController]? let transitionDuration: Double = 1 init(viewControllers: [UIViewController]?) { self.viewControllers = viewControllers } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return TimeInterval(transitionDuration) } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from), let fromView = fromVC.view, let fromIndex = getIndex(forViewController: fromVC), let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to), let toView = toVC.view, let toIndex = getIndex(forViewController: toVC) else { transitionContext.completeTransition(false) return } let frame = transitionContext.initialFrame(for: fromVC) var fromFrameEnd = frame var toFrameStart = frame fromFrameEnd.origin.x = toIndex > fromIndex ? frame.origin.x - frame.width : frame.origin.x + frame.width toFrameStart.origin.x = toIndex > fromIndex ? frame.origin.x + frame.width : frame.origin.x - frame.width toView.frame = toFrameStart DispatchQueue.main.async { transitionContext.containerView.addSubview(toView) UIView.animate(withDuration: self.transitionDuration, animations: { fromView.frame = fromFrameEnd toView.frame = frame }, completion: {success in fromView.removeFromSuperview() transitionContext.completeTransition(success) }) } } func getIndex(forViewController vc: UIViewController) -> Int? { guard let vcs = self.viewControllers else { return nil } for (index, thisVC) in vcs.enumerated() { if thisVC == vc { return index } } return nil } } 
+11
source

To deploy @gmogames answer: fooobar.com/questions/1268510 / ...

I couldn’t get the animation when choosing the index of the tab bar using code, for example:

 tabBarController.setSeletedIndex(0) 

It doesn't seem to go through the same heirarchy call, and it skips the method:

 tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) 

completely, resulting in no animation.

In my code, I wanted to make an animated transition for a user regarding a tab bar item, in addition to the fact that under certain circumstances I manually set the tab bar item code.

Here is my addition to the solution above, which adds another method for setting the selected index through code that will animate the transition:

 import Foundation import UIKit @objc class CustomTabBarController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() delegate = self } @objc func set(selectedIndex index : Int) { _ = self.tabBarController(self, shouldSelect: self.viewControllers![index]) } } @objc extension CustomTabBarController: UITabBarControllerDelegate { @objc func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { guard let fromView = selectedViewController?.view, let toView = viewController.view else { return false // Make sure you want this as false } if fromView != toView { UIView.transition(from: fromView, to: toView, duration: 0.3, options: [.transitionCrossDissolve], completion: { (true) in }) self.selectedViewController = viewController } return true } } 

Now just call

 tabBarController.setSelectedWithIndex(1) 

for an animated transition in the code!

I still think that unfortunately, to do this, we have to override a method that is not an installer and manipulate the data inside it. This does not make the tab bar controller as extensible as it should be if it is a method that we need to override to do this.

0
source

Source: https://habr.com/ru/post/1268510/


All Articles