Reputation: 751
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?
Upvotes: 3
Views: 2544
Reputation: 12385
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.
Upvotes: 5
Reputation: 124997
Is it possible to perform a segue programmatically?
Sure. UIStoryboardSegue
has initializers that you can use to create a segue, and it has a perform()
method that you can call to make the segue do it's thing. But segues are mostly a tool that makes it easier to transition between scenes in a storyboard, so there's little reason to want to do that if you're not using storyboards in the first place.
In other words, is it possible to pass data from one view controller to another without touching the story board?
Again, yes. Storyboards didn't even exist until iOS 5, and you can bet people were writing apps with transitions between view controllers well before that. The answer you've already gotten about how to do that is fine. More generally, though, view controllers are just objects, and all that one needs to send information to another is a valid reference to the destination controller.
However, is there any other way in order to portray the application in the StoryBoard if someone has been creating it completely programmatically?
If you mean that you want to switch over to using storyboards, then yes, there are some good ways to do that. If you mean that you want to somehow represent what your app is doing programmatically inside a storyboard but continue to do everything programmatically, then no, there's no way to do that (and really no point in doing it).
Transitioning to storyboards doesn't have to be an all or none kind of thing. You can model just one view controller in a storyboard and load it as needed using init(name:bundle:)
(to initialize the storyboard itself) and instantiateViewController(withIdentifier:)
(to load a particular view controller), and then just use that view controller the same way you do now. All the code that you use to set up that view controller and it's view hierarchy can then be removed, since you're now loading it from the storyboard instead. Once that's working, repeat the process for other view controllers, expanding your storyboard and excising the setup code as necessary.
Upvotes: 1