iOS Swift – Building a Times Tables App – Part 3

In two previous posts, we first added the UI elements and then added a bit of logic to make the initial view of the app work. You can find those posts here:

iOS Times Tables App – Part 1
iOS Times Tables App – Part 2

The full code for the app can be found on my Github page:

https://github.com/jamesmcclay/swift_times_tables

Today we’re going to add a UINavigationController as well as a second view controller for the actual times tables flash card view.

Adding a UINavigationController

UINavigationController is a handy object available in UIKit. It allows us to put a navigation bar that persists on the screen even as the user changes from one UIViewController to another. By default, it will come with a functioning “back” button.

Since we’re taking a programmatic approach for pretty much everything except adding UIViewControllers, we need to delete the storyboard entrypoint and add one from code. It shows up as a big arrow pointing to the initial UIViewController:

Storyboard Entry Point in Xcode

Once we have it selected (you can click on either the arrow on the canvas or on the one to the left), just hit delete and it’s gone. While we’re in the storyboard, make sure the main view controller has “Storyboard ID”, you can add it from the identity inspector. I called mine “homeVC”:

Identity Inspector for UIViewController

The next step is to add the UINavigationController to the app as well as an entry point from code. If you are developing for iOS 13 and onward, the place to do this is in SceneDelegate.swift. It used to be AppDelegate.swift, but no longer. We’ll add our code to SceneDelegate, in the function willConnectTo. The top of my SceneDelegate.swift file looks like this:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow? //Gotta add this reference to the app window

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = scene as? UIWindowScene else { return }

        //Adding the UINavigationController here
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController (withIdentifier: "homeVC") as! ViewController
        let navigationController = UINavigationController(rootViewController: vc)
        window = UIWindow(windowScene: windowScene)
        window?.rootViewController = navigationController
        window?.makeKeyAndVisible()
        //End of custom code

        guard let _ = (scene as? UIWindowScene) else { return }
    }

In the first couple lines, we grab references to the storyboard and the home UIViewController. Then we create a UINavigationController, and add the home UIViewController as the root view controller. In the last three lines, we start up the window, make it visible and make the UINavigationController the root view controller for the window (yes, two roots). That should replace the storyboard entrypoint as well as add a navigation controller.

Add a second UIViewController

We’re going to use the storyboard to add a second UIViewController. Click the “+” button at the top left and a view controller:

Adding a UIViewController from the storyboard

Then we need to connect to it a swift file. Over on the left side where all the rest of the project files are, right click on the “view” folder and click “new file”. Create a “Cocoa Touch Class”:

Creating a Cocoa Touch Class

Let’s call it “TimesTableViewController”. Click through and create. The last step is to connect the newly created swift file to the newly created UIViewController on the storyboard. We can do that from the storyboard by clicking on the UIViewController and going to its identity inspector and changing the class to the new class we just created, and be sure to give it a storyboard ID:

Connecting a swift file to UIViewController

And our second UIViewController is complete. Now we just need to add code to jump to this view (or rather, push it on to the UINavigationController, to be precise) when the user taps the start button. Please check the github code or previous posts if you’re not sure what I’m talking about here. Let’s add this snippet to startButtonTapped:

let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let ttvc: TimesTableViewController = storyBoard.instantiateViewController(withIdentifier: "ttVC") as! TimesTableViewController
self.navigationController!.setViewControllers([self,ttvc], animated: true)

This looks pretty complicated, but it’s mostly just some magic that Apple wants you to do when you’re pushing a new view controller to the navigation controller. Lastly, I didn’t find that I wanted the navigation bar to show up on the home view controller, just the times tables one. So I added this to my code in the (home) ViewController:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)
    self.navigationController!.navigationBar.isHidden = true
}

This just tells the navigation bar to be hidden when the view appears.

Try it out!

The main view is unchanged, but when we click start, we head to the next view! And there’s a back button! And it works!

Hope you liked it. Next time we’ll code in the times tables view. Stay tuned!

Leave a Reply

Your email address will not be published.