I want to subclass UITableViewController and be able to instantiate it by calling a default initializer with no arguments.
class TestViewController: UITableV
matt's answer is the most complete, but if you do want to use a tableViewController in the .plain style (say for legacy reasons). Then all you need to do is call
super.init(nibName: nil, bundle: nil)
instead of
super.init(style: UITableViewStyle.Plain)
or self.init(style: UITableViewStyle.Plain)
I did it like this
class TestViewController: UITableViewController {
var dsc_var: UITableViewController?
override convenience init() {
self.init(style: .Plain)
self.title = "Test"
self.clearsSelectionOnViewWillAppear = true
}
}
Creating and displaying a instance of TestViewController
in a UISplitViewController
did work for me with this code.
Maybe this is bad practice, please tell me if it is (just started with swift).
For me there's still a problem when there are non optional variables and the solution of Nick Snyder is the only one working in this situation
There's just 1 problem:
The variables are initialized 2 times.
Example:
var dsc_statistcs_ctl: StatisticsController?
var dsrc_champions: NSMutableArray
let dsc_search_controller: UISearchController
let dsrc_search_results: NSMutableArray
override init() {
dsrc_champions = dsrg_champions!
dsc_search_controller = UISearchController(searchResultsController: nil)
dsrc_search_results = NSMutableArray.array()
super.init(style: .Plain) // -> calls init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) of this class
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
// following variables were already initialized when init() was called and now initialized again
dsrc_champions = dsrg_champions!
dsc_search_controller = UISearchController(searchResultsController: nil)
dsrc_search_results = NSMutableArray.array()
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
Xcode 6 Beta 5
It appears that you can no longer declare a no-argument convenience initializer for a UITableViewController subclass. Instead, you need to override the default initializer.
class TestViewController: UITableViewController {
override init() {
// Overriding this method prevents other initializers from being inherited.
// The super implementation calls init:nibName:bundle:
// so we need to redeclare that initializer to prevent a runtime crash.
super.init(style: UITableViewStyle.Plain)
}
// This needs to be implemented (enforced by compiler).
required init(coder aDecoder: NSCoder!) {
// Or call super implementation
fatalError("NSCoding not supported")
}
// Need this to prevent runtime error:
// fatal error: use of unimplemented initializer 'init(nibName:bundle:)'
// for class 'TestViewController'
// I made this private since users should use the no-argument constructor.
private override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
}
I wanted to subclass UITableViewController and add a non-optional property which requires overriding the initializer and dealing with all the problems described above.
Using a Storyboard and a segue gives you more options if you can work with an optional var rather than a non-optional let in your subclass of UITableViewController
By calling performSegueWithIdentifier and overriding prepareForSegue in your presenting view controller, you can get the instance of the UITableViewController subclass and set the optional variables before initialization is completed:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segueA"{
var viewController : ATableViewController = segue.destinationViewController as ATableViewController
viewController.someVariable = SomeInitializer()
}
if segue.identifier == "segueB"{
var viewController : BTableViewController = segue.destinationViewController as BTableViewController
viewController.someVariable = SomeInitializer()
}
}
Props to matt for a great explanation. I've made use of both matt's and @Nick Snyder's solutions, however I ran into a case in which neither would quite work, because I needed to (1) initialize let
fields, (2) use init(style: .Grouped)
(without getting a runtime error), and (3) use the built-in refreshControl
(from UITableViewController). My workaround was to introduce an intermediate class MyTableViewController
in ObjC, then use that class as the base of my table view controllers.
#import <UIKit/UIKit.h>
// extend but only override 1 designated initializer
@interface MyTableViewController : UITableViewController
- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER;
@end
#import "MyTableViewController.h"
// clang will warn about missing designated initializers from
// UITableViewController without the next line. In this case
// we are intentionally doing this so we disregard the warning.
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
@implementation MyTableViewController
- (instancetype)initWithStyle:(UITableViewStyle)style {
return [super initWithStyle:style];
}
@end
#import "MyTableViewController.h"
class PuppyViewController : MyTableViewController {
let _puppyTypes : [String]
init(puppyTypes : [String]) {
_puppyTypes = puppyTypes // (1) init let field (once!)
super.init(style: .Grouped) // (2) call super with style and w/o error
self.refreshControl = MyRefreshControl() // (3) setup refresh control
}
// ... rest of implementation ...
}
class ExampleViewController: UITableViewController {
private var userName: String = ""
static func create(userName: String) -> ExampleViewController {
let instance = ExampleViewController(style: UITableViewStyle.Grouped)
instance.userName = userName
return instance
}
}
let vc = ExampleViewController.create("John Doe")