我想继承UITableViewController并能够通过调用没有参数的默认初始化程序来实例化它。
class TestViewController: UITableViewController {
convenience init() {
self.init(style: UITableViewStyle.Plain)
}
}
从Xcode 6 Beta 5开始,上面的示例不再有效。
Overriding declaration requires an 'override' keyword
Invalid redeclaration of 'init()'
答案 0 :(得分:59)
注意此错误已在iOS 9中修复,因此整个问题在此时都没有实际意义。以下讨论仅适用于明确适用的特定系统和Swift版本。
这显然是一个错误,但这也是一个非常简单的解决方案。我将解释问题,然后给出解决方案。请注意,我是为Xcode 6.3.2和Swift 1.2写的;自从Swift首次问世以来,Apple已经在这方面全力以赴,所以其他版本的行为会有所不同。
您将手动实例化UITableViewController (即通过在代码中调用其初始化程序)。并且你想要UITableViewController的子类,因为你有你想要的实例属性。
所以,你从一个实例属性开始:
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:)
的实现。并且也不继承 init(nibName:bundle:)
,因为您已经提供了指定的初始化程序,从而切断了继承。
您唯一的解决方案是实施 init(nibName:bundle:)
。但是你不能,因为这个实现需要你设置实例属性greeting
- 你不知道该把它设置为什么。
简单的解决方案 - 几乎太简单了,这就是为什么这么难以想到 - 是:不是UITableViewController的子类。为什么这是合理的解决方案?因为你从来没有真正需要来创建子类。 UITableViewController是一个毫无意义的类;它不能为你做任何你不能为自己做的事。
所以,现在我们将把我们的类重写为UIViewController子类。我们仍然需要一个表视图作为我们的视图,因此我们将在loadView
中创建它,我们也会在那里将它连接起来。更改标记为已加星标的评论:
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,我们就失去了从故事板中获取原型单元的能力。但这并不是反对意见,因为我们首先从未拥有这种能力。请记住,我们的假设是我们是子类并调用我们自己的子类的初始化器。如果我们从故事板中获取原型单元格,那么故事板将通过调用init(coder:)
来实例化我们,并且问题永远不会出现在第一位。
答案 1 :(得分:20)
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)
}
}
答案 2 :(得分:3)
我这样做了
class TestViewController: UITableViewController {
var dsc_var: UITableViewController?
override convenience init() {
self.init(style: .Plain)
self.title = "Test"
self.clearsSelectionOnViewWillAppear = true
}
}
使用此代码在TestViewController
中创建和显示UISplitViewController
的实例对我有用。
也许这是不好的做法,请告诉我它是否(刚开始使用swift)。
对于我来说,当存在非可选变量并且Nick Snyder的解决方案是唯一在此情况下工作的解决方案时仍然存在问题 只有一个问题: 变量初始化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)
}
答案 3 :(得分:2)
道具要哑光以获得很好的解释。我已经使用了matt和@Nick Snyder的解决方案,但我遇到了一个既不能正常工作的情况,因为我需要(1)初始化let
字段,(2)使用{{1} (没有得到运行时错误),以及(3)使用内置的init(style: .Grouped)
(来自UITableViewController)。我的解决方法是在ObjC中引入一个中间类refreshControl
,然后使用该类作为我的表视图控制器的基础。
MyTableViewController
#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"
答案 4 :(得分:1)
matt的答案是最完整的,但是如果你确实想要使用.plain风格的tableViewController(比如遗留原因)。然后你需要做的就是打电话
super.init(nibName: nil, bundle: nil)
而不是
super.init(style: UITableViewStyle.Plain)
或self.init(style: UITableViewStyle.Plain)
答案 5 :(得分:0)
我想继承UITableViewController并添加一个非可选属性,它需要覆盖初始化程序并处理上述所有问题。
如果您可以在UITableViewController的子类中使用可选的var而不是非可选的let,则使用Storyboard和segue可以提供更多选项
通过在呈现视图控制器中调用performSegueWithIdentifier并覆盖prepareForSegue,您可以获取UITableViewController子类的实例并在初始化完成之前设置可选变量:
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()
}
}
答案 6 :(得分:0)
我在使用静态tableview单元时发现了类似的错误,你必须实现这个:
init(coder decoder: NSCoder) {
super.init(coder: decoder)
}
如果您实施:
required init(coder aDecoder: NSCoder!) {
// Or call super implementation
fatalError("NSCoding not supported")
}
我刚刚在那里遇到了崩溃......有点像预期的那样。希望这会有所帮助。
答案 7 :(得分:0)
不确定它与您的问题有关,但如果您想使用xib初始化UITableView控制器,Xcode 6.3 beta 4发行说明提供了一种解决方法:
- 在Swift项目中,创建一个新的空iOS Objective-C文件。这将触发一张表格,询问您“是否要配置Objective-C桥接标题。”
- 点按“是”以创建桥接标题
- 在[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
答案 8 :(得分:0)
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")