如何在IIT的uitableview中使用可重用单元

时间:2014-08-12 06:16:08

标签: ios uitableview uibutton

确定。我似乎无法深入了解tableviews如何工作。有人请向我解释如何在表格视图中重复使用单元格,尤其是在滚动时?我对此有一个主要的痛点是,当我在一个单元格中创建一个动作时,其他单元格在滚动时会受到影响。我尝试使用一个数组作为模型的后端,但是当我没有想到时,我仍然会得到更改的单元格。要弄清楚的是,为什么当数组中的模型没有改变时它们会改变。

一个简单的例子:

表视图单元格,按钮“喜欢”。当我单击其中一个单元格中的按钮时,按钮文本将变为“不同”(到目前为止一直很好)。但是当我向下滚动时,其他单元格也会显示“不同”,即使我没有选择它们。当我向上滚动时,我最初选择的单元格会再次更改,而更新的单元格也会更改。

我似乎无法弄清楚这一点。如果你能告诉我一个有用的示例源代码,那就太棒了!谢谢!

- (void)viewDidLoad
{
    [super viewDidLoad];


    likeState = [[NSMutableArray alloc]init];

    int i =0;
    for (i=0; i<20; i++) {
        [likeState addObject:[NSNumber numberWithInt:0]];
    }
} 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [myButton setTitle:@"Like" forState:UIControlStateNormal];
    [myButton addTarget:self action:@selector(tapped:) forControlEvents:UIControlEventTouchUpInside];
    myButton.frame = CGRectMake(14.0, 10.0, 125.0, 25.0);
    myButton.tag =indexPath.row;
    [cell.contentView addSubview:myButton];

    if (cell ==nil) {


    }

    if ([[likeState objectAtIndex:indexPath.row]boolValue]==NO) {
        [myButton setTitle:@"Like" forState:UIControlStateNormal];

    }
    else{
        [myButton setTitle:@"Unlike" forState:UIControlStateNormal];


    }


    return cell;
}
-(void)tapped:(UIButton *)sender{

[likeState replaceObjectAtIndex:sender.tag withObject:[NSNumber numberWithInt:1]];

    [sender setTitle:@"Unlike" forState:UIControlStateNormal];

}

6 个答案:

答案 0 :(得分:12)

我假设您是通过Storyboard执行此操作的,因为您还没有通过Interface Builder创建按钮,您需要检查正在重复使用的单元格是否已经按钮与否。
根据您当前的逻辑,您将在单元格重新出现时创建一个新的按钮实例。

我建议如下:

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

    //following is required when using XIB but not needed when using Storyboard
    /*
     if (cell == nil) {
         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
     }
     */
    //Reason:
    //[1] When using XIB, dequeueReusableCellWithIdentifier does NOT create a cell so (cell == nil) condition occurs
    //[2] When using Storyboard, dequeueReusableCellWithIdentifier DOES create a cell and so (cell == nil) condition never occurs

    //check if cell is being reused by checking if the button already exists in it
    UIButton *myButton = (UIButton *)[cell.contentView viewWithTag:100];

    if (myButton == nil) {
        myButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [myButton setFrame:CGRectMake(14.0,10.0,125.0,25.0)];
        [myButton setTag:100]; //the tag is what helps in the first step
        [myButton setTitle:@"Like" forState:UIControlStateNormal];
        [myButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
        [myButton addTarget:self action:@selector(tapped:andEvent:) forControlEvents:UIControlEventTouchUpInside];
        [cell.contentView addSubview:myButton];

        NSLog(@"Button created");
    }
    else {
        NSLog(@"Button already created");
    }

    if ([likeState[indexPath.row] boolValue]) {
        [myButton setTitle:@"Unlike" forState:UIControlStateNormal];
    }
    else {
        [myButton setTitle:@"Like" forState:UIControlStateNormal];
    }

    return cell;
}

-(void)tapped:(UIButton *)sender andEvent:(UIEvent *)event
{
    //get index
    NSSet *touches = [event allTouches];
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:currentTouchPosition];

    //toggle "like" status
    if ([likeState[indexPath.row] boolValue]) {
        [likeState replaceObjectAtIndex:indexPath.row withObject:@(0)];
        [sender setTitle:@"Like" forState:UIControlStateNormal];
    }
    else {
        [likeState replaceObjectAtIndex:indexPath.row withObject:@(1)];
        [sender setTitle:@"Unlike" forState:UIControlStateNormal];
    }
}

答案 1 :(得分:3)

最大的问题是每次更新单元格时都会创建按钮。

例如,如果您在屏幕上有可见的4个roes,请执行以下操作:

*-----------------------*
| cell A   with button  |
*-----------------------*
| cell B   with button  |
*-----------------------*
| cell C   with button  |
*-----------------------*
| cell D   with button  |
*-----------------------*

现在当你向下滚动以便单元格A不再可见时,它会被重用并放在下面:

*-----------------------*
| cell B   with button  |
*-----------------------*
| cell C   with button  |
*-----------------------*
| cell D   with button  |
*-----------------------*
| cell A   with button  |
*-----------------------*

但是对于单元格A,它再次被称为cellForRowAtIndexPath。 你做的是在它上面放置另一个按钮。所以你实际上有:

