Ios 如何在UITableView中正确重用单元格

Ios 如何在UITableView中正确重用单元格,ios,objective-c,uitableview,Ios,Objective C,Uitableview,我是个新手。我正在使用此代码创建一个UITableViewCell,但是当我重新加载表格时,按钮的图像并不总是正确的,尽管所有标签都可以正常工作。我不知道为什么。如何解决此问题 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITa

我是个新手。我正在使用此代码创建一个
UITableViewCell
,但是当我重新加载表格时,按钮的图像并不总是正确的,尽管所有标签都可以正常工作。我不知道为什么。如何解决此问题

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];

        UILabel *FileNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 100, 30)];
        FileNameLabel.tag = 1000;
        FileNameLabel.backgroundColor = [UIColor clearColor];
        FileNameLabel.font = [UIFont fontWithName:@"Helvetica" size:16];
        FileNameLabel.font = [UIFont boldSystemFontOfSize:16];
        FileNameLabel.textColor = [UIColor blackColor];
        [cell.contentView addSubview: FileNameLabel];
        [FileNameLabel release];


        UILabel *UploadTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 20, 150, 25)];
        UploadTimeLabel.tag = 2000;
        UploadTimeLabel.backgroundColor = [UIColor clearColor];
        UploadTimeLabel.font = [UIFont fontWithName:@"Helvetica" size:12];
        UploadTimeLabel.textColor = [UIColor grayColor];
        [cell.contentView addSubview: UploadTimeLabel];
        [UploadTimeLabel release];


        UILabel *pricelabel = [[UILabel alloc] initWithFrame:CGRectMake(80, 0, 80, 30)];
        pricelabel.backgroundColor = [UIColor clearColor];
        pricelabel.font = [UIFont fontWithName:@"Helvetica" size:16];
        pricelabel.font = [UIFont boldSystemFontOfSize:16];
        pricelabel.textColor = [UIColor darkGrayColor];
        pricelabel.tag = 3000;
        //pricelabel.hidden = YES;
        pricelabel.textAlignment = NSTextAlignmentRight;
        [cell.contentView addSubview: pricelabel];
        [pricelabel release];


        market = [[UIButton alloc] init];;
        [market setFrame:CGRectMake(200, 6, 30, 30)];
         market.tag = 4000;

        [market addTarget:self action:@selector(marketPressedAction:) forControlEvents:UIControlEventTouchDown];


        [cell.contentView addSubview:market];
   }

    if( [temp count] > 0)
    {
        UILabel *fileNameLbl = (UILabel*)[cell.contentView viewWithTag:1000];
        fileNameLbl.text =[temp objectAtIndex:indexPath.row];

        UILabel *uploadlbl = (UILabel*)[cell.contentView viewWithTag:2000];
        uploadlbl.text =[UploadTimeAllArr objectAtIndex:indexPath.row];
    }

    UIButton *marketButton = (UIButton*)[cell.contentView viewWithTag:4000];
    [marketButton setTag:indexPath.row];
    if([sellingArray count]>0)
    {
        NSLog(@"sellingArray %@",sellingArray);
        if([[sellingArray objectAtIndex:indexPath.row] isEqualToString:@"0"]) // nothing
        {

            [marketButton setSelected:NO];
            [marketButton setImage:[UIImage imageNamed:@"Marketplace.png"] forState:UIControlStateNormal];
            marketButton.enabled = YES;

        }
        else if([[sellingArray objectAtIndex:indexPath.row] isEqualToString:@"2"])  // marketplace
        {

            [marketButton setSelected:YES];
            [marketButton setImage:[UIImage imageNamed:@"MarketplaceSelect.png"] forState:UIControlStateNormal];
            marketButton.enabled = YES;

        }
    }


    return cell;
}

这里的主要问题是,每次调用此方法时,都要在单元格中重新创建新视图。您希望在
if(cell==nil)
中创建所有可重用的元素,否则它将复制。任何动态的东西都必须在此之外创建。我把你的代码修改了。这应该更有效

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        // Everything that does not change should go in here!

        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
        UILabel *pricelabel = [[UILabel alloc] initWithFrame:CGRectMake(80, 0, 80, 30)];


        pricelabel.backgroundColor = [UIColor clearColor];
        pricelabel.font = [UIFont fontWithName:@"Helvetica" size:16];
        pricelabel.font = [UIFont boldSystemFontOfSize:16];
        pricelabel.textColor = [UIColor darkGrayColor];
        pricelabel.tag = 3000;
        //pricelabel.hidden = YES;
        pricelabel.textAlignment = NSTextAlignmentRight;
        [cell addSubview:pricelabel];

        UIButton *market = [UIButton buttonWithType:UIButtonTypeCustom];
        [market setFrame:CGRectMake(200, 6, 30, 30)];
        [market addTarget:self action:@selector(marketPressedAction:) forControlEvents:UIControlEventTouchDown];
        [cell addSubview:market];
    }

    // find market button, since we could be reusing a cell we cannot rely on a tag
    // value to find it. (This would only work with one button though).
    UIButton *market;
    for (UIView *subview in cell.subviews) {
    if ([subview isKindOfClass:[UIButton class]]) {
            market = (UIButton *)subview;
            break;
        }
    }

    // set all defaults in case of reuse
    [market setImage:[UIImage imageNamed:@"DefaultImage.png"] forState:UIControlStateNormal];
    market.selected = YES;
    market.enabled = NO;
    market.clearsContextBeforeDrawing = NO;

    if([sellingArray count] > 0) {
        NSLog(@"sellingArray %@",sellingArray);
        if([[sellingArray objectAtIndex:indexPath.row] isEqualToString:@"0"]) {
            // not sure if this is supposed to be YES or NO
            market.clearsContextBeforeDrawing = YES;
            [market setSelected:NO];
            [market setImage:[UIImage imageNamed:@"Marketplace.png"] forState:UIControlStateNormal];
            market.enabled = YES;
        }
    }
    [market setTag:indexPath.row];

    return cell;
}
由于您似乎没有使用ARC,请确保查看此代码以了解所需的引用计数。

