迭代给定节 id 的所有 UITableCell

Posted

技术标签:

【中文标题】迭代给定节 id 的所有 UITableCell【英文标题】:Iterate over all the UITableCells given a section id 【发布时间】:2015-05-16 22:36:11 【问题描述】:

使用 Swift,我如何在给定节 id 的情况下遍历所有 UITableCells(例如:第 2 节中的所有单元格)?

我只看到这个方法:tableView.cellForRowAtIndexPath,它返回 1 个给定绝对索引的单元格,所以它没有帮助。

有没有优雅又简单的方法?

注意:我想将一个部分中的所有单元格的 AccesoryType 设置为 None,以编程方式,例如:单击按钮后,或发生某些事情后(发生的事情与问题)

我有 UITableView 的参考和该部分的索引。

【问题讨论】:

致所有人:鉴于人们不断为这个问题添加更新的答案,展示如何以更新的方式执行此技术(例如 Swift 3,然后是 Swift 4),请阅读我在 isPha's answer 下的所有 cmets .直接修改 UITableCells 几乎总是一个坏习惯,当 ios 重用单元格或从模型重建视图时,最终会导致一些模糊的错误(您是否测试过中断应用程序的电话?您是否在具有最大数据集的小型手机上进行了测试?)。如果可能,请改为更改数据源 【参考方案1】:

您误解了表格视图的工作原理。当您想要更改单元格的配置时,您不要直接修改单元格。相反,您更改这些单元格的数据(模型),然后告诉您的表格视图重新加载更改的单元格。

这是基本的,如果您尝试以其他方式进行操作,它将无法正常工作。

您说过“在修改单元格数组之前我需要它们......”同样的事情也适用。您不应将状态数据存储在单元格中。一旦用户对单元格进行更改,您应该收集更改并将其保存到模型中。单元格可以滚动到屏幕外,并且可以随时丢弃它们的设置。

@LordZsolt 要求您展示您的代码,因为从您提出的问题来看,很明显您做错了事情。

编辑:

如果你确信你需要遍历一个节中的单元格,那么你可以向表视图询问目标节中的行数,然后你可以从 0 循环到 rows-1,询问表视图依次使用 UITableView cellForRowAtIndexPath 方法为每个单元格(这与名称相似的数据源方法不同。)该方法将为您提供当前在屏幕上可见的单元格。然后,您可以对这些单元格进行更改。

请注意,这只会为您提供当前屏幕上的单元格。如果目标部分中有其他当前不可见的单元格,则这些单元格当前不存在,并且如果用户滚动,则可能会创建其中一些单元格。出于这个原因,您需要将某种状态信息保存到您的模型中,以便当您在数据源 tableView:cellForRowAtIndexPath: 方法中从目标部分设置单元格时,您可以正确设置它们。

【讨论】:

您可以将 UITableView 与静态数据或动态数据一起使用。另外:您的帖子没有回答问题。 是的,答案是“不要那样做”。 SO 不适合“你的问题没有意义”类型的答案,这就是我的答案。 这并没有提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。 @Schemetrical:解释为什么一种方法无效或不明智的答案,然后至少解释了一些替代方法的答案是完全合法的。就是这样一个答案。 为了结束这个讨论,我的回答不是意见,苹果有据可查,也有痛苦的经历。如果您抓取屏幕上的单元格并更改它们而不更新数据模型以反映更改,那么当您滚动表格视图并向后滚动时,您手动更改的单元格将丢失其更改,而其他回收您更改的单元格单元格也可能设置不正确。【参考方案2】:

我使用相同的方式迭代所有表格视图单元格,但此代码仅适用于可见单元格,所以我只添加一行允许迭代所有表格视图单元格,无论它们是否可见

   //get section of interest i.e: first section (0)
       for (var row = 0; row < tableView.numberOfRowsInSection(0); row++)
       

        var indexPath = NSIndexPath(forRow: row, inSection: 0)

            println("row")
            println(row)
    //following line of code is for invisible cells
        tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Top, animated: false)


    //get cell for current row as my custom cell i.e :roomCell

            var cell :roomCell = tableView.cellForRowAtIndexPath(indexPath) as roomCell
      

* 这个想法是将 tableview 滚动到我在循环中接收到的每一行,因此,在每一个回合中,我的当前行都是可见的 ->所有 table view 行现在都是可见的:D

