假设我有一个带有指定初始化程序的自定义 UIView 子类:
class MyView: UIView {
init(custom: String) {
super.init(frame: .zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
正如预期的那样,我无法调用MyView(frame: .zero)
因为它不是 automatically inherited来自 UIView
.
然后假设我有一个 View 生成器类:
class Builder<V: UIView> {
func build() -> V? {
// Check if can call init(frame:) or not
if V.instancesRespond(to: #selector(V.init(frame:))) {
return V.init(frame: .zero) // <-- Crash here, because the check doesn't work
} else {
return nil
}
}
}
这里我首先检查了自定义 View 是否V
有init(frame:)
or not, yes, then call it.
但是,它不起作用,Builder<MyView>().build()
会崩溃:
Fatal error: Use of unimplemented initializer 'init(frame:)' for class '__lldb_expr_14.MyView'
V.instancesRespond(to: #selector(V.init(frame:)))
总是返回 true
,使此检查无用。 (还是我用错了?)
问题:如何检查通用 View 类 V
实际上回应init(frame:)
?
更新 1:
我也试过V.responds(to: #selector(V.init(frame:)))
正如@kamaldeep 和@Pranav 所指出的那样。但是,它总是返回 false
无论我在 MyView
上覆盖 init(frame:)还是不是。
更新 2:我正在尝试做什么:
为了更清楚,我正在构建一个 framework自动初始化 UIView
来自这个枚举:
enum ViewBuildMethod {
case nib
case nibName(String)
case frame(CGRect)
case custom(() -> UIView)
}
并且要与框架一起使用的 View 必须采用此协议(protocol)并指定如何构建:
protocol SomeViewProtocol where Self: UIView {
// ... other funcs
static func buildMethod() -> ViewBuildMethod
}
问题是我想要 override init(frame:)
可选 并允许自定义指定的初始化器(如 MyView
)。然后,发出 fatalError
init(frame:)
时出现错误消息在尚未覆盖它的 View 上(间接)使用。这表示非法使用框架(例如,MyView
的 buildMethod
返回 .frame(.zero)
),无法在编译时检查:
class MyView: UIView, SomeViewProtocol {
init(custom: String) {
super.init(frame: .zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// ILLEGAL!!
static func buildMethod() -> ViewBulidMethod {
return .frame(.zero) // <-- Illegal, the framework will call V.init(frame: .zero) eventually
}
// LEGAL
// static func buildMethod() -> ViewBuildMethod {
// return .custom({ () -> UIView in
// return MyView(custom: "hello")
// })
// }
}
错误信息可能类似于V.init(frame:) is called indirectly but V doesn't override this designated initializer. Please override init(frame:) to fix this issue
。 .
我可以让它像上面那样崩溃,但如果我可以在此之前添加一些有意义的消息,它会更清楚。
最佳答案
因为 Swift 不会自动继承自 UIView
将无法检查您的类(class)是否实现了 init(frame:)
构造函数。
我想 instancesRespond(to:)
返回 true
,因为它正在检查您的类(class)是否通过消息调度符合此消息。然而,Swift 对类中声明的方法使用表调度。所以这个检查将适用于 Objective-C 但不适用于 Swift
所以,要实现你想要的,你可以使用protocol
创建协议(protocol) Buildable
这将有 init(frame: CGRect)
方法
protocol Buildable {
init(frame: CGRect)
}
让你的类(class)遵守这个协议(protocol)
class MyView: UIView, Buildable {...}
现在init(frame:)
您的类(class)将需要方法。改变你的通用类型Builder
类别为 Buildable
现在你的类(class)可能是这样的
class Builder<V: Buildable> {
func build() -> V? {
return V(frame: .zero)
}
}
现在您将能够构建您的 View Builder<MyView>().build()
, 如果你上课将不会确认到 Buildable
协议(protocol)你会得到 compile-time
错误:
class SecondView: UIView {
init(custom: String) {
super.init(frame: .zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Builder<SecondView>().build()
会抛出编译时错误:
error: type 'SecondView' does not conform to protocol 'Buildable'
Builder<SecondView>().build()
关于ios - 如何检查通用 View 类是否实际实现了 init(frame :)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51264448/