Which initializer(s) to override for UITableViewController subclass
Solution 1
My confusion was based on the mistaken belief that each class should have a single designated initializer. This is not true, and in the case of UITableViewController
there are 3 designated initializers (as far as I can tell):
-
initWithStyle:
declared locally -
initWithNibName:bundle:
inherited fromUIViewController
-
initWithCoder:
from adoptingNSCoding
protocol
You need to override 1 or more of these in your subclass depending on how your subclass gets instantiated. In my case I had to implement #2 and #3 since the class can be loaded from a NIB, or instantiated via code with reference to a NIB. (I imagine it's rare that you'll use both initWithStyle:
and initWithNibName:bundle:
for a single class.)
I found Apple's Coding Guidelines for Cocoa helpful.
Solution 2
Internally,
- UITableViewController's
-initWithStyle:
calls the super's-init
then set the_tableViewStyle
ivar. - UIViewController's
-init
simply calls-initWithNibName:bundle:
with default arguments. - UITableViewController does not override
-initWithNibName:bundle:
.
Therefore, if you override -initWithNibName:bundle:
then -initWithStyle:
will adopt the change too. Of course, to play safe (as you shouldn't rely on implementation details), override both of them.
(And no need to override -initWithCoder:
unless you will un/archive the instances.)
Solution 3
To clarify, initWithStyle:
, being UITableViewController
's only published initializer in the docs, is its one explicit designated initializer.
initWithNibName:bundle:
is inherited from UIViewController and is the designated initializer for that class. As such, in accordance with Cocoa guidelines, UITableViewController
must override this method (by implementing it). However, this does not make it a designated initializer of UITableViewController
.
initWithCoder:
is, as you point out, an implicit designated initializer from NSCoding
.
Daniel Dickison
I love Lisp, finding Swift pretty great, and I'd probably get hooked on Rust if I found some time to get to know it. Also: I don't hate JavaScript.
Updated on July 12, 2022Comments
-
Daniel Dickison almost 2 years
I have a
UITableViewController
subclass that's instantiated, depending on where it's used, in a NIB or via code. In both cases I want to do customization in the initializer method. Does that mean I need to implement bothinitWithNibName:bundle:
andinitWithCoder:
, and would each method call its respective super initializer?While I don't need this right now, what if I also want to be able to instantiate the view controller with
initWithStyle:
? Would I then need 3 different init methods that replicate the same behavior?It seems like this violates the whole designated initializer convention, as there would essentially be 3 separate initializers that don't end up calling a common init method. Or is there a way to create a common designated initializer while supporting the 3 different instantiate routes?
-
Daniel Dickison about 15 yearsI can't use viewDidLoad because, specifically, I need to set up self.navigationItem which may be used before the view is loaded. I could make a separate setup method. So is it just that NSCoding is fundamentally an exception to the "single designated initializer" rule?
-
TBBle over 13 yearsOverriding -initWithNibName:bundle: by itself should be safe, because it's not really an implementation detail but an aspect of the framework that designated initialisers are called all the way up the inheritance chain. And that inheritance chain itself is part of the UITableViewController's interface.
-
fnf about 11 yearsThat's not accurate. -[UIViewController initWithCoder:] seems to invoke -[UIViewController initWithNibName:bundle:]. No need to override it. Follow KennyTM's advice.
-
Sam over 10 yearsit is accurate. I have a situation where only initWithCoder is called. (I have all 3 methods overridden).
-
Gabriele Mondada over 8 yearsWhat is really confusing on iOS8 it that the initWithStyle: calls initWithNibName:bundle: and both have to be declared as designated initializers. This is not conform to swift initializer rules. On iOS8, if you do not define initWithNibName:bundle: and call initWithStyle:, the app crashes. This has been fixed on iOS9.