I have been creating my Swift project completely programmatically thus far. I have created detailed Navigation Controllers and Table View Controllers without touching the StoryBoard (SB) once. I have come to a point where I would like to pass data from a TableViewCell click onto another ViewController, however, I believe that I need to use a segue and create/identify it inside of the SB. Since this application has become pretty complex over time, it has become quite difficult to mimic all of the view controllers inside of the SB, and creating any changes inside of the SB is not reflecting any changes inside of the Views on the simulator (I have paired the views inside of the SB to their respective class, yet nothing is working. Even the segue is not being recognized when the identifier matches up). Therefore, I have a couple of questions.
Is it possible to perform a segue programmatically? In other words, is it possible to pass data from one view controller to another without touching the story board?
Is there a simple guide or technique one can follow in order to mimic their code inside of the StoryBoard? With a simple application, it shouldn't be too difficult. However, is there any other way in order to portray the application in the StoryBoard if someone has been creating it completely programmatically?
Interface Builder (IB) is just a GUI for programmatic development. Anything you can do in IB you can definitely do programmatically but not everything you can do programmatically you can do in IB, because IB is just for interface building—and just some interface building, not all.
segue
is just IB terminology. In iOS, you can only display a view controller three ways: push
(via UINavigationController
), present
(via a presentation object vendor UIViewControllerTransitioningDelegate
), or show
(a more generic approach to displaying a view controller).
Since you're new to programmatic iOS development (the best kind, IMO), a quick 101:
class ProgrammaticViewController: UIViewController {
override func loadView() {
// do not call super.loadView()
// most IB developers don't even know this method exists
// because this is where IB does its work
// add all of your view objects here (scroll views,
// table views, buttons, everything)
setView()
addTableView()
...
}
override func viewDidLoad() {
super.viewDidLoad()
// do call super.viewDidLoad(), however
// do your post-view setup here, like adding observers
// or fetching data
// this method is called after the entire view
// has been loaded into memory so consider that
}
// other lifecycle events that come after viewDidLoad where you
// can perform last-second work include viewDidLayoutSubviews(),
// viewWillAppear(), viewDidAppear(), etc.
deinit {
// do any cleanup here like deactivating timers, removing
// observers, etc.
}
// MARK: Methods
func setView() {
// if you're creating your view controller programmatically,
// you must create the view controller's actual view property
// and it must be done before adding any subviews to the
// view controller's view
view = UIView()
view.frame = UIScreen.main.bounds
view.backgroundColor = UIColor.white
}
}
If you are using auto layout (and you probably should be), you don't always need to explicitly set the frame of the view with view.frame = UIScreen.main.bounds
. If it's the root view controller of the app, it's not needed—the window owns that view controller and it will set the frame. If you've then put a UINavigationController
in the root and you're using auto layout, none of the view controllers you push to ever need their frame set as well. Therefore, the only real time you need to explicitly set the view's frame using something like view.frame = UIScreen.main.bounds
is when you present a view controller modally. This is because a modally-presented view controller is not owned by the window or a navigation controller; it exists temporarily inside a transient container view. In this case, you must set its frame.
Passing data forward programmatically is simpler than it is for IB developers. Just instantiate a view controller, inject one of its (non-private) properties with a value, and push, present, or show it. Here's what a UITableView
delegate for didSelectRowAt
may look like:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let p = indexPath.row
let detailViewController = SomeDetailViewController()
detailViewController.someProperty = searchResults[p]
navigationController?.pushViewController(detailViewController, animated: true)
}
And this is obviously doable because you've created that property in that view controller and not given it a private access modifier.
class SomeDetailViewController: UIViewController {
var dataObject: SomeType? // injectable
private var notInjectable: SomeType?
}
UPDATE: IB is clearly not the future of iOS development as far as Apple is concerned, with the introduction of SwiftUI, which is another way of programmatically developing iOS. Personally, I hate GUI programming and I'm happy to see it slowly phase out.