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.
我正在尝试创建 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. 中是非常不寻常的情况
是returns
UIBezierPath*
,不是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.