Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Custom animation for hidesBottomBarWhenPushed

My app's structure is TabBarController -> NavigationController -> FirstViewController -> SecondViewController. I use custom push transition from FirstViewController to SecondViewController to immitate circular transition. I don't want to display the bottom TabBar on the SecondViewController. So I set 'hidesBottomBarWhenPushed=true'on the SecondViewController.

The problem is that when the circular animation occurs, the bottom tabBar is being slidden leftwards by default. I want to customize that animation to do something different (maybe dissolve or something).

Is this possible?

p.s. I'd try to avoid just hiding the bottom TabBar by setting 'isHidden=true' or 'alpha=0' because it will add some minor complications.

like image 590
Michael Voline Avatar asked Dec 15 '17 03:12

Michael Voline


People also ask

What does-hidesbottombarwhenpushed on a view controller mean?

It seems obvious what is happening, when you use -hidesBottomBarWhenPushed on a view controller, it means that when THAT view controller is pushed onto a navigation stack, AND if that navigation stack has a bottom bar (presumably this is either a tab bar or toolbar), then hide that bar along with the push/pop animation.

How do I present a view controller using custom animations?

To present a view controller using custom animations, do the following in an action method of your existing view controllers: Create the view controller that you want to present. Create your custom transitioning delegate object and assign it to the view controller’s transitioningDelegate property.

How to animate the “to” view in an animation block?

In your animation block, animate the “to” view to its final location in the container view. Set any other properties to their final values as well.

What is interactive animator object in UIView?

Interactive animator objects. An interactive animator object drives the timing of custom animations using touch events or gesture recognizers. Interactive animator objects conform to the UIViewControllerInteractiveTransitioning protocol.


1 Answers

I ran across this problem today and successfully managed to have the TabBar slide down instead of to the left. It's working on iOS 13 at least.

I use the animator for my transition to additionally animate the TabBar and counteract the default animation.

class CustomTransition: NSObject, UIViewControllerAnimatedTransitioning {
    static let duration: TimeInterval = 0.5

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        Self.duration
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let from = transitionContext.viewController(forKey: .from)!
        let to = transitionContext.viewController(forKey: .to)!

        let animator = UIViewPropertyAnimator(duration: Self.duration, curve: .easeInOut)

        // Configure animator for transition like normal.
        // ...

        // Now handle the TabBar.
        if
            to.hidesBottomBarWhenPushed,
            !from.hidesBottomBarWhenPushed,
            let tabBar = from.tabBarController?.tabBar
        {
            // TabBar is going away.

            animator.addAnimations {
                // Counteract default animation by animating x in opposite direction.
                tabBar.center.x += tabBar.bounds.width

                // Animate TabBar down.
                tabBar.center.y += tabBar.bounds.height

                // Or alternatively animate opacity.
                // tabBar.alpha = 0
            }
        }
        else if
            !to.hidesBottomBarWhenPushed,
            from.hidesBottomBarWhenPushed,
            let tabBar = to.tabBarController?.tabBar
        {
            // TabBar is coming back.

            // TabBar by default will be animated toward default position.
            // Make sure it's already there on x so default animation does nothing for x.
            tabBar.center.x = tabBar.bounds.width / 2

            // Move y down, so default animation will move TabBar up to default position.
            tabBar.center.y += tabBar.bounds.height

            // Or alternatively animate opacity.
            // tabBar.alpha = 0
            // animator.addAnimations {
            //    tabBar.alpha = 1
            //}
        }

        animator.startAnimation()
    }
}

EDIT:
The above solution doesn't seem to be working in landscape orientation. I settled on the following solution instead, but it's getting hackier. Somehow you can only remove the default animation from the TabBar layer after you start your own animation, like somehow those are related.

I've tested this on a variety of devices in different orientations and it seems to be working consistently, at least on iOS 13.

// Start transition animation.
animator.startAnimation()

if let tabBar = pushedController.tabBarController?.tabBar {
    // Remove default TabBar animation.
    tabBar.layer.removeAllAnimations()
    if pushedController == to {
        // Move TabBar back to its place.
        tabBar.center.x = tabBar.bounds.width / 2

        // Now animate it out again.
        animator.addAnimations {
            tabBar.center.y += tabBar.bounds.height
        }
    } else {
        // Move TabBar down out of view.
        tabBar.center.y += tabBar.bounds.height

        // Now animate it in.
        animator.addAnimations {
            tabBar.center.y -= tabBar.bounds.height
        }
    }
}
like image 179
Onne van Dijk Avatar answered Oct 13 '22 23:10

Onne van Dijk