Shubam Gupta
Shubam Gupta

Reputation: 61

How to Handle UIButton in TableviewCell in Objective c?

I am new to iOS Programming.I have created custom cell with Three textfield and two button,I have Populated cell count with 10.my textfield working fine.I want to change the button images based on user select.Here My problem is if I change the button images first three cells means,It has repeat for the below cells.I don't know how to handle and how can I find Which button is selected and all.can any one help me.I have tried below codes.

Tableviewcell.m

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
    self.textfield1.delegate=self;
    self.textfield2.delegate=self;
    self.textfield3.delegate=self;
    [self.bttn1 addTarget:self action:@selector(button1:) forControlEvents:UIControlEventTouchUpInside];
    [self.bttn2 addTarget:self action:@selector(button1:) forControlEvents:UIControlEventTouchUpInside];

}
- (void)button1:(UIButton *)sender {

    if([_bttn1 isHighlighted]==YES)
    {
        [_bttn1 setImage:[UIImage imageNamed:@"check.png"] forState:UIControlStateNormal];
        [_bttn2 setImage:[UIImage imageNamed:@"uncheck.png"] forState:UIControlStateNormal];
    }
    else if([_bttn2 isHighlighted]==YES){
        [_bttn1 setImage:[UIImage imageNamed:@"uncheck.png"] forState:UIControlStateNormal];
        [_bttn2 setImage:[UIImage imageNamed:@"check.png"] forState:UIControlStateNormal];

    }
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}
- (void)prepareForReuse {
    self.textfield1.text = @"";
     self.textfield2.text = @"";
     self.textfield3.text = @"";
    [self.bttn1 setTitle:@"" forState:UIControlStateNormal];
    [self.bttn2 setTitle:@"" forState:UIControlStateNormal];
}


//Tableview.m

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"cellview";
    TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier     forIndexPath:indexPath];
    cell.textfield1.text = _texts[indexPath.row];
    cell.textfield1.tag = indexPath.row;
    cell.textfield1.delegate = self;
    cell.textfield2.text = _texts[indexPath.row+20];
    cell.textfield2.tag = indexPath.row+20;
    cell.textfield2.delegate = self;
    cell.textfield3.text = _texts[indexPath.row+40];
    cell.textfield3.tag = indexPath.row+40;
    cell.textfield3.delegate = self;
    [cell.bttn1 setTitle:@"" forState:UIControlStateNormal];
    [cell.bttn2 setTitle:@"" forState:UIControlStateNormal];

    return cell;
}

Upvotes: 0

Views: 1690

Answers (4)

Tanvir Nayem
Tanvir Nayem

Reputation: 722

If you just want to change the cell's button image within tap then the baisc logic is :

1. create a custom cell class.
2. add IBOutlet and IBAction of that button on that custom cell class.
3. then add logic in the button's IBAction method.
  logic could be : i. self.button1.isSelected
                  ii. self.button1.isHighlighted etc. 
  which one you need.

Upvotes: 0

Matic Oblak
Matic Oblak

Reputation: 16774

The thing with table views and its cells is they are reused and need to be refreshed fully once a cell is used.

In your case the texts work because you set them inside cellForRowAtIndexPath method while you leave the selection within the cell. So in your case you need to move the selection into data source. What I mean is you should create a custom class that holds your data for instance:

@interface MyObject: NSObject
@property (nonatomic) BOOL isSelected;
@property (nonatomic, strong) NSString *text1;
@property (nonatomic, strong) NSString *text2;
@property (nonatomic, strong) NSString *text3;
@end

So I am not sure about your _texts[indexPath.row+20] but from now on let's assume this is mapped to @property (nonatomic, strong) NSArray<MyObject *> *items; so that code would be changed to:

cell.textfield2.text = [self.items[indexPath.row] text2];

I hope you know how to remap these objects of yours.

Now that we prepared the model we can separate the logic so the cells do what they need to do with the object. There is no reason for view controller to ever touch or even have access to buttons or to text fields. All it needs is to set the item on the cell:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellview" forIndexPath:indexPath];
    cell.item = self.items[indexPath.row];
    return cell;
}

So then the cell should look like this:

@interface TableViewCell: UITableViewCell

@property (nonatomic, strong) MyObject *item;

@end

And its implementation:

@interface TableViewCell()<UITextFieldDelegate>

@property (nonatomic, strong) UITextField *textfield1;
@property (nonatomic, strong) UITextField *textfield2;
@property (nonatomic, strong) UITextField *textfield3;

@property (nonatomic, strong) UIButton *bttn1;
@property (nonatomic, strong) UIButton *bttn2;

@end

@implementation TableViewCell

- (void)setItem:(MyObject *)item {
    _item = item;
    [self refresh];
}

- (void)awakeFromNib {
    [super awakeFromNib];
    self.textfield1.delegate = self;
    self.textfield2.delegate = self;
    self.textfield3.delegate = self;
}

- (void)refresh {
    self.textfield1.text = self.item.text1;
    self.textfield2.text = self.item.text2;
    self.textfield3.text = self.item.text3;

    if(self.item.isSelected) {
        [self.bttn1 setImage:[UIImage imageNamed:@"check.png"] forState:UIControlStateNormal];
        [self.bttn2 setImage:[UIImage imageNamed:@"uncheck.png"] forState:UIControlStateNormal];
    }
    else {
        [self.bttn1 setImage:[UIImage imageNamed:@"uncheck.png"] forState:UIControlStateNormal];
        [self.bttn2 setImage:[UIImage imageNamed:@"check.png"] forState:UIControlStateNormal];
    }
}

