如何在Swift中inheritanceUITableViewController

我想要inheritanceUITableViewController并能够通过调用没有参数的默认初始化器来实例化它。

class TestViewController: UITableViewController { convenience init() { self.init(style: UITableViewStyle.Plain) } } 

从Xcode 6 Beta 5开始,上面的例子不再适用。

 Overriding declaration requires an 'override' keyword Invalid redeclaration of 'init()' 

注意这个bug在iOS 9中是固定的,所以整个事情在这个时候都是没有意义的。 下面的讨论仅适用于明确适用的Swift的特定系统和版本。


这显然是一个错误,但也有一个非常简单的解决scheme。 我会解释这个问题,然后给出解决scheme。 请注意,我正在写Xcode 6.3.2和Swift 1.2; 自从Swift第一次出现以来,苹果就已经在这个地图上出现了,所以其他版本的performance会有所不同。

存在的基础

您将手动实例化UITableViewController(即通过在代码中调用其初始值设定项)。 而你想要inheritanceUITableViewController,因为你有实例属性要给它。

问题

所以,你从一个实例属性开始:

 class MyTableViewController: UITableViewController { let greeting : String } 

这没有默认值,所以你必须写一个初始化程序:

 class MyTableViewController: UITableViewController { let greeting : String init(greeting:String) { self.greeting = greeting } } 

但是这不是一个合法的初始值 – 你必须调用super 。 假设你对super的调用是调用init(style:)

 class MyTableViewController: UITableViewController { let greeting : String init(greeting:String) { self.greeting = greeting super.init(style: .Plain) } } 

但是你仍然不能编译,因为你需要实现init(coder:) 。 所以你也是:

 class MyTableViewController: UITableViewController { let greeting : String required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } init(greeting:String) { self.greeting = greeting super.init(style: .Plain) } } 

你的代码现在编译! 你现在很高兴(你认为)通过调用你写的初始化程序实例化这个表视图控制器子类:

 let tvc = MyTableViewController(greeting:"Hello there") 

一切看起来都很快乐,直到你运行这个应用程序,在这个时候你会崩溃的消息:

致命错误:使用未实现的初始化程序init(nibName:bundle:)

什么导致崩溃,为什么你不能解决它

崩溃是由cocoa中的一个错误引起的。 对你不了解, init(style:)本身调用init(nibName:bundle:) 。 它会self调用。 那就是你 – MyTableViewController。 但MyTableViewController没有实现init(nibName:bundle:) 。 并且不会inheritance init(nibName:bundle:) ,因为您已经提供了一个指定的初始化程序,从而中断了inheritance。

你唯一的解决scheme是实现 init(nibName:bundle:) 。 但是你不能,因为这个实现需要你设置实例属性greeting ,而你不知道该怎么设置它。

简单的解决scheme

简单的解决scheme – 几乎太简单了,这就是为什么它很难想象 – 是: 不要inheritanceUITableViewController 。 为什么这是合理的解决scheme? 因为你从来没有真正需要首先将其子类化。 UITableViewController是一个毫无意义的类。 它不会为你自己做任何事情。

所以,现在我们要改写我们的类作为一个UIViewController子类。 我们仍然需要一个表视图作为我们的视图,所以我们将在loadView创build它,我们也将它挂在那里。 更改被标记为已加星标的评论:

 class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { // * let greeting : String weak var tableView : UITableView! // * init(greeting:String) { self.greeting = greeting super.init(nibName:nil, bundle:nil) // * } required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func loadView() { // * self.view = UITableView(frame: CGRectZero, style: .Plain) self.tableView = self.view as! UITableView self.tableView.delegate = self self.tableView.dataSource = self } } 

当然,您也可以添加所需的最小数据源方法。 我们现在像这样实例化我们的类:

 let tvc = MyViewController(greeting:"Hello there") 

我们的项目编译和运行顺利。 问题解决了!

反对意见 – 不是

您可能会反对,因为不使用UITableViewController,我们失去了从故事板获取原型单元格的能力。 但这并不是什么反对意见,因为我们从来没有这种能力。 请记住,我们的假设是我们正在inheritance并调用我们自己的子类的初始化器。 如果我们从故事板获得原型单元格,故事板将通过调用init(coder:)来实例化我们,问题不会首先出现。

Xcode 6 Beta 5

看来,你不能再为UITableViewController子类声明一个无参数的便利初始值设定项。 相反,您需要重写默认的初始化程序。

 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) } } 

我是这样做的

 class TestViewController: UITableViewController { var dsc_var: UITableViewController? override convenience init() { self.init(style: .Plain) self.title = "Test" self.clearsSelectionOnViewWillAppear = true } } 

UISplitViewController创build并显示一个TestViewController实例,对于我来说这个代码是UISplitViewController 。 也许这是不好的做法,请告诉我,如果是(刚刚开始与迅速)。

对于我来说还有一个问题,当有非可选variables时, Nick Snyder的解决scheme是唯一在这种情况下工作的人
只有1个问题:variables被初始化2次。

例:

 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) } 

道具马特的一个很好的解释。 我已经使用了马特和尼克斯奈德的解决scheme,但是我遇到了一个情况,其中既不会工作,因为我需要(1)初始化let字段,(2)使用init(style: .Grouped) (没有得到一个运行时错误),和(3)使用内置的refreshControl (从UITableViewController)。 我的解决方法是在ObjC中引入一个中间类MyTableViewController ,然后使用该类作为我的表视图控制器的基础。

MyTableViewController.h

 #import <UIKit/UIKit.h> // extend but only override 1 designated initializer @interface MyTableViewController : UITableViewController - (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER; @end 

MyTableViewController.m:

 #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 

将以下内容添加到项目的Bridging-Header.h中

 #import "MyTableViewController.h" 

然后迅速使用。 例如:“PuppyViewController.swift”:

 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 ... } 

马特的答案是最完整的,但如果你想在.plain风格(如出于传统原因)使用tableViewController。 那么你所要做的就是打电话

super.init(nibName: nil, bundle: nil)

代替

super.init(style: UITableViewStyle.Plain)self.init(style: UITableViewStyle.Plain)

我想要inheritanceUITableViewController并添加一个非可选属性,它需要重写初始化器并处理上述所有问题。

使用Storyboard和Segue为您提供了更多的select,如果您可以在UITableViewController的子类中使用可选的var而不是非可选的let

通过调用performSegueWithIdentifier并在呈现视图控制器中重写prepareForSegue,可以获得UITableViewController子类的实例,并在完成初始化之前设置可选variables:

 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() } } 

我注意到使用静态tableview单元格时类似的错误,你得实现这个:

 init(coder decoder: NSCoder) { super.init(coder: decoder) } 

如果你执行:

 required init(coder aDecoder: NSCoder!) { // Or call super implementation fatalError("NSCoding not supported") } 

我只是在那里崩溃…有点像预期的那样。 希望这可以帮助。

不知道它与你的问题有关,但是如果你想用xib初始化UITableView控制器,Xcode 6.3 beta 4 Release Notes提供了一个解决方法:

  • 在您的Swift项目中,创build一个新的iOS iOS Objective-C文件。 这会触发一个表单,询问你“你想configuration一个Objective-C桥接头”。
  • 点击“是”创build一个桥接头
  • 在[YOURPROJECTNAME] -Bridging-Header.h中添加以下代码:
 @import UIKit; @interface UITableViewController() // Extend UITableViewController to work around 19775924 - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER ; @end 
 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")