2015-11-01 106 views
1

我有一个UITableview,它有一些历史记录。我使用自定义单元来处理标签,图像和按钮的显示。 UITableview内有大约50行。但问题是当Tableview向上滚动时,那么在第一个显示的下面的行中已经改变了它的值。不确定Tableview究竟发生了什么。当UITableview向上或向下滚动时值发生了变化

static NSString *CellIdentifier = @"HistoryCell"; 
cellHistory = [self.notificationTableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 

[cellHistory.label clearActionDictionary]; 
//Step 2: Define a selection handler block 
void(^handler)(FRHyperLabel *label, NSString *substring) = ^(FRHyperLabel *label, NSString *substring) 
{ 
    [self userNameTapped:[[[[self.arrayHistory objectAtIndex:indexPath.row] objectForKey:Details] objectForKey:@"key"] objectForKey:@"key"]]; 
}; 

//Step 3: Add link substrings 
[cellHistory.label setLinksForSubstrings:@[userName] withLinkHandler:handler]; 
return cellHistory; 
+0

请提供更多代码,如何将数据设置到您的单元格中。 –

+0

我已经更新了上面的代码。 – Nishan29

+1

您的细胞正在被重复使用。基本的iOS设计知识。大量的例子在线 – soulshined

回答

8

谨防

以下的答案必须解决的具体实施,而不是设计。使用NSFetchedResultsController的样板代码将首先防止此错误发生。 (Apple documentation)。不惜一切代价,避免操纵一旦通过cellForRowAtIndexPath返回的单元格:你做不是拥有它。


1.重复使用的细胞不会被缓存

中特别令人担忧的是这一行:

[self.arrayHistory objectAtIndex:indexPath.row] 

与其说是因为该特定实例的,而是因为它使我认为你在这个课程的其他地方做假设:通过dequeueReusableCellWithIdentifier检索的UITableViewCell不过是一个瞬态对象,纯粹用于UITableView显示。

的非常相同的小区的实例将具有多个indexPath随时间:这样的细胞可能不被缓存或在进一步的日期操纵。一旦作为cellForRowAtIndexPath的最后一条语句返回,可能会再次访问而不是,无论是通过某种外部指针通过某种异步方法保留。始终将通过dequeueReusableCellWithIdentifier获取的缓存表视图的索引路径视为陈旧

不是修改一次返回的单元格的内容。按照以下Losiowaty所述的正确设计。

对于更好的判断,如果您必须修改已经返回到表视图的单元格的内容,那么您必须为此使用一个持久单元格,例如使用字典UITableViewCell。除非该标识符专用于该单元,否则您不得在此类单元上调用dequeueReusableCellWithIdentifier;而对于大型数据集而言,高度的内存效率低下,这将保证单元不被重新使用。


2.不要猜tableview

- (UITableViewCell *)tableView:(UITableView *)tableView 
     cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    cellHistory = [self.notificationTableView // etc. 

正确的逻辑UITableViewDataSourcecellForRowAtIndexPath是使用传递给你的参数:

- (UITableViewCell *)tableView:(UITableView *)tableView 
     cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    cellHistory = [tableView // etc. 

3 。再用干净的再生细胞

假设你已经掌握了使用dequeueReusableCellWithIdentifier的回收副作用,并确保你不交叉授粉不同类型的细胞,你可能要cleanup您的重用单元(信贷Timur Bernikowich对他的评论)

func prepareForReuse() { 
    super.prepareForReuse() 
    // reset attributes of the cell that are not related to content 
} 

4.内省第一

总是比较喜欢,我做错了什么它曾经工作在以前的操作系统。虽然第二个命题通常是正确的,但总的来说,历史可以让你思考如何在天堂里工作。

恰恰相反,我通常把这些情况作为一个确定的信号,我正在做一些可怕的错误,我只是幸运地穿过了裂缝。


5.尊重命名约定

虽然我欣赏你块通过在所述方法中

- (void)setLinksForSubstrings:(NSArray*)string withLinkHandler:(void(^)(FRHyperLabel *label, NSString *substring))handler {} 

我有点由Details对象或userName的性质揭去,更不用说在[self userNameTapped:[[[[中的4个括号。如果我被抛弃,其他工程师在第一次接触到您的代码时会有机会。

从上下文中,我不能推导出什么是变量,常量,静态,实例属性等。很可能你的单元格在这个层次上被混淆了(你可能会重复使用你认为是当地人或基于堆栈的值,但不是)。

我可以建议使用typedef作为块,采用大小写一致性,避免嵌套[太深。花了不小的努力将代码与样板UITableView代码进行比较,只是为了修剪噪音。

+2

我不得不不同意在需要修改时专用持久单元。更明确的方法是修改模型中的数据,然后调用'reloadRowsAtIndexPaths:withRowAnimation:',或重新加载部分的类似方法。 – Losiowaty

+1

我完全同意。我认为第1项是非常明确的,不挂在返回的'UITableViewCell'上。我已添加对您的评论的参考。 – SwiftArchitect

+1

不是由'UITableView'调用的'prepareForReuse'? –

相关问题