【问题标题】:save selected row in UITableView after reloadDatareloadData 后在 UITableView 中保存选定的行
【发布时间】:2009-12-01 16:06:19
【问题描述】:

我在 iphone 中编写自定义 jabber 客户端。

我使用xmppframework 作为引擎。

我有 UITableViewController 和 NSMutableArray 用于表示联系人列表。

当我收到(或有人更改它的内容)名册(又名联系人列表)时,我想更改 UITableView 项目(添加/删除/修改)。因此,如果用户在列表更新时使用 listView

[items addObject:newItem];
[self.tableView reloadData];

用户丢失了当前选择项。

所以,我的问题是如何在 reloadData 后保存(如果可能,我的意思是如果给定的选定项未删除)当前选择项?

谢谢。

【问题讨论】:

    标签: iphone user-interface


    【解决方案1】:

    简单的方法是这样的:

    NSIndexPath *ipath = [self.tableView indexPathForSelectedRow];
    [self.tableView reloadData];
    [self.tableView selectRowAtIndexPath:ipath animated:NO scrollPosition:UITableViewScrollPositionNone];
    

    【讨论】:

    • [cell setSelected:YES] 无法正常工作。所以我使用了这种方法,效果很好
    • 发现你不应该把它放在 [self.tableView beginUpdates]/[self.tableView endUpdates] 中。
    • 太棒了,你拯救了我的一天......快乐编码。
    • 在 iOS8 中不工作;显然 reloadData 方法在 selectRowAtIndexPath 触发之前还没有完成。如果您添加一点延迟,它会起作用,但是行选择会闪烁...
    • 有没有办法修复它,但在 ios8 中有点延迟?
    【解决方案2】:

    在 Marco 的回答中添加一点:

    首先,如果您使用多选,您可以将此方法添加到您的ViewController,并在需要调用[tableView reloadData]时调用它:

    - (void)reloadTableView
    {
        NSArray *indexPaths = [self.tableView indexPathsForSelectedRows];
        [self.tableView reloadData];
        for (NSIndexPath *path in indexPaths) {
            [self.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
        }
    }
    

    其次,如果您在UITableViewController 中并且希望在 tableView 出现后保留选择,则 UITableViewController 中有一个功能:clearsSelectionOnViewWillAppear 您可以打开或关闭。

    请看这里:http://developer.apple.com/library/ios/#documentation/uikit/reference/UITableViewController_Class/Reference/Reference.html

    【讨论】:

      【解决方案3】:

      Swift 4.2 测试

      在重新加载表视图后更新所选行的正确方法是:

      let selectedRows = tableView.indexPathsForSelectedRows
      
      tableView.reloadData()
      
      DispatchQueue.main.async {
          selectedRows?.forEach { selectedRow in
              tableView.selectRow(at: selectedRow, animated: false, scrollPosition: .none)
          }
      }
      

      【讨论】:

      • 我没有这样做,而是在所选单元格上使用setNeedsLayout()tableView.cellForRow(at: selectedRow)?.setNeedsLayout()
      【解决方案4】:

      解决方法是使用 reloadSections: 而不是 reloadData。出于某种原因,reloadData 删除了当前选择。

      【讨论】:

      • 我必须纠正自己。它在 iOS 4.0 中完美运行,但在 4.2 及更高版本中无法运行
      【解决方案5】:

      添加延迟对我不起作用(在 iOS 8.4 和 iOS 9 上测试)。在调用-selectRowAtIndexPath:animated:scrollPosition: 之后,在选定的单元格上添加对-layoutIfNeeded 的调用。

      NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
      [self.tableView reloadData];
      [self.tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
      [[self.tableView cellForRowAtIndexPath:selectedIndexPath] layoutIfNeeded];
      

      【讨论】:

      • 延迟闻起来不可靠,这对我有用。如果您是从viewWillAppear 执行此操作,请在调用super 之前执行重新选择。
      【解决方案6】:

      在 iOS 9.3 和 Swift 2.x 上,我只需要在 主线程 上调用函数即可:)

      self.tableView?.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None)
      

      【讨论】:

        【解决方案7】:

        这对我有用:

        NSIndexPath *indexPath = [table indexPathForSelectedRow];
        [table reloadData];
        double delayInSeconds = 0.01;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [self.table selectRowAtIndexPath:indexPath animated:YES
                                      scrollPosition:UITableViewScrollPositionNone];
        });
        

        诀窍是让 selectRowAtIndexpath 稍后运行。上面的代码是你开始写dispatch_after时可以选择的Xcode模板。

        【讨论】:

        • 知道我们需要延迟多长时间吗?在我使用 iOS 8 的情况下,在模拟器上,这个错误从未发生过,我怀疑是因为模拟器比实际设备快得多。但是当我在 iPhone 上测试它时,我将延迟设置为 0.15 秒,我仍然得到异常。
        【解决方案8】:

        如果您想进行表格更新并保留选择,这是一种解决方案:

            NSIndexPath* pathToSelect = [self.tableView indexPathForSelectedRow];
            if (pathToSelect && newRows) {
                int row = pathToSelect.row;
                for (NSIndexPath* path in newRows) {
                    if (path.section == pathToSelect.section && path.row <= pathToSelect.row) {
                        row++;
                    }
                }
                pathToSelect = [NSIndexPath indexPathForRow:row inSection:pathToSelect.section];
            }
        
            [self.tableView beginUpdates];
            if (reloadRows) [self.tableView reloadRowsAtIndexPaths:reloadRows withRowAnimation:UITableViewRowAnimationFade];
            if (newSections) [self.tableView insertSections:newSections withRowAnimation:UITableViewRowAnimationFade];
            if (newRows) [self.tableView insertRowsAtIndexPaths:newRows withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView endUpdates];
        
            if (pathToSelect) {
                [self.tableView selectRowAtIndexPath:pathToSelect animated:NO scrollPosition:UITableViewScrollPositionNone];
            }
        

        【讨论】:

          【解决方案9】:

          编辑:

          经过测试, 它并不总是有效!,

          原因是索引可能会改变!

          所以正确的方法是
          新建变量

          var currentIndex : IndexPath?
          

          在 didSelectRow 上

              func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
          
                  currentIndex = indexPath
          }
          

          然后把函数改成

           func reloadTableView() {
                  let indexPaths = tableview.indexPathsForSelectedRows
                  if let currentIndex = self.currentIndex {
                  self.tableview.reloadRows(at: [currentIndex], with: .fade)
                  for path in indexPaths ?? [] {
                      tableview.selectRow(at: path, animated: false, scrollPosition: .none)
                      }}
              }
          

          ===========

          @marmor 答案有效

          这是他的代码的 Swift 版本

          斯威夫特 4.2.3

          func reloadTableView() {
                  let indexPaths = tableView.indexPathsForSelectedRows
                  tableView.reloadData()
                  for path in indexPaths ?? [] {
                      tableView.selectRow(at: path, animated: false, scrollPosition: .none)
                  }
              }
          

          【讨论】:

            【解决方案10】:

            SWIFT 3

            self.collectionView.reloadData()
            self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])
            

            【讨论】:

              【解决方案11】:

              我通过声明一个数组来更好地解决这个问题,比如说,您选择的单元格的 ID 或字符串文本,并将其添加到 didSelectItemAtIndexPath 函数中。 这样,即使行数发生变化,选定的行数也不会发生变化。 例如:

              var selectedItems: [String] = [] // This array type really depends on your preference and what property you're using to store
              func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
                  guard let cell = collectionView.cellForItem(at: indexPath) else { return }
                  selectedItems.append(cell.textField.text)
              }
              

              在你的初始 cellForItemAtIndexPath 上

              func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
                  let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath)
                  if let text = cell.textField.text {
                      if selectedItems.contains(text) {
                          collectionView.selectItem(at: indexPath, animated: true, scrollPosition: [])
                      }
                  }
                  return cell
              }
              

              【讨论】:

                【解决方案12】:

                UICollectionView 我的两分钱:(归功于 Basil..)

                当我从后台任务重新加载时,我确实在主线程中运行:

                final override func safeReloadData(){
                
                            DispatchQueue.main.async { [weak self] in
                
                                guard let self = self else {
                                    return
                                }
                
                                let indexPaths = self.collectionView.indexPathsForSelectedItems
                                self.collectionView.reloadData()
                                for path in indexPaths ?? [] {
                                    self.collectionView.selectItem(at: path, animated: false, scrollPosition: [])
                                }
                            }
                    } 
                

                【讨论】:

                  【解决方案13】:

                  很多这些答案基本上是使用黑客重新选择之前突出显示的行,方法是从表格视图中找出之前选择的行,这似乎是一个不好的方法,因为通常当你tableview 重新加载,填充 tableview 的数据发生了变化。

                  因此,您可以根据自己的数据重新选择行

                  let data = ["some", "array", "of", "data"]
                  let selected = "of" // this should be populated from didSelectRowAt indexPath:
                  
                  tableView.reloadData()
                  
                  if let index = data.firstIndex(of: selected) {
                    tableView.selectRow(at: IndexPath(row: index, section: 0), animated: false, scrollPosition: .none) // the "of" row should now be highlighted, regardless if the data changed order
                  }
                  

                  【讨论】:

                    【解决方案14】:

                    听起来您没有对数据使用“模型” - 而只是更新“视图”(用户界面),因此可能是一个糟糕的设计。

                    reloadData 应该使用模型中的数据更新视图,其中应该包含要显示的最新数据。

                    在“模型视图控制器模式”上搜索资源

                    【讨论】:

                    • 不,我确实使用 NSMuttableArray 作为委托表数据源并在这个数组中添加/删除数据,但是为了刷新 tableView 我在修改数组后调用 reloadData 方法。我对吗?但是在这个 tableView 失去选择之后。
                    • 不看代码很难帮你,但是tableView的方法应该从数据源创建表,因此你只需更新数据模型并触发reloadData。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-01-28
                    • 2012-04-05
                    • 2017-12-14
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多