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,因为他们肯定会比我的更好。