I Have a problem with my custom UItableViewCell. It's seems that all UIButton of UITableViewCell disappear on scroll. Specilay on my 25 cell. Any one have any idea ?
any help would be appreciated :)
That some code :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString* cellIdentifier = #"Cell";
Cell * __strong cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell){
cell = [[Cell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
InfoObject __weak *obj = [_pos objectAtIndex:indexPath.row];
[cell configCell];
cell.id = obj.id;
{// EVENT ON BUTTON
[cell.checkbox addTarget:self action:#selector(checkup:) forControlEvents:UIControlEventTouchUpInside];
[cell.btnPaymentState addTarget:self action:#selector(changePaymentState:) forControlEvents:UIControlEventTouchUpInside];
}
{// SET BUTTON CHECKBOX
cell.checkbox.tag = indexPath.row;
[cell.checkbox setImage:[UIImage imageNamed:IMG_BLACK_UNCHECKBOX] forState:UIControlStateNormal];
[cell.checkbox setImage:[UIImage imageNamed:IMG_BLACK_CHECKBOX] forState:UIControlStateSelected];
[cell.checkbox setImage:[UIImage imageNamed:IMG_BLACK_CHECKBOX] forState:UIControlStateHighlighted];
}
{// SET BUTTON PAYMENT STATE
cell.btnPaymentState.tag = indexPath.row;
[cell.btnPaymentState setImage:[self getImageAssociateWithName:obj.stringPaymentState] forState:UIControlStateNormal];
[cell.btnPaymentState setImage:[self getImageAssociateWithName:obj.stringPaymentState] forState:UIControlStateSelected];
[cell.btnPaymentState setImage:[self getImageAssociateWithName:obj.stringPaymentState] forState:UIControlStateHighlighted];
}
[self SetSelectedBackgroundColorSelectedForFolderCellAtRow:indexPath.row
:obj.selected Cell: cell];
[cell.checkbox setSelected:obj.selected];
[self setCellColorTextOfObjectPrepare:cell withObj:obj];
return cell;
}
I solve my problem. The problem is that i was using Tag for my UIButton and the identifier was the same number tag of another UIView so when i was removing my UIView the UIButton disappear also. Thanks all for your answer.
Remove if (!cell)
And you're good to go.
Related
I am trying to create a custom cell with favorite button on it. Its working fine but I think reuse identifier is creating problem here. I have tried it in two ways and it is working fine in both.
Here is my first try:
When I am selecting the button, it is changing to yellow button and vice -versa. But after scrolling up or down,it is changing to its normal state again. Why?
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CI=#"CELL";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:CI];
if(!cell) {
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CI];
}
favbtn=[[UIButton alloc]initWithFrame:CGRectMake(160, 10, 30, 35)];
[cell.contentView addSubview:favbtn];
[favbtn setBackgroundImage:[UIImage imageNamed:#"starr.png"] forState:UIControlStateNormal];
[favbtn addTarget:self action:#selector(fav:) forControlEvents:UIControlEventTouchUpInside];
cell.textLabel.text=[favdata objectAtIndex:indexPath.row];
return cell;
}
-(void)fav:(id)sender
{
if ([sender isSelected]) {
[sender setImage:[UIImage imageNamed:#"starr.png"] forState:UIControlStateNormal];
[sender setSelected:NO];
} else {
[sender setImage:[UIImage imageNamed:#"star.png"] forState:UIControlStateSelected];
[sender setSelected:YES];
}
}
Here is my second try:
Here every sixth cell is showing the same state.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CI=#"CELL";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:CI];
if(!cell) {
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CI];
favbtn=[[UIButton alloc]initWithFrame:CGRectMake(160, 10, 30, 35)];
[cell.contentView addSubview:favbtn];
[favbtn setBackgroundImage:[UIImage imageNamed:#"starr.png"] forState:UIControlStateNormal];
[favbtn addTarget:self action:#selector(fav:) forControlEvents:UIControlEventTouchUpInside];
cell.textLabel.text=[favdata objectAtIndex:indexPath.row];
}
return cell;
}
First of all modify this methods:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CI=#"CELL";
UIButton * favbtn;
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:CI];
if(!cell)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CI];
favbtn=[[UIButton alloc]initWithFrame:CGRectMake(160, 10, 30, 35)];
[cell.contentView addSubview:favbtn];
[favbtn setTag:11221133];
}
favbtn = [cell.contentView viewWithTag:11221133];
[favbtn setBackgroundImage:[UIImage imageNamed:#"starr.png"] forState:UIControlStateNormal];
[favbtn setImage:[UIImage imageNamed:#"starr.png"] forState:UIControlStateNormal];
[favbtn setImage:[UIImage imageNamed:#"star.png"] forState: UIControlStateSelected];
//Check selections here and if this index is selected make btn selected.
[favbtn addTarget:self action:#selector(fav:) forControlEvents:UIControlEventTouchUpInside];
cell.textLabel.text=[favdata objectAtIndex:indexPath.row];
cell.contentView.tag = indexPath.row;
return cell;
}
-(void)fav:(id)sender
{
UIButton * btn = (UIButton *)sender;
btn.selected = !btn.selected;
int index = [[btn superview] tag]; // use this index for storing selections
}
Then make a array for storing selections. Compare this selection in cellForRowAtIndexPath and make btn selected or not selected.
Hope this will help you.....
You need to understand the way the cell is being reused. When your cell is scrolled out of the screen, it will be reused for the cell that is entering the screen. Table view will keep calling the method cellForRowAtIndexPath every time they want to render the cell. In your code, you init a new favbtn all the time, so even if the cell already contains it and being reused, you will keep on adding more buttons into it. Here's my suggestions:
Create your own subclass of UITableViewCell. Let's say FavoriteCell. Put all your code to add the favorite button inside your subclass.
In ViewDidLoad register your cell with your table view. Something like this: [self.tableView registerClass:...] or [self.tableView registerNib:...] depending on if you have a nib file for your cell or not.
In cellForRowAtIndexPath, dequeue your cell as usual but you don't have to check if it's nil or not, and you don't have to create the favorite button since you already put it inside your subclass.
Just remember that, the method cellForRowAtIndexPath will be called all the time so your logic of inserting favorite button has to be done properly. Make sure that you only insert it once and update its property properly.
Following is a code fragment from cellForRowAtIndexPath. I have added 2 buttons on a Cell.
When the user clicks on Button A the image of Button A should be a.png. And Button B should change to default.png.
When the user clicks on Button B the image of Button B should be b.png. And Button A should change to default.png.
How can i do this ?
cellForRowAtIndexPath Method
[cell.aButton addTarget:self action:#selector(methodA:) forControlEvents:UIControlEventTouchUpInside];
[cell.bButton addTarget:self action:#selector(methodB:) forControlEvents:UIControlEventTouchUpInside];
- (void) methodA:(id) sender {
[((UIButton *)sender) setImage:[UIImage imageNamed:#"a.png"] forState:UIControlStateSelected];
// How to change the image of Button B to default.png ??
}
- (void) methodB:(id) sender {
[((UIButton *)sender) setImage:[UIImage imageNamed:#"b.png"] forState:UIControlStateSelected];
// How to change the image of Button A to default.png ??
}
If you have set tag to your buttons like aButton has tag= 1, and bButton has tag= 2
- (void) methodB:(id) sender {
UIButton *bButton =((UIButton *)sender) ;
[bButton setImage:[UIImage imageNamed:#"b.png"] forState:UIControlStateSelected];
UIButton *aButton = (UIButton *)[bButton.superview viewWithTag:bButton.tag-1];
[aButton setImage:[UIImage imageNamed:#"default.png"] forState:UIControlStateSelected];
}
- (void) methodA:(id) sender {
UIButton *aButton =((UIButton *)sender) ;
[aButton setImage:[UIImage imageNamed:#"a.png"] forState:UIControlStateSelected];
UIButton *bButton = (UIButton *)[bButton.superview viewWithTag:aButton.tag+1]; //updated here, instead of bButton, put aButton
[bButton setImage:[UIImage imageNamed:#"default.png"] forState:UIControlStateSelected];
}
Enjoy Coding !!
Try this,
- (void) methodA:(id) sender {
UIButton *buttonA = (UIButton *)sender;
CGPoint pointInTable = [field convertPoint:buttonA.bounds.origin toView:_tableView];
NSIndexPath *indexPath = [_tableView indexPathForRowAtPoint:pointInTable];
UITableViewCell *cell = [_tableView cellForRowAtIndexPath:nowIndex];
//now access the button and change button images
//cell.aButton setImage:
//cell.bButton setImage:
}
Do the same for button B action.
Note: I recommend using custom tableview cell and handling this button actions inside the custom cell.
For both the buttons, set a.png and default.png for UIControlStateNormal and UIControlStateSelected respectively
Now you can simply make the button selected and deselected accordingly and donot worry about the image.
Hope this image gives you another hint.
To handle your selection issue, the best way is to handle it in your tableViewCell class, where you can access both the buttons and write a delegate or callback if required.
Try this...
cellForRowAtIndexPath
[cell.aButton addTarget:self action:#selector(methodToggle:) forControlEvents:UIControlEventTouchUpInside];
[cell.bButton addTarget:self action:#selector(methodToggle:) forControlEvents:UIControlEventTouchUpInside];
[cell.aButton setImage:[UIImage imageNamed:#"default.png"] forState:UIControlStateNormal];
[cell.bButton setImage:[UIImage imageNamed:#"default.png"] forState:UIControlStateNormal];
[cell.aButton setImage:[UIImage imageNamed:#"a.png"] forState:UIControlStateSelected];
[cell.bButton setImage:[UIImage imageNamed:#"b.png"] forState:UIControlStateSelected];
cell.aButton.tag == 111;
cell.aButton.tag == 222;
Just One method for both Buttons
- (void) methodToggle:(id) sender {
UIButton * btn = (UIButton *)sender;
if (btn.tag == 111)
{
UIButton * btnAnother = (UIButton *)[[btn superview] viewWithTag:222];
btnAnother.selected = NO;
}
if (btn.tag == 222)
{
UIButton * btnAnother = (UIButton *)[[btn superview] viewWithTag:111];
btnAnother.selected = NO;
}
btn.selected = YES;
}
Inside tableView:cellForRowAtIndexPath: add following code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
[cell.aButton setTag:1];
[cell.aButton addTarget:self action:#selector(methodA:) forControlEvents:UIControlEventTouchUpInside];
[cell.aButton setTag:2];
[cell.bButton addTarget:self action:#selector(methodB:) forControlEvents:UIControlEventTouchUpInside];
}
Implement the button event handlers as-
- (void) methodA:(id) sender
{
UIView *view = [sender superView];
UIButton *senderButton = (UIButton *)sender;
[senderButton setImage:[UIImage imageNamed:#"a.png"] forState:UIControlStateSelected];
UIButton *otherButton = [view viewWithTag:2];
[otherButton setImage:[UIImage imageNamed:#"default.png"] forState:UIControlStateNormal];
}
- (void) methodB:(id) sender
{
UIView * view = [sender superView];
UIButton *senderButton = (UIButton *)sender;
[senderButton setImage:[UIImage imageNamed:#"b.png"] forState:UIControlStateSelected];
UIButton *otherButton = [view viewWithTag:1];
[otherButton setImage:[UIImage imageNamed:#"default.png"] forState:UIControlStateNormal];
}
Write a CustomCell class which has 2 buttons with target methods and delegate method to notify the table view when buttons clicked. Use this cell in the table view.
In target methods do the image changes(A/B) and fire the delegate method to notify the tableViewController.
You can keep indexpath as a property of cell and pass it with the delegate to tableView.(Set the indexpath for the cell in tableView's cellForRowAtIndexPath:)Then you can identify which cell's button was clicked and relevant data object can fetch from your data source.
CustomCell.m
- (IBAction)onButtonAClicked:(id)sender {
// change your images
[delegate cellButtonClicked:self.indexpath];
}
TableViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
cell.delegate = self;
}
cell.indexpath = indexPath;
}
//delegate method
-(void)cellButtonClicked:(NSIndexPath *)indexPath{
//get the related data object for the cell using indexPath from your data source
}
I have searched all over the web and cant seem to find a solution to this simple problem. I have a table view of buttons when the work "like". When the Button is pressed, it changes the word to "Unlike". I got it to work but when I scroll down the table, I see other buttosn also change to "unlike" and sometimes overlaps it with "Like". And when I scroll back up, the original button I selected changes back to normal state. I understand the cells are reusable and thats why I am using a mutable array for my data source and still it doesnt work. Please help!
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
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];
cell.textLabel.text = [recipes objectAtIndex:indexPath.row];
return cell;
}
the action method:
-(void)tapped:(id)sender {
UIButton *senderButton = (UIButton *)sender;
UITableViewCell *parentCell = [[sender superview]superview];
NSIndexPath *indexPathLiked = [table indexPathForCell:parentCell];
[array replaceObjectAtIndex:senderButton.tag withObject:[NSNumber numberWithInt:1]];
[sender setTitle:#"Unlike" forState:UIControlStateNormal];
}
If the table calls the cellForRowAtIndexPath you create all the time a new button. When you get a reused cell the button still exists and you put a new one over there.
Change your method to:
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
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];
}
else {
// todo: change the button title to "like" or "unliked" value
}
cell.textLabel.text = [recipes objectAtIndex:indexPath.row];
P.S. This make no sense, you doesn't use that, why you do that?
UITableViewCell *parentCell = [[sender superview]superview];
NSIndexPath *indexPathLiked = [table indexPathForCell:parentCell];
If you have only one section you can get the cell without using superview:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0]
UITableViewCell *parentCell = [table cellForRowAtIndexPath:indexPath];
my viewController contains a tableView where the user can add cells via coreData.
I defined the cells:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Travel";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSManagedObject *travel = [self.travelling objectAtIndex:indexPath.row];
UILabel *countryLabel = (UILabel *)[cell viewWithTag:101];
kategorieLabel.text = [travel valueForKey:#"country"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setBackgroundImage:[UIImage imageNamed:#"button.png"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(15.0f, 32.0f, 24.0f, 20.0f);
[cell addSubview:button];
if ([[travel valueForKey:#"tick"] isEqualToString:#"yes"]) {
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}else{
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
return cell;
}
- (void)buttonTapped:(UIButton *)sender {
UITableViewCell *owningCell = (UITableViewCell*)[sender superview];
[owningCell setAccessoryType:UITableViewCellAccessoryCheckmark];
NSIndexPath *indexPath = [_tableView indexPathForCell:owningCell];
NSManagedObject *travel = [self.travelling objectAtIndex:indexPath.row];
[travel setValue:#"yes" forKey:#"tick"];
}
When the user taps a button on the cell, the checkmark should appear. But when I reload or restart the app not all checkmarks are there. Maybe there is a better method to do this.
I have a UITableView with 20 rows. In each row, two UIButtons are added, so totally I have 40 buttons. How can I access each button in each cell? All those UIButton have the two tags 1 and 2.
For example: I want to write a method to change the background color of 2 buttons in a particular row:
-(void)changeColor:(int)row{
//code here
}
Do you have any suggestion?
-(UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"Cell"] autorelease];
UIButton *button1=[[UIButton alloc] initWithFrame:CGRectMake(x1,y1,w1,h1)];
UIButton *button2=[[UIButton alloc] initWithFrame:CGRectMake(x2,y2,w2,h2)];
[button1 setTag:1];
[button2 setTag:2];
[[cell contentView] addSubview:button0 ];
[[cell contentView] addSubview:button1 ];
[button0 release];
[button1 release];
}
UIButton *button1 = (UIButton *)[cell viewWithTag:1];
UIButton *button2 = (UIButton *)[cell viewWithTag:2];
return cell;
}
You should be able to do this:
-changeColor:(int)row
{
NSIndexPath indexPath = [NSIndexPath indexPathForRow:row inSection:0]; // Assuming one section
UITableViewCell *cell = [myTableView cellForRowAtIndexPath:indexPath];
UIButton *button1 = (UIButton *)[[cell contentView] viewWithTag:1];
UIButton *button2 = (UIButton *)[[cell contentView] viewWithTag:2];
}
*some syntax might be wrong. I dont have Xcode handy
If you want to change the button color to have a highlighted / downstate I suggest to use the following:
[yourButton addTarget:self action:#selector(goToView:) forControlEvents:UIControlEventTouchUpInside];
[yourButton addTarget:self action:#selector(changeColor:) forControlEvents:UIControlEventTouchDown];
[yourButton addTarget:self action:#selector(touchCancel:) forControlEvents:UIControlEventTouchDragExit];
-(void)buttonAction:(UIButton*)sender
{
[self touchCancel:sender];
/* DO SOME MORE ACTIONS */
}
-(void)changeColor:(UIButton*)sender
{
sender.backgroundColor = [UIColor redColor];
}
-(void)touchCancel:(UIButton*)sender
{
sender.backgroundColor = [UIColor clearColor];
}
If you want to make the button have a different selected color, create an array that holds all the selected states. Then use the array to check if the button color needs a different color in cellForRowAtIndexPath.
When you want to change the button color just change the value in the array and call
[self.tableView reloadData];
Be sure to do it outside the if (cell == nil) method so it is also called when the cell is reused.