The button action is SongsSelectionSongs_Click. When I click this button, the button image changing, the button tap count is getting correct and after selected button images also changing, but when I scroll back and forth in the UITableView the button image seems to be randomly changing.
This is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"SongsTAbleViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SongsTAbleViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
btn_songSelect.tag = indexPath.row;
lbl_songLabel.text = [[arr_tablVArray objectAtIndex:indexPath.row] objectForKey:#"SongTitle"];
lbl_artistLabel.text = [[arr_tablVArray objectAtIndex:indexPath.row] objectForKey:#"SongArtist"];
return cell;
}
-(IBAction)SongsSelectionSongs_Click:(id)sender
{
UIButton *button = sender;
CGPoint correctedPoint = [button convertPoint:button.bounds.origin toView:self.tblv_SongslisttableView];
NSIndexPath *indexPath = [self.tblv_SongslisttableView indexPathForRowAtPoint:correctedPoint];
NSLog(#"Button tapped in row %d",indexPath.row);
SelectedAlbumUrl = [[arr_tablVArray objectAtIndex:indexPath.row] objectForKey:#"SongUrl"];
str_songtitle = [[arr_tablVArray objectAtIndex:indexPath.row] objectForKey:#"SongTitle"];
if ([[button backgroundImageForState:UIControlStateNormal] isEqual:[UIImage imageNamed:#"add.png"]])
{
btn_songsShareButton.userInteractionEnabled = YES;
[btn_songSelect setBackgroundImage:[UIImage imageNamed:#"remove.png"] forState:UIControlStateNormal];
buttonStatus = buttonStatus +1;
[btn_songsShareButton setImage:[UIImage imageNamed:#"share selected.png"] forState:UIControlStateNormal];
}
else
{
[btn_songSelect setBackgroundImage:[UIImage imageNamed:#"add.png"] forState:UIControlStateNormal];
buttonStatus = 1;
[btn_songsShareButton setImage:[UIImage imageNamed:#"share unselected.png"] forState:UIControlStateNormal];
}
}
You are not doing anything within your cellForRowAtIndexPath to select or deselect image. When you reuse a cell, it doesn't change the state of the cell unless you explicitly tell it to in cellForRow. Therefore, it will either reuse a selected or deselected cell (whatever is the first available reusable cell) and put that on the screen as-is.
To fix this issue, you need logic in your cellForRowAtIndexPath method to either select or deselect the image based on what is appropriate.
In general, if your problem has anything to do with "my cells don't show up right when scrolling" odds are you're not reusing your cells properly.
EDIT: in response to your comment, no, I will not rewrite your code. But I will give you some direction.
I would recommend keeping an additional key/value on your arr_tablVArray that will track whether or not the "share" should be enabled or disabled (I would suggest a bool value). This would make it so that you could check whether or not the "share" is enabled/disabled by checking a bool instead of checking the contents of the button's image in your IBAction method.
This info would now be available in your cellForRowAtIndexPath method as well, and you could check the value for the current record in arr_tablVArray and set your images accordingly just like you set your lbl_songLabel and lbl_artistLabel.
//Try It, it's Working Fine
pragma .h File
NSMutableArray * rowIdArray;
pragma .M File
#synthesize rowIdArray;
- (void)viewDidLoad
{
rowIdArray=[[NSMutableArray alloc]init];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [NamesArray count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
ViewControllerCell *cell = (ViewControllerCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
NSArray *nib;
nib = [[NSBundle mainBundle] loadNibNamed:#"ViewControllerCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
// Configure the cell...
cell.nameslbl.text = [NamesArray objectAtIndex:indexPath.row];
cell.nameBtn.tag=indexPath.row;
[cell.nameBtn addTarget:self action:#selector(NamesClick_Tapped:) forControlEvents:UIControlEventTouchUpInside];
NSString *a=[NSString stringWithFormat:#"%d", indexPath.row];
NSString *b=[[NSString alloc]init];
for (int i=0;i<[rowIdArray count];i++)
{
b=[rowIdArray objectAtIndex:i];
if ([a isEqualToString:b])
{
UIImage *buttonImage = [UIImage imageNamed:#"star_selected.png"];
[cell.nameBtn setBackgroundImage:buttonImage forState:UIControlStateNormal];
}
}
return cell;
}
-(IBAction)NamesClick_Tapped:(id)sender
{
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.NameTableView];
NSIndexPath *indexPath = [self.NameTableView indexPathForRowAtPoint:buttonPosition];
NSString *rowIdStr = [NSString stringWithFormat:#"%d", indexPath.row];
if(![rowIdArray containsObject:rowIdStr])
{
[rowIdArray addObject:rowIdStr];
}else{
[rowIdArray removeObject: rowIdStr];
}
[self.NameTableView reloadData];
}
when you reuse a cell where the button has been already set, the same button appears with the previously set image. Instead of creating a new button every time you need a cell, you should just be resetting the state of an existing button. This link might help you.
Related
I have a table view & a custom cell. the cell contains 3 buttons (check box type) . on button click the respective buttons text i need to change (check / uncheck).
I achieved this, but when i click 1st button on top cell and scroll down the new cell at the bottom also has this check mark, and when i scroll back to top the check mark is moved to next cell.. how to fix this??
code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *strCellIdentifier = #"RemoteCell";
RemoteCustomCell *cell = (RemoteCustomCell*)[tableView ![dequeueReusableCell][2]WithIdentifier:strCellIdentifier];
if (cell == nil) {
cell = [[RemoteCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strCellIdentifier];
}
else {
cell = [cell initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strCellIdentifier];
}
[cell.btnCheck1 addTarget:self action:#selector(CheckButton1_Click:) forControlEvents:UIControlEventTouchUpInside];
}
return cell;
}
- (void)CheckButton1_Click:(UIButton*)sender
{
RemoteControllCustomCell *clickedCell = (RemoteControllCustomCell *)[[sender superview] superview];
if(clickedCell.btnCheck1.selected)
{
[clickedCell.btnCheck1 setTitle:#"O" forState:UIControlStateNormal];
clickedCell.btnCheck1.selected = NO;
}
else
{
[clickedCell.btnCheck1 setTitle:#"X" forState:UIControlStateSelected];
clickedCell.btnCheck1.selected = YES;
}
}
screenshot:
In your RemoteCustomCell.m file you should implement
- (void)prepareForReuse
{
[super prepareForReuse];
cell.btnCheck1.selected = NO;
}
This way every cell that is reused will have it's btnCheck1.selected value set to NO, and when you load your cell in cellForRowAtIndexPath it will only set it to YES when the cell comes visible and you set it to that.
But it is key to store all your values in an NSMutableArray. There is no such thing as storing your values in the cells only, they get reused on a basis that can not be foreseen. Add your values to the array and use [myArray objectAtIndex:indexPath.row]; to open those values in a cell.
An example:
Somewhere in viewDidLoad
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:#"1", #"0", #"1", #"1", nil];
In your cellForRowAtIndexPath
BOOL yesOrNo = [[myArray objectAtIndex:indexPath.row] boolValue];
And then set your button.selected to the boolean.
This is a typical issue, where you are relying on the UI to the job of your model. The model, the thing that you should pass to your UITableViewCell, so it can be built, would tell it, if it should be displaying an "X" or an "O". Since you are not doing this, the easiest solution, would be to simply reset the state of the cell everytime it gets dequeued.
I think you need to store the state in a array and check the state in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
CheckButton1_Click do change the state, but when dequeueReusableCell , it loads from cellForRowAtIndexPath again.
It seems like you have dequeue of cell issue. You may implement cellForRowAtIndexPath method as below.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"RemoteCell";
RemoteCustomCell *cell = (RemoteCustomCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell==nil) {
NSArray *arrNib=[[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
cell= (RemoteCustomCell *)[arrNib objectAtIndex:0];
}
[cell.btnCheck1 addTarget:self action:#selector(CheckButton1_Click:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
I realize this is a common problem but I'm just not getting it. I have a button that when pressed on a cell, changes to a check mark. However, when pressed it changes other reused cell's image to a check mark as well. I'm using a cell's delegate to manipulate local data on the tableView's VC.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Configure the cell...
static NSString *ReusableIdentifier = #"Cell";
SetListTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ReusableIdentifier forIndexPath:indexPath];
cell.delegate = self;
if ([self.selectedRows containsIndex:indexPath.row]) {
[cell.plusButton setBackgroundImage:checkImage forState:UIControlStateNormal];
}
else {
[cell.plusButton setBackgroundImage:plusImage forState:UIControlStateNormal];
}
cell.tag = indexPath.Row
return cell;
}
Method in custom cell's class.
- (IBAction)addSongButtonPressed:(UIButton *)sender
{
[self.delegate addSongButtonPressedOnCell:self];
UIImage *checkImage = [UIImage imageNamed:#"check.png"];
[self.plusButton setBackgroundImage:checkImage forState:UIControlStateNormal];
}
Method from the cell's delegate.
-(void)addSongButtonPressedOnCell:(id)sender
{
NSInteger index = ((UIButton *)sender).tag;
NSMutableDictionary *track = [self.searchTracks objectAtIndex:indexPath];
[self.selectedRows addIndex:indexPath];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1; //count of section
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 30; //count number of row from counting array hear cataGorry is An Array
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *testword=#"pass";
if ([testword isEqualToString:#"pass"]) {
static NSString *MyIdentifier = #"cell1";
TextTableViewCell *cell ;
cell= [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
cell.lblText.text=[NSString stringWithFormat:#"textcelll= %i", indexPath.row+1];
cell.btnTextbox.tag=indexPath.row;
[cell.btnTextbox addTarget:self action:#selector(customActionPressed:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}else{
static NSString *MyIdentifier1 = #"cell2";
ImageTableViewCell *cell1 = [tableView dequeueReusableCellWithIdentifier:MyIdentifier1];
return cell1;
}
}
-(void)customActionPressed:(UIButton*)sender{
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView1];
NSIndexPath *indexPath = [self.tableView1 indexPathForRowAtPoint:buttonPosition];
TextTableViewCell *cell = (TextTableViewCell*)[self.tableView1 cellForRowAtIndexPath:indexPath];
if (indexPath != nil)
{
int currentIndex = indexPath.row;
NSLog(#"currentIndex == %d",currentIndex);
int tableSection = indexPath.section;
NSLog(#"tableSection == %d",tableSection);
}
if (!cell.btnTextbox.isSelected ){
cell.btnTextbox.selected=YES;
[cell.btnTextbox setImage:[UIImage imageNamed:#"checkClick.png"] forState:UIControlStateNormal];
NSLog(#"button tag %i",cell.btnTextbox.tag);
NSLog(#"check click");
}else{
cell.btnTextbox.selected=NO;
[cell.btnTextbox setImage:[UIImage imageNamed:#"check.png"] forState:UIControlStateNormal];
NSLog(#"check ");
}
in simulator , when i am clicked the button in first row (indexpath.row=0) then i am scrolling tableview, button click will auto display in 7th row (indexpath.row=6)
Question is ,i want to know , what happened in really and how to avoid this (when i'm scrolling)?
Since cells are being reused, you have to (in your cellForRowAtIndexPath) to set image to one state or another (to set one image or another).
What really happen is that you set Image for one cell but that cell is being reused through whole table. You use only 5(lets say that is how much you cells can fit into one screen) cells and when you load next you actually reuse the one with an image.
So, in your cellForRowAtIndexPath you will have to check if the button state is selected or not and then assign appropriate image.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *testword=#"pass";
if ([testword isEqualToString:#"pass"]) {
static NSString *MyIdentifier = #"cell1";
TextTableViewCell *cell ;
cell= [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
cell.lblText.text=[NSString stringWithFormat:#"textcelll= %i", indexPath.row+1];
cell.btnTextbox.tag=indexPath.row;
if (cell.btnTextbox.isSelected ){
[cell.btnTextbox setImage:[UIImage imageNamed:#"checkClick.png"] forState:UIControlStateNormal];
}else{
[cell.btnTextbox setImage:[UIImage imageNamed:#"check.png"] forState:UIControlStateNormal];
}
[cell.btnTextbox addTarget:self action:#selector(customActionPressed:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}else{
static NSString *MyIdentifier1 = #"cell2";
ImageTableViewCell *cell1 = [tableView dequeueReusableCellWithIdentifier:MyIdentifier1];
return cell1;
}
}
For completness sake, I would also suggest you to use Accessory type as well as Xib files for these kind of cells. You can set it in storyboard under accessory dropdown or through code:
cell.accessoryType = UITableViewCellAccessoryCheckmark;
I need to implement this functionality.please help me.
After "unblockbtnClick" click change the image ,it's fine ,but when scroll the tableview the selected images all are changing to previous images.
Here is my code.
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"BlockedCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil)
{
NSArray *nib;
nib = [[NSBundle mainBundle] loadNibNamed:#"BlockedCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
[btn_tblvcAddbtn addTarget:self action:#selector(unblockbtnClick:) forControlEvents:UIControlEventTouchUpInside];
[btn_tblvcAddbtn setTag:indexPath.row];
return cell;
}
-(IBAction)unblockbtnClick:(UIButton *)sender{
NSLog(#"Value of selected button = %ld",(long)[sender tag]);
[sender setBackgroundImage:[UIImage imageNamed:#"remove.png"] forState:UIControlStateNormal];
}
You need to assign the nib file outside if condition
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil)
{
// initialize cell here
}
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"BlockedCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
-(IBAction)unblockbtnClick:(id)sender
{
UIButton *btn=(UIButton *)sender;
UITableViewCell *cellll;
if([[[UIDevice currentDevice] systemVersion] intValue]<6)
cellll=(UITableViewCell *)[btn superview];
else
cellll=(UITableViewCell *)[[btn superview]superview];
NSIndexPath *aaa=[tblOption indexPathForCell:cellll];
if([ArrSelectedData containsObject:[TblDataArrr objectAtIndex:aaa.row]])
{
//tblDataArr is table dataArray && ArrSelectedData is a array which contain selected itme of table
[ArrSelectedData removeObject:[TblDataArrr objectAtIndex:aaa.row]];
[btn setBackgroundImage:[UIImage imageNamed:#"blank_checkbox.png"] forState:UIControlStateNormal];
}
else
{
[ArrSelectedData addObject:[TblDataArrr objectAtIndex:aaa.row]];
[btn setBackgroundImage:[UIImage imageNamed:#"tick_checkbox.png"] forState:UIControlStateNormal];
}
}
and this in cellForRowAtIndexPath
{
if([ArrSelectedData containsObject:[TblDataArrr objectAtIndex:indexPath.row]])
[tempBtn setBackgroundImage:[UIImage imageNamed:#"tick_checkbox.png"] forState:UIControlStateNormal];
else
[tempBtn setBackgroundImage:[UIImage imageNamed:#"blank_checkbox.png"] forState:UIControlStateNormal];
}
When you're scrolling a cell out of the screen and then back - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method is called. You're not setting the background there, this is why it gets reset.
To fix you need to store information about selected cell and check it in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath and apply proper background accordingly.
I have a tableview and I am setting its cells in xib. I have a button in it and when I click it, it changes button color from red to green. When I scroll tableview and come back to green button, it returns to red again. Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"ProgramCell";
ProgramCell *cell = (ProgramCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ProgramCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
[cell.btnHatirlat addTarget:self action:#selector(remind:)
forControlEvents:UIControlEventTouchUpInside];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell.contentView setUserInteractionEnabled: NO];
return cell;
}
- (void) remind:(UIButton *)sender {
UIButton *btnTemp = (UIButton *)sender;
int iTag = btnTemp.tag;
// I want to change image of sender
[btnTemp setBackgroundColor:[UIColor greenColor]];
}
EDIT: I have added my solution as an answer. I hope it will help.
I'd do this:
The Tableview doesn't know how to "remember" the button state when it reuses the cell.
I'd create a NSMutableArray which holds a BOOL (or any other) flag wether the button is "on" or "off". Init that Array with all "off" if you like and then change the value at the index corresponding with the indexPath.row accordingly on click to "on".
Read the value in cellForRowAtIndexPath and change the color accordingly.
If you got multiple sections, create an Array for each section and let it hold an Array for the respective number of cells.
If that tableView is backed up by a DB add a flag for the state (on/off), and save it. Out of my head I don't know wether this requires a reloadData for everything to work as expected.
You need store pressed button's cell index.
Try this->
- (void)remind:(UIButton *)sender {
UIButton *btnTemp = (UIButton *)sender;
self.iTag = btnTemp.tag;
/*For many buttons
[btnTemp setSelected:![btnTemp isSelected]];
self.buttonStates[btnTemp.tag] = #([btnTemp isSelected);
*/
// I want to change image of sender
[btnTemp setBackgroundColor:[UIColor greenColor]];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"ProgramCell";
ProgramCell *cell = (ProgramCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ProgramCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
if (self.iTag == indexPath.row) {
[cell.btnHatirlat setBackgroundColor:[UIColor greenColor]];
}
/*For many buttons
[cell.btnHatirlat setSelected:[self.buttonStates[indexPath.row] boolValue]];
if ([cell.btnHatirlat isSelected]) {
[cell.btnHatirlat setBackgroundColor:[UIColor greenColor]];
}
*/
[cell.btnHatirlat addTarget:self action:#selector(remind:)
forControlEvents:UIControlEventTouchUpInside];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell.contentView setUserInteractionEnabled: NO];
return cell;
}
Good luck!
Here is my solution and I want to thank again to #Bernard Grabowski and #MobiDevCom. I have saved clicked button's section and row number and checked if it is clicked or not.
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"ProgramCell";
ProgramCell *cell = (ProgramCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ProgramCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSString* strTemp = [NSString stringWithFormat: #"%d/%d", indexPath.section, indexPath.row ];
//To check clicked button at current section and row is clicked
for (int i = 0; i < buttonClickedArray.count; i++)
{
if([[self.buttonClickedArray objectAtIndex:i] isEqualToString:strTemp])
[cell.btnHatirlat setBackgroundColor:[UIColor greenColor]];
}
NSString *str = [[etkinlikArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
[cell.btnHatirlat addTarget:self action:#selector(remind:)
forControlEvents:UIControlEventTouchUpInside ];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell.contentView setUserInteractionEnabled: NO];
}
return cell;
}
- (void) remind:(UIButton *)sender
{
//to get clicked button's indexpath
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath *clickedButtonPath = [self.tableProgram indexPathForCell:clickedCell];
self.currentIndexPathStr = [NSString stringWithFormat: #"%d/%d", clickedButtonPath.section, clickedButtonPath.row];
[self.buttonClickedArray addObject:currentIndexPathStr];
[btnTemp setBackgroundColor:[UIColor greenColor]];
}