I have a UITableViewCell with 3 checkmark buttons. Each checkmark button is a custom UIButton (just the images really), and have an outlet in the custom UITableViewCell subclass. In the cellForRowAtIndexPath method, I
[cell.firstcheckmark addTarget:self action:#selector(checkmarkPressed:) forControlEvents:UIControlEventTouchUpInside];
[cell.secondcheckmark addTarget:self action:#selector(checkmarkPressed:) forControlEvents:UIControlEventTouchUpInside];
[cell.thirdcheckmark addTarget:self action:#selector(checkmarkPressed:) forControlEvents:UIControlEventTouchUpInside];
- (void)checkmarkPressed:(id)sender {
// how do I get which checkmark was pressed?
}
I was not sure how to get which button was pressed from the sender object. Other times I can use the title of the button, but in this case, all the buttons are the same except their outlet. How do I get which button was pressed? Or do they each need to call a different method, and then in each of those three methods, I can update my model by calling the same modelUpdateMethod: from each of those three methods? Thanks.
You can identify them by their tag:
cell.firstcheckmark.tag = 0;
And then
- (void)checkmarkPressed:(id)sender {
UIButton *button = (UIButton*)sender;
if(button.tag == 0)
{
...
}
...
}
i think you can also use :
UITableViewCell *cell = (UITableViewCell *)[[sender superview] superview];
it can get which cell the button in.
Related
In my app I am facing with a really weird problem. I am trying to solve it for hours but could not find what is the problem so far.
Well, I have a tableview with custom cells. and each cell I have a button. It is is like an empty box. When the button pressed, I want to change the related cell image of that button. Actually I successfully do that but somehow, some other cell's button images are also changing and I could not figure out why. Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
//create new cell
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[cell.tickButton setTag:indexPath.row];
[cell.tickButton addTarget:self action:#selector(buttonWasPressed:)forControlEvents:UIControlEventTouchUpInside];
//cell has 2 label so I filled them with the contents of my object array that part is working with no problem
cell.leftLabel.text = [NSString stringWithFormat:#"%#", [[contacts objectAtIndex:indexPath.row]name]];
cell.rightLabel.text = [NSString stringWithFormat:#"%#", [[contacts objectAtIndex:indexPath.row]email]];
return cell;
}
in my buttonWasPressed method I simply do:
-(IBAction)buttonWasPressed:(id)sender
{
UIButton *button = (UIButton *)sender;
[button setImage:[UIImage imageNamed:#"ticck.jpeg"] forState:UIControlStateNormal];
}
As I said before it works but also changes some other buttons images and also when I scroll down and get back to the initial position I see that some button images are changed.
You can access that cell and can change the button image as :
-(IBAction)buttonWasPressed:(id)sender
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:your_row inSection:your_section];
UITableViewCell *currentCell = [table cellForRowAtIndexPath:indexPath];
UIButton *button = (UIButton *)[currentCell viewWithTag:(UIButton *)sender.tag];
[button setImage:[UIImage imageNamed:#"ticck.jpeg"] forState:UIControlStateNormal];
}
Hope it helps you.
You can deal with touch within CustomCell class. Just move method -(IBAction)buttonWasPressed:(id)sender within your custom class and add
tickButton addTarget:selfaction:#selector(buttonWasPressed:)forControlEvents:UIControlEventTouchUpInside in init method.
The problem is calling
[cell.tickButton addTarget:self action:#selector(buttonWasPressed:)forControlEvents:UIControlEventTouchUpInside];
method.
You add target everytime when your cells are reused and remove never. So your buttons have many targets and action method get called multiple times respectively. It may cause a memory leak.
Just put the addTarget method in if clause.
It will solve your one problem, which is your method get called multiple times. But when the cell is reused the button has the changed image. You need to set it back.
The best way to do that, you add a property to the contact object which are preseted in cells and set a value when the action method is fired. Then in cellForRowAtIndexPath: method you can set the image of the button by checking that property.
Do this
NSString *CellIdentifier = [NSString stringWithFormat:#"cell%d",indexPath.row];
While you set the default image to button also do this
[button setImage:[UIImage imageNamed:#"ticck.jpeg"] forState:UIControlStateSelected];
In your button action method do this:
-(IBAction)buttonWasPressed:(id)sender
{
UIButton *button = (UIButton *)sender;
if(button.selected==NO) {
[button setSelected:YES];
}
}
Hope this helps.
To reuse your cell, you have to make it clean after dequeueing. In this case, you should reset your image first:
[cell.tickButton setImage:nil forState:UIControlStateNormal];
Before assigning anything to your cell.
You must save button's tag which was pressed than try this
if(pressed[cell.tickButton.tag])
[button setImage:[UIImage imageNamed:#"ticck.jpeg"] forState:UIControlStateNormal];
else
[cell.tickButton setImage:nil forState:UIControlStateNormal];
I guess you are giving the default image to your button inside the CustomCell class. This problem is very common in UITableviews.
All you need to do is keep track of the cells for which the button has been pressed. Do all this inside the cellForRowAtIndexPath method.
For example:
update the code inside cellForRowAtIndexPath method
if (cell == nil)
{
//create new cell
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if(yourCondition==YES){
change the button image to the special image.
}
else{
keep the button image as default image.
}
return cell;
To keep track of the cells you can store the index path of the cells(whose button has been tapped) in an array. Inside cellForRowAtIndexPath check if the current indexPath is present inside your array or not
I have my custom uitableviewcell. It has the uibutton, uibutton with action. So how do I know indexPath of the row with pressed uibutton??
Target: uibuttons on each rows has an image. So when user taps the button it shows the alertview and if answer is "yes" uibutton's image changes.
Try below code snippet:
In cellForRowAtIndexPath method. set indexpath.row as button tag
[cellButton addTarget:self action:#selector(cellBottonClicked:)
forControlEvents:UIControlEventTouchUpInside];
cellButton.tag = indexPath.row;
-(IBAction) cellBottonClicked:(id)sender{
NSInteger row = [sender tag];
//you can get indexpath from row
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
CustomTableViewCell *cell = (CustomTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
// from this cell you can fetch and change image of selected cell's button.
}
[EDIT : Change Only image of Button]
If you only required to change Image of Button on click event.then I think, you do not need for indexpath or cell.
try this code. Hope it will helps:
-(IBAction) cellBottonClicked:(id)sender{
UIButton *btn = (UIButton *)sender;
[btn setImage:yourImage forState:UIControlStateNormal];
}
You should also be able to get the indexPath by:
NSIndexPath *indexPathForButton = [self.tableView indexPathForSelectedRow];
or in your .h file, add:
#property (nonatomic, retain) NSIndexPath* indexPathForButton;
and change the indexPath, whenever it's used. (self.indexPathForButton = indexPath;)
But using tags is much comfortable…
You can assign the row of the current cell as the tag of the UIButton. On your target/action, you can check for the tag of the button to find the row in which it was pressed.
I am trying to create a UITableView with a custom UIButton in each table cell. I implemented like this..
#implementation CouponDetailsCustomTableViewCell
...............
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
[self setBackgroundColor:[UIColor whiteColor]];
CGRect frame = self.contentView.frame;
self.radioButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.radioButton setImage:[UIImage imageNamed:#"radio_blank.png"] forState:UIControlStateNormal];
[self.radioButton setImage:[UIImage imageNamed:#"radio_selected"] forState:UIControlStateSelected];
[self.radioButton setFrame:CGRectMake(16, 10, 29, 29)];
[self.radioButton addTarget:nil action:#selector(radioButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:radioButton];
}
#end
and UITableView Delegate as......
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *COUPON_CELL_ID = #"CouponCell" ;
CouponDetailsCustomTableViewCell * couponCell = (CouponDetailsCustomTableViewCell *) [tableView dequeueReusableCellWithIdentifier:COUPON_CELL_ID];
if (couponCell == nil) {
couponCell = [[[CouponDetailsCustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:COUPON_CELL_ID] autorelease];
couponCell.selectionStyle = UITableViewCellSelectionStyleNone;
}
[couponCell.radioButton setSelected:NO];
return couponCell;
}
and the radioButtonPressed method is
-(void)radioButtonPressed:(id) sender
{
[sender setSelected:YES];
}
Now i run the program and a custom UIButton is shown in every table row . If i click on a button it gets selected (shows the radio_selected.png).
The problem arises when i scroll down the table (i am showing only 4 rows of the table in the window) . When i scroll up again..what i see is the clicked button is showing the radio_blank.png.
I am new in iPhone development. i dont know why is this happening. The most i can guess is the button property is changing .. setSelected:NO.
Someone please give me suggestions to fix this problem.
Thank you.
When you scroll your UITableView, hidden cells are not rendered anymore and might be reused for cells that are becoming visible. If a new cell becomes visible, tableView:cellForRowAtIndexPath: gets called.
The problem is that you're setting the selected state there:
[couponCell.radioButton setSelected:NO];
Therefore, whenever you scroll your cell out of the visible area and back again, it gets reset to selected = NO.
I suggest you create a NSMutableDictionary where you store the selection state of each row/NSIndexPath, which you then re-apply in tableView:cellForRowAtIndexPath:.
replace [couponCell.radioButton setSelected:NO]; in tableView:cellForRowAtIndexPath: with code that sets the selected property depending on a state from your dataSource.
something along those lines:
/* get the button state from your data source */
FancyCouponObject *coupon = [self.coupons objectAtIndex:indexPath.row];
BOOL buttonState = coupon.buttonState;
[couponCell.radioButton setSelected:buttonState];
The cells of a tableView are reused when they are moved off screen. You can't save state in them.
problem is when you scroll the table at that time your cellForRowAtIndexPath: delegate method called for every row... so here when its called at time your setSelected Method call with NO argument like bellow...
[couponCell.radioButton setSelected:NO];
so when you scroll table at time your setSelected method call and your button turn with radio_blank.png
...
:)
-(void)radioButtonPressed:(id) sender
{
[sender setSelected:YES];
}
in this method you are setting button as selected and for selected button you have set the radio_blank.png image
I have a UITableView, I can add and delete cell to this table. I also have two buttons. 1 button adds "1" to the cell's text, Which is 1 so basically it counts when pressing the + button and subs when pressing the - button. My problem is with the very last cell. If i add 5 cells, its the 5th cell that has the problem. If i add 200 cells its the 200th cell, etc. The problem is when i press the - button, all the other cells keep turning blue when pressed, and this button stops turning blue. It stays white when i press it when the cells text is 0. I want it to keep turning blue like all the other cells when pressed. Here is my code:
- (IBAction)subtractLabelText:(id)sender
{
cell = (UITableViewCell*)[sender superview];
if ( [[cell.textLabel text] intValue] == 0){
[newBtn setEnabled:NO];
}
else{
cell.textLabel.text = [NSString stringWithFormat:#"%d",[cell.textLabel.text
intValue] -1];
[newBtn setEnabled:YES];
}
}
This method is hooked up to the sub "-" button. Also, when i press the button when the text is = 0, the button is there but when i press it, it selects the cell and the table cell turns blue as if i selected that! Please help! Thanks everybody!
cellForRow:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
cell.imageView.image = [imageArray objectAtIndex:indexPath.row];
cell.textLabel.text = [cells objectAtIndex:indexPath.row];
newBtn = [[UIButton alloc]init];
newBtn=[UIButton buttonWithType:UIButtonTypeRoundedRect];
[newBtn setFrame:CGRectMake(260,20,55,35)];
[newBtn addTarget:self action:#selector(subtractLabelText:)
forControlEvents:UIControlEventTouchUpInside];
[newBtn setTitle:#"-" forState:UIControlStateNormal];
[newBtn setEnabled:YES];
[cell addSubview:newBtn];
subBtn = [[UIButton alloc]init];
subBtn=[UIButton buttonWithType:UIButtonTypeRoundedRect];
[subBtn setFrame:CGRectMake(200,20,55,35)];
[subBtn addTarget:self action:#selector(addLabelText:)
forControlEvents:UIControlEventTouchUpInside];
[subBtn setTitle:#"+" forState:UIControlStateNormal];
[subBtn setEnabled:YES];
[cell addSubview:subBtn];
return cell;
}
Based on your cellForRowAtIndexPath method, you are creating and adding new buttons to each cell regardless of whether they are being reused or 'brand new'.
Your code is doing something like this:
Check if there is a cell to be dequeued/reused
If yes, grab a reference to it. If no, create a new one.
Add buttons XYZ to the cell (your mistake is here)
Return the cell for the tableview to use
Imagine that in step 2, if you a reusing an existing cell, this cell already has the buttons XYZ added to it so you are essentially adding two extra button to it when you didn't need to.
You should move the code that sets up the cell buttons to inside the if (cell == nil) loop so the buttons are only created and added to the content view if you are initialising a new cell object.
Also make sure you look at the additional comments below which are not directly related to your question but may be relevant nonetheless.
==================================================================
[EDIT - ANSWER ABOVE, BUT THIS IS ALSO RELEVANT SO I LEFT IT HERE]
This will not solve your problem, but you should be adding your buttons to the cell's contentview instead of directly to the cell, i.e.:
[cell.contentView addSubView:buttonABC];
Once that's done, you will need to call superview twice to get the cell reference in your subtract/add method:
cell = (UITableViewCell*)[[sender superview] superview];
From Apple's documentation on UITableViewCell
The content view of a UITableViewCell object is the default superview
for content displayed by the cell. If you want to customize cells by
simply adding additional views, you should add them to the content
view so they will be positioned appropriately as the cell transitions
into and out of editing mode.
At a guess, I'd say it has to do with how the UITableViewCells are being reused, in that the UI doesn't 'know' about the subview buttons on those cells.
Try commenting out the code in -cellForRowAtIndexPath that dequeues the reusable cells (so in effect you're always creating a new UITableViewCell regardless of whether cell==nil or not).
Edit:
You're referencing newBtn in -subtractLabelText, but this is an ivar that doesn't necessarily refer to the button that sent the message. Try [sender setEnabled:NO] instead.
I use UITableview, using IB I defined a UIButton in a custom cell and some labels, The custom cell subclass already have definitions of IBAction of the button and necessary IBOutlets on it, but I want to handle the button click events in the tableview controller it self but not in the custom cell subclass.
How can I do this? also I need to get which row's button is exactly clicked so I will show the content relavant to it.
I solved problem by addding this into my controller;
[cell.expButton setTag:indexPath.row];
UIButton *button = (UIButton *)[cell expButton];
[button addTarget:self action:#selector(buttonTapped:event:) forControlEvents:UIControlEventTouchUpInside];
For the need of clickable event(touch effect on the subView), I usually do this :
-(void)viewDidLoad{
self.tableView.delaysContentTouches = NO;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// This will do the touch down effect on the subView of your UIButton
for (id obj in cell.subviews)
{
if ([NSStringFromClass([obj class]) isEqualToString:#"UITableViewCellScrollView"])
{
UIScrollView *scroll = (UIScrollView *) obj;
scroll.delaysContentTouches = NO;
break;
}
}
// I need to get which row's button is exactly clicked so I will show the content relavant to it.- here it is
UIButton *btn = (UIButton*)[cell viewWithTag:200];
// btn = cell.myButton; If you are connected with the IBOutlet
[btn setTag:200 + indexPath.row];
[self.btnPlay addTarget:self action:#selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)btnAction: (id)sender{
UIButton *btn = (UIButton *)sender;
NSLog(#"Selected subView of the Row is %d th tag", btn.tag-200);
// You can get the UITableViewCell using the tag property
selectedIP = [NSIndexPath indexPathForRow:btn.tag - 200 inSection:1;
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedIP];
// Do the relevant code
}
the way how you want to do this is right (how MVC have to coded clean) - thats beautiful!
To make it more efficent you have to use delegation - thats the best way how to do this!
Here a way how you can do this (e.g. in swift).
https://stackoverflow.com/a/29920564/1415713