Tuesday, September 20, 2016

Hiding the Toolbar of a UINavController

NavVCToolbars

So I am in the final stages of finishing up the latest revision of Pain Logger. I had hoped to get it in the app store prior to iOS 10 but I just couldn't make it.

At any rate, iOS 10 and the iPhone hit the stores this week and of course sales jumped.

Sales of the old version, of course :-(

So I got an email from one of my customers asking for a feature.

Since I am basically dev complete I figured, prior to submitting to the store, I would try to squeeze this last requested feature in.

After looking at the requirement I decided the UX for this would be a toolbar to provide a control to activate the feature.

The feature isn't important, but what is important was how I got this to work.

The UI this will go on looks like and behaves like a UITableViewController but it isn't implemented that way.

I moved away from using UITableViewControllers in my UIs and instead use a vanilla UIViewController and then place UITableViews in their UIView.

I find this works best as my UIs change.

This particular view is the third view in a stack of views inside a UINavigationController stack.

I wanted the navigation bar to show on all views, but I only wanted a toolbar to show on the bottom of the view in question, I'll call it the DataVC from here on out.

I also wanted to enable the behavior you see in other apps where when you scroll up on the table, the navigation bar AND the toolbar move out of the way.

And of course when I swipe down, I wanted the navigation bar and the toolbar to come back.

So the first issue I ran into was how to get the toolbar on the view to begin with. To do that, in interface builder (IB), I turned on the switch to show the toolbar on the UINavigationController itself.

This caused all the connected ViewControllers in the stack to get a toolbar.

So in the viewDidAppear method in each ViewController I turned off the toolbar until I got to the DataVC like this:

navigationController?.toolbarHidden = true

Now we get to the DataVC itself. Originally the controls in DataVC were configured in the following order:

  • NavigationBar
  • UITableView
  • UIView (to hold ads)

Everything was properly laid out with constraints. I then added these two lines to the DataVC viewDidAppear method:

    navigationController?.toolbarHidden = false
    navigationController?.hidesBarsOnSwipe = true

This got the swiping behavior I was looking for going. But when I swiped up the NavBar didn't come back.

I found this stack overflow post which solved the problem.

The trickiest part was changing the Top constraint on the UITableView. The answer of how to do that is in the StackOverflow link.

After that fix, the bars would come back correctly, but cells in the tableview were hidden by the NavBar and the toolbar.

The fix was to turn off the settings for "under top" and "bottom bars" for the DataVC itself.

At this point it all worked, pretty simple really, the biggest issue was the one solved by the StackOverflow fix I found.

There are some "gotchas" though.

Once you turn on the toolbar and enable the swipe to hide functionality, any subsequent ViewController pushed on the stack will have the configuration as when the DataVC was left.

This wasn't the behavior I wanted.

I only wanted to do it on the DataVC, so at first I was dismayed that I would need to reverse these settings for any ViewController I pushed on the stack after the DataVC.

The key to keep the configuration localized to the DataVC is to use the viewWillDisappear method. So to put everything back, I implemented the method like this:

  override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    navigationController?.toolbarHidden = true
    navigationController?.hidesBarsOnSwipe = false
    navigationController?.setNavigationBarHidden(false, animated: true)
  }

Works perfectly.

Another issue, that kind of irked me, was that when I added the toolbar to the root UINavigationController, a number of my constraints on other UIViewControllers started giving me warnings.

This was because I had views that had bottom constraints connected to the bottom of the superview. But when IB puts in the toolbar it leaves the constraint at zero but moves the view up 44 points.

So you get a bunch of warnings when before everything was clean.

I had to review each and adjust accordingly. Annoying, yes, but after thinking about it, it made some sense. I wish IB would have been smarter on that.

Oh well, till next time.