仅在表格视图的特定单元格上显示和隐藏视图

Posted

技术标签:

【中文标题】仅在表格视图的特定单元格上显示和隐藏视图【英文标题】:Showing and hiding a view only on a specific cell of a table view 【发布时间】:2018-03-31 10:52:53 【问题描述】:

我有一个带有自定义单元格的表格视图。它们非常高,因此只有一个单元格在屏幕上完全可见,并且根据该单元格的位置,可能是第二个单元格的前 25%。这些单元格代表具有名称的虚拟项目。每个单元格内部都有一个按钮。第一次点击时,它会在单元格内显示一个小的UIView 并将项目添加到数组中,第二次点击时,将其隐藏并删除项目。添加和删​​除项目的部分工作正常,但是,由于单元格在UITableView中重用,存在与显示和隐藏视图相关的问题@

当我添加视图时,例如,在第一个单元格、第三个或第四个单元格上(重复使用单元格后)我仍然可以看到该视图。

为了防止这种情况,我尝试循环项目数组并根据每个单元格的名称标签文本检查它们的名称。我知道这种方法效率不高(如果有数千个呢?),但我还是尝试过。

这是它的简单代码(checkedItems 是项目数组,视图应该是可见的):

  if let cell = cell as? ItemTableViewCell 
        if cell.itemNameLabel.text != nil 

            for item in checkedItems 

                if cell.itemNameLabel.text == item.name 
                    cell.checkedView.isHidden = false

                 else 
                    cell.checkedView.isHidden = true

                
            
        

乍一看,这段代码运行良好,但深入挖掘后发现了一些问题。当我点击第一个单元格以显示视图,然后我点击第二个单元格以在其上显示视图时,它工作正常。但是,例如,当我点击第一个和第三个时,第一个单元格上的视图消失了,但该项目仍在数组中。我怀疑,原因仍然是单元格被重复使用的事实,因为单元格的高度再次很大,因此当第三个单元格可见时,第一个单元格不可见。我尝试在tableView(_:,cellForRow:)tableView(_:,willDisplay:,forRowAt:) 方法中使用上面的代码,但结果是一样的。

所以,问题来了:我需要找到一种有效的方法来检查单元格并在checkedItems 中的项目中显示ONLY视图数组。

已编辑 这是单元格在有和没有视图的情况下的外观(紫色圆圈是按钮,视图是橙色的)

这里是按钮的代码:

protocol ItemTableViewCellDelegate: class 
func cellCheckButtonDidTapped(cell: ExampleTableViewCell)
 

细胞内:

 @IBAction func checkButtonTapped(_ sender: UIButton) 

    delegate?.cellCheckButtonDidTapped(cell: self)

在视图控制器内部(注意:这里的代码只是显示和隐藏视图。代码的目的是显示按钮如何与table view 交互):

extension ItemCellsTableViewController: ItemTableViewCellDelegate 
func cellCheckButtonDidTapped(cell: ItemTableViewCell) 

    UIView.transition(with: cell.checkedView, duration: 0.1, options: .transitionCrossDissolve, animations: 
        cell.checkedView.isHidden = !cell.checkedView.isHidden
    , completion: nil)




已编辑 2

这是tableView(_ cellForRowAt:) 方法的完整代码(我已经从问题中删除了循环部分,以明确该方法最初是做什么的)。单元格上的item 属性只是设置项目的名称(itemNameLabeltext)。

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    if let cell = tableView.dequeueReusableCell(withIdentifier:
        ItemTableViewCell.identifier, for: indexPath) as? ItemTableViewCell
        cell.item = items[indexPath.row]
        cell.delegate = self 

        cell.selectionStyle = .none

        return cell
    
    return UITableViewCell()

我已经尝试了解决方案,建议here,但这对我不起作用。

如果您遇到过这样的问题并且知道如何解决,我将非常感谢您的帮助和建议。

【问题讨论】:

您想显示您点击的单元格中的视图以及隐藏的其他单元格视图吗? @Kuldeep,是的。视图应该仅在点击按钮for the first time 的单元格中可见。所以,基本上,我的意思是在一个单元格中显示和隐藏视图,不应该影响其他单元格 执行以下操作:1. 在数组中添加 Tapped 单元格indexPath,然后重新加载UITableView 并在cellForRow 方法中检查if([arrIndexpaths constainsobject:indexpath]) show your view else hide your view @Kuldeep,问题是,当我点击一个按钮时,单元格本身并没有被点击,所以我无法得到它的indexPath 我更新了答案,请查看。 【参考方案1】:

试试这个。

全局定义:var arrIndexPaths = NSMutableArray()

