我试图创建一个简单的UIBarButtonItem的Swift子类:
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类子类化
时,出现了类似的问题答案 0 :(得分:6)
问题在于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中的一个错误。在最近的修订版(Xcode 6.1?)之前,它并没有像IIRC这样的行为。 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)
}
}
在最初提出问题的时刻,这些都不可能。
答案 1 :(得分:4)
我必须将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")
}