@IBInspectable 为 Class 设置类型别名
@IBInspectable to set a Typealias for a Class
我正在尝试构建一个通用 UITableViewController
并以领域 Results<Object>
作为模型。
这些是我简化的 类:
领域对象:
import RealmSwift
class Test: Object {
dynamic var name = ""
}
TableViewCell:
import UIKit
import RealmSwift
class RealmCell: UITableViewCell {
typealias Entity = Test // from above
var object: Entity? {
didSet {
if let object = object {
textLabel?.text = object.name
}
}
}
}
表格视图控制器:
import UIKit
import RealmSwift
class RealmTableViewController: UITableViewController {
typealias TableCell = RealmCell // From example above
var objects = try! Realm().objects(TableCell.Entity.self) {
didSet { tableView.reloadData() }
}
// MARK: - UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objects.count
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
let cell =
tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableCell
cell.object = objects[indexPath.row]
return cell
}
}
我想不出使 TableCell 类型别名为 @IBInspectable 的方法。我一直在尝试 NSClassFromString(_:) 但没有成功。
希望有人能提供帮助。
如果我没理解错的话,您基本上希望能够在 Interface Builder 中指定实体名称,是吗? IE。您希望基本上 select 您的自定义 class 来自检查员?
如果是这样,那么不幸的是这不是直接可能的。 @IBInspectable 只能用于特定类型,如文档中所述 here:
You can add the IBInspectable attribute to any property in a class declaration, class extension, or category of type: boolean, integer or floating point number, string, localized string, rectangle, point, size, color, range, and nil.
但是,您可以将字符串 属性 指定为 IBInspectable(具有有意义的默认值),并在您的初始化程序中从中扣除 class。这将留下错误的可能性,即打字错误,但它仍然可以工作。
另见 回答。
在您发表评论后编辑:
在这种情况下,我担心这是不可能的(至少据我所知,可能有一些我不知道的深层黑客,但这可能会很丑陋)。
问题是 IB 中指定的内容只能在运行时评估,而类型别名是在编译时定义的。
我认为您基本上想要的只是一个 协议 每个单元格 class 应该具有的功能(以及通用视图控制器所依赖的功能)。你甚至不需要类型别名,因为协议本身就是一种类型。
然后可以根据 IBInspectable
-ed 字符串选择具体单元格 classes,协议甚至可以为此定义一个方法。
根据您场景的详细信息,您甚至可以为所有单元格编写一个通用的 superclass。一个已经采用(部分)协议的协议(在这种情况下你甚至可以省略协议,但我建议使用一个以提高可读性)。
这显然假设您拥有为通用单元格定义的视图控制器所需的所有功能,但这是您在任何情况下都会遇到的问题(即使您可以使用在运行时定义的类型别名)。
第二次编辑 在我看了你的示例代码之后:
好的,我又看了一遍,希望现在可以更好地解释这一点。不幸的是,我不能直接添加到你的存储库,因为它不编译(我缺少领域 framework/pod,即使我添加了我可能什么也赢不了,因为我不知道你到底在做什么与它)。
如评论中所述,我会说您不需要任何进一步的 IBInspectable 属性 来设置 class。这应该已经发生在您的故事板中,即您应该将给定原型单元格的 class 值设置为您拥有的具体单元格之一 classes。但是,您的通用 RealmTableViewController
不需要知道 class。如果我对你的理解正确的话,你似乎想给它关于这方面的知识,可能是让它根据它的具体特性正确地准备细胞。不要那样做(我稍后会在 viewDidLoad
中介绍你想做的事情)。相反, 定义一个所有单元都采用并且 RealmTableViewController
知道的协议。在您的 tableView(_:cellForRowAt:)
方法中,您在 as! ...
部分中使用此协议来使单元格出队。协议应该定义每个具体 class 应该实现的准备方法,然后在此时由 RealmTableViewController
.
调用
这样,您的 table 视图控制器就保持了通用性,它实际上对它显示的单元格一无所知,这就是设计目的。
现在你(我认为)面临的问题是:你想让控制器知道它使用哪种原型单元,这样它也可以准备特定的东西(你也可以外包到协议中, 例如)。这是有问题的,因为您基本上是在尝试重建 table 视图控制器的核心方面:它能够同时 处理不同类型的单元格 。我从你的其他代码中得出结论,在 viewDidLoad
和 objects
属性 中,这最终似乎也取决于单元格的 class。这与 table 架构本身的设计冲突,这不仅仅是一个语法问题。然而,有一个解决方案:另一个协议,加上一个 "companion" class 到具体的单元格 classes。
对于您拥有的每个单元格 class,定义相应的 class 来处理您的视图控制器需要执行的领域内容 。也许对于某些单元格,你有相同的对象,然后相应地编写它们(新的 class 应该有一个 属性 来定义它对应于哪个单元格)。 定义一个所有人都采用的协议,并且至少有两种方法(也许使用更好的名称,这里晚了...):doControllerStuff
,和getCellIdentifier
.
然后您的 RealmTableViewController
最终 确实 获得了 IBInspectable。如果已设置,控制器会将伴随对象实例化到单元 (具体使用哪个 class 显然取决于 IBInspectable 的值)。如果同一个伴侣处理多个单元格,IBInspectable 还必须定义将使用哪个单元格,即必须正确配置伴侣。您可以使用某种字符串约定,甚至可以编写一个工厂 class 从视图控制器中隐藏具体的 class 并返回一个作为协议类型的正确对象。
无论如何,无论您在 中基于 class 在 viewDidLoad
中做什么,请将其更改为 doControllerStuff
方法 。如果在 cells/companions 中常见,你甚至可能有一些 super- 和 subclasses,那没关系。 最后,在你的 `tableView(_:cellForRowAt:) 方法中,你不直接使用标识符,而是向伴随对象询问标识符。
这里有一个小警告:您显然必须确保正确设置单元格标识符的接口构建器值,即它与您在视图控制器实例中设置的 IBInspectable 相匹配。不过,您可以围绕此编写一个 catch 并使用默认单元格,只需抛出一个异常。
这已经变得很长了,尽管如此,我希望它是可以理解的。我没有时间为这个或其他东西做一个漂亮的图形,所以如果你还有问题我建议我们拿这个来聊天。 :) 只需 ping 我,我们就会(尽管从明天开始我有点空闲)。
我正在尝试构建一个通用 UITableViewController
并以领域 Results<Object>
作为模型。
这些是我简化的 类:
领域对象:
import RealmSwift
class Test: Object {
dynamic var name = ""
}
TableViewCell:
import UIKit
import RealmSwift
class RealmCell: UITableViewCell {
typealias Entity = Test // from above
var object: Entity? {
didSet {
if let object = object {
textLabel?.text = object.name
}
}
}
}
表格视图控制器:
import UIKit
import RealmSwift
class RealmTableViewController: UITableViewController {
typealias TableCell = RealmCell // From example above
var objects = try! Realm().objects(TableCell.Entity.self) {
didSet { tableView.reloadData() }
}
// MARK: - UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objects.count
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
let cell =
tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableCell
cell.object = objects[indexPath.row]
return cell
}
}
我想不出使 TableCell 类型别名为 @IBInspectable 的方法。我一直在尝试 NSClassFromString(_:) 但没有成功。
希望有人能提供帮助。
如果我没理解错的话,您基本上希望能够在 Interface Builder 中指定实体名称,是吗? IE。您希望基本上 select 您的自定义 class 来自检查员?
如果是这样,那么不幸的是这不是直接可能的。 @IBInspectable 只能用于特定类型,如文档中所述 here:
You can add the IBInspectable attribute to any property in a class declaration, class extension, or category of type: boolean, integer or floating point number, string, localized string, rectangle, point, size, color, range, and nil.
但是,您可以将字符串 属性 指定为 IBInspectable(具有有意义的默认值),并在您的初始化程序中从中扣除 class。这将留下错误的可能性,即打字错误,但它仍然可以工作。
另见
在您发表评论后编辑: 在这种情况下,我担心这是不可能的(至少据我所知,可能有一些我不知道的深层黑客,但这可能会很丑陋)。 问题是 IB 中指定的内容只能在运行时评估,而类型别名是在编译时定义的。
我认为您基本上想要的只是一个 协议 每个单元格 class 应该具有的功能(以及通用视图控制器所依赖的功能)。你甚至不需要类型别名,因为协议本身就是一种类型。
然后可以根据 IBInspectable
-ed 字符串选择具体单元格 classes,协议甚至可以为此定义一个方法。
根据您场景的详细信息,您甚至可以为所有单元格编写一个通用的 superclass。一个已经采用(部分)协议的协议(在这种情况下你甚至可以省略协议,但我建议使用一个以提高可读性)。
这显然假设您拥有为通用单元格定义的视图控制器所需的所有功能,但这是您在任何情况下都会遇到的问题(即使您可以使用在运行时定义的类型别名)。
第二次编辑 在我看了你的示例代码之后:
好的,我又看了一遍,希望现在可以更好地解释这一点。不幸的是,我不能直接添加到你的存储库,因为它不编译(我缺少领域 framework/pod,即使我添加了我可能什么也赢不了,因为我不知道你到底在做什么与它)。
如评论中所述,我会说您不需要任何进一步的 IBInspectable 属性 来设置 class。这应该已经发生在您的故事板中,即您应该将给定原型单元格的 class 值设置为您拥有的具体单元格之一 classes。但是,您的通用 RealmTableViewController
不需要知道 class。如果我对你的理解正确的话,你似乎想给它关于这方面的知识,可能是让它根据它的具体特性正确地准备细胞。不要那样做(我稍后会在 viewDidLoad
中介绍你想做的事情)。相反, 定义一个所有单元都采用并且 RealmTableViewController
知道的协议。在您的 tableView(_:cellForRowAt:)
方法中,您在 as! ...
部分中使用此协议来使单元格出队。协议应该定义每个具体 class 应该实现的准备方法,然后在此时由 RealmTableViewController
.
这样,您的 table 视图控制器就保持了通用性,它实际上对它显示的单元格一无所知,这就是设计目的。
现在你(我认为)面临的问题是:你想让控制器知道它使用哪种原型单元,这样它也可以准备特定的东西(你也可以外包到协议中, 例如)。这是有问题的,因为您基本上是在尝试重建 table 视图控制器的核心方面:它能够同时 处理不同类型的单元格 。我从你的其他代码中得出结论,在 viewDidLoad
和 objects
属性 中,这最终似乎也取决于单元格的 class。这与 table 架构本身的设计冲突,这不仅仅是一个语法问题。然而,有一个解决方案:另一个协议,加上一个 "companion" class 到具体的单元格 classes。
对于您拥有的每个单元格 class,定义相应的 class 来处理您的视图控制器需要执行的领域内容 。也许对于某些单元格,你有相同的对象,然后相应地编写它们(新的 class 应该有一个 属性 来定义它对应于哪个单元格)。 定义一个所有人都采用的协议,并且至少有两种方法(也许使用更好的名称,这里晚了...):doControllerStuff
,和getCellIdentifier
.
然后您的 RealmTableViewController
最终 确实 获得了 IBInspectable。如果已设置,控制器会将伴随对象实例化到单元 (具体使用哪个 class 显然取决于 IBInspectable 的值)。如果同一个伴侣处理多个单元格,IBInspectable 还必须定义将使用哪个单元格,即必须正确配置伴侣。您可以使用某种字符串约定,甚至可以编写一个工厂 class 从视图控制器中隐藏具体的 class 并返回一个作为协议类型的正确对象。
无论如何,无论您在 中基于 class 在 viewDidLoad
中做什么,请将其更改为 doControllerStuff
方法 。如果在 cells/companions 中常见,你甚至可能有一些 super- 和 subclasses,那没关系。 最后,在你的 `tableView(_:cellForRowAt:) 方法中,你不直接使用标识符,而是向伴随对象询问标识符。
这里有一个小警告:您显然必须确保正确设置单元格标识符的接口构建器值,即它与您在视图控制器实例中设置的 IBInspectable 相匹配。不过,您可以围绕此编写一个 catch 并使用默认单元格,只需抛出一个异常。
这已经变得很长了,尽管如此,我希望它是可以理解的。我没有时间为这个或其他东西做一个漂亮的图形,所以如果你还有问题我建议我们拿这个来聊天。 :) 只需 ping 我,我们就会(尽管从明天开始我有点空闲)。