【问题标题】:What initializers should a MKAnnotationView subclass have in Swift?MKAnnotationView 子类在 Swift 中应该有哪些初始化程序?
【发布时间】:2015-06-15 13:16:21
【问题描述】:

我正在我的项目中创建MKAnnotationView 的子类。它需要有两个属性来存储我需要在开始时在某处初始化的子视图。

MKAnnotationView 在其文档中列出了一个初始化程序 initWithAnnotation:reuseIdentifier:,所以我想我会简单地覆盖它:

class PulsatingDotMarker: MKAnnotationView {

    let innerCircle: UIView
    let outerCircle: UIView

    override init!(annotation: MKAnnotation!, reuseIdentifier: String!) {
        innerCircle = ...
        outerCircle = ...

        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    }

    ...
}

但这会导致运行时异常:

致命错误:对“PulsatingDotMarker”类使用未实现的初始化程序“init(frame:)”

好的,所以我猜initWithAnnotation:reuseIdentifier: 内部调用了initWithFrame:,所以它可能是我应该覆盖的那个。让我们试试吧:

class PulsatingDotMarker: MKAnnotationView {

    let innerCircle: UIView
    let outerCircle: UIView

    override init(frame: CGRect) {
        innerCircle = ...
        outerCircle = ...

        super.init(frame: frame)
    }

    ...
}

然而,这会在创建注释视图对象时导致编译错误:

调用中的额外参数“reuseIdentifier”

嗯,所以如果我实现(必需的)初始化程序 initWithFrame:,它现在会丢失默认初始化程序 initWithAnnotation:reuseIdentifier:

也许如果我添加一个只调用 superinitWithAnnotation:reuseIdentifier: 覆盖,它将再次可用,这会起作用吗?

class PulsatingDotMarker: MKAnnotationView {

    let innerCircle: UIView
    let outerCircle: UIView

    init!(annotation: MKAnnotation!, reuseIdentifier: String!) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    }

    override init(frame: CGRect) {
        innerCircle = ...
        outerCircle = ...

        super.init(frame: frame)
    }

    ...
}

不行,还是不好——编译错误:

属性 'self.innerCircle' 未在 super.init 调用中初始化

好的,如果我有一个initWithFrame:,但在initWithAnnotation:reuseIdentifier: 中初始化了子视图怎么办? (但是如果有人直接打电话给initWithFrame:呢?...)

class PulsatingDotMarker: MKAnnotationView {

    let innerCircle: UIView
    let outerCircle: UIView

    init!(annotation: MKAnnotation!, reuseIdentifier: String!) {
        innerCircle = ...
        outerCircle = ...

        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    ...
}

毫不奇怪,Swift 告诉我:

属性 'self.innerCircle' 未在 super.init 调用中初始化

(这次是initWithFrame:)。

那我该怎么办?我不能在这里和那里都创建子视图,对吧?

class PulsatingDotMarker: MKAnnotationView {

    let innerCircle: UIView
    let outerCircle: UIView

    init!(annotation: MKAnnotation!, reuseIdentifier: String!) {
        innerCircle = ...
        outerCircle = ...

        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    }

    override init(frame: CGRect) {
        innerCircle = ...
        outerCircle = ...

        super.init(frame: frame)
    }

    ...
}

又错了,这确实有效 - 即使我在同一个对象中分配了两次常量属性 (!)。

这应该如何正确完成?

(注意:该类还包括一个必需的 initWithCoder: 初始化程序,它仅调用第一个示例中的 fatalError,但该对象从未从情节提要中创建。)

【问题讨论】:

  • 如果子视图被声明为var innerCircle: UIView!怎么办?这可以避免“required init(xxx)”错误。
  • 这正是我最终做的事情,但这听起来像是一个 hack - 将属性标记为可为空和可变,即使它应该是非空和常量只是为了取悦编译器......跨度>
  • 我花了一些时间解决类似的问题,并做了一个测试项目,我认为我设法解决了原因(Objective C 类在其公共init 方法中调用[self initWith...] 方法),但不幸的是,目前还没有解决方案 - stackoverflow.com/questions/31161143/…

标签: swift initialization mapkit mkannotationview initializer


【解决方案1】:

不幸的是,MKAnnotationView 迫使您实现init(frame: CGRect),这意味着您还必须在该方法中初始化所有实例变量。

This article 再解释一下

对于只能使用传入值初始化的变量,您必须将这些变量设为可选,并在 init(frame: CGRect) 中将它们设置为 nil。

原因是我怀疑 MKAnnotationView 在其 Objective-C 的 init 方法中调用了self.initWithFrame: rect。这样如果子类覆盖initWithFrame:(CGRect) rect,它将被调用。但是,这会导致 swift 出现问题,因为如果您声明自定义指定的初始化程序,您不会继承超类的初始化程序。因此你必须在你的子类中实现init(frame: CGRect)

UITableViewController 也有同样的问题。它的标题看起来遵循相同的模式。即两个错误的指定初始化程序。

这让我很伤心。但你能做什么呢。

【讨论】:

    【解决方案2】:

    对于我的应用,我选择的解决方案是将子视图声明为可选并在 initFrame 中实例化它...

    var innerCircle: UIView?

    这是我的代码...

    class EventAnnotationView: MKPinAnnotationView
    {    
        static var REUSE_ID = "EventAnnotationView"
    
    var imageView: UIImageView?
    
    override init(frame: CGRect)
    {
        super.init(frame: frame)
    
        // Create subview for custom images
        imageView = UIImageView(frame: CGRectMake(0, 0, 22, 22))
    
        ...
    
    }
    
    override init(annotation: MKAnnotation!, reuseIdentifier: String!)
    {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    }
    
    required init(coder aDecoder: NSCoder)
    {
        super.init(coder: aDecoder)
    }
    }
    

    感觉不像是 hack :),但需要更多代码/工作,因为子视图是可选的。

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      鉴于指定的初始化程序实际上并没有在运行时被 iOS 调用,显然 Swift 3 中确实存在一些问题。

      我发现其他答案中的建议无法编译(在 XCode 8.1 GM / iOS 10.1 上测试),但经过各种黑客攻击后,我发现这种组合有效:

      override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
           super.init(annotation: annotation, reuseIdentifier: reuseIdentifier);
      
           /* Your actual init code */
      }
      
      convenience init(frame: CGRect) {
          self.init(annotation: nil, reuseIdentifier: nil);
      }
      

      【讨论】:

        猜你喜欢
        • 2015-01-11
        • 2016-10-20
        • 2017-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-27
        • 1970-01-01
        相关资源
        最近更新 更多