- (void)onButtonPressed:(id)sender {
    self.item.isSelected = sender == self.bttn2; // At least it seems this is what you are doing
    [self refresh]; // Will reload the whole UI depending on the model changes
}

// TODO: add text field delegate methods as you used them inside your view controller

@end

So to clear things a bit: Your view controller holds an array of all items. Each of the cell gets one item which it can modify at any time. When cell modifies an item it is the same item in the view controller array so updates are reflected. This should be enough in your case but in general you might need the controller to know that a cell triggered something like a button pressed. In those cases you need to use delegates. For instance like so:

@protocol TableViewCellDelegate
- (void)tableViewCell:(TableViewCell *)sender requestSpecialActionForObject:(MyObject *)item;
@end

Then add a property in table view cell:

@interface TableViewCell: UITableViewCell

@property (nonatomic, strong) MyObject *item;
@property (nonatomic, weak) id<TableViewCellDelegate> delegate;

@end

Add extra code to view controller:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellview" forIndexPath:indexPath];
    cell.item = self.items[indexPath.row];
    cell.delegate = self;
    return cell;
}

- (void)tableViewCell:(TableViewCell *)sender requestSpecialActionForObject:(MyObject *)item {
    // TODO: code here
}

And some logic inside the cell:

- (void)onSpecialButtonPressed:(id)sender {
    [self.delegate tableViewCell:self requestSpecialActionForObject:self.item];
}

Upvotes: 0

Priya
Priya

Reputation: 749

You should create separate class of type UITableViewCell for your cell

TableViewCell.h

@protocol TableViewCellDelegate <NSObject>

-(void)selectedButton:(int)button rowNumber:(int)row;

@end

@interface TableViewCell : UITableViewCell <UITextFieldDelegate>

@property (weak, nonatomic) IBOutlet UITextField *textfield1;
@property (weak, nonatomic) IBOutlet UITextField *textfield2;
@property (weak, nonatomic) IBOutlet UITextField *textfield3;
@property (weak, nonatomic) IBOutlet UIButton *bttn1;
@property (weak, nonatomic) IBOutlet UIButton *bttn2;

@property (nonatomic, weak) id <TableViewCellDelegate> delegate;

-(void)setupView:(int)selectedButton;

@end

TableViewCell.m

#import "TableViewCell.h"

@implementation TableViewCell

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code

    self.textfield1.delegate=self;
    self.textfield2.delegate=self;
    self.textfield3.delegate=self;
    [self.bttn1 addTarget:self action:@selector(button1:) forControlEvents:UIControlEventTouchUpInside];
    [self.bttn2 addTarget:self action:@selector(button1:) forControlEvents:UIControlEventTouchUpInside];

}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}


- (void)button1:(UIButton *)sender {

     if(sender == self.bttn1){

         [self.delegate selectedButton:1 rowNumber:(int)self.tag];
      }
      else if([_bttn2 isHighlighted]==YES){

          [self.delegate selectedButton:2 rowNumber:(int)self.tag];
      }
 }


 - (void)setupView:(int)selectedButton {

    self.textfield1.text = @"";
    self.textfield1.tag = self.tag;
    self.textfield1.delegate = self;
    self.textfield2.text = @"";
    self.textfield2.tag = self.tag+20;
    self.textfield2.delegate = self;
    self.textfield3.text = @"";
    self.textfield3.tag = self.tag+40;
    self.textfield3.delegate = self;
    [self.bttn1 setTitle:@"" forState:UIControlStateNormal];
    [self.bttn2 setTitle:@"" forState:UIControlStateNormal];
    if (selectedButton == 1) {

        self.bttn1.backgroundColor = [UIColor redColor];
        self.bttn2.backgroundColor = [UIColor blueColor];
    }else if (selectedButton == 2) {

        self.bttn1.backgroundColor = [UIColor blueColor];
        self.bttn2.backgroundColor = [UIColor redColor];
    }else {
        self.bttn1.backgroundColor = [UIColor blueColor];
        self.bttn2.backgroundColor = [UIColor blueColor];
    }
}

@end

ViewController.m

#import "ViewController.h"
#import "TableViewCell.h"

@interface ViewController ()<UITableViewDelegate,UITableViewDataSource,TableViewCellDelegate>

@property (nonatomic, strong)NSMutableArray *arrSelectedBtns;
@property (weak, nonatomic) IBOutlet UITableView *tableView;

@end

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];
     // Do any additional setup after loading the view, typically from a nib.

    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    self.arrSelectedBtns = [NSMutableArray array];
    for (int i=0; i<10; i++) { //10 respresents number of rows in tableview

        [self.arrSelectedBtns addObject:[NSNumber numberWithInt:0]];
    }
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return 10;
}

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

     TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellview"     forIndexPath:indexPath];
    cell.tag = indexPath.row;
    cell.delegate = self;

    [cell setupView:[[self.arrSelectedBtns objectAtIndex:indexPath.row] intValue]];
    return cell;
 }

 - (void)selectedButton:(int)button rowNumber:(int)row {

    [self.arrSelectedBtns insertObject:[NSNumber numberWithInt:button] atIndex:row];
     NSIndexPath *indexpath = [NSIndexPath indexPathForRow:row inSection:0];
     [self.tableView reloadRowsAtIndexPaths:@[indexpath] withRowAnimation:UITableViewRowAnimationNone];
 }

 @end

Upvotes: 1

Dipak Kacha
Dipak Kacha

Reputation: 423

Here, You have to manage it by array with size of same number of rows, and you have to manage it with conditions in CellForRowAtIndexPath method Like,

 if (cell.btn.isSelected) {
    ...
    } else {
    ...
    }

Because UITableView reuses the UITableViewCell so whatever you have changed will be applied to all.

Upvotes: 0

Related Questions