What is it?
The below image shows the objects managed by a navigation controller. It stores child view controllers in an array. Pushing a view controller to the array make the view controller's view presented. The last view controller in the array or stack displays over the others.
Why use it?
How to do it?
I have MyCalculator app with a Settings page as shown in the image below.
1). Embed the view controller in a navigation controller
Below is SettingsViewController defined in the Interface Builder (IB).
Click on the view controller and select Editor menu then Embed In > Navigation Controller. It then would look like this:
2). SettingsViewController.swift
import UIKit
class SettingsViewController: UITableViewController {
typealias Item = (cellLabel: String, vcId: String?)
private var items: [Item] = [
(cellLabel: "Themes", vcId: "vcTheme"),
(cellLabel: "Decimal Places", vcId: "vcDecimalPlaces"),
(cellLabel: "Decimal Notation", vcId: "vcDecimalNotation"),
(cellLabel: "Disable Auto-Lock", vcId: nil)
]
private var tapGestureRecognizer = UITapGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "SwitchTableViewCell", bundle: nil), forCellReuseIdentifier: "tbcSwitch")
tableView.addGestureRecognizer(tapGestureRecognizer)
tapGestureRecognizer.addTarget(self, action: #selector(tapGestureHandler(_:)))
let closeButton = UIBarButtonItem(title: "Close", style: .done, target: self, action: #selector(closeButtonHandler(_:)))
navigationController?.navigationBar.topItem?.rightBarButtonItem = closeButton
}
@objc public func closeButtonHandler(_ sender:UIBarButtonItem) {
dismiss(animated: true, completion: {})
}
@objc public func tapGestureHandler(_ sender: UITapGestureRecognizer) {
let tappedPoint = sender.location(in: tableView)
if let indexPath = tableView.indexPathForRow(at: tappedPoint) {
let item = items[indexPath.row]
if let vcId = item.vcId {
let vc = storyboard?.instantiateViewController(withIdentifier: vcId)
vc?.title = item.cellLabel
navigationController?.pushViewController(vc!, animated: true)
}
}
}
...
// Required tableView() methods
}
I'm using TapGestureRecognizer to detect the selected row in the Settings page and then present the corresponding view controller.
The items variable stores identifiers of the child view controllers designed in the Interface Builder. The selected identifier is used to instantiate the corresponding view controller and present it, by pushing it to the navigation controller's array.
navigationController?.pushViewController(vc!, animated: true)
3). Add action to the Settings button/icon in the home page
Suppose I have ViewController for the home page designed in the Interface Builder and its file named ViewController.swift. Control-drag the Settings button in the ViewController from the Storyboard to the ViewController.swift file and create an action connection.
@IBAction func settingsButtonTouched(_ sender: SettingsButton) {
present(settingsViewController, animated: true, completion: {})
}
After that, add a variable to the ViewController class as following:
private var settingsViewController:UIViewController!
Then, initialize it in viewDidLoad() method:
settingsViewController = storyboard?.instantiateViewController(withIdentifier: "vcNavigation") as! UINavigationController
Note that we present the SettingsViewController from the home page, not its navigation controller. The navigation controller, as its name implied, just navigates the child view controllers of the SettingsViewController.
References