>我正在尝试创建一个简单的 UIBarButtonItem 子类:
class LabelBarButtonItem: UIBarButtonItem {
let label: UILabel
init(text: String) {
self.label = UILabel(frame: CGRectZero)
self.label.tintColor = UIColor.grayColor()
super.init(customView: self.label)
self.label.text = text
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
但是当我尝试实例化它时:
let countButton = LabelBarButtonItem(text: "0 items")
代码正确编译,但在运行时失败,并显示:
fatal error: use of unimplemented initializer 'init()' for class 'TestProject.LabelBarButtonItem'
我不明白为什么在这种情况下会发生这种情况,或者通常是什么原则导致这个问题。init(text)
初始值设定项应直接委托给超类中的init(customView)
。为什么叫init()
?它不应该参与其中。
谁能解释一下发生了什么?当我尝试对其他 UIKit 类进行子类化时,也出现了类似的问题
问题是init(customView:)
调用init()
.
而且由于您具有所需的初始值设定项,因此您不再继承init()
.因此,您必须实现它,即使只是调用super.init()
。
然后,您将面临必须初始化init()
和init(text:)
中的所有属性的(轻微)烦恼。但是,在您的特定情况下,首先不需要在初始值设定项中执行此操作。我会这样写你的类:
class LabelBarButtonItem: UIBarButtonItem {
let label = UILabel(frame: CGRectZero)
init(text: String) {
super.init(customView: self.label)
self.label.text = text
self.label.tintColor = UIColor.grayColor()
}
private override init() {
super.init()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
在您的评论中,您问:
但是现在如果我覆盖 init(),我的类的客户端可以通过调用 LabelBarButtonItem() 来创建 LabelBarButtonItem 的无效实例
正确。这就是为什么我将init()
覆盖标记为private
。请注意,仅当此类在文件中单独关闭时,这才有效。
另一种可能性是将所有初始值设定项(包括init(coder:)
)标记为convenience
。现在你继承了init()
,整个问题就消失了。但是,这引入了另一组问题,并留给读者练习。
澄清一下,我认为这整个情况是 Swift 中的一个错误。IIRC直到最近的修订版(Xcode 6.1?)才表现得像这样。你被super.init(customView:)
打电话给你init()
的事实所包围。你可以争辩说这是错误的;您有一个很好的错误报告用例。运行时的崩溃似乎是错误的行为;如果这会发生,为什么编译器不阻止我们?也许其他人可以证明 Swift 在这种情况下的所作所为是合理的。我只是想解释一下。
编辑 这个答案来自 2014 年,旨在解决当时存在的错误。在现代iOS版本中,该错误已消失,您可以这样说:
class LabelBarButtonItem: UIBarButtonItem {
init(text: String) {
let label = UILabel(frame: .zero)
label.text = text
label.sizeToFit()
super.init()
self.customView = label
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
或者你可以这样说:
class LabelBarButtonItem: UIBarButtonItem {
convenience init(text: String) {
let label = UILabel(frame: .zero)
label.text = text
label.sizeToFit()
self.init(customView: label)
}
}
在最初提出这个问题的那一刻,这两种情况都是不可能的。
我不得不为一个项目对UIBarButtonItem进行子类化,遵循Matt的答案就绰绰有余了,只有一点点不同。我必须为初始值设定项增加便利性才能使代码正常工作。就我而言,我的子类中没有属性。
这是适用于 Swift 3 的代码,以防它对任何人有帮助。
class PlayerBarButtonItem: UIBarButtonItem {
convenience init(assetName: String, target: Any, selector: Selector) {
let barButton = UIButton(frame: CGRect(x: 0, y: 0, width: 34, height: 34))
let barButtonImage = UIImage(named: assetName)?.withRenderingMode(.alwaysTemplate)
barButton.setImage(barButtonImage, for: .normal)
barButton.tintColor = UIColor.white
barButton.addTarget(target, action: selector, for: .touchUpInside)
self.init(customView: barButton)
}
private override init() {
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}