如何使用 if 语句将变量类型设置为特定类型的自定义 tableCell?
Posted
技术标签:
【中文标题】如何使用 if 语句将变量类型设置为特定类型的自定义 tableCell?【英文标题】:How can I use an if statement to set a type of variable to a specific type of custom tableCell? 【发布时间】:2017-01-13 18:03:49 【问题描述】:我有三种不同类型的自定义 UITableCell。我有一个 if 语句来设置它们:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
if somePosts[indexPath.row].typeOfPost == .linkPost
let cell: LinkTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "linkTableViewCell") as! LinkTableViewCell
else if somePosts[indexPath.row].typeOfPost == .picturePost
let cell: PictureTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "pictureTableViewCell") as! PictureTableViewCell
else if somePosts[indexPath.row].typeOfPost == .textPost
let cell: TextTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "textTableViewCell") as! TextTableViewCell
else
print("Type of post is not link, picture, or text")
每个自定义单元格都有类似的标签,例如标题和时间。我想使用同一行代码设置这些标签,例如:
cell.titleLabel.text = "Some title here"
但是,在此示例中,我收到一条错误消息,提示我正在使用未解析的标识符“单元格”,这显然是因为我的变量是非全局声明的。由于 swift 是强类型的,有没有办法解决这个问题?谢谢!
【问题讨论】:
不相关,但从数据源数组中检索该类型三次是不必要的昂贵。您应该使用 switch 语句switch somePosts[indexPath.row].typeOfPost case .linkPost ...
谢谢,这很有帮助。我对此很陌生,所以任何建议都会受到赞赏
这里重复的代码太多了。停止疯狂。
除了 vadian 所说的以外?所有建议表示赞赏;我不是最好的编码员。让我变得更好!
@SomeGuy 查看我的 cmets 和 Alexander 的回答。他们将帮助您更好地了解 POP
【参考方案1】:
创建一个您的 TableViewCell 类扩展的协议,并将 cell
存储为该类型的变量。
protocol MyTableViewCell
var titleLabel: UILabel get
// ...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let identifier: String
switch somePosts[indexPath.row].typeOfPost
case .linkPost: identifier = "linkTableViewCell"
case .picturePost: identifier = "pictureTableViewCell"
case .textPost: identifier = "textTableViewCell"
default: fatalError("Type of post is not link, picture, or text")
guard let cell = self.tableView.dequeueReusableCell(withIdentifier: identifier) as? MyTableViewCell else
fatalError("Cell isn't castable to MyTableViewCell")
cell.titleLabel.text = "Some title here"
// ...
【讨论】:
好吧,我真的很讨厌这个,我提前道歉。它一直在第二个 fatalError 行崩溃。不知道为什么……所有标签都必须相同吗?每个自定义单元格中有些不同,有些相同。我试图这样做,所以不需要多次输入相同的内容。 @SomeGuy 你忘记让你现有的类符合这个新协议了吗? @Honey 0. 我怀疑他没有让他的课程(LinkTableViewCell
、TextTableViewCell
等)符合 MyTableViewCell。 1. 是的,协议确实需要一个更好的名称,但我对问题域的了解不够,无法提出建议。 2. 是的,他的类应该符合这个协议,以便可以将这些类的实例引用为MyTableViewCell
,而不考虑它们的具体类型。 3. 也许,如果该按钮是协议的逻辑部分。 4. Apple 对此有一段视频。
@Honey 5. 为了向编译器表达无论什么具体类型(LinkTableViewCell
、Text...
、Picture...
)是例如,我们始终可以确定它将是一个具有titleLabel
的类型,我们可以在不关心该类型的任何其他细节的情况下使用它。
@Honey 假设协议不存在,而cell
的类型为NSTableViewCell
。我们在这里处理的所有实例都可以分配给cell
。但是,当我们说cell.titleLabel
时,编译器会报错。 NSTableViewCell
没有titleLabel
,因此不能保证实例的具体类型有titleLabel
。该协议正式保证titleLabel
将存在于cell
,无论其具体类型如何。【参考方案2】:
你有三个基本的解决方案。
-
在每个块内重复
cell.text = ...
。但这不是您在问题中所说的真正想要的。
让您的三个自定义单元类都扩展一个公共基类。让这个基类定义任何公共属性。
定义一个具有公共属性的协议,并使您的每个自定义单元类都符合该协议。
对于选项 2 和 3,您将在第一个 if
语句之前声明一个基本/协议类型的变量。然后在整个if/else
块之后,您可以分配任何公共属性。
如果您需要更新任何特定于单元格类型的属性,您也可以在相应的块中执行此操作。
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
var cell: BaseTableViewCell?
if somePosts[indexPath.row].typeOfPost == .linkPost
cell = self.tableView.dequeueReusableCell(withIdentifier: "linkTableViewCell") as! LinkTableViewCell
else if somePosts[indexPath.row].typeOfPost == .picturePost
cell = self.tableView.dequeueReusableCell(withIdentifier: "pictureTableViewCell") as! PictureTableViewCell
else if somePosts[indexPath.row].typeOfPost == .textPost
cell = self.tableView.dequeueReusableCell(withIdentifier: "textTableViewCell") as! TextTableViewCell
else
print("Type of post is not link, picture, or text")
if let cell = cell
cell.commonProperty = ...
return cell
else
return nil // this shouldn't happen but if it does, you have a bug to fix
【讨论】:
【参考方案3】:如果每个子类都有自己的 titleLabel 属性,则需要使它们都符合协议。我们就叫它ConfigurableCell
吧。
protocol ConfigurableCell
var titleLabel: UILabel get set
然后,您可以以同样的方式初始化单元格,但将它们声明为ConfigurableCell
:
var cell: ConfigurableCell? = nil // not set yet
if somePosts[indexPath.row].typeOfPost == .linkPost
cell = self.tableView.dequeueReusableCell(withIdentifier: "linkTableViewCell") as! LinkTableViewCell
else if somePosts[indexPath.row].typeOfPost == .picturePost
cell = self.tableView.dequeueReusableCell(withIdentifier: "pictureTableViewCell") as! PictureTableViewCell
else if somePosts[indexPath.row].typeOfPost == .textPost
cell = self.tableView.dequeueReusableCell(withIdentifier: "textTableViewCell") as! TextTableViewCell
guard let cell = cell else
// how to handle this error case is up to you
print("Type of post is not link, picture, or text")
return UITableViewCell()
// now, cell is a ConfigurableCell with a titleLabel property, regardless of class
cell.titleLabel.text = "Some title"
当然,UITableViewCell
确实有一个内置的textLabel
属性,您可以尝试在单元类中使用它,然后不需要协议,因为该属性在UITableViewCell
中。
【讨论】:
嘿,我是新手,所以如果这是一个愚蠢的问题,请不要杀了我,但是当我声明协议时,我收到一个错误:“协议中的属性必须有明确的 get 或 get set 说明符。 好吧,解决了这个错误,但现在我得到“无法将类型“LinkTableViewCell”(以及所有其他类型的单元格)的值分配给类型 ConfigurableCell?” 你是否让你的类符合协议?class LinkTableViewCell: UITableViewCell, ConfigurableCell ...
您需要为所有表格单元格类型添加协议一致性(直接或通过扩展)。我强烈建议阅读整个protocols section of the Swift language guide,因为这是了解如何编写好的 Swift 代码的最重要部分之一。以上是关于如何使用 if 语句将变量类型设置为特定类型的自定义 tableCell?的主要内容,如果未能解决你的问题,请参考以下文章