I'm doing the following to set the cell image if a message is unread
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MessageCell"];
Message *m = [self.messages objectAtIndex:indexPath.row];
cell.textLabel.text = m.subject;
NSString *detail = [NSString stringWithFormat:#"%# - %#", m.callbackName, m.dateSent];
cell.detailTextLabel.text = detail;
if([m.messageRead isEqualToString:#"False"])
cell.imageView.image = [UIImage imageNamed:#"new.png"];
return cell;
}
This is correctly showing an image when it's supposed to. If I scroll down however and scroll back up, they all show the image whether it's supposed to or not
Cells are reused. So you need to set each property for every condition.
if([m.messageRead isEqualToString:#"False"])
cell.imageView.image = [UIImage imageNamed:#"new.png"];
else
cell.imageView.image = nil;
Since UITableViewCells are reused, you should set cell.imageView.image to nil in case you do not need image.
if([m.messageRead isEqualToString:#"False"]) {
cell.imageView.image = [UIImage imageNamed:#"new.png"];
} else {
cell.imageView.image = nil;
}
Related
The question sums it up-- I'm trying to have a box get checked when someone clicks on the UITableViewCell, which involves changing the image in the cell. The table is set up well and works just fine-- it's displaying the information that I want it to, and the cells select like they should. However I can't get the image to change when the method didSelectRowAtIndexPath is called.
Here's what I have so far. I've removed extraneous code (from other tables, etc), but this should be everything that's relevant.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
if (tableView == diabetesVisitTable) {
if (indexPath.section==0) {
cell.textLabel.text = [_microvascularValues objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:#"Family Med Unchecked Box.jpg"];
}
else if (indexPath.section==1) {
cell.textLabel.text = [_macrovascularValues objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:#"Family Med Unchecked Box.jpg"];
}
else if (indexPath.section==2) {
cell.textLabel.text = [_bloodSugarValues objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:#"Family Med Unchecked Box.jpg"];
}
cell.backgroundColor = [UIColor colorWithRed:0.75 green:0.52 blue:0.53 alpha:1.0];
self.diabetesVisitTable.backgroundColor = [UIColor colorWithRed:0.75 green:0.52 blue:0.53 alpha:1.0];
return cell;
}
else return 0;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
cell.imageView.image = [UIImage imageNamed:#"Family Med Checked Box.jpg"];
}
In your tableView:didSelectRowAtIndexPath: implementation, you're asking the table view for a new cell to configure with dequeueReusableCellWithIdentifier:. You want to fetch the actual cell used for that indexPath so you can update it directly:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.imageView.image = [UIImage imageNamed:#"Family Med Checked Box.jpg"];
}
You'll also need to update the data source for the table view somehow to record the fact that one of the rows is selected, and update tableView:cellForRowAtIndexPath: to set the checked image if the data source indicates that the row is selected. If you don't do those steps, the cell will appear unselected when it refreshes.
UITableViewDelegate has 3 methods to handle cell highlighting:
- tableView:shouldHighlightRowAtIndexPath:
- tableView:didHighlightRowAtIndexPath:
- tableView:didUnhighlightRowAtIndexPath:
Try like this :
- (BOOL)tableView:(UITableView*)tableView shouldHighlightRowAtIndexPath:(NSIndexPath*)indexPath
{
return YES;
}
- (void)tableView:(UITableView*)tableView didHighlightRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell* cell = (UITableViewCell*)[tableView cellForRowAtIndexPath:indexPath];
cell.imageView.image = [UIImage imageNamed:#"Highlightimage"];
}
- (void)tableView:(UITableView*)tableView didUnhighlightRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell* cell = (UITableViewCell*)[tableView cellForRowAtIndexPath:indexPath];
cell.imageView.image = [UIImage imageNamed:#"image"];
}
You're trying to update the cell at the didSelectRowAtIndexPath function which returns void. So it will not update the tableview.
You should take the indexpath.row value from the diddSelectRowAtIndexPath and pass it to the cellForRowAtIndexPath. if they're matching, update the cell, it will work.
Also make sure you add a beginUpdate and endUpdate to reflect the table value reload
One of my ViewControllers has a UITableView inside of it, and it crashes when it loads the cellForRowAtIndexPath with the following error message:
I've placed NSLogs in various parts of the function to see where it crashes, but they all log successfully, so I'm a bit confused as to where it's going wrong.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cellForRowAtIndexPath has begun");
// parse data for the respective section's items
NSDictionary *currentSectionDictionary = _matchCenterData[indexPath.section];
NSArray *top3ArrayForSection = currentSectionDictionary[#"Top 3"];
// Cell defaults
static NSString *CellIdentifier = #"MatchCenterCell";
MatchCenterCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
// if no cell could be dequeued create a new one
cell = [[MatchCenterCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [NSString stringWithFormat:#"Title"];
cell.textLabel.font = [UIFont systemFontOfSize:14];
tableView.separatorColor = [UIColor clearColor];
// if no results for that item
if (top3ArrayForSection.count-1 < 1) {
cell.detailTextLabel.text = [NSString stringWithFormat:#""];
[cell.imageView setImage:[UIImage imageNamed:#""]];
NSLog(#"no results for this item");
return cell;
}
// if results for that item found
else {
// Load images using background thread to avoid the laggy tableView
[cell.imageView setImage:[UIImage imageNamed:#"Placeholder.png"]];
NSLog(#"FOUND results for this item");
return cell;
}
}
I have a problem with cellForRowAtIndexPath always returning nil when called from tableView:cellForRowAtIndexPath:
The thing is I am actually calling cellForRowAtIndexPath inside a block inside tableView:cellForRowAtIndexPath: and I guess that this might be the problem.
I am populating cells with remote images that load (and cache) ad hoc. When the image returns fully loaded in the completion handler block (inside tableView:cellForRowAtIndexPath:) I need to get the cell again because it might have scrolled out of view... So I call cellForRowAtIndexPath to get the cell again - cellForRowAtIndexPath would only return a cell if it's visible. In my case I never get it to return anything but nil. Even though I am scrolling very slowly (or fast, or whatever speed I tried...) all I get is nil.
I'll try to get some code in here soon but until then any suggestion why this would occur - I guess the block thing would be a hint.
Here is the code: https://dl.dropboxusercontent.com/u/147970342/stackoverflow/appsappsappsappsapps.zip
The -tableView:cellForRowAtIndexPath: implementation:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSNumber* numberAppleid = self.appids[indexPath.row];
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"Mycell" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"Appleid: %#", numberAppleid];
NSString* appleidasstring = [NSString stringWithFormat:#"%#", numberAppleid];
cell.imageView.hidden = YES; // Hide any previous until image loads
[self getAppIconForAppleid:appleidasstring handlerImage:^(UIImage *image) {
if (image == nil) {
return;
}
DLog(#"cellForRowAtIndexPath tableView: %#, indexPath: %#", tableView, indexPath);
UITableViewCell* localcell = [tableView cellForRowAtIndexPath:indexPath];
if (localcell != nil) {
localcell.imageView.image = image;
localcell.imageView.hidden = NO;
}
else {
DLog(#"Nah indexpath is not visible for: %# because localcell is nil.", appleidasstring);
}
}];
return cell;
}
Fixed version:
#define KEYAPPLEID #"keyappleid"
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSNumber* numberAppleid = self.appids[indexPath.row];
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"Mycell" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"Appleid: %#", numberAppleid];
NSString* appleidasstring = [NSString stringWithFormat:#"%#", numberAppleid];
[cell setAssociativeObject:appleidasstring forKey:KEYAPPLEID];
cell.imageView.hidden = YES; // Hide any previous until image loads
[self getAppIconForAppleid:appleidasstring handlerImage:^(UIImage *image) {
if (image == nil) {
return;
}
NSString* currentappleidofcell = [cell associativeObjectForKey:KEYAPPLEID];
if ([currentappleidofcell isEqualToString:appleidasstring]) {
cell.imageView.image = image;
cell.imageView.hidden = NO;
}
else {
DLog(#"Returning appleid: %# is not matching current appleid: %#", appleidasstring, currentappleidofcell);
}
}];
return cell;
}
And this category is needed: https://stackoverflow.com/a/10319083/129202
If the block is within the tableView:cellForRowAtIndexPath: callback, you can just reference the cell directly in the block. The block will automatically keep the cell around until the block goes away. However, be careful of retain cycles. If the block is owned by the cell, you will have to use a weak reference to the cell.
So your implementation would look like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber* numberAppleid = self.appids[indexPath.row];
// Create your own UITableViewCell subclass that has an "appleidasstring" property
MyTableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"Mycell" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"Appleid: %#", numberAppleid];
NSString* appleidasstring = [NSString stringWithFormat:#"%#", numberAppleid];
cell.imageView.hidden = YES; // Hide any previous until image loads
// Set the appleidasstring on the cell to be checked later
cell. getAppIconForAppleid:appleidasstring = appleidasstring;
// Modify the handler callback to also callback with the appleidasstring
[self
getAppIconForAppleid:appleidasstring
handlerImage:^(UIImage *image, NSString *imageAppleIdaString)
{
if (image == nil) {
return;
}
// Ensure the cell hasn't been repurposed for a
// different imageAppleIdaString
if ([cell.appleidasstring isEqualToString:imageAppleIdaString]) {
cell.imageView.image = image;
cell.imageView.hidden = NO;
}
}
];
return cell;
}
I have a tableview that is made up of custom tableview cells which contain an imageview. In cellForRowAtIndexPath I set the image of each cell. I want the image to change when the cell is selected, so in didSelectRowAtIndexPath I get the cell and change the image, no problems there. However, when I scroll the table (read: table reloads the cells) the new image is no longer present. Also when the cell is no longer selected I want the image to switch back to the original.
I have tried doing the following in cellForRowAtIndexPath:
if(cell.isSelected){
cell.imageview.image = [UIImage ImageNamed: #"selected.png"];
}
else
cell.imageview.image = [UIImage ImageNamed: #"not selected.png"];
I have also tried using the BOOL values cell.highlighted or cell.selected to no avail.
Any thoughts are appreciated.
Try using a class variable selectedCellIndexPath of type NSIndexPath. In didSelectRow... you set it's value, and in the cellForRow... you write:
if([selectedCellIndexPath isEqual:indexPath]){
cell.imageview.image = [UIImage ImageNamed: #"selected.png"];
} else {
cell.imageview.image = [UIImage ImageNamed: #"not selected.png"];
}
Edit:
Or you could simply write:
if([indexPath isEqual:[tableView indexPathForSelectedCell]]){
cell.imageview.image = [UIImage ImageNamed: #"selected.png"];
} else {
cell.imageview.image = [UIImage ImageNamed: #"not selected.png"];
}
but the first solution is a little more efficient.
If you have an array with the Name of the Images it would be the easiest to change the name at the selected index in the array and reload the table... Like This:
myImageArray = [[NSMutableArray alloc] init];
[myImageArray addObject:#"name1.jpg"];
[myImageArray addObject:#"name2.jpg"];
// etc..
And the in the cellForRowAtIndexPath Method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
[cell.imageView setImage:[UIImage imageNamed:[myImageArray objecAtIndex:indexPath.row]]];
}
In the didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[myImageArray setObject:#"newName.jpg" atIndexedSubscript:indexPath.row];
[tableView reloadData];
}
In my uitableview I have two sections
first is fetched from core data
other is added through textfield which is stored in NSMutableArray(otherFriends)
here is my code
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if([otherFriends count]>0)
{
return 2;
}
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)sectionIndex
{
if(otherFriends == 0)
{
return [[[[self fetchedResultsController]sections]objectAtIndex:0]numberOfObjects];
}
return [otherFriends count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"newGroupCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.font = [UIFont fontWithName:#"Helvetica-Light" size:20.0];
cell.textLabel.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.0f];
if(indexPath.section == 0)
{
user = [[self fetchedResultsController] objectAtIndexPath:indexPath];
cell.textLabel.text = user.name;
cell.detailTextLabel.text = user.primaryResource.status;
[cell.imageView setFrame:CGRectMake(0, 0, 50, 50)];
[cell.imageView.layer setMasksToBounds:YES];
if (user.photo != nil)
{
cell.imageView.image = user.photo;
}
else
{
cell.imageView.image = [UIImage imageNamed:#"defaultPerson"];
}
}
else
{
cell.textLabel.text = [otherFriends objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:#"defaultPerson"];
}
return cell;
}
First section also have subtitles but second section cells have no subtitles
when I am adding new friend it works fine till all rows are visible, but when adding new friend and that row is not visible and to view that row I must scroll, then this row show the subtitle of first row in section 0(the very first cell) and its repeating in section 1 third row. Only subtitle is repeating but the main text is not.
For several hours I am trying to figure out this but no luck.
This is because in your else branch you are not setting the cell.detailTextLabel.text.
When a cell gets recycled, the old detailTextLabel stays there. You need to set all properties of the cell in both branches of the conditionsl, on the chance that it has been recycled.
if(indexPath.section == 0)
{
user = [[self fetchedResultsController] objectAtIndexPath:indexPath];
cell.textLabel.text = user.name;
cell.detailTextLabel.text = user.primaryResource.status;
[cell.imageView setFrame:CGRectMake(0, 0, 50, 50)];
[cell.imageView.layer setMasksToBounds:YES];
if (user.photo != nil)
{
cell.imageView.image = user.photo;
}
else
{
cell.imageView.image = [UIImage imageNamed:#"defaultPerson"];
}
}
else
{
cell.textLabel.text = [otherFriends objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:#"defaultPerson"];
// ADDED
cell.detailTextLabel.text = #"";
// You may also need to adjust the frame of the cell.imageView
// because it could have been recycled.
}
Cells get reused. Since you are using the same cells for both sections, you must be sure to set/reset the same set of cell properties in all conditions.
The problem is when the cell is used for section 1, you don't reset the detailTextLabel. In your else block, add:
cell.detailTextLabel.text = nil;
This ensures that in all cases you are setting the textLabel, detailTextLabel, and the imageView properties for the reused cells.