如何在Swift中继承UITableViewController

Nic*_*der 40 uitableview ios swift xcode6

我想继承UITableViewController,并能够通过调用没有参数的默认初始化程序来实例化它.

class TestViewController: UITableViewController {
    convenience init() {
        self.init(style: UITableViewStyle.Plain)
    }
}
Run Code Online (Sandbox Code Playgroud)

从Xcode 6 Beta 5开始,上面的示例不再有效.

Overriding declaration requires an 'override' keyword
Invalid redeclaration of 'init()'
Run Code Online (Sandbox Code Playgroud)

mat*_*att 61

注意此错误在iOS 9中已修复,因此整个问题在此时都没有实际意义.以下讨论仅适用于明确适应的特定系统和Swift版本.


这显然是一个错误,但也有一个非常简单的解决方案.我将解释问题,然后给出解决方案.请注意,我正在为Xcode 6.3.2和Swift 1.2写这个; 自从Swift首次问世以来,Apple已经在这方面全力以赴,因此其他版本的行为将有所不同.

存在的基础

您将手动实例化UITableViewController (即通过在代码中调用其初始化程序).并且您希望继承UITableViewController,因为您有要提供的实例属性.

问题

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

class MyTableViewController: UITableViewController {
    let greeting : String
}
Run Code Online (Sandbox Code Playgroud)

这没有默认值,因此您必须编写初始值设定项:

class MyTableViewController: UITableViewController {
    let greeting : String
    init(greeting:String) {
        self.greeting = greeting
    }
}
Run Code Online (Sandbox Code Playgroud)

但那不是合法的初始化者 - 你必须打电话super.比方说,您的来电super是调用init(style:).

class MyTableViewController: UITableViewController {
    let greeting : String
    init(greeting:String) {
        self.greeting = greeting
        super.init(style: .Plain)
    }
}
Run Code Online (Sandbox Code Playgroud)

但是你仍然无法编译,因为你需要实现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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

let tvc = MyTableViewController(greeting:"Hello there")
Run Code Online (Sandbox Code Playgroud)

在您运行应用程序之前,一切看起来都很快乐和美好,此时您会崩溃并显示以下消息:

致命错误:使用未实现的初始化程序 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
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您还需要添加最少的必需数据源方法.我们现在像这样实例化我们的类:

let tvc = MyViewController(greeting:"Hello there")
Run Code Online (Sandbox Code Playgroud)

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

异议 - 不是

你可能会反对说,如果不使用UITableViewController,我们就失去了从故事板中获取原型单元的能力.但这并不是反对意见,因为我们从一开始没有这种能力.请记住,我们的假设是我们是子类并调用我们自己的子类的初始化器.如果我们从故事板中获取原型单元,故事板将通过调用实例化我们,init(coder:)并且问题永远不会出现在第一位.


Nic*_*der 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)
    }
}
Run Code Online (Sandbox Code Playgroud)