How to Animate Tab Bar Tab Switch with a Crossdissolve Slide Transition

How to animate Tab bar tab switch with a CrossDissolve slide transition?

There is a simpler way to doing this. Add the following code in the tabbar delegate:

Working 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 (4/23/18)
Since this answer is getting popular, I updated the code to remove the force unwraps, which is a bad practice, and added the guard statement.

EDIT (7/11/18)
@AlbertoGarcía is right. If you tap the tabbar icon twice you get a blank screen. So I added an extra check

iPhone: How to switch tabs with an animation?

Update 04/2016: Justed wanted to update this to say thank you to everyone for all the votes. Please also note that this was originally written way back when ... before ARC, before constraints, before ... a lot of stuff! So please take this into account when deciding whether to use these techniques. There may be more modern approaches. Oh, and if you find one. Please add a response so everyone can see. Thanks.

Some time later ...

After much research I came up with two working solutions. Both of these worked and did the animation between tabs.

Solution 1: transition from view (simple)

This is the easiest and makes use of a predefined UIView transition method. With this solution we don't need to manage the views because the method does the work for us.

// Get views. controllerIndex is passed in as the controller we want to go to. 
UIView * fromView = tabBarController.selectedViewController.view;
UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view];

// Transition using a page curl.
[UIView transitionFromView:fromView
toView:toView
duration:0.5
options:(controllerIndex > tabBarController.selectedIndex ? UIViewAnimationOptionTransitionCurlUp : UIViewAnimationOptionTransitionCurlDown)
completion:^(BOOL finished) {
if (finished) {
tabBarController.selectedIndex = controllerIndex;
}
}];

Solution 2: scroll (more complex)

A more complex solution, but gives you more control of the animation. In this example we get the views to slide on and off. With this one we need to manage the views ourselves.

// Get the views.
UIView * fromView = tabBarController.selectedViewController.view;
UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view];

// Get the size of the view area.
CGRect viewSize = fromView.frame;
BOOL scrollRight = controllerIndex > tabBarController.selectedIndex;

// Add the to view to the tab bar view.
[fromView.superview addSubview:toView];

// Position it off screen.
toView.frame = CGRectMake((scrollRight ? 320 : -320), viewSize.origin.y, 320, viewSize.size.height);

[UIView animateWithDuration:0.3
animations: ^{

// Animate the views on and off the screen. This will appear to slide.
fromView.frame =CGRectMake((scrollRight ? -320 : 320), viewSize.origin.y, 320, viewSize.size.height);
toView.frame =CGRectMake(0, viewSize.origin.y, 320, viewSize.size.height);
}

completion:^(BOOL finished) {
if (finished) {

// Remove the old view from the tabbar view.
[fromView removeFromSuperview];
tabBarController.selectedIndex = controllerIndex;
}
}];

This Solution in Swift:

extension TabViewController: UITabBarControllerDelegate {
public func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {

let fromView: UIView = tabBarController.selectedViewController!.view
let toView : UIView = viewController.view
if fromView == toView {
return false
}

UIView.transitionFromView(fromView, toView: toView, duration: 0.3, options: UIViewAnimationOptions.TransitionCrossDissolve) { (finished:Bool) in

}
return true
}
}

How to change tab bar programmatically with animation?

As a workaround you could trigger your animation manually. I don't know if it is recommended but it is working for me.

    func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
animateTabBarChange(tabBarController: tabBarController, to: viewController)
return true
}

func animateTabBarChange(tabBarController: UITabBarController, to viewController: UIViewController) {
let fromView: UIView = tabBarController.selectedViewController!.view
let toView: UIView = viewController.view

// do whatever animation you like

}

Then you call it like this:

let index = 2
animateTabBarChange(tabBarController: self.tabBarController!, to: self.tabBarController!.viewControllers![index])
self.tabBarController?.selectedIndex = index

ViewController slide animation

You can use the following idea: https://samwize.com/2016/04/27/making-tab-bar-slide-when-selected/

Also, here's the code updated to Swift 4.1 and I also removed the force unwrappings:

import UIKit

class MyTabBarController: UITabBarController {

override func viewDidLoad() {
super.viewDidLoad()

delegate = self
}
}

extension MyTabBarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
guard let tabViewControllers = tabBarController.viewControllers, let toIndex = tabViewControllers.index(of: viewController) else {
return false
}
animateToTab(toIndex: toIndex)
return true
}

func animateToTab(toIndex: Int) {
guard let tabViewControllers = viewControllers,
let selectedVC = selectedViewController else { return }

guard let fromView = selectedVC.view,
let toView = tabViewControllers[toIndex].view,
let fromIndex = tabViewControllers.index(of: selectedVC),
fromIndex != toIndex else { return }

// Add the toView to the tab bar view
fromView.superview?.addSubview(toView)

// Position toView off screen (to the left/right of fromView)
let screenWidth = UIScreen.main.bounds.size.width
let scrollRight = toIndex > fromIndex
let offset = (scrollRight ? screenWidth : -screenWidth)
toView.center = CGPoint(x: fromView.center.x + offset, y: toView.center.y)

// Disable interaction during animation
view.isUserInteractionEnabled = false

UIView.animate(withDuration: 0.3,
delay: 0.0,
usingSpringWithDamping: 1,
initialSpringVelocity: 0,
options: .curveEaseOut,
animations: {
// Slide the views by -offset
fromView.center = CGPoint(x: fromView.center.x - offset, y: fromView.center.y)
toView.center = CGPoint(x: toView.center.x - offset, y: toView.center.y)

}, completion: { finished in
// Remove the old view from the tabbar view.
fromView.removeFromSuperview()
self.selectedIndex = toIndex
self.view.isUserInteractionEnabled = true
})
}
}

So, you need to subclass UITabBarController and you also have to write the animation part, you can tweak the animation options (delay, duration, etc).

I hope it helps, cheers!

Sample Image

View transitions when TabBatController.selectedIndex changed programmatically

Ok. I have found the solution, using ViewController slide animation

as Matt proposed.

So using the extension + the animateToTab function in the extension and changing my swipe method it works just as expected.

   @objc func swiped(_ gesture: UISwipeGestureRecognizer) {
if (CanChangeTab) {
let thisTabController = self.tabBarController as! iBayTabController

thisTabController.prevIndex = (thisTabController.selectedIndex)
if gesture.direction == .left {
if thisTabController.selectedIndex < 4 { // set your total tabs here
thisTabController.animateToTab(toIndex: thisTabController.selectedIndex+1)
}
} else if gesture.direction == .right {
if (self.tabBarController?.selectedIndex)! > 0 {
thisTabController.animateToTab(toIndex: thisTabController.selectedIndex-1)
}
}
}
}

Slide up menu with UIPresentationController and Tab bar

In this line

outFrame.origin.y = transitionContext.containerView.frame.size.height - 88

you should also subtract the height of your tab bar

outFrame.origin.y = transitionContext.containerView.frame.size.height - 88 - *height of tab bar*

I believe the height of tab bar used to be 49, but I haven't used tab bar in a while so probably better to just get the height by using

tabBarController.tabBar.frame.size.height


Related Topics



Leave a reply



Submit