11

我正在尝试创建一个简单的 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 类时,出现了类似的问题

4

2 回答 2

13

问题是init(customView:)调用init().

而且由于您有一个必需的初始化程序,因此您不再继承init(). 因此,您必须实现它,即使只是调用super.init().

然后,您将面临必须初始化 ininit()和 in 中的所有属性的(轻度)烦恼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)
    }
}

在最初提出问题的那一刻,这两者都不可能。

于 2014-11-24T22:55:04.693 回答
5

我不得不将 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")
}
于 2017-09-01T14:56:25.350 回答