UITableViewDelegate 中的问题 - RowSelected 给出错误的 NSIndexPath

Posted

技术标签:

【中文标题】UITableViewDelegate 中的问题 - RowSelected 给出错误的 NSIndexPath【英文标题】:Problem in UITableViewDelegate - RowSelected gives wrong NSIndexPath 【发布时间】:2011-02-01 10:05:21 【问题描述】:

我有一个 UITableViewSource 子类。我正在覆盖 GetCell 并使用我自己的子类单元格,如下所示:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)

  MarketItem item=_tableItems[indexPath.Section].Items[indexPath.Row];
  MarketCell cell=tableView.DequeueReusableCell(_cellIdentifier) as MarketCell;

  if (cell==null)
  
       cell=new MarketCell(UITableViewCellStyle.Subtitle,_cellIdentifier,item);
  

  // decorate the cell
  // ...

  return cell;

这可行,但是当我在 UITableViewDelegate 中获取事件时,索引路径会获取错误的单元格(AccessoryButtonTapped、WillSelectRow 等事件)。

Section 和 Row 编号看起来是正确的,但是当我这样做时

 tableView.CellAt(indexPath)

我弄错了单元格。 (行号和节号再次看起来正确。)

注意事项:

表不断更新 - 项目到达不同的线程,然后 InvokeOnMainThread'd 虽然表格不断更新,但仅添加行和部分 - 没有重新排序或删除任何内容 如果我在收到“WillSelectRow”时暂停更新,则无济于事 最有趣的是(但不是可交付的解决方案),如果我每次都创建一个新单元而不是执行 DequeueReusableCell,它可以正常工作。

我不禁认为这是我自己制造的一个愚蠢的错误,但找不到它。任何帮助将不胜感激!

【问题讨论】:

每次更新表数据时是否调用table.ReloadData()? 仅当添加新行时 - 对于现有单元格的更新,我正在执行 ReloadRows() 可能与信元排队有关。您的 MarketCell 构造函数如何将 _cellIdentifier 分配给新创建的单元格? 谢谢@riha,构造函数是:public MarketCell(UITableViewCellStyle style, string identifier, MarketItem item) : base(style,identifier) Item=item; (抱歉格式化,无法在 cmets 中解决。) 【参考方案1】:

您可以尝试不同的方法,如下所示:http://simon.nureality.ca/?p=91

基本上,不要将 UITableViewCell 子类化,而是将 UIViewController 子类化为“MarketCellController”。这个自定义控制器维护一个标准的 UITableViewCell 以及您的自定义内容,并且只需通过 AddSubview() 添加您的自定义内容。

您为每个需要的单元创建一个新控制器并将它们存储到字典中。

诀窍:通过为单元格分配唯一标签,您可以从字典中检索关联的控制器。

快速示例:

Dictionary<int,MarketCellController> controllers = new Dictionary<int,MarketCellController>();

// ...

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) 
  UITableViewCell cell = tableView.DequeueReusableCell(_cellIdentifier);
  MarketCellController cellController;

  if (cell == null) 
    cellController = new MarketCellController();
    cell = cellController.Cell;
    controllers.Add(cell.Tag, cellController);
   else 
    cellController = controllers[cell.Tag];
  

  // Decorate the cell (using Methods of your cellController)
  // ...

  return cell;

这最终可以规避子类 UITableViewCells 和出队的问题。

编辑:

我刚刚想到了一些其他事情:您似乎只是在 MarketCell 构造函数中分配您的项目。但是,出队的单元格有其旧项集,您需要在出队后将其重置为新项。

删除构造函数的 item 参数(无论如何它都会被覆盖)并创建一个公共属性(或 setter)。在获取单元格后使用它来分配正确的项目(无论它是出队还是新创建的)。应该这样做:

MarketCell cell = tableView.DequeueReusableCell(_cellIdentifier) as MarketCell;
if (cell == null) 
  cell = new MarketCell(UITableViewCellStyle.Subtitle, _cellIdentifier);


// Assign the correct item
cell.Item = item;
// or (whatever you like more):
// cell.SetItem(item);

// Decorate the cell
// ...

return cell;

我的 cellController 方法也是如此。无论单元格是出队还是新单元格,您都应该始终重置该单元格上可能与任何其他单元格不同的所有内容。

顺便说一句:如果您将单元格硬编码为 UITableViewCellStyle.Subtitle,您也可以从构造函数中省略它并将其硬编码到您的 MarketCell 类中。

至于 cellController 方法的价值:您将 UITableViewCell 与您的自定义数据和行为分离,从而获得一个很好的分离层。您的单元格控制器不必像表格单元格一样工作;)

【讨论】:

我喜欢它,但不确定仅跟踪自己的细胞的优势在哪里? (这就是我必须做的,而且工作正常。) 随着我对它的了解越来越多,我越来越喜欢您的解决方案。谢谢!【参考方案2】:

看来这在 Xamarin 中仍然是一个问题。不幸的是,@vlad259 发布的链接早就消失了——这个“错误”今天刚刚咬了我一口。我不知道为什么会发生这种情况——为了追查原因,我采取了削减 UITableViewSource 实现的方法,一直返回固定数量的行,并简单地将行号分配给单元格标签。 ..

该错误具体表现为,加载视图,滚动到第一个屏幕外单元格(即,如果您的表格可以呈现 10 个单元格,滚动以使第 11 个单元格出现在屏幕上),然后点击该单元格 - 你会得到RowSelected 事件中几乎随机的行索引。

我已经找到了几个解决方案。

解决方案 A

只有当我覆盖 GetHeightForRow 以返回自定义行高时,此错误才会出现 - 即使它返回一个常量。如果不需要...不要覆盖它。

解决方案 B

该错误似乎特别存在于采用NSIndexPath 参数的DequeueReusableCell(s) 中。

使用

UITableView.DequeueReusableCell(NSString)

而不是

UITableView.DequeueReusableCell(NSString, NSIndexPath)

【讨论】:

以上是关于UITableViewDelegate 中的问题 - RowSelected 给出错误的 NSIndexPath的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 iOS 中的 UITableViewDelegate 方法的情况下获取选择的 UITableViewCell

UITableViewDelegate 和 UITableViewDatasource 的区别

无法覆盖 UITableViewDataSource 和 UITableViewDelegate

分离 UITableViewDelegate 和 UITableViewDataSource 导致没有数据显示

UIViewController 和 UITableViewDelegate 之间的通信

在 UITableViewDelegate 方法 heightForRowAtIndexPath 中获取表格视图单元格属性: