Transition Delegate for UITabBarController animation
animationControllerForPresentedController
is the wrong approach for the tab bar controller.
In the UITabBarController subclass, adopt the UITabBarControllerDelegate
protocol and set it as its own delegate
. Then, use tabBarController: animationControllerForTransitionFromViewController: toViewController:
to return the custom UIViewControllerAnimatedTransitioning
object.
To get a better visualization, look at VCTransitionsLibrary in the TabBarDemo folder.
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
How to enable tab bar transition animation to work without initial view controller
You need to subclass UITabBarController
and use
class CustomTab:UITabBarController,UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return MyFadeTransition()
}
}
Then assign CustomTab
as class name to the tabBar in IB
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
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)
}
}
}
}
iOS / Objective c - UITabBarControllerDelegate - Custom tab animation delegates not called
I think you are using the wrong delegate method for what you are trying to accomplish. Try using:
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC;
This will be called when the user taps on a different tab.
Related Topics
Fft Calculating Incorrectly - Swift
Uitableview Height Does Not Change Dynamically in Swift iOS
How to Display Cgpdfdocument in Swift
Cocoapods, Add Dependencies to All Targets in a Generic Way
Show Posts from the Users You Are Following - Swift
How to Create a Ntlm Authentication Header to Use with Alamofire
Save Image with the Correct Orientation - Swift & Core Image
How to Send Different Users to Separate View Controllers Using Firebase and Xcode
iOS Swift. How to Get a Gps Metadata from Just a Uiimage (Nsurl)
Geolocation by Iphone's Ip Address
Swift: Bring View from Stack View to Front
Swiftui Multiple Navigationlinks in Form/Sheet - Entry Stays Highlighted
Locationmanager Didupdatelocations Fires Twice on Device, Only Once on Simulator
How to Add File Picker to the App on iOS 14+ and Lower