*-----------------------*
| cell B   with button  |
*-----------------------*
| cell C   with button  |
*-----------------------*
| cell D   with button  |
*-----------------------*
| cell A with 2 buttons |
*-----------------------*

你可以看到你很快就会有很多按钮堆积起来。您可以通过@Timur Kuchkarov建议的故事板来修复此问题,或者通过

修复代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];



    if (cell ==nil) {
         UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        [myButton setTitle:@"Like" forState:UIControlStateNormal];
        [myButton addTarget:self action:@selector(tapped:) forControlEvents:UIControlEventTouchUpInside];
        myButton.frame = CGRectMake(14.0, 10.0, 125.0, 25.0);
        myButton.tag = 10;
        [cell.contentView addSubview:myButton];

    }

    UIButton * myButton = (UIButton * )[cell.contentView viewWithTag:10]


    if ([[likeState objectAtIndex:indexPath.row]boolValue]==NO) {
        [myButton setTitle:@"Like" forState:UIControlStateNormal];

    }
    else{
        [myButton setTitle:@"Unlike" forState:UIControlStateNormal];
    }


    return cell;
}

这样你只需添加一个按钮,如果没有重复使用单元格(因此它没有任何内容)。

通过这种方式,您不能依赖羊肉标签号码来点击功能,(我不管怎么说),所以你必须改变它。

此部分未经过测试:

您可以检查按钮的父级以查看它所属的女性单元格。

UITableViewCell * cell = [[button superview] superview] /// superview of button is cell.contentView
NSIndexPath * indexPath = [yourTable indexPathForCell:cell];

答案 2 :(得分:2)

要重新使用单元格,请将您的单元格标识符值放在界面构建器中。

答案 3 :(得分:1)

首先,当你点击单元格按钮时,你总是设置@(1)([NSNumber numberWithInt:1],最好使用@(YES)或@(NO))。这就是为什么你会看到不正确的结果。然后你总是在你的单元格中添加按钮,所以如果它被重复使用了10次,你就可以在那里添加10个按钮。最好使用xib或storyboard原型。

答案 4 :(得分:1)

接受答案的Swift 2.1版本。对我很有用。

        override func viewDidLoad() {
        super.viewDidLoad();

        self.arrayProducts = NSMutableArray(array: ["this", "that", "How", "What", "Where", "Whatnot"]); //array from api for example
        var i:Int = 0;
        for (i = 0; i<=self.arrayProducts.count; i++){
            let numb:NSNumber = NSNumber(bool: false);
            self.arrayFavState.addObject(numb); //array of bool values
            }
        }

TablView数据源方法::

        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("SubCategoryCellId", forIndexPath: indexPath) as! SubCategoryCell;


        cell.btnFavourite.addTarget(self, action: "btnFavouriteAction:", forControlEvents: UIControlEvents.TouchUpInside);

        var isFavourite: Bool!;
        isFavourite = self.arrayFavState[indexPath.row].boolValue;
        if isFavourite == true {
            cell.btnFavourite.setBackgroundImage(UIImage(named: "like-fill"), forState: UIControlState.Normal);
        } else {
            cell.btnFavourite.setBackgroundImage(UIImage(named: "like"), forState: UIControlState.Normal);
        }


        return cell;
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.arrayProducts.count;
    }

按钮操作方法::

        func btnFavouriteAction(sender: UIButton!){

        let position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableProducts)
        let indexPath = self.tableProducts.indexPathForRowAtPoint(position)
        let cell: SubCategoryCell = self.tableProducts.cellForRowAtIndexPath(indexPath!) as! SubCategoryCell
        //print(indexPath?.row)
        //print("Favorite button tapped")
        if !sender.selected {
            cell.btnFavourite.setBackgroundImage(UIImage(named: "like-fill"), forState: UIControlState.Normal);
            sender.selected = true;
            self.arrayFavState.replaceObjectAtIndex((indexPath?.row)!, withObject:1);
        } else {
            cell.btnFavourite.setBackgroundImage(UIImage(named: "like"), forState: UIControlState.Normal);
            sender.selected = false;
            self.arrayFavState.replaceObjectAtIndex((indexPath?.row)!, withObject:0);
        }

    }

答案 5 :(得分:0)

我需要查看您的代码,但我可以说您没有更改模型或者您更改了数组中的所有行。

您可以使用从UITableViewCell扩展的某些类,并在“cellForRowAtIndexPath”方法中将其用作可重复使用的单元格。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
 {
   MyCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyCell"];


    // Configure the cell...
    MyModel *model = [_models objectAtIndex:indexPath.row];
    cell.nameLabel.text = model.name;
    cell.image.image = model.image;
    cell.likeButton.text = model.likeButtonText;
    return cell;
 } 

然后在“didSelectRowAtIndexPath”方法中更改“model.likeButtonText”。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
   MyCell cell* =     [tableView cellForRowAtIndexPath:indexPath];
   MyModel *model = [_models objectAtIndex:indexPath.row];
   model.likeButtonText = [model.likeButtonText isEqual:@"like"]?@"unlike":@like;
   cell.likeButton.text = model.likeButtonText;
}

这将更新你的手机