UIBezierPath 子类初始化器

UIBezierPath Subclass Initializer

我正在尝试创建 UIBezierPath 的子类以添加一些对我有用的属性。

class MyUIBezierPath : UIBezierPath {
   var selectedForLazo : Bool! = false

   override init(){
       super.init()
   }

   /* Compile Error: Must call a designated initializer of the superclass 'UIBezierPath' */
   init(rect: CGRect){
       super.init(rect: rect)
   }

   /* Compile Error: Must call a designated initializer of the superclass 'UIBezierPath' */
   init(roundedRect: CGRect, cornerRadius: CGFloat) {
       super.init(roundedRect: roundedRect, cornerRadius: cornerRadius)
   }

   required init(coder aDecoder: NSCoder) {
       fatalError("init(coder:) has not been implemented")
   }
}

编辑: 我需要这个,因为在我的代码中我写了

var path = MyUIBezierPath(roundedRect: rect, cornerRadius: 7)

并导致编译错误:

"Must call a designated initializer of the superclass 'UIBezierPath'"

我试图在子类中添加那个初始化器,但它似乎不起作用。

你能帮帮我吗?

我发现这个简单的解决方法似乎可以正常工作。

class MyUIBezierPath : UIBezierPath {
   var selectedForLazo : Bool! = false

   override init() {
       super.init()
   }

   init(rect: CGRect){
       super.init()
       self.appendPath(UIBezierPath(rect: rect))
   }

   init(roundedRect: CGRect, cornerRadius: CGFloat) {
       super.init()
       self.appendPath(UIBezierPath(roundedRect: roundedRect, cornerRadius: cornerRadius))
   }

   required init(coder aDecoder: NSCoder) {
       super.init()
   }
}

请 post 其他解决方案,因为它们肯定会比我的更好。

注意:此问题在iOS 9 中已解决,其中 API 已被重写,因此 init(rect:) 存在,因此做所有其他的,作为便利的初始值设定项,因为它们应该是。


问题

简而言之,您遇到的问题是以下代码无法编译:

    class MyBezierPath : UIBezierPath {}
    let b = MyBezierPath(rect:CGRectZero)

从 Swift 的角度来看,这似乎是错误的。文档似乎说 UIBezierPath 有一个初始值设定项 init(rect:)。但是为什么UIBezierPath的init(rect:)没有被继承到我们的子classMyBezierPath中呢?按照初始化器继承的正常规则,应该是。

解释

UIBezierPath 不适用于子classing。因此,它没有任何初始化器——除了 init(),它继承自 NSObject。在 Swift 中,UIBezierPath 看起来 好像它有初始化器;但这是错误的陈述。 UIBezierPath 实际上有什么,如果我们看 Objective-C headers,我们可以看到,是方便的构造函数,它们是 class 方法,例如:

+ (UIBezierPath *)bezierPathWithRect:(CGRect)rect;

现在,这个方法(连同它的兄弟方法)展示了一些 Swift 不能很好处理的不寻常的特征:

  • 它不仅仅是初始化器的变体;它是一个 pure 方便的构造函数。 Objective-C 向我们展示了 UIBezierPath 没有对应的真实初始化程序 initWithRect:。这在 Cocoa.

  • 中是非常不寻常的情况
  • 是returnsUIBezierPath*,不是instancetype。这意味着它不能被继承,因为它 returns 是错误类型的实例。在 subclass MyBezierPath 中,调用 bezierPathWithRect: 会产生一个 UIBezierPath, 而不是 一个 MyBezierPath。

Swift 处理这种情况很糟糕。一方面,它根据其通常的策略将 class 方法 bezierPathWithRect: 转换为明显的初始化程序 init(rect:)。但另一方面,这不是 "real" 初始化器,不能被子 class.

继承

您因此被明显的初始化程序 init(rect:) 误导,然后当您无法在您的 subclass 上调用它时感到惊讶和难过,因为它不是继承的。

注意: 我并不是说 Swift 这里的行为不是错误;我认为它 一个错误(尽管我有点不清楚是将错误归咎于 Swift 还是 UIBezierPath API)。 Swift 不应该将 bezierPathWithRect: 变成初始化器,或者,如果它 确实 使它成为初始化器,它应该使该初始化器可继承。不管怎样,它应该是可继承的。但事实并非如此,所以现在我们必须寻找解决方法。

解决方案

那你该怎么办?我有两个解决方案:

  • 不要 subclass. Subclassing UIBezierPath 一开始是个坏主意。它不是为这种事情而制作的。代替 subclass,制作 wrapper - class 或构造它,而不是 is ] 一个 UIBezierPath,具有 具有 一个 UIBezierPath 的特性。我们称它为 MyBezierPathWrapper:

    struct MyBezierPathWrapper {
        var selectedForLazo : Bool = false
        var bezierPath : UIBezierPath!
    }
    

    这只是将您的自定义属性和方法与普通的 UIBezierPath 结合起来。然后您可以分两步创建它,如下所示:

    var b = MyBezierPathWrapper()
    b.bezierPath = UIBezierPath(rect:CGRectZero)
    

    如果感觉不满意,您可以通过添加采用 UIBezierPath 的初始化程序来创建 one-step:

    struct MyBezierPathWrapper {
        var selectedForLazo : Bool = false
        var bezierPath : UIBezierPath
        init(_ bezierPath:UIBezierPath) {
            self.bezierPath = bezierPath
        }
    }
    

    现在您可以这样创建它:

    var b = MyBezierPathWrapper(UIBezierPath(rect:CGRectZero))
    
  • Subclass 带有方便的构造函数。 如果你坚持使用 subclassing,即使 UIBezierPath 不是为那种事情,你可以通过提供一个方便的构造函数来完成。这是有效的,因为关于 UIBezierPath 的唯一重要的事情是它的 CGPath,所以你可以使这个方便的构造函数成为一个 copy 构造函数,仅仅从一个真正的 UIBezierPath 传输路径:

    class MyBezierPath : UIBezierPath {
        var selectedForLazo : Bool! = false
        convenience init(path:UIBezierPath) {
            self.init()
            self.CGPath = path.CGPath
        }
    }
    

    现在我们可以创建一个与以前的方法非常相似的方法:

    let b = MyBezierPath(path:UIBezierPath(rect:CGRectZero))
    

    它不是很好,但我认为它比必须重新定义 所有 初始化器(如您的解决方案所做的那样)稍微更令人满意。最后,我确实在以更压缩的方式做着与您正在做的完全相同的事情。但总的来说,我更喜欢第一种解决方案:首先不要 subclass.