我有一个像这样的基本协议:
public protocol BasicSwiftClassLayout {
var nibName: String { get }
}
我的班级是这样的:
public class BasicSwiftClass<Layout: BasicSwiftClassLayout> {}
我已经定义了2个不同的结构,以便我可以用2个不同的笔尖启动这个类:
public struct NormalLayout: BasicSwiftClassLayout {
public let nibName = "NormalLayoutNibName"
}
和
public struct OtherLayout: BasicSwiftClassLayout {
public let nibName = "OtherLayoutNibName
}
我现在有2个问题基于此。
let myView = BasicSwiftClass<NormalLayout>
启动,我希望能够为类中的NormalLayout检索nibName(“NormalLayoutNibName”)。我会想做像let myNib = Layout.nibName
这样的东西,但它只是告诉我Instance member nibName cannot be used on type Layout
所以我该如何去检索nibName?public class BasicSwiftClass
时,classForCoder
只是MyProject.BasicSwiftClass
。现在我已经添加了通用行为,classForCoder
被返回为MyProject.BasicSwiftClass<Layout.NormalLayout>
,在调用let bundle = Bundle(for: classForCoder)
时不再返回正确的包我是否需要覆盖classForCoder
变量?或者我在这里做错了什么?谢谢!
由于您的BasicSwiftClass没有符合BasicSwiftClassLayout
类型的实例,而只是了解类型本身,您应该使nibName
要求为静态,因此可以在类型本身上调用它,而不是实例,像这样:
public protocol BasicSwiftClassLayout {
static var nibName: String { get }
}
public struct NormalLayout: BasicSwiftClassLayout {
public static let nibName = "NormalLayoutNibName"
}
public struct OtherLayout: BasicSwiftClassLayout {
public static let nibName = "OtherLayoutNibName"
}
至于你的第二个问题,我不认为Bundle(for class: AnyClass)
初始化程序适用于泛型类,句点。如果有人找到了解决方法的话,我很高兴出错,但我的猜测是,因为编译器会生成泛型类型的具体变体,而且只有实际使用的那些,这会带来一些挑战:
public struct DifferentLayout: BasicSwiftClassLayout
类型,变量BasicSwiftClass<DifferentLayout>
是你的包的一部分,它定义了BasicSwiftClass
的所有逻辑,但在编译时没有那个变化,或者另一个模块,这导致编译器创建该专业化?BasicSwiftClass<OtherLayout>
,编译器基于确定它将在何处使用。所以再次,它可以模糊这个真正属于哪个捆绑。我猜测如果有一种方法来引用泛型类型本身,即一个非专业的BasicSwiftClass<Layout>
类型引用,那么Bundle(for:)
将正常工作。但是没有办法像这样引用泛型类型,因为它更像是编译器用于生成具体特化的模板,而不是运行时存在的实际类型。
如果使协议需要类型的一致性,则可以获取布局的包而不是BasicSwiftClass。例如。:
public protocol BasicSwiftClassLayout: class {
static var nibName: String { get }
}
final public class NormalLayout: BasicSwiftClassLayout {
public static let nibName = "NormalLayoutNibName"
}
public class BasicSwiftClass<Layout: BasicSwiftClassLayout> {
func loadFromNib() {
let view = Bundle(for: Layout.self).loadNibNamed(Layout.nibName, owner: self, options: nil)?.first
}
}
在回答您关于如何为BasicSwiftClass<Layout>
创建具有不同值的Layout
数组的问题时,根据您需要如何使用从数组中检索的元素,有不同的方法来解决此问题。最终,Swift要求集合(包括数组)包含所有相同类型的元素或可以作为相同的常见类型进行寻址,因此所有不同的方法都基于将每个BasicSwiftClass<Layout>
实例转换,包装或转换为暴露的某些常见类型要在每个实例上调用的常用功能。
最简单的解决方案可能是创建一个BasicSwiftClass<Layout>
符合的协议,它公开了您需要的功能。例如,如果您关心的是从每个实例获取视图以添加到视图层次结构,您可以执行以下操作:
public protocol ViewRepresentable {
var view: UIView { get }
}
extension BasicSwiftClass<Layout>: ViewRepresentable {
public var view: UIView { // load the nib, connect outlets and do other setup, then return the appropriate UIView }
}
let array: [ViewRepresentable] = [BasicSwiftClass<IPadLayout>, BasicSwiftClass<NormalLayout>]
array.forEach { self.containerView.addSubview($0.view) }
如果您需要更专业的东西或在内部保留某些类型信息等,您可能需要使用包装器或类型橡皮擦。您也可以尝试从具有所需基本功能的非泛型超类继承BasicSwiftClass<Layout>
,并将该集合键入为该超类类型的实例数组。但是,如果您可以在非通用协议中表达共同要求,那么这应该让您保持最简单的路径。