【讨论】:

啊,很多人的答案都被误导了,他们提倡一种看似可行但有时在某些设备上失败的方法——一场测试的噩梦。这是问题所在:如果用户向下滚动行列表,则在某些时候第一行将被丢弃。如果用户对当前可见的行进行更改,则当此代码从第一行重新开始时,更改可能会丢失 - 一旦开始滚动很长一段时间,就无法保证何时保留单元格桌子。更糟糕的是,在不同的设备或 iOS 版本上可能会有所不同。 嗨@ToolmakerSteve,对于您提到的测试用例,很容易通过 like dictionary 保持更改的索引。这个想法是,当您滚动 cellforrowatindexpath 时,它会被调用。因此,请注意更改的索引并在由于滚动而重新加载表时注意它们。如果您的测试用例是这样的:您有一个很长的评分列表并且您在滚动时评分,将为我经常遇到的这个案例提供代码,否则如果它在技术上有所不同,您可以提供有关您的测试用例的更多详细信息。谢谢你的回复。 等等@ToolmakerSteve,这是真的吗? tableView/collectionView 实际上会 discard 单元格吗?显然,出于性能原因,它不会更新/渲染它们,但我不理解它实际上会“丢弃”它们。这表明,不仅仅是“可见”和“不可见”的细胞,实际上还有第三种状态......这看起来很奇怪。我想,真正的意义在于,如果我们想要的话,我们不应该迭代“所有单元格”。我们应该迭代数据源(否则,我们将视图视为模型)。 @IsPha - 底线:不要做你想做的事。它最终会咬你。这是对建筑设计的滥用。不要将单元阵列用作信息存储。例如,如果模型数组有 1000 行,您是否真的想要遍历 1000 个单元格,强制将它们全部创建然后丢弃?您真的想管理其他信息以跟踪哪些信息发生了变化吗?您想依靠 iOS 而不是在它们离开屏幕时丢弃它们吗?按预期使用它要好得多。每个单元格都应该有一个支持模型,该模型随着单元格的变化而变化.. .. 但我如此强烈反对您使用的方法的主要原因是它不能保证是正确的 - 它不可测试。这就是我被咬的原因,它永远治愈了我做你所做的事情:我有 4 个细胞。它们都是可见的。工作得很好。我没有打扰后备商店。然后进行了更改,使所有单元格增加了 1 个文本行。仍然工作得很好。除了在一部小手机上,包含矩形被挤压。一个单元格现在不在屏幕上。似乎仍然有效,除非您滚动到底部然后返回。信息丢失。事后看来很明显,但它是一个 PITA。【参考方案3】:

对于 Swift 4,我一直在使用以下内容,它似乎运行良好。

for section in 0...self.tableView.numberOfSections - 1 
            for row in 0...self.tableView.numberOfRows(inSection: section) - 1 
                let cell = self.tableView.cellForRow(at: NSIndexPath(row: row, section: section) as IndexPath)

                print("Section: \(section)  Row: \(row)")

            
        

【讨论】:

在 Swift 3 中也能正常工作 这对我来说效果很好。我想启用/禁用特定标题的单元格,这让我可以这样做。【参考方案4】:

回答我自己的问题:“如何在给定部分 id 的情况下遍历所有 UITableCells?”

要遍历section 部分的所有 UITableCell,必须使用两种方法:

tableView.numberOfRowsInSection(section) tableView.cellForRowAtIndexPath(NSIndexPath(forRow: row, inSection: section))

所以迭代是这样的:

// Iterate over all the rows of a section
for (var row = 0; row < tableView.numberOfRowsInSection(section); row++) 
    var cell:Cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: row, inSection: section))?

   // do something with the cell here.

在我的问题结束时,我还写了一条注释:“注意:我想以编程方式将一个部分中的所有单元格的 AccesoryType 设置为 None ”。请注意,这是一个注释,而不是问题。

我最终这样做了:

// Uncheck everything in section 'section'
for (var row = 0; row < tableView.numberOfRowsInSection(section); row++) 
    tableView.cellForRowAtIndexPath(NSIndexPath(forRow: row, inSection: section))?.accessoryType = UITableViewCellAccessoryType.None

