我正在尝试创建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的角度来看,这似乎是错误的。该文档似乎说UIBezierPath有一个初始化程序
init(rect:)
。但是,为什么UIBezierPath的init(rect:)
没有在我们的子类MyBezierPath中继承?根据初始化程序继承的一般规则,应该这样。说明
UIBezierPath不适用于子类化。因此,它没有任何初始化程序-除了
init()
之外,它是从NSObject继承的。在Swift中,UIBezierPath看起来好像具有初始化程序。但这是一个错误的表示。正如我们看到的Objective-C标头所示,UIBezierPath实际上具有的是便捷构造函数,它们是类方法,例如:+ (UIBezierPath *)bezierPathWithRect:(CGRect)rect;
现在,此方法(及其 sibling )展示了一些Swift无法很好处理的异常功能:
initWithRect:
。在可可中,这是非常不寻常的情况。 UIBezierPath*
,而不是instancetype
。这意味着它不能被继承,因为它返回错误类型的实例。在MyBezierPath子类中,调用bezierPathWithRect:
会生成UIBezierPath,而不是MyBezierPath。 斯威夫特严重地应付这种情况。一方面,它根据其通常的策略将类方法
bezierPathWithRect:
转换为表面上的初始化器init(rect:)
。但是,另一方面,这不是“真正的”初始化程序,并且不能由子类继承。因此,您已经被表面上的初始化程序
init(rect:)
所迷惑,然后由于无法继承子类而无法对其进行调用时,感到惊讶和沮丧。注意:我并不是说Swift的行为不是错误;我认为这是一个错误(尽管我对将错误归咎于Swift还是UIBezierPath API还是有些困惑)。 Swift不应将
bezierPathWithRect:
转换为初始化器,或者如果确实使它成为初始化器,则应使该初始化器可继承。无论哪种方式,它都应该是可继承的。但这不是,所以现在我们必须寻找一种解决方法。解决方案
那你该怎么办?我有两种解决方案:
struct MyBezierPathWrapper {
var selectedForLazo : Bool = false
var bezierPath : UIBezierPath!
}
这只是将您的自定义属性和方法与普通的UIBezierPath结合在一起。然后可以分两步创建它,如下所示:
var b = MyBezierPathWrapper()
b.bezierPath = UIBezierPath(rect:CGRectZero)
如果感觉不满意,可以通过添加采用UIBezierPath的初始化程序,使其一步创建。
struct MyBezierPathWrapper {
var selectedForLazo : Bool = false
var bezierPath : UIBezierPath
init(_ bezierPath:UIBezierPath) {
self.bezierPath = bezierPath
}
}
现在,您可以像这样创建它:
var b = MyBezierPathWrapper(UIBezierPath(rect:CGRectZero))
CGPath
,因此您可以使此便捷构造函数成为一个复制构造函数,仅从真实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))
这不是很好,但是我认为比在解决方案中必须重新定义所有初始化程序要令人满意的多。最后,我实际上以一种压缩的方式做着与您正在做的事情完全相同的事情。但是总的来说,我更喜欢第一个解决方案:首先不要继承子类。
关于ios - UIBezierPath子类初始化器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29867782/