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

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

问题描述

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

  class TestViewController: UITableViewController {
方便init(){
self.init(样式:UITableViewStyle.Plain)
}
}

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

 覆盖声明需要'override'关键字
无效重新声明'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 =问候
}
}

但这不是合法的初始化程序 - 你必须拨打 super 。假设您对 super 的调用是调用 init(样式:)

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

但是你仍然无法编译,因为你需要实现 init(编码器:) 。所以你这样做:

  class MyTableViewController:UITableViewController {
let greeting:String
required init(coder aDecoder :NSCoder){
fatalError(init(编码器:)尚未实现)
}
init(问候语:字符串){
self.greeting = greeting
super.init(style:.Plain)
}
}

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

  let tvc = MyTableViewController(问候语) :你好那里)

在你运行之前,一切看起来都很愉快和美好应用程序,此时你崩溃了这条消息:


致命错误:使用未实现的初始值设定项 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:字符串
弱var tableView:UITableView! // *
init(问候语:字符串){
self.greeting = greeting
super.init(nibName:nil,bundle:nil)// *
}
必需init(编码器aDecoder:NSCoder){
fatalError(init(编码器:)尚未实现)
}
覆盖func loadView(){// *
self.view = UITableView(框架:CGRectZero,样式:.Plain)
self.tableView = self.view as! UITableView
self.tableView.delegate = self
self.tableView.dataSource = self
}
}

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

 让tvc = MyViewController(问候:你好)

我们的项目编译和运行没有任何障碍。问题解决了!



异议 - 不



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

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

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

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

解决方案

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.


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.

The Ground of Being

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.

The Problem

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

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

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:

fatal error: use of unimplemented initializer 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.

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.

The Simple Solution

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.

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!

An Objection - Not

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天全站免登陆