func numberOfSections(in tableView: UITableView) -> Int 
    return 1

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    return 30

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell:TableViewCell = self.tblVW.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell

    cell.textLabel?.text = String.init(format: "Row %d", indexPath.row)

    cell.btn.tag = indexPath.row
    cell.btn.addTarget(self, action: #selector(btnTapped), for: .touchUpInside)

    if arrIndexPaths.contains(indexPath) 
        cell.backgroundColor = UIColor.red.withAlphaComponent(0.2)
    
    else 
        cell.backgroundColor = UIColor.white
    

    return cell;



@IBAction func btnTapped(_ sender: UIButton) 
    let selectedIndexPath = NSIndexPath.init(row: sender.tag, section: 0)
    // IF YOU WANT TO SHOW SINGLE SELECTED VIEW AT A TIME THAN TRY THIS
    arrIndexPaths.removeAllObjects()
    arrIndexPaths.add(selectedIndexPath)
    self.tblVW.reloadData()

【讨论】:

问题是,当我点击一个按钮时,单元格本身并没有被点击,所以我无法获取它的 indexPath @TigranIskandaryan,我更新了我的答案,请查看此内容。 我认为这也行不通,因为在点击按钮后发送操作我使用委托。该按钮是在单元格本身上定义的,因此我无法通过发件人的tag 获得row。此外,您的答案假设每个单元格上的按钮应该有不同的 tag 值,因为可能有 1000 个或只有 10 个单元格,所以无法设置。 @TigranIskandaryan,能否请您添加一些代码和单元格图像,以便我更好地了解。 @TigranIskandaryan,我不明白,你在Cell 中写了什么代码,在ViewController 中写了什么代码。使用cellForRow 方法更新。【参考方案2】:

我会将您的各个单元格的状态作为模型数据的一部分保留在每个单元格后面。

我假设您在tableView(_:,cellForRow:) 中填充表格视图时使用了一组模型对象。该模型由某个后端服务填充,该服务为您提供一些 JSON,然后在第一次加载视图后将其映射到模型对象。

如果您向模型对象添加一个属性来指示单元格是否被按下,您可以在填充单元格时使用它。

您可能应该创建一个包含原始 JSON 数据的“包装器对象”,然后创建一个包含状态的变量,我们称之为 isHidden。您可以使用Bool 值,也可以使用enum,如果您愿意的话。这是一个仅使用Bool的示例

struct MyWrappedModel 
    var yourJSONDataHere: YourModelType
    var isHidden = true

    init(yourJSONModel: YourModelType) 
        self.yourJSONDataHere = yourJSONModel
    

无论如何,当您的单元格被点击时(didSelectRow),您会:

    在基于索引路径的包装模型数据对象数组中找到正确的 MyWrappedModel 对象 在其上切换 isHidden 值 使用reloadRows(at:with:) 在表格视图中重新加载受影响的行

tableView(_:,cellForRow:) 中,您现在可以检查是否isHidden 并基于此进行一些渲染:

...//fetch the modelObject for the current IndexPath
cell.checkedView.isHidden = modelObject.isHidden

此外,知道prepareForReuse 方法存在于UITableViewCell 上。当单元格即将被回收时,将调用此方法。这意味着您可以将其用作最后的手段,以便在呈现表格视图单元格之前“初始化”它们。因此,在您的情况下,您可以默认隐藏 checkedView

如果您这样做,您不再需要使用数组来跟踪哪些单元格已被点击。 modeldata它自己知道它持有什么状态并且完全独立于cell位置和回收。

希望这会有所帮助。

【讨论】:

感谢您的建议。这确实可行,但是我有一个问题:如果数据来自 JSON 怎么办?我的意思是,如果 JSON 是我制作的,我可以向它和我的数据结构中添加这样的键值对,但是如果我无法更改 JSON 结构怎么办? 但是您不将 JSON 映射到模型对象吗?我猜您从某处的后端服务接收到一些 JSON,然后您应该将其映射到模型对象中,然后您可以使用默认的 isHidden 值 true 对其进行初始化。在这里查看如何将您的 JSON 映射到模型对象:developer.apple.com/documentation/foundation/jsondecoder 是的,应该而且现在确实如此。但据我了解,您的建议是向模型添加一个属性(称为isVisible),然后对其进行检查。但是,当我添加这种属性时,模型不再正确映射到 JSON,因此无法解码数据,因为没有与该键关联的值。 @TigranIskandaryan 我已经更新了我的答案,希望对您有所帮助。我不确定您是如何打开和存储 JSON 数据的,所以我们在那里看待事物的方式可能存在一些差异? 其实这是一个相当不错的建议。我会试一试并通知你结果:)

以上是关于仅在表格视图的特定单元格上显示和隐藏视图的主要内容,如果未能解决你的问题,请参考以下文章

如何在表格视图单元格内的视图顶部和底部放置阴影?

iOS - 如何查看表格视图单元格上的“滑动删除”操作?

iOS 8 中的表格视图单元格自动布局

自定义表格视图单元格上的多个图像(Swift)

选择表格视图单元格时显示自定义 UIImageView

Swift - 表格视图中的单元格出现在另一个单元格上