UITableviewController - Update specific Cells - objective-c

I have created custom cells with a button that changes the state of a row from "availible" to "bought", and I also have an UIImageview which shows the state. I want the cell to redraw with the new state when the user presses the button.
I have been able to get the UIImage to be redrawn after the User scrolls the cell out of view. This is what I have so far:
Initialization
- (void)viewDidLoad
{
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = YES;
self.contentSizeForViewInPopover = CGSizeMake(600.0, 560.0);
//[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
[self.tableView setRowHeight:60];
[self.tableView setBackgroundColor:[UIColor blackColor]];
}
Got only one section:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
And all items are in the "paketListe" array:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [paketListe count];
}
The cellForRow function only gets called, when the cell is out of view and when the cell is initially drawn.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"CellForRow called!");
PaketTableViewCell *cell = [[[PaketTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil] autorelease];
[cell setDelegate:self];
Paket *tempPaket = [paketListe objectAtIndex:[indexPath row]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *userDocumentsPath = [paths objectAtIndex:0];
NSString *fileName = [NSString stringWithFormat:#"%#/%#/%#",userDocumentsPath, [tempPaket productID], [tempPaket previewPic]];
[cell setProductIndex:[indexPath row]];
//... Setting some Attributes in my custom cell, cut out for simplicity....
cell.backgroundColor = [UIColor clearColor];
return cell;
}
The code for the Button, this is a delegate call, so my custom sell calls [delegate buttonPushed:(int)];
- (void) buttonPushed:(int) myProductIndex {
NSLog(#"Pushed: %#", [[paketListe objectAtIndex:myProductIndex] productID]);
CoreDataWrapper *tempCDW = [[CoreDataWrapper alloc] init];
[tempCDW initCoreData];
Paket *tempPaket = [paketListe objectAtIndex:myProductIndex];
//Doing some work in Core Data an my "paketListe" array. Left out for simplicity...
[tempCDW release];
//Here it begins....
[[self tableView] reloadData];
[[self tableView] beginUpdates];
[[self tableView] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:myProductIndex inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
[[self tableView] endUpdates];
}
Now I tried the reloading with different commands... Reload the whole thing with just reloadData or the begin-/endUpdate thing. Nothing refreshes the cell. After several hours of reading I think I did everything right, but no refresh.
I appreciate any help.

Quick and dirty:
- (void)configureCell:(PaketTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
//Any cell-specific code
[cell setNeedsLayout]; //or [cell setNeedsDisplay];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Code to create any/a general cell
[self configureCell:cell];
return cell;
}
- (void) buttonPushed:(int) myProductIndex {
//Code to identify the cell in which your button is (use "super")
[self configureCell:cellForPushedButton];
}

I got to know your problem you have missed in reloadRowsAtIndexPaths "nil".
Do as i wrote below, it will work.
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:myProductIndex inSection:0], nil] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];

Related

UITableViewCell separator lines are missing

The separator lines on my table view are absent from the view. Need suggestions on what may be the cause. I have checked my storyboard and everything seems to check out. The separator and separator color are both set to default.
As for my code:
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES];
[self loadObjects];
self->restaurants = [NSArray array];
[self performSelector: #selector(retreiveFromParse)];
[_restaurantTable reloadData];
}
- (void)retreiveFromParse {
PFQuery *retrieveRestaurants = [PFQuery queryWithClassName:#"Restaurants"];
[retrieveRestaurants findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
restaurants = [[NSArray alloc] initWithArray:objects];
NSLog(#"successful");
}
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section {
return [self->restaurants count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath object:(PFObject*)object
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
UILabel *title = (UILabel*) [cell viewWithTag:1];
title.text = [object objectForKey:#"title"];
return cell;
}
Update: I solved it by simply deleting the [self loadObjects]; line my viewDidLoad method. The separator lines now show in my view !
The problem might be of separator color itself .By default it is White
so try changing it to another color either in storyboard or by code itself.
[self.tableView setSeparatorColor:[UIColor blackColor]];
When the UITableView is selected in the storyboard, the Separator dropdown should be set to: "Default" or "Single Line"
I have the same issue on simulator for xcode6.1 but it works fine for the device. Have you tried running it on the device?

UITableViewCell update image on willDisplayCell

I am trying to update the second last row of a tableview just after the last one (of the same kind) has been inserted. What I want is to change the image in a custom uiimageview i put in the xib.
I achieved this feature by calling [_tableView reloadData] just after the insertion, but this way the insert animation has gone and the performance have got worse.
Here is part of the code I wrote:
•in the delegate:
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
[_dataSource setIsRead:(BOOL)read AtIndexPath:indexPath];
[_dataSource configureCell:(WMChatTableViewCell *)cell AtIndexPath:indexPath];
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
[cell setNeedsLayout];
[cell layoutIfNeeded];
}
• in the dataSource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
WMMessage *message = [self messageAtIndexPath:indexPath];
/**
Message sent by the user
*/
if ([message.sender_id intValue] == [[[NSUserDefaults standardUserDefaults]valueForKey:kUserIdKey]intValue]) {
WMRightChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:rightTextIdentifier];
if (cell == nil) {
cell = [[WMRightChatTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:rightTextIdentifier];
}
[self configureCell:cell AtIndexPath:indexPath];
return cell;
}
/**
Message sent by the recipient
*/
else{
WMLeftChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:leftTextIdentifier];
if (cell == nil) {
cell = [[WMLeftChatTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:leftTextIdentifier];
}
[self configureCell:cell AtIndexPath:indexPath];
return cell;
}
return nil;
}
-(void)configureCell:(WMChatTableViewCell *)cell AtIndexPath:(NSIndexPath *)indexPath{
WMMessage *message = [self messageAtIndexPath:indexPath];
....
/**
Last message sent by the recipient
*/
if (!([message.sender_id intValue] == [[[NSUserDefaults standardUserDefaults]valueForKey:kUserIdKey]intValue]) &&
[message isEqual:[[_messagesMatrix lastObject]lastObject]]) {
/**
An highlighted version of the background.
Normally the left cell has the "BoxChat_sx" image, only when it's the last one, the image should change
*/
[cell.balloonImageView setImage:[UIImage imageNamed:#"BoxChat_sx_new"]];
....
}
else if(!([message.sender_id intValue] == [[[NSUserDefaults standardUserDefaults]valueForKey:kUserIdKey]intValue])){
/*
If it's not the last anymore, the image should change back to it's original state
*/
[cell.balloonImageView setImage:[UIImage imageNamed:#"BoxChat_sx"]];
...
}
}
Without calling [_tableView reloadData] this code won't update the imageView. Is there a way to achieve this feature without calling reloadData or, at least, keeping the insertion animation?

iOS6 UITableView section header duplicates

I have an UITableView with expandable rows in section 4. When one row is expanded the others need to be collapsed. I reload specified rows and scroll table to have the expanded one on top. Here's the code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (indexPath.section == 4)
{
if (indexPath.row == 0)
{
if (hypoExpanded)
{
hypoExpanded = NO;
[self performSelector:#selector(reloadRowWithScrollForIndexPath:) withObject:indexPath afterDelay:kAnimationDelay];
}
else
{
hypoExpanded = YES;
moodExpanded = NO;
activityExpanded = NO;
NSArray *indexArray = [NSArray arrayWithObjects:indexPath, [NSIndexPath indexPathForRow:1 inSection:4], [NSIndexPath indexPathForRow:2 inSection:4], nil];
[self performSelector:#selector(reloadRowWithScrollForIndexArray:) withObject:indexArray afterDelay:kAnimationDelay];
}
... }
}
- (void)reloadRowWithScrollForIndexArray:(NSArray *)indexArray
{
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:((NSIndexPath *)[indexArray objectAtIndex:0]).row inSection:((NSIndexPath *)[indexArray objectAtIndex:0]).section] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
- (void)reloadRowWithScrollForIndexPath:(NSIndexPath *)indexPath
{
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
switch (section)
{
case 1:
return [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"bgc-info"]];
break;
case 2:
return [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"in-info"]];
break;
case 3:
return [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"bp-info"]];
break;
case 4:
return [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"per-info"]];
break;
}
return nil;
}
Sometimes a random section header (1, 2 or 3) duplicates and overlays the cell that belongs to that section. Scrolling the table to make the section disappear doesn't help. When I scroll back the duplicated header still exists.
It only happens on iOS 6. Invoking performSelectorOnMainThread: is not helpful either. Also, without calling scrollToRowAtIndexPath: everything works fine. What could possibly cause this behaviour?
You can see a screenshot here:
I had the same problem. Check for uncommitted animation in your code
[UIView beginAnimations:nil context:NULL];
... should be committed below

How to keep selection of untouched cells intact?

I have a tableView where I select cells and add the data in an array, I also have the option of swiping and then deleting a particular cell which eventually deletes the data from the array.
The problem is that once I delete a row, I lose all my selection state after I reload the table,
For that I checked again with the selection array and reselected all these cells,
BUT I am stuck at one place, Much before I actually delete a cell and reload the tableView, as soon as I swipe over a cell, selection state of all other cells also go away.
NOTE: I have two arrays, one with list of itmes to be displayed in the tableView and one with the selected items.
Here is some code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.contactList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
cell.selectionStyle = UITableViewCellSelectionStyleGray;
[cell.textLabel setTextColor:[UIColor colorWithRed:103.0/255.0 green:103.0/255.0 blue:103.0/255.0 alpha:1.0]];
[cell.textLabel setFont:[UIFont fontWithName:#"ITCAvantGardeStd-Bk" size:14.0]];
if (![[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"nickName"] isEqualToString:#""])
cell.textLabel.text = [NSString stringWithFormat:#"%#",[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"nickName"]];
else
cell.textLabel.text = [NSString stringWithFormat:#"%# %#",[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"firstName"],[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"lastName"]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Selected cell index==>%d\n",indexPath.row);
//NSString *emailID = [NSString stringWithFormat:#"%#",[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"email_key"]];
NSLog(#"emailID==>%#\n",[self.contactList objectAtIndex:indexPath.row]);
[self.emailShareList addObject:[self.contactList objectAtIndex:indexPath.row]];
//[self.emailShareList insertObject:emailID atIndex:indexPath.row];
NSLog(#"Array value==>%#\n",self.emailShareList);
//[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"deSelected cell index==>%d\n",indexPath.row);
NSString *emailID = [NSString stringWithFormat:#"%#",[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"email_key"]];
NSLog(#"emailID==>%#\n",emailID);
[self.emailShareList removeObject:[self.contactList objectAtIndex:indexPath.row]];
NSLog(#"deSelect row Array value==>%#\n",self.emailShareList);
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
if(indexPath.row != 0)
{
NSString *contactID = [[self.contactList objectAtIndex:indexPath.row] objectForKey:#"contactId"];
NSLog(#"content on delete row==>%#\n",contactID);
[self.contactList removeObjectAtIndex:indexPath.row];
[self deleteContactToServer:contactID];
}
}
[contactTableView reloadData];
for (int i = 0; i < [self.emailShareList count]; i++)
{
for (int j = 0; j < [self.contactList count]; j++)
{
if([[[self.contactList objectAtIndex:j] valueForKey:#"email"] isEqualToString: [[self.emailShareList objectAtIndex:i] valueForKey:#"email"]])
{
NSIndexPath *path1 = [NSIndexPath indexPathForRow:j inSection:0];
[contactTableView selectRowAtIndexPath:path1 animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
}
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCellEditingStyle style = UITableViewCellEditingStyleNone;
if(indexPath.row != 0)
style = UITableViewCellEditingStyleDelete;
return style;
}
When you delete an item, you don't necessary have to reload the entire tableview. You could use the – deleteRowsAtIndexPaths:withRowAnimation: method to just remove the cell in question (along with an according model update). This will probably retain your selection.
To keep your selections when entering editing mode (swipe for delete is a special case of editing mode as well) you nee to do two things:
First, enable allowsSelectionDuringEditing on your tableView:
self.tableView.allowsSelectionDuringEditing = YES;
Second, create a UITableView subclass and override setEditing:animated: like this:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
NSArray *indexPaths = self.indexPathsForSelectedRows;
[super setEditing:editing animated:animated];
for (NSIndexPath *ip in indexPaths) {
[self selectRowAtIndexPath:ip animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
Personally, I would rather use some sort of custom selection mechanism, when selections are important from a model point of view. I would create a custom cell subclass, add a selection property to it let it change the cell styling accordingly. The build-in features that affect regular table view selections won't cause problems with such an approach.
Here is an additional method of preserving table selections in and out of edit mode without having to subclass UITableView. Add the following to your UITableViewControllerView.
Within viewDidLoad add:
self.tableView.allowsSelectionDuringEditing = YES;
Then override setEditing:animated:
- (void)setEditing:(BOOL)editing animated:(BOOL)animate
{
NSArray *selectedIndexPaths = [self.tableView indexPathsForSelectedRows];
[super setEditing:editing animated:animate];
for (NSIndexPath *selectedIndexPath in selectedIndexPaths) {
[self.tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}

Repeated issues in ItemsViewController.m

Am getting repeated issues in ItemsViewController.m even though I have not changed anything in the relevant methods. Before there were some issues which I asked about on SO earlier, then corrected them, but they have popped up again. Have not made any changes to the methods/areas which are kicking up a fuss again. Have commented out the problem areas.
Could there be compiler issues?
Here is the file; thanks in advance.
ItemsViewController.m
#import "ItemsViewController.h"
#import "BNRItemStore.h"
#import "BNRItem.h"
#implementation ItemsViewController //#end is missing in implementation context
- (id)init
{
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
UINavigationItem *n = [self navigationItem];
[n setTitle:#"Homepwner"];
// Create a new bar button item that will send
// addNewItem: to ItemsViewController
UIBarButtonItem *bbi = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(addNewItem:)];
// Set this bar button item as the right item in the navigationItem
[[self navigationItem] setRightBarButtonItem:bbi];
[[self navigationItem] setLeftBarButtonItem:[self editButtonItem]];
}
return self;
}
- (IBAction)addNewItem:(id)sender
{
// Create a new BNRItem and add it to the store
BNRItem *newItem = [[BNRItemStore defaultStore] createItem];
DetailViewController *detailViewController = [[DetailViewController alloc]initForNewItem:YES];
[detailViewController setItem:newItem];
[detailViewController setDismissBlock:^{[[self tableView]reloadData];
UINavigationController *navController = [[UINavigationController alloc]initWithRootViewController:detailViewController];
[navController setModalPresentationStyle:UIModalPresentationFormSheet];
[self presentViewController:navController animated:YES completion:nil];
}
- (id)initWithStyle:(UITableViewStyle)style //use of undeclared identifier 'initWithStyle'
{
return [self init];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[self tableView] reloadData];
}
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath
{
[[BNRItemStore defaultStore] moveItemAtIndex:[fromIndexPath row]
toIndex:[toIndexPath row]];
}
- (void)tableView:(UITableView *)aTableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController *detailViewController = [[DetailViewController alloc] initForNewItem:NO];
NSArray *items = [[BNRItemStore defaultStore] allItems];
BNRItem *selectedItem = [items objectAtIndex:[indexPath row]];
// Give detail view controller a pointer to the item object in row
[detailViewController setItem:selectedItem];
// Push it onto the top of the navigation controller's stack
[[self navigationController] pushViewController:detailViewController
animated:YES];
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)io
{
if ([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPad) {
return YES;
} else {
return (io==UIInterfaceOrientationPortrait);
}
}
- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
// If the table view is asking to commit a delete command...
if (editingStyle == UITableViewCellEditingStyleDelete)
{
BNRItemStore *ps = [BNRItemStore defaultStore];
NSArray *items = [ps allItems];
BNRItem *p = [items objectAtIndex:[indexPath row]];
[ps removeItem:p];
// We also remove that row from the table view with an animation
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [[[BNRItemStore defaultStore] allItems] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create an instance of UITableViewCell, with default appearance
// Check for a reusable cell first, use that if it exists
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
// If there is no reusable cell of this type, create a new one
if (!cell) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"UITableViewCell"];
}
// Set the text on the cell with the description of the item
// that is at the nth index of items, where n = row this cell
// will appear in on the tableview
BNRItem *p = [[[BNRItemStore defaultStore] allItems]
objectAtIndex:[indexPath row]];
[[cell textLabel] setText:[p description]];
return cell;
}
#end // expected '}'
This line looks to be missing the end the of the block:
[detailViewController setDismissBlock:^{[[self tableView]reloadData];
should be:
[detailViewController setDismissBlock:^{[[self tableView]reloadData]}];