【问题标题】:Objective-C: UITableView cells get written over each time when scrolling up and downObjective-C:每次上下滚动时,UITableView 单元格都会被覆盖
【发布时间】:2009-12-22 06:56:33
【问题描述】:

我有一个通过CoreData 填充的UITableView,并且刚刚注意到一些奇怪的事情。我在 UITable 中有大约 20 行左右,当我向下滚动表格并再次备份时,单元格的标签会写在现有文本的顶部,并且每次我上下移动时都会继续这样做。我的CellForRowAtIndexPath 代码是:

  // Customize the appearance of table view cells.
  - (UITableViewCell *)tableView:(UITableView *)tableView   cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Some constants ---
const NSInteger TOP_LABEL_TAG = 1001;
const NSInteger BOTTOM_LABEL_TAG = 1002;
UILabel *topLabel;
UILabel *bottomLabel;
const CGFloat LABEL_HEIGHT = 20;
//CGRect Imageframe = CGRectMake(7, 5, 35, 35);
//----



static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
}

//custom implementation of UIView for lazy loading of image 
//LazyUIImageView *lazy = [[LazyUIImageView alloc] initWithFrame:Imageframe];


//[cell addSubview:lazy];



Post *post = [fetchedResultsController objectAtIndexPath:indexPath];

NSSet *medias = post.PostMedia;
Media *media = [medias anyObject];
NSSet *thumbs  = media.MediaThumb;
Thumb *thumb = [thumbs anyObject];


UIImage *img = thumb.url;

if (img)
    cell.imageView.image = img;
else 
    cell.imageView.image = post.authorpic;

//The creation of the top label
topLabel =
[[[UILabel alloc]
  initWithFrame:
  CGRectMake(
             35 + 2.0 * cell.indentationWidth,
             0.5 * (tableView.rowHeight - 2 * LABEL_HEIGHT),
             tableView.bounds.size.width -
             45 - 4.0 * cell.indentationWidth
             - 35,
             LABEL_HEIGHT)]
 autorelease];
[cell.contentView addSubview:topLabel];

topLabel.tag = TOP_LABEL_TAG;
topLabel.backgroundColor = [UIColor clearColor];
topLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
topLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
topLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
//---------------------------------

//The creation of the bottom label
bottomLabel =
[[[UILabel alloc]
  initWithFrame:
  CGRectMake(
             35 + 2.0 * cell.indentationWidth,
             0.5 * (tableView.rowHeight - 2 * LABEL_HEIGHT) + LABEL_HEIGHT,
             tableView.bounds.size.width -
             35 - 4.0 * cell.indentationWidth
             - 35,
             LABEL_HEIGHT)]
 autorelease];
[cell.contentView addSubview:bottomLabel];
//--------------------------------

//
// Configure the properties for the text that are the same on every row
//
bottomLabel.tag = BOTTOM_LABEL_TAG;
bottomLabel.backgroundColor = [UIColor clearColor];
bottomLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
bottomLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
bottomLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize] - 2];

//
// Create a background image view.
//
cell.backgroundView =
[[[UIImageView alloc] init] autorelease];
cell.selectedBackgroundView =
[[[UIImageView alloc] init] autorelease];;


topLabel.text = post.title;
bottomLabel.text = @"put something here";


