UIBezierPath子类初始化程序

我试图创build一个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) 

并导致编译错误:

“必须调用超类”UIBezierPath“的指定初始值设定项”

我试图在子类中添加初始化器,但似乎不工作。

你能帮我吗?

注意 :iOS 9已经解决了这个问题,在那里API已经被重写了,所以init(rect:)存在,所有其他的,就像便捷初始化器一样。


问题

简而言之,您遇到的问题是下面的代码不能编译:

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

从Swift的angular度来看,这似乎是错误的。 该文档似乎说,UIBezierPath有一个初始化程序init(rect:) 。 但是为什么不是UIBezierPath的init(rect:)在我们的子类MyBezierPath中inheritance? 按照初始化器inheritance的一般规则,应该是。

说明

UIBezierPath不适用于子类。 因此,它没有任何初始化程序 – 除了init() ,它从NSObjectinheritance。 在Swift中,UIBezierPath 看起来好像有初始化器; 但这是一个虚假的表示。 什么UIBezierPath实际上,我们可以看到,如果我们看看Objective-C头文件,是便利的构造函数,这是类方法,如这样:

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

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

  • 它不仅仅是一个初始化器的变体。 这是一个纯粹的便利构造。 Objective-C向我们展示了UIBezierPath没有相应的真正的初始化函数initWithRect: :。 这是cocoa的一个非常不寻常的情况。

  • 它返回UIBezierPath* ,而不是instancetype 。 这意味着它不能被inheritance,因为它返回了一个错误types的实例。 在子类MyBezierPath中,调用bezierPathWithRect:产生UIBezierPath, 而不是 MyBezierPath。

Swift在这种情况下应付得很厉害。 一方面,它将类方法bezierPathWithRect:翻译成一个明显的初始化程序init(rect:) ,按照其通常的策略。 但另一方面,这不是一个“真正的”初始化器,不能被子类inheritance。

你已经被这个显而易见的初始值设定项init(rect:)所误导了,然后当你不能在你的子类上调用它时会感到惊讶和难住,因为它没有被inheritance。

注意:我并不是说Swift在这里的行为不是bug, 我认为这一个错误(虽然我有点朦胧是否责怪在Swift或UIBezierPath API上的错误)。 Swift不应该把bezierPathWithRect:变成一个初始化器,或者,如果它确实使它成为一个初始化器,它应该使得这个初始化器是可inheritance的。 无论哪种方式,它应该是可inheritance的。 但事实并非如此,现在我们必须寻找解决方法。

解决scheme

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

  • 不要inheritance。 子类化UIBezierPath是一个好主意。 这不是为了这样的事情。 而不是一个子类,做一个包装 – 一个类或结构,而不是具有UIBezierPathfunction, 具有 UIBezierPath的function。 我们称之为MyBezierPathWrapper:

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

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

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

    如果感觉不能令人满意,可以通过添加一个采用UIBezierPath的初始化程序来完成这一步骤:

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

    现在你可以像这样创build它:

     var b = MyBezierPathWrapper(UIBezierPath(rect:CGRectZero)) 
  • 子类有一个方便的构造函数。 如果你坚持inheritance子类,即使UIBezierPath不是用于这样的事情,你可以通过提供一个方便的构造函数来实现。 这是有效的,因为关于UIBezierPath的唯一重要的事情是它的CGPath ,所以你可以使这个便利的构造函数成为一个拷贝构造函数,只是从一个真正的UIBezierPath传递path:

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

    现在我们可以创build一个非常类似于以前的方法:

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

    这并不是很好,但是我认为它比根据您的解决scheme重新定义所有的初始化程序更令人满意。 最后,我真的正在做更完全一样的事情,以更加紧缩的方式。 但总的来说,我更喜欢第一个解决scheme:首先不要inheritance。

我发现这个简单的解决方法,似乎做了它的工作。

 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() } } 

请张贴其他解决scheme,因为他们肯定会比我的更好。