UINavigationController Tips & Tricks

Tim Johnson
Swifty Tim
Published in
3 min readAug 15, 2017

--

This is my first post, so bear with me. Also leave any feedback you may have!

UINavigationController is a pretty fundamental building block for any kind of iOS application. It drives the core, you guessed it, navigation of views throughout most applications. How a user gets from View A to View B is typically done via UINavigationController.

Basically, in an iOS application, you’re going to be using UINavigationController a lot. That being said, it's easy to run into some pretty frustrating issues with it (for how wonderful Apple’s frameworks generally are, man do some of them have shortcomings).

I’ve found the snippets of code below particularly useful for me when dealing with UINavigationController, and who knows, maybe they can end up being useful for you also.

Navigation and Completion

Presenting a UIViewController as a modal is really nice.

let presentedVC = UIViewController()
self.present(presentedVC, animated: true) {
// Handle presentation completion here
}

Look at that! You can say whether or not its animated, and you get a nifty little completion block for post processing!

Let’s see what a UINavigationController gives us.

// Push a UIViewControllerlet pushedVC = UIViewController()
self.navigationController?.pushViewController(pushedVC, animated: true)
// Pop a UIViewControllerlet _ = self.navigationController?.popViewController(animated: true)

Hmmm. That doesn’t seem so nice. What if I wanted to do something when the pushing or popping finished?!

Continue reading :)

I’ve found using extensions is pretty nice in cases like this, where you want some functionality that doesn’t quite exist. You can use the UINavigationController API’s to help with this one. For pushing:

And for popping:

Pretty simple right! All we’re doing here is taking advantage of UINavigationController’s transitionCoordinator property to perform any additional work during the animation, and listen for completion. Now when you want to know when you’re navigation has completed, it’s as easy as this:

// Push View Controllerlet pushedVC = UIViewController()
navigationController?.pushViewController(pushedVC, animated: true) {
// Handle completion block here
}
// // Pop View Controllerlet _ = navigationController?.popViewController(animated: true) {
// Handle completion block here
}

Customizing the Back Button

One of the nice things built into UINavigationController is the ability to go back in a nice, smooth, animated fashion. There’s even a built in back button for it.

And because iOS has the greatest UI in the world, why would you ever want to customize it? At least, that seems like what the team at Apple had in mind.

Customizing the back button for a UIViewController can be surprisingly annoying, but its pretty simple. If you subclass your navigation controller, you can simply override func pushViewController(_ viewController: UIViewController, animated: animated) :

This can be frustrating, because it requires subclassing a UINavigationController, which can be a lot of extra work if you really don’t need to subclass.

Luckily, with the help of the UINavigationController transitionCoordinator we don’t need to have a subclass. Instead, we can simply implement a method in an extension of UINavigationController, just like we did above.

Pretty neat! I personally like to build out extensions like this, and when there is a default back button throughout the app, it's only a few lines to implement.

Navigation Bar Shadow

The UINavigationBar shadow is a great addition to the UINavigationBar. It helps create that distinction between the view and the bar nicely. It can also be customized, which is a real treat.

To change the color of the shadow, all you need to do is set the UINavigationBar’s shadowImage to an image entirely composed of a single color. Simple right?

It actually is. The only real additional work you need to do is build an image from a UIColor. That can be easily put into an extension on UIColor:

var image: UIImage? {
get {
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContext(rect.size)
context.setFillColor(cgColor)
context.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}

And now all you need to do is define a method that takes a color, and applies it to the navigation controller’s navigationBar :

func setShadowColor(_ color: UIColor) {
navigationBar.setBackgroundImage(#backgroundImage, for: .default)
self.navigationBar.shadowImage = color.image
}

Setting the background image is required to change the color of the shadow image, as Apple’s documentation states:

For a custom shadow image to be shown, a custom background image must also be set with the setBackgroundImage(_:for:) method. If the default background image is used, then the default shadow image will be used regardless of the value of this property.

These are just a few of the things I’ve found to be useful over the years with UINavigationController, but there are plenty more. Feel free to leave any comments below!

--

--