Sometimes I want to subclass UIViewController for some app wide customizations. Eg. something that all view controllers should perform during viewDidLoad or viewWillAppear or so.
Naturally I subclass UIViewController and go from there and all view controllers inherit from that. But some of the controllers run tables. And there is UITableViewController designed for that purpose.
So I subclass UITableViewController too and just do the same things there. Which does not seem to be the smartest thing in OOP terms. And there is no multiple inheritance etc.
And as UITableViewController inherits from UIViewController ...
Now I am asking myself why I don't just create my own table view controller that inherits from my very own view controller subclass and adds all the table stuff. But what is "all the table stuff"?
There is the skeleton code that xcode adds to every new table view controller. Quite handy but that can be easily moved to code snippets.
There is the declaration of the protocols UITableViewDelegate and UITableViewDataSource. Easy going. The implementation of those methods has to follow in each subclass of UITableViewController anyway.
There are probably reasonable default implementations of all those mandatory methods in the protocol. Such as returning 0 for numberOfSectionsInTableView: or nil for titleForHeaderInSection or 44.0f for heightForRowAtIndexPath: (Bad example though. Could be smarter not implementing that at all)
So despite the obvious stuff, are there any miracles that UITableViewController takes care of?
I believe all of the behavior UITableViewController adds is well defined in the class documentation: https://developer.apple.com/documentation/uikit/uitableviewcontroller?language=objc
The UITableViewController class creates a controller object that manages a table view. It implements the following behavior:
• If a nib file is specified via the initWithNibName:bundle: method (which is declared by the superclass UIViewController), UITableViewController loads the table view archived in the nib file. Otherwise, it creates an unconfigured UITableView object with the correct dimensions and autoresize mask. You can access this view through the tableView property.
• If a nib file containing the table view is loaded, the data source and delegate become those objects defined in the nib file (if any). If no nib file is specified or if the nib file defines no data source or delegate, UITableViewController sets the data source and the delegate of the table view to self.
• When the table view is about to appear the first time it’s loaded, the table-view controller reloads the table view’s data. It also clears its selection (with or without animation, depending on the request) every time the table view is displayed. The UITableViewController class implements this in the superclass method viewWillAppear:. You can disable this behavior by changing the value in the clearsSelectionOnViewWillAppear property.
• When the table view has appeared, the controller flashes the table view’s scroll indicators. The UITableViewController class implements this in the superclass method viewDidAppear:.
• It implements the superclass method setEditing:animated: so that if a user taps an Edit|Done button in the navigation bar, the controller toggles the edit mode of the table.
All of these are behaviors which should be easy to re-implement if they apply to your specific controller and table view.
In general I have found it preferable to implement these behaviors myself in order to allow for alternate inheritance hierarchies as you noted and because I usually consider setting both the delagate and datasource of a table view to be a view controller a design smell. Those are independent concerns which usually can and should be handled by some other class (e.g. a dedicated data source for a particular model class) rather than bloating a view controller.
So despite the obvious stuff, are there any miracles that UITableViewController takes care of?
Not that I am aware of. As far as I know UITableViewController is mostly a convenience class that can be replaced with your own subclass that adds a few lines of code.
Apple's class documentation pretty much says all that UITableViewController does (and I'm not repeating that here because it may well change in the future). Sometimes, additional information can be gleaned from a class' header file, but in the case of UITableViewController.h the source code comments just repeat what is already in the class docs.
In the end you must decide yourself what you want to replicate in your own subclass. Maybe your project does not need .nib handling? Or none of your table views is editable? So just don't code that...
It seems that the UITableViewController takes care of a lot of management issues that really you could just do for yourself, if you wanted to. Check out the documentation -- it will automatically create a UITableView for you, reload it, etc.
Related
In my project I have two view controllers, and I am having trouble connecting objects such as an UIImageView to the view controller. When I try to create the IBOutlet, it tells me that "Could not insert new outlet collection: could not find any information for the class named UIViewController". I believe this problem stems from the fact that my original declaration of my class is as follows:
class UIViewController: UIViewController {
when in fact the view controller is named mainScene instead. However, when I change the first UIViewController to what I think it should be (mainScene), it doesn't even show me the option of connecting an IBOutlet...
class mainScene: UIViewController {
So, I have two questions.
Do I need to have a whole separate class for the second UIViewController and would that solve my issues?
Is there a better way to link objects to the UIViewController or am I just doing something horribly wrong (the likely scenario)?
Thanks so much
Short answer: 1. Yes, and yes. 2. There's no better way, and you're not doing something horribly wrong. (You probably just missed a step.)
You have two view controllers. Assuming they are different, you would subclass each one from UIViewController with a different name. E.g., mainSceneViewController and otherSceneViewController.
Your mainSceneViewController and otherSceneViewController would each have their own properties and IBOutlets.
Where you're probably stuck, is needing to change the class of your viewController within Interface Builder to match the class name in its .swift file, so IB knows what outlets it can connect for that view controller.
Each scene in your storyboard corresponds to a view controller. When the segue is performed, iOS instantiates your view controller from the storyboard.
While it is possible to only have one view controller subclass, and use the same subclass for different views, it doesn't happen too often.
Update:
Subclassing lets you add properties and methods to a class, and override their superclass.
In your comment, UIViewController is the class, and mainSceneViewController is subclassed from UIViewController. For your second view controller, it would likely be class otherSceneViewController: UIViewController {, as your other scene would likely require a different properties and methods from your main scene view controller.
You can read more about Inheritance in the Swift Programming Language guide.
Most of my app is created programmatically, but I have a handful of complex views that are created using Interface Builder and loaded programmatically.
In one view I have a UITableViewController subclass created programmatically, and its tableView property is assigned to the UITableView instance in the XIB, referenced as an IBOutlet. This happens in the XIB's UIViewController's viewDidLoad, and the table view displays correctly.
At one point, I need to change the entire contents of the table view. Ideally, I would like to disconnect that view from the first UITableViewController and connect it to a different one that will feed it different data. Attempting to assign the view to the new controller's tableView property results in "A view can only be associated with at most one view controller at a time!" Assigning the previous controller's tableView to nil before assigning the new one avoids the error, but the table view disappears from the screen.
Honestly, I know the table view instance is usually managed by the UITableViewController. I can't find anything in the docs about setTableView automatically setting the back references for delegate and dataSource in an assigned tableView, so I'm surprised my externally-created tableView works in the first place. In fact, the TVC docs seem to discuss UITableViewController's tableView property as readonly, even though it is not tagged as such.
I'm sure I don't have all your information, but on the surface it seems that you are making this more complicated than it needs to be. Why don't you just use the current UITableViewController and corresponding UITableView and reload it with "new data". Alternatively, just programmatically create another UITableViewController and use that for your new data. Is there a compelling reason to recycle the UITableView?
The answer is no, and here's why:
I checked with the debugger, and the normal setTableView does set the view property on self as well as setting the delegate and dataSource properties on the new incoming tableView. This is not documented by Apple, but it was the assumed behaviour and so it was all well and good.
What I did not realize is that the default setView (from UIViewController) will remove an existing view from its superview before setting the new one. To prevent the "one view controller at a time" runtime error I was setting the old TVC's tableView to nil, which caused the tableView I intended to reuse to be removed from the view hierarchy. To avoid this behaviour, one would have to override setView, which is more complicated than I care to get into for this task.
Reusing views (in this case, a UITableView instance) is not supported by Apple. It is not mentioned in the UITableViewController documentation, but it is mentioned in the UITableViewController documentation regarding the view property. Arguably, UITableViewController's tableView property should be marked readonly by Apple, with its tableView solely managed internally, but there's probably a good reason why its not.
I have a Table View Controller that displays information using JSON. I want to change the styling of my app, and I don't want it to have that "table" view that it has now. Whats the easiest way to change my Table View Controller to a regular View Controller, the biggest problem I have is that the code uses a tableView and I dont know how to get it to work as a regular view controller.
I using a Storyboard with a TableViewController thats linked to a controller called UpcomingReleasesViewController.
I want my app:
To look like this:
My original answer was assuming you just wanted to convert from a UITableViewController to a UIViewController. Looking at your screen snapshots, I infer you really want to switch from a UITableViewController to a UICollectionViewController (which is an iOS 6 feature than allows you to do precisely what you want).
In that case, change your base class to UICollectionViewController, and replace your UITableViewDataSource methods with UICollectionViewDataSource methods. And then redesign your scene using a Collection View Controller.
See the Collection View Programming Guide for more information. Or see WWDC 2012 sessions Introducing Collection Views or Advanced Collection Views and Building Custom Layouts.
If you need to support iOS versions prior to 6, then you have to do this collection view style coding yourself manually, putting your image views on a scroll view and using a standard UIViewController. It require more effort than using a collection view, but can be done.
Original answer:
If this view controller will have a table view on it, but you just want to add more controls to the scene? If so, just change the view controller's base class from UITableViewController to UIViewController, create the new scene, add a table view to it, and specify the table view's delegate and data source to be the view controller:
Also, make sure you define an IBOutlet for your table view (and if you call it tableView, that will minimize any coding changes needed).
If you do that, you can quickly convert a UITableViewController based controller to a UIViewController with minimal code changes.
If you're looking to make something like your new UI mockup, look into UICollectionView. You'll see many of the same concepts (i.e. dataSource, delegate method signatures are similar) that are used in UITableViews used in the collectionView API.
In Programming iOS 4 by Matt Newburg he states:
“To provide a UIViewController with a view manually, implement its
loadView method… You must NOT call super”.
In the iOS 5 Developer's Cookbook by Erica Sadun she states:
“The loadView method allows you to set up the screen and layout any
subviews… Make sure to call [super loadView] whenever you inherit
from a specialized subclass such as UITableViewController or
UITabBarController.”
This, to me at least, is confusing.
Apple is the source of truth and they say NO super call.
If you override this method in order to create your views manually,
you should do so and assign the root view of your hierarchy to the
view property. (The views you create should be unique instances and
should not be shared with any other view controller object.) Your
custom implementation of this method should not call super.
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621454-loadview
[edit]
Another important note scattered around in the UIViewController class reference:
The default loadView method attempts to load the view from the nib
file associated with the view controller (if any).
This is a very old question, but I find that it needs a better answer than the one it got.
Should [super loadView] be called from loadView or not?
It depends. The two sources you cite are talking about different situations, and they're both correct in the context they're describing.
The quote from Neuberg is talking about view controllers that inherit directly from UIViewController. That class has its own implementation of loadView that provides default behavior; specifically, it automatically loads the view hierarchy from a .xib (or storyboard) file that's associated with the view controller. If you call UIViewController's version of that method, the view hierarchy created in that method will either replace your own implementation's view hierarchy, or vice versa. Nine years after this question was posed, the documentation for UIViewController's -loadView method still warns against that:
You can override this method in order to create your views manually. If you choose to do so, assign the root view of your view hierarchy to the view property. The views you create should be unique instances and should not be shared with any other view controller object. Your custom implementation of this method should not call super. [emphasis added]
The quote from Sadun is talking about a different situation, i.e. one in which your view controller is not a direct subclass of UIViewController, but is instead derived from UITableViewController, UITabBarController, etc. Those classes override -loadView themselves and need their versions called. At least in the case of UITableViewController, this is called out in the Overview section:
You may override loadView or any other superclass method, but if you do, be sure to invoke the superclass implementation of the method, usually as the first method call.
So, if you're subclassing UIViewController and providing your own -loadView implementation to generate the controller's views rather than using a .xib or storyboard to provide the views, don't call the superclass's -loadView method. On the other hand, if you're subclassing a class such as UITableView and doing the same thing, check the docs to see whether you need to call that class's -loadView method from your own override.
In the Object Library of Xcode, there are two options one can use to create table view - table view and table view controller.
What is the difference between the two and when would they be used ?
A TableViewController is a ViewController with a TableView built in. This will have the delegate methods needed already declared and setup. This VC is already a TableView delegate and datasource. It cannot be resized. Upside is ease of use, downside is very limited flexibility.
A TableView is just that a TableView (subclass of UIView). It can be added to a ViewController and resized, used alongside another view based object, etc. The upside is the flexibility, the downside is that you have to setup the delegate and datasource methods yourself (in my opinion, well worth the time to get the flexibility).
One other note is that when using the new Static TableView cells (part of iOS5), you have to use a TableViewController.
The UITableViewController is a subclass of the UIViewController. It already assumes you will have UITableView as your rootView, so you already have access from the code to a tableView (self.tableView). It implements the UITableViewDataSource and the UITableViewDelegate protocol. It also gives you alot of methods for you to override. It allows you to not depend on XIB file, because you already know what you will have (UITableView as a rootView).
The UITableView is just UIView, normally you will have to comply to the protocols I have referenced above in your UIViewController in order to populate (data source) and work with it (delegate), and you probably have to create an IBOutlet for your UITableView.
On one hand you have speed but you are not as flexible as the other path. On the other you have the opposite.