Having issues updating uibutton in specific uitableviewcell. When I change the button color it updates every button in uitableview. Attached my code below:
PFObject *post = [self.objectArray objectAtIndex:indexPath.section];
cell.likeBtn.layer.cornerRadius = 5.0f;
//To access button methods
[cell.likeBtn setTag:indexPath.section];
NSMutableDictionary *attributes = [NSMutableDictionary
dictionaryWithDictionary:[[TMMemoryCache sharedCache] objectForKey:post.objectId]];
BOOL likedByCurrentUser = [[attributes objectForKey:#"likedByCurrentUser"] boolValue];
if (likedByCurrentUser) {
cell.likeBtn.backgroundColor = [UIColor flatRedColor];
[cell.likeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[cell.likeBtn setTitle:#"Liked" forState:UIControlStateNormal];
}else{
//NSLog(#"Did not like %#", post.objectId);
cell.likeBtn.backgroundColor = [UIColor flatBlueColor];
[cell.likeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[cell.likeBtn setTitle:#"Like" forState:UIControlStateNormal];
}
is their a better way to update uibutton in just one specific cell?
There are two ways to do what you want, depending on how the updated information comes in:
When configuring the cell in cellForRowAtIndexPath:.
Let the cell manage its own contents.
More often than not, #1 is the way to go. When new information comes into your UITableViewController, do 1 of the following:
Call reloadData to force a refresh of the entire table.
Figure out which cells need to be updated and call reloadRowsAtIndexPaths:withRowAnimation: passing in the correct index path values.
In either of those scenarios, you'll set up the button correctly somewhere in your tableView:cellForRowAtIndexPath: method. Generally, I create my UITableViewCell subclasses to accept an object and configure themselves with the data in that object. That way, there's less code in my UITableViewController that deals with cell configuration.
A typical implementation of tableView:cellForRowAtIndexPath: looks like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Cell *cell = [tableView dequeueReusableCellWithIdentifier:#"item" forIndexPath:indexPath];
[cell configureForItem:self.dataSource[indexPath.row]];
return cell;
}
Each of your UITableViewCell instances will have their own button that will be configured by the data passed by the object in self.dataSource[indexPath.row]. That way, as cells are recycled, the button isn't just passed around, but a new one is recycled and configured each time a new cell is needed.
At a guess, without seeing more of your code I would say your issue is with your use of indexPath.section instead of indexPath.row
If you have multiple buttons per section then they will all be covered by this code.
Related
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.
I am building an app similar to iPhone photo app. I am able to show the images in a grid like view using PSUICollectionView. When I tap on a grid cell of collection view a checkbox image should appear. My problem is when I am using following code, I see that multiple random cells are being populated with check box images.
- (void)collectionView:(PSUICollectionView *)collectionView didSelectItemAtIndexPath: (NSIndexPath *)indexPath
{
NSLog(#"%# - %d", NSStringFromSelector(_cmd), indexPath.item);
ImageGridCell *cell = (ImageGridCell *)[collectionView cellForItemAtIndexPath:indexPath];
UIButton *chkboxBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[chkboxBtn setFrame:CGRectMake(60, 60, 30, 30)];
[chkboxBtn setImage:[UIImage imageNamed:#"Checkmark-iPhone.png"] forState:UIControlStateNormal];
[cell addSubview:chkboxBtn];
}
The problem is likely your custom cell has not implemented the prepareForReuse method. As a cell is reused, it may or may not have the check box, depending on if the check box was added on a previous use.
Several ways to address this. One simple way is to add a tag to the ckhboxBtn, and then remove the chkboxBton in the prepareForReuse method. E.g., when adding the check box, add the following:
[chkboxBtn setTag:100];
Then in your UICollectionViewCell class implementation, add/expand the prepareForReuse method:
-(void)prepareForReuse{
[[self viewWithTag:100] removeFromSuperview];
}
I have a UITableViewCell, it is scroll-disabled and with fixed sections and rows.
There are two sections, with 1 row in section 0, and several rows in section 1.
The tableView is for users to make some choices.
So, the first section (with only one row) is going to display the result of users' choices,
and no doubt the second section (with several rows) is for choosing.
Now I want to put an image in the cell of the only row of the first section,
and this image will change according to users' choose.
It is very easy to judge which png image should be displaying, but I have trouble update it.
I tried use the cell's imageView, and manually alloc a UIImageView or UIView there to display those images.
But all of them won't work, I mean they just keep what they are displaying at the beginning and never changes it, even if I set the view's background or its image to a new png.
I tried some method like
[myImage setNeedsDisplay] for the manually alloced view,
or
[thatCell setNeedsDiaplsy] & [self.tableView reloadData] for the imageView of that cell,
but in vain.
So I wonder how can I achieve this function that dynamically display an image in a UITableViewCell in different situations?
Thanks a lot!
_____update line_____
I'm sorry that I didn't provide my code.
and here they are.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *inOutTableCellIdentifier = #"DealTableViewIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:inOutTableCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:inOutTableCellIdentifier] autorelease];
cell.textLabel.font = [UIFont fontWithName:cMyFont size:[UIFont systemFontSize]];
cell.detailTextLabel.font = [UIFont fontWithName:cMyFont size:[UIFont smallSystemFontSize]];
// I tried using both imageView and UIView here, but won't work
if (indexPath.section == 0) {
//cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"moneyCell.png"]];
cell.backgroundColor = [UIColor cyanColor];
// cell.imageView.image = [UIImage imageNamed:#"dealDone2.png"];
self.undoneIcon = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)] autorelease];
//self.undoneIcon.image = [UIImage imageNamed:#"dealUndone2.png"];
self.undoneIcon.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"dealUndone2.png"]];
[cell.contentView addSubview:self.undoneIcon];
.... // deal with other rows in section 1
return cell;
}
// and this is the method that update the image, the images are named "dealDoneX.png",
// where X is an integer from 0 to 4.
- (void)checkUndoneDegree { // here I also tried 2 ways corresponding to UIView or a cell's imageView, but neither works well.
int i = 0;
if (self._date)
i++;
if (self.moneyTextField.text)
i++;
if (self._incomingAccount)
i++;
if (self._expensingAccount)
i++;
if (_dealType != kTransferView)
if (self._type)
i++;
NSLog(#"undone degree: %d",i);
NSString *imageName = [#"dealUndone" stringByAppendingFormat:#"%d",i];
self.undoneIcon.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:imageName]];
[self.undoneIcon setNeedsDisplay];
// NSUInteger p[2] = {0,0};
// UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathWithIndexes:p length:2]];
// [cell setNeedsDisplay];
}
and everytime I update the table's data, like changing some text of some cell,
I would call [self checkUndoneDegree] and then call [self.tableView reloadData],
But the picture is never updated, at least from the screen.
I even tried to put the codes that decide which png to set in the
(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
method, but it can only make the view displaying the first png, without any updating.
Thanks for helping!
Make your undoneDegree (represented by i variable in your code) an ivar of your view controller, so it is accessible in all of it's methods, also in the UITableView delegate and data source methods.
Forget about setNeedsDisplay method. Since you are using UITableView to display your content, you need to play by its rules. This means you should use reloadSections:withRowAnimation: method.
Check again if you need self.undoneIcon. I'm pretty sure that imageView property should do. That is if you really want to display an image in the cell. If you want to create some kind of progress bar by manipulating cell's background, then use backgroundView property, or just place UIProgressView in your cell. This is what I think your want to do, after looking at your code.
I've created a custom UITableViewCell class, and used the layoutSubviews method to add a custom label. Like this:
- (void)layoutSubviews
{
[super layoutSubviews];
if (statusLabel == nil)
{
statusLabel = [[UILabel alloc]initWithFrame:CGRectMake(430.0, 10.0, 100.0, 20.0)];
[statusLabel setTextAlignment:UITextAlignmentRight];
[statusLabel setText:#"Status, set in code"];
statusLabel.tag = 1;
[self.contentView addSubview:statusLabel];
}
}
As you can see, I have set the initial text of the label to "Status, set in code".
In the table view controller I set the text for this custom label in the cellForRowAtIndexPath method, like so:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
int index = [indexPath row];
NSString *introducerString =[introducers objectAtIndex:index];
NSArray *parts = [introducerString componentsSeparatedByString:#","];
static NSString *MyIdentifier = #"Requester";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[DanceCardCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:MyIdentifier] autorelease];
}
UIImage *image = [UIImage imageNamed:#"1.jpg"];
[cell.imageView setImage:image];
cell.textLabel.text = [parts objectAtIndex:1];
cell.detailTextLabel.text = #"Some text";
UILabel *statusLabel = (UILabel *)[cell viewWithTag:1];
statusLabel.text = #"Did it!";
return cell;
}
I'm using one table view to display two lists, depending on which of two buttons is pressed. When a button is pressed the appropriate table view controller is attached to the table view, and the reloadData method is called to trigger display of the new data. The new data does display, but the custom label text, which should read "Did it!" reads "Status, set in code ...", until I switch lists again twice.
How can I get the new text for the custom label to update straight away? I have checked the official documentation and cannot find any reference to refreshing a cell's display after updating its custom content.
Here is a screen shot to demonstrate what happens: http://www.dsbsystems.co.uk/images/xcode1.png
You're initializing the cell and immediately attempting to find the statusLabel with tag 1 inside it. layoutSubviews hasn't had the opportunity to be called yet, and so the label hasn't been created and added. (I suggest overriding the designated initializer method on your table view cell and creating the label there.)
Because of this, when you try to pull out statusLabel, it becomes nil because there's no such view, and messaging (calling a method on) nil simply does nothing (actually, it returns nil). You will need to watch out for this going forward if you're used to things blowing up with e.g. null reference exceptions.
When the cell is requested again, a new cell isn't needed because it's available from the reuse queue, and the label will be found correctly.