ios - 如何在 Swift 中继承 UITableViewController

标签 ios uitableview swift xcode6

我想子类化 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()'

最佳答案

注意 此错误已在 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)
    }
}

您的代码现在可以编译了!您现在很高兴(您认为)通过调用您编写的初始化程序来实例化此 TableView Controller 子类:

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

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

fatal error: use of unimplemented initializer 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 子类。我们仍然需要一个 TableView 作为我们的 View ,所以我们将在 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 我们已经失去了从 Storyboard 中获取原型(prototype)单元格的能力。但这并不反对,因为我们从一开始就没有这种能力。请记住,我们的假设是我们正在子类化并调用我们自己的子类的初始化程序。如果我们从 Storyboard 中获取原型(prototype)单元, Storyboard 将通过调用 init(coder:) 来实例化我们,问题一开始就不会出现。

关于ios - 如何在 Swift 中继承 UITableViewController,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25139494/

相关文章:

iphone - UITableview 中的 UItextView,自动更正气泡不可见

ios - 子类化 UITableViewCell 不显示添加的样式

ios - 如何排除故障,{Error=ExecutableTwiddleFailed}

ios - UIImageView 某些部分的渐变效果

swift - 如何在 UITableView Swift 上禁用尾随滑动?

swift - 如何更改 2x2 UICollectionView 的大小并适合整个屏幕 : Swift 3 Xcode 8

Swift 单例子类(继承)

swift - 在 Swift 中,如何将 CGSize 转换为其字符串表示形式?

ios - 在 iOS 7 设备的 CKContainer.defaultContainer() 处中断

ios - 动态添加的类不会快速响应触摸