//
// Set the background and selected background images for the text.
// Since we will round the corners at the top and bottom of sections, we
// need to conditionally choose the images based on the row index and the
// number of rows in the section.
//
UIImage *rowBackground;
UIImage *selectionBackground;
NSInteger sectionRows = [tableView numberOfRowsInSection:[indexPath section]];
NSInteger row = [indexPath row];
if (row == 0 && row == sectionRows - 1)  //all
{
    rowBackground = [UIImage imageNamed:@"topAndBottomRow.png"];
    selectionBackground = [UIImage imageNamed:@"topAndBottomRowSelected.png"];
}
else if (row == 0) //top
{
    rowBackground = [UIImage imageNamed:@"topRow.png"];
    selectionBackground = [UIImage imageNamed:@"topRowSelected.png"];
}
else if (row == sectionRows - 1) //bottom
{
    rowBackground = [UIImage imageNamed:@"bottomRow.png"];
    selectionBackground = [UIImage imageNamed:@"bottomRowSelected.png"];
}
else //middle
{
    rowBackground = [UIImage imageNamed:@"middleRow.png"];
    selectionBackground = [UIImage imageNamed:@"middleRowSelected.png"];
}
((UIImageView *)cell.backgroundView).image = rowBackground;
((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;


//[lazy release];

return cell;

}

提前感谢您的帮助!

【问题讨论】:

    标签: iphone objective-c iphone-sdk-3.0 uitableview


    【解决方案1】:

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    这一行告诉您的应用重用现有的屏幕外单元格(如果存在)。所以基本上你正在使用一个已经存在的单元格并在子视图已经存在的同一个地方添加一个新的子视图。在添加新的子视图之前,您需要清除现有的子视图。

    更新回答您的评论:

    jan 的代码将删除现有的子视图。您可以将它作为 if 单元格是 nil 语句的 else 子句添加到上面的代码中。

    因此,如果您没有可重复使用的单元格(单元格 == nil),您的代码将创建一个新单元格,否则您的代码将从现有单元格中删除旧的子视图:

    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
    }
    else {
        UIView* subview;
        while ((subview = [[[cell contentView] subviews] lastObject]) != nil)
          [subview removeFromSuperview];
    }
    

    实现相同目标的另一种更简洁的方法是:

    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
    }
    else {
        [[[cell contentView] subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
    }
    

    【讨论】:

    • 谢谢,但我会在哪里做呢?我会做 cell.subviews = nil 吗?或者下注方式是什么
    • jan 的代码将删除现有的子视图。您可以将其添加到上面的代码中,作为 if 单元格是 nil 语句的 else 子句。我已经用完整的 if/else 逻辑编辑了上面的答案。
    • 谢谢大家,你们都做得很棒。更重要的是帮助我更多地理解了 cell == nil 的概念。
    【解决方案2】:

    这是因为单元格正在被缓存,并且您一直在向内容视图添加内容,而没有删除已添加的内容。

    您可以使用更具体的单元格标识符并仅在创建单元格时添加到内容视图,或者在再次添加标签之前清除内容视图。

    要清除单元格的子视图,您可以添加以下内容:

    if(cell == nil)
    {...}
    else
    {
        UIView* subview;
        while ((subview = [[[cell contentView] subviews] lastObject]) != nil)
          [subview removeFromSuperview];
    }
    

    当 cell != nil 表示它是您已经创建并添加了子视图的单元格时,这就是需要清除它们的原因。

    【讨论】:

    • 在再次添加标签之前清除内容视图是什么意思。在这个委托方法的开头,我会把它放在哪里?
    【解决方案3】:

    当我们为 UITableView 使用重用标识符时,它知道单元格需要被重用,并且当我们没有指定要重用的单元格标识符的索引值时,单元格中的值会相互覆盖。

    我们需要专门告诉表格视图单元格,在索引路径上重用单元格,即

    NSString *cellIdentifier = [NSString stringWithFormat:@"S%1dR%1d",indexPath.section,indexPath.row];
    

    并在 if(cell == nil) 中使用此单元格标识符

    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellIdentifier:cellidentifier];
    

    这将消除单元格之间的错误通信,并且单元格中的值不会相互覆盖。更重要的是避免了索引位置或单元格索引值之间通信的麻烦。希望这会有所帮助,谢谢 :)

    【讨论】:

      【解决方案4】:

      不想自动释放 UITableViewCell,因为您的应用可能会在多次滚动并更改设备位置时崩溃。

      【讨论】:

        【解决方案5】:

        当单元格不为零时,您可以在 else 条件下删除子视图。

        static NSString *CellIdentifier = @"CellIdentifier";        
        UITableViewCell  *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        
            //create  a uitableviewcell
            if(cell == nil)
            {
             //Do whatever you want to do 
        
            }else{
                [[cell.contentView viewWithTag:YOUR_TAG] removeFromSuperview];
            }
        

        【讨论】:

          猜你喜欢
          • 2023-02-23
          • 1970-01-01
          • 2015-07-13
          • 1970-01-01
          • 2010-10-09
          • 2011-02-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多