第二次编辑:

这是从上面的答案复制而来的:

-cellforrowatinexpath:
方法中:

    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellSubtitle];
        UIButton *market = [UIButton buttonWithType:UIButtonTypeCustom];
            [market setFrame:CGRectMake(200, 6, 30, 30)];
        [market addTarget:self action:@selector(marketPressedAction:)     forControlEvents:UIControlEventTouchDown];

        [cell.contentView addSubview:market];

    //Add all your UILabel INITIATION stuff here as well

    }

    UIButton *marketButton;
    for (UIView *subview in cell.subviews) {    
        if ([subview isKindOfClass:[UIButton class]]) {
            marketButton = (UIButton *)subview;
            break;        
        }    
    }
    marketButton.tag = [indexPath row];

    UILabel *priceLabel = [cell.contentView viewWithTag:3000];
    UILabel *uploadTimeLabel = [cell.contentView viewWithTag:2000];

    //Set up your labels and button now

    return cell;
}


    EDIT: Leaving my original answer below for posterity but I see that you are setting the table index row as the MarketButton's tag. If you're using that to figure out which dataSource object to query, this is bad practice. You should be making a custom cell which can hold a reference to the object in your data source, so you don't have to ask the button for its tag, and then ask the data source array for the object at index:tag. 
这种情况不好的原因是,在某些地方,数组的状态可能会改变,但表单元格仍然显示,并且仍然保留指向错误索引的标记。如果您只是让单元格跟踪所讨论的对象,那么无论数组结构发生什么变化,您都可以保证修改所需的对象

关于Firo的答案,我唯一想改变的是,只需在单元格中的每个视图中添加一个“tag”属性,这样您就不必每次都迭代查找它

还删除了
[[UIButton alloc]init]
行,因为它是多余的,可能会被认为是一个悬空指针

if (cell == nil) {
    // Everything that does not change should go in here!

    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    UIButton *market = [UIButton buttonWithType:UIButtonTypeCustom];
    [market setFrame:CGRectMake(200, 6, 30, 30)];
    [market addTarget:self action:@selector(marketPressedAction:) forControlEvents:UIControlEventTouchDown];

    market.tag = 9999;

    [cell.contentView addSubview:market];
}

//don't have to do UIView iteration here
UIButton *marketButton = [cell.contentView viewWithTag:9999];

devqueryusablecellWithIdentifier:
method get返回已创建的可用单元格实例,如果引用仍然指向nil,我们需要一个有效单元格,并创建一个单元格以从该
cellForRowatIndexpath:
方法返回。这就是在
中检查的内容(cell==nil)
。当您创建一个新单元格时,它是创建,因此所有设置都是自定义的,所有设置都必须在这里完成。

您的UIButton代码中还有一行。您正在加载两个实例。第一个实例使用
alloc-init
。第二个带有
按钮,类型为:UIButtonTypeCustom
。我不知道在这种情况下arc下会发生什么,但我相信它过去被称为悬挂指针。谢谢,但我尝试了你的代码,但当重新加载数据时,它仍然无法在UIButton marketButton中显示正确的图像。我在重新加载数据时调试,marketButton是0x0000。它无法初始化?您是否删除了稍后在方法中对
market.tag=[indexPath行]
的调用?如果您将其留在那里,将不再有
-viewWithTag:9999
我尝试删除[marketButton setTag:indexPath.row];但当用户点击按钮时,我的应用程序崩溃了是的,因为按钮上的标签可能是您链接到数据源数组的方式,即您的数据源中没有索引:9999,该索引将超出范围。您需要重构并让您的单元格跟踪它打算监视/更新的对象,而不是跟踪“数据源索引”。就像我说的,如果数组的结构发生变化,而你只是在跟踪一个“索引”,你将指向错误的对象。我尝试了你的代码,但它仍然是一样的,它仍然无法在重新加载数据时更改图像。你还有别的解决办法吗?thanksI debug当重新加载数据时,marketButton为0x0000。它不能初始化?我更改了代码,请看。另外,在哪里调用重新加载数据?我想我们可能需要更多的代码。嗯,尝试使用
[tableView-dequeuereusible
而不是
[\u-tableView-dequeuereusible
I-synthesis-tableView=\u-tableView=\