UITableView cannot reload the data from CustomCell - objective-c

I have an UITableView with CustomCells. It is a favorites list and there is star images in every CustomCell. So if I click lighted stars again they have to be disappeared and removed from properties list called fav.plist.
But when I click the lighted stars they don't disappear. I can't reload the datas in Favorite list from CustomCell.m . TO disappear the datas that I clicked the star , I have to go that View again. It is the only way to reload the data.
How can I make it reload the datas after click the star with stay at the same view?
Here is a part of my CustomCell.m code where the favorite list is (UITableView for the list is favView, the plist is fav.plist):
#property (nonatomic, retain) NSMutableArray *favList;
//First I find the item from plist which one will be removed from fav.plist.
NSString *path_fav = [[self documentsDirectory] stringByAppendingPathComponent:#"fav.plist"];
self.favList = [[NSMutableArray alloc] initWithContentsOfFile:path_fav];
for (NSInteger i=0; i<[self.favList count];i++) {
d = [self.favList objectAtIndex:i];
NSString *code=[d objectForKey:#"CODE"];
NSComparisonResult res=[code compare:self.cityLabel.text];
if(res==NSOrderedSame){
//removing from the fav.plist
[self.favList removeObjectAtIndex:i];
//Here I try to reload the data for the UITableView called favView
//In Favorite.m is my UITableView called favView
NSString *path = [[self documentsDirectory]
stringByAppendingPathComponent:#"fav.plist"];
[self.favList writeToFile:path atomically:TRUE];
Favorite *favController =[[Favorite alloc]init];
[favController.favView reloadData];
[favController.favView reloadInputViews];
}
}
Here is one part of my Favorite.m code:
- (UITableViewCell*)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"cellid";
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:cellID ];
if (cell == nil)
{
cell = (CustomCell*)[[[NSBundle mainBundle]
loadNibNamed:#"CustomCell" owner:nil options:nil]
lastObject];
}
NSDictionary *d = [self.favList objectAtIndex:indexPath.row];
cell.nameLabel.text = [d objectForKey:#"NAME"];
cell.cityLabel.text = [d objectForKey:#"CODE"];
cell.index= [NSString stringWithFormat:#"%d",indexPath.row];
cell.indexPath = indexPath;
NSString *starName;
if([[d objectForKey:#"FAV"] intValue]==0){
starName=#"star.png";
}else{
starName=#"starb.png";
}
[cell.self.starbtn setImage:[UIImage imageNamed:starName] forState:UIControlStateNormal];
if((indexPath.row % 2) ==0)
cell.contentView.backgroundColor=[UIColor colorWithRed:241/256.0 green:237/256.0 blue:237/256.0 alpha:1.0];
CGSize maxSize = CGSizeMake(320/2, MAXFLOAT);
CGSize nameSize = [cell.nameLabel.text sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:maxSize lineBreakMode:NSLineBreakByWordWrapping];
if(nameSize.height>=72)
nameSize.height=63;
else
nameSize.height=38;
cell.nameLabel.frame =CGRectMake(80, 0, 213, nameSize.height);
return cell;
}

Did u implement the Method
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
If yes then in this perform deletion and reload in this method.

You cannot reload a cell from itself. As far as I got your Question,you need to implement Delegate protocol for your cell. As star is pressed, call method for your button in customcell.m, which removes star from plist file and do other stuff. Then call method on delegate(your view) to reload that cell. If you don't understand I can provide u example.

Related

Accessory checkmarks disappear when scrolling Objective-C

I have a tableview that I can add and remove multiple checkmarks. The only issue is if I put 3 checkmarks and scroll away, when I return the checkmarks are gone. I can't find anywhere on the internet a solution that works, and I've tried several variation and still nothing.
This is my code in cellForRowAtIndex that should be holding the checkmarks in place.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *reuseIdentifier = #"contactCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
NSDictionary *contact = [self.tableData objectAtIndex:indexPath.row];
UILabel *nameLabel = (UILabel *)[cell viewWithTag:1];
NSString *firstName = contact[#"firstName"];
nameLabel.text = [firstName stringByAppendingString:[NSString stringWithFormat:#" %#", contact[#"lastName"]]];
UILabel *phoneNumber = (UILabel *)[cell viewWithTag:2];
NSArray *phones = contact[#"phones"];
if ([phones count] > 0) {
NSDictionary *phoneItem = phones[0];
phoneNumber.text = phoneItem[#"value"];
}
UIImageView *cellIconView = (UIImageView *)[cell.contentView viewWithTag:888];
UIImage *image = contact[#"image"];
cellIconView.image = (image != nil) ? image : [UIImage imageNamed:#"smiley-face"];
cellIconView.contentScaleFactor = UIViewContentModeScaleAspectFill;
cellIconView.layer.cornerRadius = CGRectGetHeight(cellIconView.frame) / 2;
// Need to fix
if([checkedIndexPath isEqual:indexPath])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
Here is the didSelectRowAtIndexPath method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
UITableViewCell* checkCell = [tableView cellForRowAtIndexPath:indexPath];
if(checkCell.accessoryType == UITableViewCellAccessoryCheckmark)
{
checkCell.accessoryType = UITableViewCellAccessoryNone;
NSMutableArray *i = [[NSMutableArray alloc] init];
for (NSIndexPath *indexPath in [self.tableView indexPathsForSelectedRows]) {
[i addObject:self.tableData[indexPath.row]];
// Go inside pull the numbers from the users and save in an NSArray
// NSArray *contacts = i;
// self.recipients = [[NSMutableArray alloc] init];
for (NSDictionary* dict in i) {
// Grab phones
NSDictionary *contactNumber = [dict objectForKey:#"phones"];
for (NSDictionary* dict2 in contactNumber) {
// Grabs the phone numbers
NSString* value = [dict2 objectForKey:#"value"];
int index = [self.recipients indexOfObject:value];
[self.recipients removeObjectAtIndex:index];
[self.selectedUsers removeObjectAtIndex:index];
NSLog(#"The number that has a checkmark%#", value);
NSLog(#"the array of all%#", self.recipients);
NSLog(#"At index %lu", (unsigned long)[self.recipients indexOfObject:value]);
// [_recipients addObject:value];
}
}
// NSLog(#"Phone Numbers: %#",_recipients);
}
}
else
{
[self getNumber];
NSLog(#"clicking %#", self.recipients);
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
checkedIndexPath = indexPath;
}
}
I found The Solution:
You must save each indexPath into an array(put this code in didSelectRowAtIndexPath) and then in cellForRowAtIndexPath add the following code
if([self.checkedCells containsObject:indexPath]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
Also in the didSelectRowAtIndexPath
Make sure to delete the indexPath when deselecting the row.
if(checkCell.accessoryType == UITableViewCellAccessoryCheckmark) {
checkCell.accessoryType = UITableViewCellAccessoryNone;
[self.checkedCells removeObject:indexPath];
I hope this helps someone. I been wrestling with this all day.
Make checkedIndexPath a #property (nonatomic, strong) and use self.checkedIndexPath whenever you refer to it. You're losing the reference after didSelectRowAtIndexPath exits. Set a breakpoint in cellForRowAtIndexPath and look at checkedIndexPath, I bet it's nil.
Maybe you should check if the isEqual functionality does what you expect. You could make sure by trying:
if (_checkedIndexPath.section == indexPath.section &&
_checkedIndexPath.row == indexPath.row)
If you still do not get the expected result, perhaps log the values of section and row to see where it goes wrong.
Please note that if for some reason _checkedIndexPath is a weak variable or gets deallocated, this check will fail.
You could also check that your cells are properly dequeued before being modified and that you are returning the correct cells.
If you want to store more than one checked row, of course, you will need more than one indexPath variable (just one _checkedIndexPath will not do it).

Preloading images into the memory cache at application launch, using objective c for iOS

The issue I am having, concerns trying to display thumbnail images in a list of UITableViewCells. I have 200 thumbnails to display.
My app downloads a zip file of images from my remote server, unzips the contents into the NSDocumentDirectory. After an update, which happens once a week, the app then displays the thumbnails using [UIImage imageWithContentsOfFile:]
Once, I know this has been cached, I display the thumbnail using [UIImage imageNamed:]
My problem is that when I display 200 thumbnails using
[UIImage imageWithContentsOfFile:] on the first display event after an update, the app sometimes freezes after a few minutes, saying that too many image files are open.
ImageIO: CGImageRead_mapData 'open' failed '/Users/cdesign/Library/Application Support/iPhone Simulator/6.1/Applications/B458A3F5-5B21-49CD-B4D8-17E5189678FA/Documents/91.png'
error = 24 (Too many open files)
This never happens once the images are cached in memory.
Then, when I try and click on a UITableViewCell to proceed to my 'DetailController', I get the following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle (loaded)' with name '9ka-eC-wSa-view-LRp-aI-LGN' and directory 'MainStoryboard.storyboardc''
*** First throw call stack:
(0x1bd0012 0x1473e7e 0x1bcfdeb 0x5d3ef9 0x4987e7 0x498dc8 0x5e728e 0x498ff8 0x499232 0x4994da 0x4b08e5 0x4b09cb 0x4b0c76 0x4b0d71 0x4b189b 0x4b1e93 0x4b1a88 0x80de63 0x7ffb99 0x7ffc14 0x467249 0x4674ed 0xe715b3 0x1b8f376 0x1b8ee06 0x1b76a82 0x1b75f44 0x1b75e1b 0x16ef7e3 0x16ef668 0x3b7ffc 0x2a1d 0x2945)
libc++abi.dylib: terminate called throwing an exception
This is strange, because I do not have a file called 'MainStoryboard.storyboardc'.
My app has no localisation. My project does not even have a 'en.lproj' folder. I only have 'MainStoryboard.storyboard' in the root.
I am not sure if the 2 error are related. Or, if the second error is responsible for the first. This would suggest that there is no issue with my images?
I must say that when I have tested my app, by using a modified 'getImage' method that only returns cached images, the first error disappears but the second error, concerning the 'MainStoryboard.storyboard' does still occur, occasionally, if I leave the app idle for more than a couple of minutes, on the 'ListController' screen.
Both these issues ONLY ever occur on the 'ListController' screen. The app always launches successfully to display the 'home' screen. On the home screen there is a link to the 'ListController' screen.
Assuming, though, that the image display is the problem, is there a way to preload images during application launch to preload the newly updated images from the document folder into the memory cache, so that I never have to use
[UIImage imageWithContentsOfFile:] to display images in the - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath?
If, this is possible, do I use NSCache to achieve the image preload?
And, if I use NSCache, do files with the same name overwrite entries in NSCache, or do I have to delete files, with the same name, first?
Sorry, for preempting, the answer to my initial question, but this may save time?
My app contains a storyboard, called MainStoryboard.storyboard
Target device: iPhone 5
Xcode version: 4.6.3
Here is some relevant code:
AppDelegate.m
- (BOOL)hasImageBeenUpdated:(NSString *)postureid{
BOOL result = NO;
YTAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
if (appDelegate.imagesupdated == nil) {
appDelegate.imagesupdated = [[NSMutableArray alloc] init];
}
NSString *imageid = [NSString string];
for (int i = 0; i < appDelegate.imagesupdated.count; i++) {
imageid = [appDelegate.imagesupdated objectAtIndex:i];
if ([imageid isEqualToString: postureid]) {
result = YES;
}
}
return result;
}
- (UIImage *)getImage:(NSString *)postureid{
self.nsLogsOn = YES;
YTAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
UIImage *result = [[UIImage alloc] init];
UIImageView *imageview = [[UIImageView alloc] init];
NSString *imagePath = [NSString stringWithFormat:#"%#.png",postureid];
if (self.nsLogsOn) {
NSLog(#"appDelegate.imagesHaveBeenDownloaded: %i",appDelegate.imagesHaveBeenDownloaded);
}
if(!appDelegate.imagesHaveBeenDownloaded){
imageview.image = [UIImage imageNamed:imagePath];
if (self.nsLogsOn) {
NSLog(#"image cached no download: %#",imagePath);
}
}
else{
if (appDelegate.imagesupdated == nil) {
appDelegate.imagesupdated = [[NSMutableArray alloc] init];
}
BOOL imageHasBeenUpdated = [self hasImageBeenUpdated:postureid];
if(!imageHasBeenUpdated){
imageview.image = [UIImage imageWithContentsOfFile:[[self documentsFilePath] stringByAppendingPathComponent:imagePath]];
[appDelegate.imagesupdated addObject:postureid];
if (self.nsLogsOn) {
NSLog(#"image download: %#",imagePath);
}
}
else{
imageview.image = [UIImage imageNamed:imagePath];
if (self.nsLogsOn) {
NSLog(#"image cached download: %#",imagePath);
}
}
}
if(imageview.image==nil){
imageview.image = [UIImage imageWithContentsOfFile:[[self documentsFilePath] stringByAppendingPathComponent:imagePath]];
if (self.nsLogsOn) {
NSLog(#"image cache cleared by iOS: %#",imagePath);
}
}
if(imageview.image==nil){
imageview.image = [UIImage imageNamed:#"logo-lotus-imageview-white-c.png"];
if (self.nsLogsOn) {
NSLog(#"image cannot be found: %#",imagePath);
}
}
result = imageview.image;
return result;
}
ListController.m
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [keys count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [posturegroups objectForKey:key];
return [nameSection count];
}
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
NSString *key = [keys objectAtIndex:section];
return [key capitalizedString];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = #"plainCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
NSString *key = [keys objectAtIndex:indexPath.section];
NSArray *nameSection = [posturegroups objectForKey:key];
YTPosture *thePosture = [nameSection objectAtIndex:indexPath.row];
YTAppDelegate *appDelegate = [[YTAppDelegate alloc] init];
UIImageView *imageview = (UIImageView *)[cell viewWithTag:3];
imageview.image = [appDelegate getImage:thePosture.postureid];
imageview.backgroundColor = [appDelegate defaultColor];
UILabel *cellLabel1 = (UILabel *)[cell viewWithTag:1];
NSString *aStr = [NSString string];
NSString *bStr = [NSString string];
NSString *title = [NSString string];
if([selection valueForKey:#"symptomExists"]){
aStr = [NSString stringWithFormat:#"%# ",thePosture.memberOrder];
bStr = thePosture.title;
title = [aStr stringByAppendingString:bStr];
}
else{
title = thePosture.title;
}
cellLabel1.text = title;
UILabel *cellLabel2 = (UILabel *)[cell viewWithTag:2];
cellLabel2.text = thePosture.sanskritTransliteration;
YTKeyController *keyController = [[YTKeyController alloc] init];
NSMutableArray *branch = [keyController compileData];
NSString *branchid = thePosture.branchid;
NSString *list = #"1.0,1.0,1.0";
NSArray *listItems = [list componentsSeparatedByString:#","];
for (int i = 0; i < [branch count]; i++) {
YTBranch *theBranch = [branch objectAtIndex:i];
if ([branchid isEqualToString:theBranch.branchid]) {
list = theBranch.key;
listItems = [list componentsSeparatedByString:#","];
break;
}
}
[cell setBackgroundColor:[UIColor colorWithRed:[[listItems objectAtIndex:0] floatValue] green:[[listItems objectAtIndex:1] floatValue] blue:[[listItems objectAtIndex:2] floatValue] alpha:1]];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"Nib name" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UIViewController *destination = segue.destinationViewController;
if ([destination respondsToSelector:#selector(setDelegate:)]) {
[destination setValue:self forKey:#"delegate"];
}
if ([destination respondsToSelector:#selector(setSelection:)]) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
NSUInteger section = [indexPath section];
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [posturegroups objectForKey:key];
YTPosture *thePosture = [nameSection objectAtIndex:indexPath.row];
id object = thePosture;
NSDictionary *aselection = [NSDictionary dictionaryWithObjectsAndKeys:
indexPath, #"indexPath",
object, #"object",
nil];
[destination setValue:aselection forKey:#"selection"];
}
}
I have found what the problem was. I feel a little embarrassed!
The first point is that 'MainStoryboard.storyboardc' is meant to be present. The 'c' on the end stands for the compiled version of 'MainStoryboard.storyboard'.
Well, the image issue, was to do with the fact that I was trying to call a method that amongst other things, was compiling an SQLite statement from inside the method below:
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
Plus I was also calling another method that was looping through about 200 items from inside the same method.
Effectively, these 2 routines were being run for every UITableViewCell that was scrolling into view.
These two issues were crash and burning the system.
Once I removed these issues I decided to call the images using [UIImage imageWithContentsOfFile], without any problems. In fact the 'ListController' now has about 200 thumbnails in it, and it scrolls as smooth as silk.
I feel like a total idiot for wasting your time. Most definitely, I have learnt a big lesson from this. I need to debug & check more thoroughly, and think about every line of code and the context it is written in. In fact, I have learnt that the only information loaded into the method above, should be data that is already indexed.
Champoul's suggestion to use Instruments in Memory Leaks mode, was how I found out the source of this issue. Thanks very much for your help...

Why are my UITableViewCell images only being loaded when I leave then return to a controller and not during viewDidLoad?

Having a slight problem with my UITableViewCell images. I'm loading my data straight from parse.com. My objects array that returns PFObject's is stored inside an NSMutable array named "people".
This is how I display the data in my table:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [[self tableView] dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
Person *current;
if (tableView == [[self searchDisplayController] searchResultsTableView]) {
current = [searchResults objectAtIndex:indexPath.row];
} else {
current = [people objectAtIndex:[indexPath row]];
}
[[cell textLabel] setText: [current valueForKey:#"name"]];
PFFile *userImageFile = [current valueForKey:#"image"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
UIImage *image = [UIImage imageWithData:imageData];
[[cell imageView] setImage: image];
}];
// [[cell imageView] setImage: [current image]];
[[cell detailTextLabel] setText: [current valueForKey:#"notes"]];
return cell;
}
The problem is when I load the app up and this view which is my main loads it doesn't load any images. However when I tap on a row just before the next controller is popped on screen I see the image for that row load and then when I tap the back button and go back to the main view again the rest of the tableViews images load.
Is this something to do with the images not being thumbnail versions?
I've tried wrapping the code in dispatch_async(dispatch_get_main_queue(), ^ { )}; with no luck. Can someone help me solve this issue?
Kind regards
Update to show where I call reload data:
-(void)viewDidAppear:(BOOL)animated {
dispatch_async(dispatch_get_main_queue(), ^{
[[self tableView] reloadData];
});
}
- (void)viewDidLoad
{
[super viewDidLoad];
people = [[NSMutableArray alloc] init];
PFQuery *query = [PFQuery queryWithClassName:#"People"];
[query whereKey:#"active" equalTo:#1];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
people = objects.mutableCopy;
dispatch_async(dispatch_get_main_queue(), ^ {
[[self tableView] reloadData];
});
I don't think there is anything wrong with your loading in your viewDidLoad.
My suspicion is that the UIImageView's frame is actually zero as you did not have a placeholder image while loading the actual images. The cell will not be redrawn again until the next time layoutSubviews is called again, even if your fetched image has loaded. So either set a placeholder image, or call:
[cell setNeedsLayout];
once your image is fully loaded.
Another alternative is to use PFImageView, a subclass of UIImageView, which takes care of everything for you.
PFFile *userImageFile = [current valueForKey:#"image"];
[cell imageView].image = [UIImage imageNamed:#"placeholder.jpg"]; // placeholder image
[cell imageView].file = userImageFile;
[[cell imageView] loadInBackground];
Instead of loading my data directly from parse.com into my tableView I loaded it into an object first. So each object was no longer an PFObject and now a Person object and I stored these in a mutable array which I accessed in my tableView.
Try it:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear]; //this is necessary for most time
//viewDidAppear be called in main thread, so just call reloadData directly
[self.tableView reloadData];
}
As mentioned in Apple document about - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath:
You must register a class or nib file using the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before calling this method.
If you registered a class for the specified identifier and a new cell must be created, this method initializes the cell by calling its initWithStyle:reuseIdentifier: method.
For nib-based cells, this method loads the cell object from the provided nib file. If an existing cell was available for reuse, this method calls the cell’s prepareForReuse method instead.
So, do you forget to use the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before calling cellForRowAtIndexPath method?
Here is a discussion about this.
How I am doing this
In my UIViewController.m
#property (nonatomic, strong) NSMutableDictionary *imageDownloadsInProgress;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.imageDownloadsInProgress = [NSMutableDictionary dictionary];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
SRKProduct *productRecord = [stockArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (!productRecord.image || [productRecord.image isEqualToData:NULL] || productRecord.image.length == 0) {
if (_itemTableView.dragging == NO && _itemTableView.decelerating == NO)
{
[self startIconDownload:productRecord forIndexPath:indexPath];
}
cell.imageView.image = [[UIImage imageNamed:#"Placeholder.png"] makeThumbnailOfSize:CGSizeMake(50,50)];//This is just a placeholder and will be removed when original image is downloaded.
}
return cell;
}
#pragma mark -
- (void)startIconDownload:(SRKProduct *)srkproduct forIndexPath:(NSIndexPath *)indexPath
{
SRKIconDownloader *iconDownloader = [self.imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[SRKIconDownloader alloc] init];
iconDownloader.srkproduct = srkproduct;
[iconDownloader setCompletionHandler:^{
UITableViewCell *cell = [_itemTableView cellForRowAtIndexPath:indexPath];
// Display the newly loaded image
cell.imageView.image = [UIImage imageWithData:srkproduct.image];
NSLog(#"Image %d",[productAdapter updateproductImage:srkproduct]);
// Remove the IconDownloader from the in progress list.
// This will result in it being deallocated.
[self.imageDownloadsInProgress removeObjectForKey:indexPath];
}];
[self.imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
[iconDownloader startDownload];
}
}
Then in SRKIconDownloader.h
#interface SRKIconDownloader : NSObject
#property (nonatomic, strong) SRKProduct *srkproduct;
#property (nonatomic, copy) void (^completionHandler)(void);
And in SRKIconDownloader.m
#implementation SRKIconDownloader
#pragma mark
- (void)startDownload
{
PFQuery *queryCouple = [PFQuery queryWithClassName:#"Product"];
[queryCouple whereKey:#"Name" equalTo:_srkproduct.productName];
[queryCouple findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
if ([objects count] > 0) {
for (PFObject *object in objects) {
PFFile *image = (PFFile *)[object objectForKey:#"Image"];
[image getDataInBackgroundWithBlock:^(NSData *data, NSError *error){
_srkproduct.image = data;
// call our delegate and tell it that our icon is ready for display
if (self.completionHandler)
self.completionHandler();
}];
break;
}
}
else{
}
}
}];
}
#end

Saving state of UITableView cell accessory?

I have gesture recognisers set up on my table view.
Swipe to the right and the accessory changes to an image of a tick
Swipe to the left and is changes to a chevron image
If a cell is tapped, it loads a local HTML file.
If you swipe to the right, the tick appears as it should. However, if you then tap a cell to view a HTML file and come back to the table view, the image reverts to the chevron.
What's the best way to ensure the tick stays as it should?
EDIT
Further code:
From 'viewDidLoad':
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSwipeRight:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
[self.tableView addGestureRecognizer:recognizer];
recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSwipeLeft:)];
//recognizer.delegate = self;
[recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)];
[self.tableView addGestureRecognizer:recognizer];
- (void)handleSwipeLeft:(UISwipeGestureRecognizer *)gestureRecognizer
{
//Get location of the swipe
CGPoint location = [gestureRecognizer locationInView:self.tableView];
//Get the corresponding index path within the table view
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
//Check if index path is valid
if(indexPath)
{
//Get the cell out of the table view
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
//Update the cell or model
cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"disclosure.png"]];
}
}
- (void)handleSwipeRight:(UISwipeGestureRecognizer *)gestureRecognizer
{
CGPoint location = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
if(indexPath)
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
// cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"tick.png"]];
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"MFGCell";
MFGCell *cell = (MFGCell *) [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MFGCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.itemTitle.text = [item objectAtIndex:indexPath.row];
cell.itemDescription.text = [description objectAtIndex:indexPath.row];
cell.itemImageView.image = [UIImage imageNamed:[icons objectAtIndex:indexPath.row]];
return cell;
}
In reaction to the user's swipe you should store the user's choice (e.g. in a private instance variable of type NSMutableArray). When the user comes back to the table view you can then reuse the information in your tableView:cellForRowAtIndexPath: to setup the cell with the correct accessory style.
Property declaration:
#property(nonatomic, retain) NSMutableArray* _accessoryStyle;
Synthesize the property. Then add this snippet to the bottom of handleSwipeLeft: to store the user's choice:
- (void)handleSwipeLeft:(UISwipeGestureRecognizer *)gestureRecognizer
{
[...]
NSNumber* number = [numberWithInt:0];
[_accessoryStyle replaceObjectAtIndex:indexPath.row withObject:number];
}
Add a similar snippet to the bottom of handleSwipeRight::
- (void)handleSwipeRight:(UISwipeGestureRecognizer *)gestureRecognizer
{
[...]
NSNumber* number = [numberWithInt:1];
[_accessoryStyle replaceObjectAtIndex:indexPath.row withObject:number];
}
In tableView:cellForRowAtIndexPath::
NSString* accessoryImageName;
NSNumber* number = [_accessoryStyle objectAtIndex:indexPath.row];
switch ([number intValue])
{
case 0:
accessoryImageName = #"disclosure.png";
break;
case 1:
accessoryImageName = #"tick.png";
break;
default:
// replace with your error handling code
return nil;
}
cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:accessoryImageName]];
For all this to work you need to initialize the _accessoryStyle array with the same number of elements that you expect your table view to have cells. For instance, in your view controller's viewDidLoad:
- (void) viewDidLoad
{
[super viewDidLoad];
self._accessoryStyle = [NSMutableArray arrayWithCapacity:0];
NSNumber* defaultAccessoryStyle = [numberWithInt:0];
int numberOfRows = 17; // get the real number from somewhere
for (int index = 0; index < numberOfCells; ++index)
[_accessoryStyle addObject:defaultAccessoryStyle];
}
And to balance this you need to add
- (void) viewDidUnload
{
[super viewDidUnload];
self._accessoryStyle = nil;
}
There is still much room for improvement:
Find better variable names
Use an enumeration for the different styles instead of just hardcoded numbers 0 and 1
Do not allocate a new UIImageView for each table view cell, just allocate two of them and use the right one depending on the accessory style
For your problem, there is an underlying logic issue because there is either a swipe left event firing when it should not or the views are just being unloaded and resetting to default. See if you can log when the events fire; otherwise the state of the view should be preserved. Also what I would do is add an extra state variable like int currentCellState that you change when you enter your different states to keep track of your states. Then in your viewDIdLoad make sure that all your data and your view are in sync, ie the value of currentCellState matches the state of your view.
The best way to do this is to put the images/buttons you have in an array, and each time the view loads it shows the item which index is selected..
in order to do this, the swipeMethode should be modified to something like this
-(void)swipeMethod: (UISwipeGestureRecognizer *) sender
{
if(sender.direction ==
UISwipeGestureRecognizerDirectionLeft && index < [myArray count]){
[self setSelectedIndex:index+1 animated:YES];
index++;
}else if (sender.direction == UISwipeGestureRecognizerDirectionRight && index > 0) {
[self setSelectedIndex:index-1 animated:YES];
index--;
}else {
return;
}
}
in the viewDidLoad add this code:
leftRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeMethod:)];
[leftRecognizer setDirection: UISwipeGestureRecognizerDirectionLeft];
[self.tableView addGestureRecognizer:leftRecognizer];
rightRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeMethod:)];
[rightRecognizer setDirection: UISwipeGestureRecognizerDirectionRight];
[self.tableView addGestureRecognizer:rightRecognizer];

How can i push a TableView on clicking a button and initialize the TableViewCell with NSDictionary?

Can anyone please help me about how to push a table view on clicking a button.
I want to load the messages from NSMutableArray to the table view cells and NSMutableArray is loaded with the data parsed from a URL..
-(IBAction)readMessages:(id)sender
{
// i want to push the tableview when clicking the button in relation with this method
// WHAT MUST I DO HERE?
}
Instead of asking a new question i liked to edit this one, since the matter is in the same aspect..
I now can create the tableview programatically, but i cant initialize its cells with the data i get from Json array. Here is my code:
NSString *str1=[#"?username=" stringByAppendingString:userNameField.text];
NSString *str2=[#"&password=" stringByAppendingString:passwordField.text];
NSString *str3=[str1 stringByAppendingString:str2];
NSString *str4 =[#"http://" stringByAppendingString:serverField.text];
NSURL *url=[NSURL URLWithString:[str4 stringByAppendingString:[#"/ipad/login.php" stringByAppendingString:str3]]];
//get the url to jsondata
NSData *jSonData=[NSData dataWithContentsOfURL:url];
if (jSonData!=nil) {
NSError *error=nil;
id result=[NSJSONSerialization JSONObjectWithData:jSonData options:
NSJSONReadingMutableContainers error:&error];
if (error==nil) {
NSDictionary *mess=[result objectForKey:#"message"];
NSDictionary *messContent=[mess valueForKeyPath:#"message"];
NSDictionary *messID=[mess valueForKeyPath:#"ID"];
NSString*key1=[ result objectForKey:#"key" ];
NSString *s1=[#"http://" stringByAppendingString:serverField.text];
NSString *s2=[s1 stringByAppendingString:#"/ipad/button.php"];
NSURL *url2=[NSURL URLWithString:[s2 stringByAppendingString:[#"?key=" stringByAppendingString:key1]]];
NSData *data2=[NSData dataWithContentsOfURL:url2];
id result2=[NSJSONSerialization JSONObjectWithData:data2 options:NSJSONReadingMutableContainers error:nil];
mesID = [NSMutableArray array];//saving meesage ID s to NSMutableArray
content = [NSMutableArray array];
// i logged here and it saves the data, now i want to display my data in table view
for (NSDictionary *data in mess) {
[mesID addObject:[data objectForKey:#"ID"]];
[content addObject:[data objectForKey:#"message"]];
[[NSUserDefaults standardUserDefaults] setObject:messID forKey:#"message"];
[[NSUserDefaults standardUserDefaults] setObject:messContent forKey:#"messContent"];
//messID will be saved as the Title of the cells and messContent will be displayed as the text area of that cell, opening in a new view
And this is the output, i want to set the titles of cells as ID and their content as text:
2012-01-17 16:26:59.873 ipad_Teslim[940:f803] MessID: (
1,
3
)
2012-01-17 16:26:59.875 ipad_Teslim[940:f803] Content: (
asdf,
"this is a test"
)
As i have mentioned in my code too, messID will be saved as the Title of the cells and messContent will be displayed as the text area of that cell, opening in a new view.. How can i do it now? Please Help me, there are a lot of tutorials there, i looked a lot too but couldn't break this problem.
Try this :
-(IBAction)readMessages:(id)sender {
SecondView *secondView =[[SecondView alloc] initWithNibName:#"SecondView" bundle:nil];
[self presentModalViewController:secondView animated:YES];
}
SecondView is your UIViewController subclass which hold a UITableView.
Q1: U no need to add a navigation to return back to ur main page.
When ever u use
[self.navigationController pushViewController:next animated:YES];
by defaults it will creates back navigation in the next view to push return back.
in case it doesn't created yet, Try the following code in next view:
- (void)viewDidLoad
{
[super viewDidLoad];
//To set the back buttin on leftside of Navigation bar
UIBarButtonItem *backButton = [[[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleDone target:self action:#selector(backclick:)] autorelease];
self.navigationItem.leftBarButtonItem = backButton;
}
- (IBAction)backclick:(id)sender //first declrared in .h file
{
// To goback to the previous view
[self.navigationController popViewControllerAnimated:YES];
}
If u have a navigation control and if u want to pushed by navigation try the following:
-(IBAction)readMessages:(id)sender {
NextView *next = [[NextView alloc]initWithNibName:#"NextView" bundle:nil];
[self.navigationController pushViewController:next animated:YES];
[next release];
}
if u dont have a navigation control and if u want to just display the next view, try the following:
-(IBAction)readMessages:(id)sender {
NextView *next =[[NextView alloc] initWithNibName:#"NextView" bundle:nil];
[self presentModalViewController:next animated:YES];
[next release];
}
if u are having sub view in the same class try the following:
-(IBAction)readMessages:(id)sender {
[self.view addsubview nextView];
}
Yes U can,
try this to create xib programitically in viewDidload:
UIView *view1 = [[UIView alloc]initWithFrame:CGRectMake(10, 10,300,460)];
view1.backgroundColor = [UIColor redColor];
[self.view addSubview:view1];
But better to create by using
following path in xcode menu:
File-> New -> NewFile -> UIViewControllerSubClass -> Next -> Next -> Create
Or simply Drag & drop an view from ur Interface Builder
Q2: U can initialize ur tableView cells with JSONArray:
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [JSONarray count]; //***********
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.text = [JSONarray objectAtIndexIndexPath.row]; //***********
}