I'm currently working in a UITableView, each cell containing a button with a specific UIImage in background. I'd like to change this image when the button is clicked in a cell.
I have this code, working perfectly but when I click on a button, several buttons are modified - every 5 buttons, don't know why. Here's the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
[myCell.favorite addTarget:self action:#selector(pressed_fav:) forControlEvents:UIControlEventTouchUpInside];
return myCell;
}
- (void)pressed_fav:(id)sender
{
UIButton *myButton = sender;
[myButton setBackgroundImage:[UIImage imageNamed:#"star.png"] forState:UIControlStateNormal];
}
myCell is an instance of a customized cell, containing an IBOutlet 'favorite' pointing to the UIButton.
Its because UITableView re-uses cell.
You must have some logic in cellForRowAtIndexPath method to identify the image of a button.
Thank you so much guy !
Actually, when I delete the line:
myCell = [tableView dequeueReusableCellWithIdentifier:#"cinemaCell"];
It does not display the cell (it's well allocated and initialised instead).
Related
I am new to app development. In my app when I click on my table view cell, the cell is selected and didSelectRowAtIndexPath method is triggered irrespective of whether I clicked on image or cell. I want the image to be selected when I click on the image, not the cell. Thanks in advance.
Add UITapGestureRecognizer on your image view, and if you not want cell selection then off table view cell selection . on Tap function on image do what you want.
In your cellForRowAtIndexPath method, add a custom UIButton and assign the image as background image of the button. Then add target to the button and handle your code inside the button target method
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CellIdentifier"];
UIButton *imageButton = [[UIButton alloc] initWithFrame:<Give the frame you want>];
[imageButton setBackgroundImage:<your image> forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(imageButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:imageButton];
}
return cell;
}
Here the best method is to create a custom UITableViewCell which have imageButton as an instance variable instead of creating it inside cellForRowAtIndexPath method. Now in the imageButtonClicked method
-(void)imageButtonClicked:(UIButton *)sender {
UIImage *image = sender.currentBackgroundImage;
//Do the logic what you want here
}
Please give the frame of the button so that it is placed at the position you want.
I haven't tried out this code. If sender.currentBackgroundImage is not giving you the image, then create a custom button extending from UIButton. The custom button should have a UIImage variable as an instance variable. Assign the variable to the image you want when it is initialized(in cellForRowAtIndexPath method). And then you can access the image in the button target method
I used this code in cellForRowAtIndexPath method. When I click on button or scroll table this button is shown two times. Why is this button shown 2 times please help me?
UIButton *trashbtn=[[UIButton alloc]initWithFrame:CGRectMake(cell.frame.size.width-20, cell.frame.size.height-30, 20, 20)];
[trashbtn setImage:[UIImage imageNamed:#"editor_trash"] forState:UIControlStateNormal];
[trashbtn addTarget:self action:#selector(DeleteMyAssociate:) forControlEvents:UIControlEventTouchUpInside];
[trashbtn setTag:indexPath.row];
[cell addSubview:trashbtn];
You should firstly add a UITableViewCell within your table in IB. Then give an identifier e.g. "MyCellIdentifier" to that cell. Still on IB, add your outlets, to that cell e.g. the button, the textFields... You can initially set the button to be invisible. Then in the method cellForRowAtIndexPath, you do:
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCellIdentifier"];
if(cell){
myButton.hidden = NO; //
myTextField.text = #"BlaBlaBla";
}
}
Hey I can give you a suggestion that use auto layout and storyboard to add button rather than adding it programmatically. That is more better and cleaner approach.
The problem you are facing is that cells are reused in tableviews, So the first time you create a button and add it to the cell, it appears once. But if it is dequeued, it already has the button, so when you add another one you end up with multiple buttons.
There are two ways to correct this.
Firstly, remove the button in the cell's prepareForReuse method, which is called just after the cell is reused.
Secondly, avoid creating custom views in the cellForTableView... method. Use a custom cell that already has the button. Now, you may ask, how do I hook up the action for the button if I do it this way? You can either provide a delegate method for your cell that calls back to your view controller, or you can pass your cell a block to perform when the button is clicked.
as far as i can get u already have a custom cell.. add a button to existing cell and create an IbOutlet for the same in customCell.h file
then in cellForRowAtIndexPath method access the button using its IBOutlet
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
customCell *cell =(customCell*) [tableView dequeueReusableCellWithIdentifier:#"customCell" forIndexPath:indexPath];
[cell.btn setTitle:#"blabla" forState:UIControlStateNormal];
[cell.btn setTag:indexPath.row];
[cell.btn addTarget:self action:#selector(method:) forControlEvents:UIControlEventTouchUpInside];
if(condition)
{
cell.btn.hidden=YES;
}
else
{
cell.btn.hidden = NO;
}
return cell;
}
Do not forget to register for the customCell class in your ViewController class
[tableName registerNib:[UINib nibWithNibName:#"customCell" bundle:nil] forCellReuseIdentifier:#"customCell"];
you can hide or unhide the button as per your requirements as well as add different actions to the button in each class.
Happy Coding..!!
Do vote it if my code was helpful for you.. ;)
I have some static UITableViewCells. One of them with 5 buttons inside. Inside didSelectRowAtIndexPath I handle the selection event for all the cells except for the one with the buttons inside. The problem is the action for the buttons in that one cell does not work.
Any help will be appreciated.
Finally I figured out That the root of the problem was at a completely different area, heightForRowAtIndexPath was returning wrong height for that cell (actually smaller height),
So, the controller interpreted the touches to be in a completely different place than the actual place I was supposedly touching
You don't have the cellForAtIndexPath method, however you can implement the tableView:willDisplayCell:forRowAtIndexPath: method and add target for each button like so:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
// get a reference to button
UIButton *button = (UIButton *)[cell viewWithTag:1];
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
I put a tableview in my storyboard, and filled the cell with a button with "0" as the tag. I populated the tableview using an array. Let's just say I have 20 elements in my array, that makes it 20 cells on the tableview. This is the code I'm using to give tags to the button on each cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == _tblCart) {
static NSString *cellIdentifier = #"CartCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
UIButton *btn = (UIButton *)[cell viewWithTag:0];
[btn setTag:indexPath.row];
[btn addTarget:self action:#selector(logTag:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
return nil;
}
I thought my code will work just fine, but actually there is something wrong. On cell 0-5, the button tag is correct. On the following cell the tag will reset again to 0. What am I doing wrong?
There is also another logic problem here, you are reusing table cells, some of which you've changed the tag of the button to something other than 0. So if you get a reused tablecell, there will come a time that the UIButton won't have a tag of zero and therefore you won't be able to change it correctly.
Don't set 0 as a tag identifier to views.
All UIViews have a tag by default 0.
So
[cell viewWithTag:0];
probably will return the contentView of the cell.
Both Flexicoder and l0gg3r are correct. Also, relying on button's tag as row identifier is a bit of a clunky workaround in my opinion. Consider this approach:
- (void)logTag:(UIButton *)sender {
NSIndexPath *indexPath = [_tblCart indexPathForRowAtPoint:[_tblCart convertPoint:sender.center fromView:sender]];
// do your stuff knowing which indexPath the button belongs to
}
I have a tableview with cells (not custom), and each cell has a button (among other things).
Depending on the cell, the button that is created is different. I have 3 different types of cell/button, Rate/View/Edit (I'm not sure that's at all relevant)
This button is only used for this:
[button addTarget:self action:#selector(rateEvent:) forControlEvents:UIControlEventTouchUpInside];
The selector is different for each button (rateEvent, viewEvent, editEvent).
Here is an example, they're all 3 fairly similar and start the segue sequence:
- (IBAction)viewEvent:(UIButton*)sender
{
[self performSegueWithIdentifier:#"fromHomeToDetails" sender:self];
}
My didSelectedRowAtIndexPath saves the details of a meeting in the selectedMeeting object.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedMeeting = [_nextMeetingsArray objectAtIndex:indexPath.row];
}
The segue sends selectedMeeting to the next controller.
My issue : The meeting that is sent depends on the selected cell. That means the user has to select a cell and THEN tap the button to get the correct details on the next page. What can i do to get the correct indexpath of a tapped button inside a cell?
I tried to add parameters to the viewEvent method, to make it look like -(void)viewEvent:(int)index:(UIButton*)sender; but it doesn't work in the #selector(viewEvent:).
The solutions i can imagine but not create are :
- Manage to get the indexpath of the cell of a tapped button and send it to my viewEvent method, somehow.
- Create a customCell and/or use an accessory, so i've read here and there.
- Force row selection when i tap the button, but that also requires to know the index of said row.
Both seem "too complicated" for something that (should?) be fairly simple. I'm pretty sure any experienced programmer will have an obvious easy answer that I haven't learned yet, and i'd rather ask for it than implement something heavier than it should be.
Also, if you have any comment/criticism on how i've done that, i'm all ears :)
Thanks for your time guys, as always, i'm very grateful.
EDIT :
cellForRow method :
Note : i removed everything that has to do with labeling. Because i have different buttons there are multiple if{} statements but they are of no influence in my opinion. Also, the cell is based on a storyboard cell with a tag of 228 ; after creation a new tag is set so it shouldn't influence. Also, i have a cap of 50 items in tableview (set from webservice) so there is no way the indexpath reaches >50.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *nextCell = [self.tbvNextMeetings dequeueReusableCellWithIdentifier:#"tbNextCell" forIndexPath:indexPath];
UIButton *bt= (UIButton*) [nextCell viewWithTag:228];
[bt setTitle:#"Edit" forState:UIControlStateNormal];
[bt addTarget:self action:#selector(editEvent:) forControlEvents:UIControlEventTouchUpInside];
[bt setTag:indexPath.row];
return nextCell;
Add tag to button as indexpath.row, in cellForRowAtIndexPath() method. Then in selector method get sender tag. And pass in
NSIndexPath *path = [NSIndexPath indexPathForRow:sender.tag inSection:yoursection];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];
Edit : Setting the cell with a tag of 228 at first and then switching it to the value of indexpath made its creation fail.