如何在 Swift 中继承 UITableViewController [英] How to subclass UITableViewController in Swift

查看:68
本文介绍了如何在 Swift 中继承 UITableViewController的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想继承 UITableViewController 并能够通过调用不带参数的默认初始值设定项来实例化它.

I want to subclass UITableViewController and be able to instantiate it by calling a default initializer with no arguments.

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

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

As of the Xcode 6 Beta 5, the example above no longer works.

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

推荐答案

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

NOTE This bug is fixed in iOS 9, so the entire matter will be moot at that point. The discussion below applies only to the particular system and version of Swift to which it is explicitly geared.

这显然是一个错误,但也有一个非常简单的解决方案.我会解释问题,然后给出解决方案.请注意,我是为 Xcode 6.3.2 和 Swift 1.2 编写的;自 Swift 首次问世之日起,Apple 就一直在关注这方面的问题,因此其他版本的行为会有所不同.

This is clearly a bug, but there's also a very easy solution. I'll explain the problem and then give the solution. Please note that I'm writing this for Xcode 6.3.2 and Swift 1.2; Apple has been all over the map on this since the day Swift first came out, so other versions will behave differently.

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

You are going to instantiate UITableViewController by hand (that is, by calling its initializer in code). And you want to subclass UITableViewController because you have instance properties you want to give it.

因此,您从实例属性开始:

So, you start out with an instance property:

class MyTableViewController: UITableViewController {
    let greeting : String
}

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

This has no default value, so you have to write an initializer:

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

但这不是合法的初始化程序 - 您必须调用 super.假设您对 super 的调用是调用 init(style:).

But that's not a legal initializer - you have to call super. Let's say your call to super is to call init(style:).

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

但是你还是不能编译,因为你有实现init(coder:)的需求.所以你这样做:

But you still can't compile, because you have a requirement to implement init(coder:). So you do:

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

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

Your code now compiles! You now happily (you think) instantiate this table view controller subclass by calling the initializer you wrote:

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

一切看起来都很愉快,直到您运行应用程序,此时您因以下消息崩溃:

Everything looks merry and rosy until you run the app, at which point you crash with this message:

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

fatal error: use of unimplemented initializer init(nibName:bundle:)

导致崩溃的原因以及您无法解决的原因

崩溃是由 Cocoa 中的一个错误引起的.你不知道,init(style:) 本身调用了 init(nibName:bundle:).它在 self 上调用它.那就是你 - MyTableViewController.但是 MyTableViewController 没有 init(nibName:bundle:) 的实现.并且不会继承 init(nibName:bundle:),因为你已经提供了一个指定的初始化器,从而切断了继承.

What Causes the Crash and Why You Can't Solve It

The crash is caused by a bug in Cocoa. Unknown to you, init(style:) itself calls init(nibName:bundle:). And it calls it on self. That's you - MyTableViewController. But MyTableViewController has no implementation of init(nibName:bundle:). And does not inherit init(nibName:bundle:), either, because you already provided a designated initializer, thus cutting off inheritance.

你唯一的解决方案是实现init(nibName:bundle:).但您不能,因为该实现需要您设置实例属性 greeting - 而您不知道将其设置为什么.

Your only solution would be to implement init(nibName:bundle:). But you can't, because that implementation would require you to set the instance property greeting - and you don't know what to set it to.

简单的解决方案 - 几乎太简单了,这就是为什么很难想到的原因 - 是:不要继承 UITableViewController.为什么这是一个合理的解决方案?因为您实际上从一开始就需要对它进行子类化.UITableViewController 是一个基本上毫无意义的类;它不会为你做任何你不能为自己做的事情.

The simple solution - almost too simple, which is why it is so difficult to think of - is: don't subclass UITableViewController. Why is this a reasonable solution? Because you never actually needed to subclass it in the first place. UITableViewController is a largely pointless class; it doesn't do anything for you that you can't do for yourself.

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

So, now we're going to rewrite our class as a UIViewController subclass instead. We still need a table view as our view, so we'll create it in loadView, and we'll hook it up there as well. Changes are marked as starred comments:

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

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

Also you'll want, of course, to add the minimal required data source methods. We now instantiate our class like this:

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

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

Our project compiles and runs without a hitch. Problem solved!

您可能会反对,因为不使用 UITableViewController 我们已经失去了从故事板中获取原型单元格的能力.但这并不反对,因为我们从一开始就没有这种能力.请记住,我们的假设是我们正在继承并调用我们自己的子类的初始化程序.如果我们从情节提要中获取原型单元格,情节提要将通过调用 init(coder:) 来实例化我们,并且问题一开始就不会出现.

You might object that by not using UITableViewController we have lost the ability to get a prototype cell from the storyboard. But that is no objection, because we never had that ability in the first place. Remember, our hypothesis is that we are subclassing and calling our own subclass's initializer. If we were getting the prototype cell from the storyboard, the storyboard would be instantiating us by calling init(coder:) and the problem would never have arisen in the first place.

这篇关于如何在 Swift 中继承 UITableViewController的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