Navigation Controllers

Navigation Controllers are screen containers that help you to navigate between screens. For a user, navigation controller looks like a bar on the top of the screen. If current screen is not first in the container, then the user can see a back button on the left side of the navigation bar.

With Objective-C-like syntax and without ProMotion you would need to write next code in your App Delegate to create a navigation controller, and put the first screen into it:

        def application(app, didFinishLaunchingWithOptions: options)
          first_screen = HomeScreen.new
          nav_controller = UINavigationController.alloc.initWithRootViewController first_screen
          @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
          @window.rootViewController = nav_controller
          @window.makeKeyAndVisible
        end
      

With ProMotion code will be much shorter and easier to read:

        def on_load(app, opts={})
          screen = HomeScreen.new nav_bar: true
          open screen
        end
      
It is not even that different from what we currently have! The only new thing here is nav_bar: true parameter when creating a new screen. It tells ProMotion that we want to create a screen with a navigation controller.

Let's go further, and try to open the second screen, to see how does navigation controller behaves when it has multiple screens. Let's modify our App Delegate to load the first screen, and then open the second one from it:

        def on_load(app, opts={})
          screen = MoodHistoryScreen.new nav_bar: true
          screen.title = "Mood History"
          open screen

          home_screen = HomeScreen.new
          home_screen.title = "Home"
          screen.open home_screen
        end
      
When you launch the app now, you will see a transition from the MoodHistory screen to the yellow HomeScreen. In addition iOS will add Back button - when you tap on it, it will automatically take you to the previous screen. Sometimes you may want to move user one screen back without making him tap that button. In this case, you can always use the close method on a screen that has to be closed. Let's launch the app again, and test it in the REPL (in the interactive console of Rubymotion):
        
        # get navigation controller from App Delegate. Notice the
        # class of a returning object:
        nav_controller = UIApplication.sharedApplication.delegate.window.rootViewController

        # get current visisble screen from navigation controller:
        screen = nav_controller.visibleViewController

        # close it:
        screen.close
      
Nevermind the first line of the code - since we are in the REPL, it takes some lines of code to get to the object we need. UIApplication.sharedApplication.delegate returns us our App Delegate object than we need to get its window that displays the app and its main view controller, which is Navigation Controller.

Navigation Controllers will be used a lot. I don't remember any app that I was working on that did not use it. Fortunately, they are very easy to use: open them when the app starts and manage your screens by opening and closing them inside of it. Sometimes you may want to come back to the first screen in the stack. This can be done with just one command:

        # inside of the screen with navigation controller:
        navigation_controller.popToRootViewControllerAnimated true
      
Sometimes you may want to get back to some specific screen, and it can also be easily done with:
        # inside of the screen with navigation controller:

        # get all screens in a current navigation controller stack:
        screens = navigation_controller.viewControllers

        # find a screen where we need to get back to:
        screen = screens.find do |s|
          # in our example we want to get back to instance of a MoodHistoryScreen:
          s.is_a? MoodHistoryScreen
        end

        if screen.nil?
          # there is no screen with a given class, show error
        else
          navigation_controller.popToViewController screen, animated: true
        end
      

Screen Titles

Each screen has a title attribute. By default screen title is displayed in Navigation Bar, and can be quickly changed with:

        self.title = "My Awesome Screen"
      

At this point, Navigation Bar just shows us screen's class name. It does not look good, and we will need to change HomeScreen to Home, and MoodHistoryScreen to Mood History. As usual, ProMotion helps us to do it in a prettier way:

        class MoodHistoryScreen < PM::Screen
          # ProMotion way of changing title:
          title "Mood History"

          # rest of the MoodHistoryScreen code ...
        end

        class HomeScreen < PM::Screen
          def on_load
            # Default way of changing screen title:
            self.title = "Home"

            # rest of on_load code ...
          end

          # rest of the HomeScreen code
        end
      
Both ways are fine and correct, and you can use whatever you like best. Now when you launch the app, Navigation controller will show updated screen names.

Summary

Now we know how to navigate between multiple screens. With ProMotion, we just need to specify nav_bar: true on our first screen to start using Navigation Controller. To open or close screens you can use the open or close command. Seems easy!

You can always find more info on Navigation Controllers at Apple's Official Docs.

Book Index | Next