如果有更优雅的解决方案,请继续发布。

注意:我的表使用静态数据。

【讨论】:

给大家的注意事项:如果有人问如何在 java 中使用“goto”,您不会回答:“you should not use a goto in Java”。当然,您可以将其添加为评论,但不能作为答案。 (“评论”和“回答”是指在 *** 的上下文中) 太好了,我搜索了2个多小时,非常感谢! @sports - 是和不是。如果人们开始问一些版本的“你想解决什么问题,你认为这是一种有用的技术”,那会好得多。然后然后决定如何回答。但是,在这种特定情况下,对于为什么您几乎不应该尝试做您所要求的事情的解释太长,无法放入评论中。人们正在学习一种似乎可以正常工作的技术——然后他们会遇到一个失败的情况,并且不知道为什么。你似乎知道自己在做什么;但下一个人阅读可能不会。 (抱歉,如果不清楚,我在评论您的评论“如果有人问如何在 java 中使用 goto ...”)【参考方案5】:

斯威夫特 4

比以前的答案更“迅速”。我确信这可以通过函数式编程严格完成。如果我还有 5 分钟的时间,请使用 .reduce 代替。 ✌️

func cells(tableView:UITableView) -> [UITableViewCell]
    var cells:[UITableViewCell] = []
    (0..<tableView.numberOfSections).indices.forEach  sectionIndex in
        (0..<tableView.numberOfRows(inSection: sectionIndex)).indices.forEach  rowIndex in
            if let cell:UITableViewCell = tableView.cellForRow(at: IndexPath(row: rowIndex, section: sectionIndex)) 
                cells.append(cell)
            
        
    
    return cells

【讨论】:

有用的答案!【参考方案6】:

我正在使用这种迭代所有表格视图单元格的方式,但此代码仅适用于可见单元格,所以我只添加一行允许迭代所有表格视图单元格,无论它们是否可见

  //get section of interest i.e: first section (0)
   for (var row = 0; row < tableView.numberOfRowsInSection(0); row++)
   

    var indexPath = NSIndexPath(forRow: row, inSection: 0)

        println("row")
        println(row)
//following line of code is for invisible cells
    tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Top, animated: false)


//get cell for current row as my custom cell i.e :roomCell

        var cell :roomCell = tableView.cellForRowAtIndexPath(indexPath) as roomCell
  

* 想法是将 tableview 滚动到我在循环中收到的每一行,因此,在每一个回合中,我的当前行都是可见的 ->所有 table view 行现在都是可见的

【讨论】:

:D :D :D 不错的一个 xD @Abo3atef 真正高兴的是你在技术面试后没有接受我 :D :D 不,这不是一个好的。这是一次黑客攻击,以加强错误的加载单元格的方式。您不应该尝试强制表格视图加载其所有单元格。这不是表格视图的设计方式,并且注定会在某些时候失败。这在客观上是不好的做法。【参考方案7】:

你可以使用 reloadSections(_:withRowAnimation:) UITableView 的方法。

这将通过调用cellForRowAtIndexPath(_:) 重新加载指定部分中的所有单元格。在该方法中,您可以对这些单元格做任何您想做的事情。

在您的情况下,您可以应用您的逻辑来设置适当的配件类型:

if (self.shouldHideAccessoryViewForCellInSection(indexPath.section)) 
    cell.accessoryType = UITableViewCellAccessoryTypeNone

【讨论】:

但是在修改它们之前我需要单元格数组...在使用 realodSections 之前。 为什么需要它们? I want to set the AccesoryType to None for all of the cells in a section - 您可以在 cellForRowAtIndexPath 方法中执行此操作。 给定部分中单元格的索引是多少? 请问?我真的不明白你在问什么。你知道表格视图是如何工作的吗?将您的代码发布到 pastebin 并提供一个链接,这可能会有所帮助。 你说得对,OP 需要了解UITableViews 的工作原理。

以上是关于迭代给定节 id 的所有 UITableCell的主要内容,如果未能解决你的问题,请参考以下文章

删除节点(removeChild())

如何迭代栅格网格中的环?

选择具有给定 id 位置的所有数据

Pandas对Pyspark函数的迭代。

jQuery:选择给定类的所有元素,除了特定的 Id

迭代此数据并呈现节标题和项目的正确方法?