如何使用 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. 我怀疑他没有让他的课程(LinkTableViewCellTextTableViewCell 等)符合 MyTableViewCell。 1. 是的,协议确实需要一个更好的名称,但我对问题域的了解不够,无法提出建议。 2. 是的,他的类应该符合这个协议,以便可以将这些类的实例引用为MyTableViewCell,而不考虑它们的具体类型。 3. 也许,如果该按钮是协议的逻辑部分。 4. Apple 对此有一段视频。 @Honey 5. 为了向编译器表达无论什么具体类型(LinkTableViewCellText...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?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 DOM 更改为“if/else”语句单独为真,如果所有 if 语句都为真,也将变量设置为真?

将字符串类型的时间转换为秒

如何在 C++ 的条件语句中检查变量类型?

针对特定类型的所有变量的 django 模板过滤器

mysql自定函数

Day 01 :变量数据类型用户交互if语句循环for循环