I have a test for whether a cell was clicked by the user. If it was clicked, the image changes. I want that image to remain checked, even if the user clicks it again or the app reloads.
It's really close to working. Right now, the image changes from the grey version to the green version when the cell was selected. The problem is the grey image shows back up when the user clicks the cell again. What do I need to do to fix this?
//begin checking for selected row and add checkmark
- (NSString *)getKeyForIndex:(int)index
{
return [NSString stringWithFormat:#"KEY%d",index];
}
- (BOOL) getCheckedForIndex:(int)index
{
if([[[NSUserDefaults standardUserDefaults] valueForKey:[self getKeyForIndex:index]] boolValue]==YES)
{
return YES;
}
else
{
return NO;
}
}
- (void) checkedCellAtIndex:(int)index
{
BOOL boolChecked = [self getCheckedForIndex:index];
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:!boolChecked] forKey:[self getKeyForIndex:index]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tableList count];
}
// Customize the content and the look of table view cells.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UIImage *greyck =[UIImage imageNamed:#"greycheck-sd.png"];
UIImage *greenck =[UIImage imageNamed:#"greencheck-sd.png"];
//step 1 check to see if we can reuse a cell that has just rolled off the screen
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath: indexPath];
//step 2: if there are no cells to be reused, create a new cell
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
//step 3: set the text in the cell using items from the array
cell.textLabel.text = [tableList objectAtIndex:indexPath.row];
//set custom font
cell.textLabel.font = [UIFont fontWithName:#"Chalkboard SE" size:18.0f];
//check for previously viewed then set the image
if([self getCheckedForIndex:indexPath.row]==YES)
{
//sets green checkmark after user clicks
cell.imageView.image = greenck;
}
else
{
cell.imageView.image = greyck;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIImage *greyck =[UIImage imageNamed:#"greycheck-sd.png"];
UIImage *greenck =[UIImage imageNamed:#"greencheck-sd.png"];
NSString *str= [tableList objectAtIndex:indexPath.row];
if ([str isEqual:#"introduction"])
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *moviePath = [bundle pathForResource:#"Step1-Intro" ofType:#"mp4"];
NSURL *movieURL = [NSURL fileURLWithPath:moviePath];
MPMoviePlayerController *theMovie = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
theMovie.scalingMode = MPMovieScalingModeAspectFill;
[theMovie play];
MPMoviePlayerViewController *moviePlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:movieURL];
[self presentMoviePlayerViewControllerAnimated:moviePlayer];
}
else if ([str isEqual:#"skating"])
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *moviePath = [bundle pathForResource:#"Step2-Skating" ofType:#"mp4"];
NSURL *movieURL = [NSURL fileURLWithPath:moviePath];
MPMoviePlayerController *theMovie = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
theMovie.scalingMode = MPMovieScalingModeAspectFill;
[theMovie play];
MPMoviePlayerViewController *moviePlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:movieURL];
[self presentMoviePlayerViewControllerAnimated:moviePlayer];
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
//Use checkedCellAtIndex for check or uncheck cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self checkedCellAtIndex:indexPath.row];
if([self getCheckedForIndex:indexPath.row]==YES)
{
cell.imageView.image = greenck;
}
else
{
cell.imageView.image = greyck;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You do set the displayed image to green at 2 places with
if([self getCheckedForIndex:indexPath.row]==YES)
{
cell.imageView.image = greenck;
}
But getCheckedForIndex: returns YES
if([[[NSUserDefaults standardUserDefaults] valueForKey:[self getKeyForIndex:index]] boolValue]==YES)
So far, so good. But it seems to me that you never update your user defaults. If so, you always would return the same value, and nothing will change.
Related
I have two UIViewControllers with tableview. When the first cell loads in the second UIViewController it calls the cellForRowAtIndexPath in the same class but when it loads the second cell it calls the first viewControllers cellForRowAtIndexPath.
My code as follows:
SecondViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NotificationsTableViewCell *cell = [self.notificationsTableView dequeueReusableCellWithIdentifier:#"NotificationCell"];
if(cell == nil)
{
cell = [[NotificationsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"NotificationCell"];
}
NSMutableDictionary *cellData = [self.databaseCall transactionFromDatabase:indexPath.row];
NSLog(#"%#", cellData);
cell.goalNameLabel.text = [cellData objectForKey:#"categoryName"];
NSString *cardTypeId = [cellData objectForKey:#"cardTypeId"];
NSString *tipsId = [cellData objectForKey:#"tipsId"];
if([self.defaultCardTypeId containsObject:cardTypeId])
{
NSUInteger index = [self.defaultCardTypeId indexOfObject:cardTypeId];
[self.defaultCardTypeId replaceObjectAtIndex:index withObject:cardTypeId];
}
else{
[self.defaultCardTypeId addObject:cardTypeId];
}
if([self.defaultTipId containsObject:tipsId])
{
NSUInteger index = [self.defaultCardTypeId indexOfObject:cardTypeId];
[self.defaultTipId replaceObjectAtIndex:index withObject:cardTypeId];
}
else{
[self.defaultTipId addObject:tipsId];
}
if([cardTypeId isEqualToString:#"1"])
{
UIImage *cellImage = [UIImage imageNamed:#"icon2.jpg"];
cell.cardTypeImage.image = cellImage;
cell.cardTypeLabel.text = #"GOOD TO KNOW";
cell.cardTypeLabel.textColor = [UIColor colorWithRed:252/255.0 green:171/255.0 blue:19/255.0 alpha:1];
}
if([cardTypeId isEqualToString:#"2"])
{
UIImage *cellImage = [UIImage imageNamed:#"icon1.jpg"];
cell.cardTypeImage.image = cellImage;
cell.cardTypeLabel.text = #"TO CONSIDER";
cell.cardTypeLabel.textColor = [UIColor colorWithRed:0/255.0 green:191/255.0 blue:243/255.0 alpha:1];
}
cell.notificationCard.layer.cornerRadius = 5;
// Configure the cell...
return cell;
}
FirstViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
GoalsCustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"GoalsListCell" forIndexPath:indexPath];
if(cell == nil)
{
cell = [[GoalsCustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"GoalsListCell"];
}
NSInteger indexOfCategory = [self.databaseCall.arrColumnName indexOfObject:#"CategoryName"];
NSInteger indexOfImage = [self.databaseCall.arrColumnName indexOfObject:#"CategoryImage"];
NSInteger indexOfActive = [self.databaseCall.arrColumnName indexOfObject:#"coulmn"];
//Assigning the contents of cell
cell.goalName.text = [NSString stringWithFormat:#"%#", [[self.arrCategoryTitle objectAtIndex:indexPath.row] objectAtIndex:indexOfCategory]];
NSString *categoryImage = [NSString stringWithFormat:#"%#", [[self.arrCategoryTitle objectAtIndex:indexPath.row] objectAtIndex:indexOfImage]];
NSString *activeStatus = [NSString stringWithFormat:#"%#", [[self.arrCategoryTitle objectAtIndex:indexPath.row] objectAtIndex:indexOfActive]];
UIImage *cellImage = [UIImage imageNamed:categoryImage];
cell.goalImage.image = cellImage;
[cell.favouriteButton addTarget:self action:#selector(favouriteButtonPressed:) forControlEvents:UIControlEventTouchDown];
NSMutableString *selectedRowImage = [[NSMutableString alloc] initWithString:#""];
//Checking whether the category is selected by user or not
if([activeStatus isEqualToString:#"yes"])
{
selectedRowImage = [NSMutableString stringWithFormat:#"starsel.png"];
}
else
{
selectedRowImage = [NSMutableString stringWithFormat:#"stardef.png"];
}
UIImage *favouriteIconImage = [UIImage imageNamed:selectedRowImage];
[cell.favouriteButton setBackgroundImage:favouriteIconImage forState:UIControlStateNormal];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Configure the cell...
return cell;
}
Thanks in advance.
First of all i would say sorry for this stupid question.
The problem is due to the tableview datasource as specifies by #Paulw11, #Onik IV, #Kannan Vora. The secondViewController tableView has the datasource of firstViewController.
I'm trying to show a button on certain cells in my tableview depending on type of image showing in that cell
soo i have two NSMutableArray's one holding the thumbnail image URL's and the other NSMutableArray holding the type of the url if it was image or video
the problem is the button doesn't show on all the video type cells
here is my code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TVCustomCell";
TVCustom *cell = (TVCustom *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"TVCustom" owner:self options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[TVCustom class]]) {
cell = (TVCustom *) currentObject;
break;
}
}
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *imgURL = [arrayImagesURL objectAtIndex:indexPath.row];
NSURL *imageURL = [NSURL URLWithString:imgURL];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:imageData];
cell.thumbImg.image = image;
});
});
NSString *MediaType = [NSString stringWithFormat:#"%#", [arrayType objectAtIndex:indexPath.row]];
if ([MediaType isEqualToString:#"video"]) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(120, 319, 50, 30);
[button setTitle:#"Play" forState:UIControlStateNormal];
button.tag = indexPath.row;
[button addTarget:self action:#selector(PlayBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor= [UIColor clearColor];
[cell.contentView addSubview:button];
}
return cell;
}
i don't know what i'm doing wrong, any idea ?
I see different problems here.
First
Your code creates new buttons every time the user scrolls in the tableView. This is because you use dequeueReusableCellWithIdentifier which is great and returns existing cells instead of creating new ones.
But then you add a new button on cells which already exist (and may or may not have a button)
[cell.contentView addSubview:button];
I suggest you add the button in your TVCustom class:
In the TVCustom.h
#property (strong, nonatomic) UIButton *viedoButton;
In TVCustom.m
- (UIButton *)videoButton
{
if (!_videoButton) {
_videoButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_videoButton.frame = CGRectMake(120, 319, 50, 30);
[_videoButton setTitle:#"Play" forState:UIControlStateNormal];
[_videoButton addTarget:self action:#selector(PlayBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
_videoButton.backgroundColor= [UIColor clearColor];
[self.contentView _videoButton];
}
return _videoButton;
}
Then in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath you only do :
if ([MediaType isEqualToString:#"video"]) {
cell.videoButton.hidden = NO;
} else {
cell.videoButton.hidden = YES;
}
This way every cell will have only one button and it will be visible only when you need it but not re-allocated every time. But PlayBtnClicked will have to be in your TVCustom class and call your viewController through a delegate.
Second
You should not handle the loading of your image in the cellForRowAtIndexPath method. Encapsulate it your TVCustom class:
In TVCustom.h
#property (strong, nonatomic) NSString *imgURL;
In TVCustom.m
- (void)setImgURL:(NSString *)imgURL
{
_imgURL = imgURL;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *imageURL = [NSURL URLWithString:imgURL];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:imageData];
self.thumbImg.image = image;
});
});
}
Then in cellForRowAtIndexPath it will look like :
cell.imgURL = [arrayImagesURL objectAtIndex:indexPath.row];
Third
I do not know if you have any particular reason for this:
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"TVCustom" owner:self options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[TVCustom class]]) {
cell = (TVCustom *) currentObject;
break;
}
}
}
But a good way to do this is in your viewController's viewDidLoad (or other method):
NSString *identifier = #"TVCustomCell";
NSString *nibName = #"TVCustom";
UINib *cellNib = [UINib nibWithName:nibName];
[self.tableView registerNib:cellNib forCellReuseIdentifier:identifier];
Your final code should look like :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TVCustomCell";
TVCustom *cell = (TVCustom *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.imgURL = [arrayImagesURL objectAtIndex:indexPath.row];
if ([MediaType isEqualToString:#"video"]) {
cell.videoButton.hidden = NO;
} else {
cell.videoButton.hidden = YES;
}
return cell;
}
I have an existing UITableView that lists a number of cafes in the area. The data for each cafe is being pulled from a MySQL database. When a user clicks on a cafe (cell), it brings a user to a detail view. Currently, users can "Favorite" a cafe by clicking on the star image in each cell (this adds the favorited cell to FavoritesTableView). However, I want users to be able to add a cafe to the FavoritesTableView from the DetailView as well (in other words, "favorite" a cafe from the DetailView). Does anyone know how I would implement this?
Right now, I have the star button in place in my DetailView.m (cafe details):
- (IBAction)buttonpressed:(UIButton *)fave {
if (!checked) {
[checkedButton setImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateNormal];
checked = YES;
}
else if (checked) {
[checkedButton setImage:[UIImage imageNamed:#"unchecked.png"] forState:UIControlStateNormal];
checked = NO;
}
}
ViewController.m (cafes tableview)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *strainTableIdentifier = #"StrainTableCell";
StrainTableCell *cell = (StrainTableCell *)[tableView dequeueReusableCellWithIdentifier:strainTableIdentifier];
if (cell == nil)
cell = [[StrainTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strainTableIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"StrainTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
if (tableView == self.searchDisplayController.searchResultsTableView) {
NSLog(#"Using the search results");
cell.titleLabel.text = [[searchResults objectAtIndex:indexPath.row] objectForKey:#"Title"];
cell.descriptionLabel.text = [[searchResults objectAtIndex:indexPath.row] objectForKey:#"Description"];
cell.ratingLabel.text = [[searchResults objectAtIndex:indexPath.row] objectForKey:#"Rating"];
cell.ailmentLabel.text = [[searchResults objectAtIndex:indexPath.row] objectForKey:#"Ailment"];
cell.actionLabel.text = [[searchResults objectAtIndex:indexPath.row] objectForKey:#"Action"];
cell.ingestLabel.text = [[searchResults objectAtIndex:indexPath.row] objectForKey:#"Ingestion"];
NSLog(#"%#", searchResults);
} else {
NSLog(#"Using the FULL LIST!!");
cell.titleLabel.text = [[Strains objectAtIndex:indexPath.row] objectForKey:#"Title"];
cell.descriptionLabel.text = [[Strains objectAtIndex:indexPath.row] objectForKey:#"Description"];
cell.ratingLabel.text = [[Strains objectAtIndex:indexPath.row] objectForKey:#"Rating"];
cell.ailmentLabel.text = [[Strains objectAtIndex:indexPath.row] objectForKey:#"Ailment"];
cell.actionLabel.text = [[Strains objectAtIndex:indexPath.row] objectForKey:#"Action"];
cell.ingestLabel.text = [[Strains objectAtIndex:indexPath.row] objectForKey:#"Ingestion"];
}
NSMutableDictionary *item = [Strains objectAtIndex:indexPath.row];
cell.textLabel.text = [item objectForKey:#"text"];
[item setObject:cell forKey:#"StrainTableCell"];
BOOL checked = [[item objectForKey:#"checked"] boolValue];
NSLog(#"%i",checked);
UIImage *image = (checked) ? [UIImage imageNamed:#"checked.png"] : [UIImage imageNamed:#"unchecked.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
button.frame = frame;
[button setBackgroundImage:image forState:UIControlStateNormal];
[button addTarget:self action:#selector(checkButtonTapped:event:) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor clearColor];
cell.accessoryView = button;
return cell;
}
- (void)checkButtonTapped:(id)sender event:(id)event
{
NSLog(#"made it here and event is %#",event);
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.StrainTableView];
NSIndexPath * indexPath ;
indexPath = [self.StrainTableView indexPathForRowAtPoint: currentTouchPosition];
NSLog(#"indexpath is below");
NSLog(#"%#",indexPath);
if (indexPath != Nil)
{
NSMutableDictionary *item = [Strains objectAtIndex:indexPath.row];
BOOL isItChecked = [[item objectForKey:#"checked"] boolValue];
NSMutableArray *quickArray = [[NSMutableArray alloc] initWithArray:Strains];
[quickArray replaceObjectAtIndex:indexPath.row withObject:item];
[item setObject:[NSNumber numberWithBool:!isItChecked] forKey:#"checked"];
Strains = [quickArray copy];
[StrainTableView reloadData];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
StrainDetailViewController *detailViewController = [[StrainDetailViewController alloc] initWithNibName:#"StrainDetailViewController" bundle:nil]; if ([searchResults count]) {
detailViewController.title = [[searchResults objectAtIndex:indexPath.row] objectForKey:#"Title"];
detailViewController.strainDetail = [searchResults objectAtIndex:indexPath.row];
} else {
detailViewController.title = [[Strains objectAtIndex:indexPath.row] objectForKey:#"Title"];
detailViewController.strainDetail = [Strains objectAtIndex:indexPath.row];
NSLog(#"%#", Strains);
}
[self.navigationController pushViewController:detailViewController animated:YES];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"strains"] != Nil) {
NSData *dataSave = [[NSUserDefaults standardUserDefaults] objectForKey:#"strains"];
Strains = [NSKeyedUnarchiver unarchiveObjectWithData:dataSave];
}
if (favoritesArray == Nil) {
favoritesArray = [[NSMutableSet alloc] init];
}
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"favorites"] != Nil) {
NSData *dataSave = [[NSUserDefaults standardUserDefaults] objectForKey:#"favorites"];
favoritesArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataSave];
}
I'm just not sure what sort of code I would add to this in order to make the "Checked" button add the selected cell from UITableView to FavoritesTableView.
Hope this made sense. Any ideas?
From a maintainability stand point, the approach I generally take might not be the best. However, from an implementation stand point, it's very easy. I pass a reference to my object (in your case, the object for the table view that was selected) to the details view. From the details view, I can update that object by switching the flag. Since this object is the same object as the table view, updating it will also update the table view (you may have to redraw the table view cell). Last, I update the database from the details view.
Edit
So in your code, it would look like this.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
StrainDetailViewController *detailViewController = [[StrainDetailViewController alloc] initWithNibName:#"StrainDetailViewController" bundle:nil]; if ([searchResults count]) {
detailViewController.title = [[searchResults objectAtIndex:indexPath.row] objectForKey:#"Title"];
//YOU'RE ALREADY DOING IT :)
detailViewController.strainDetail = [searchResults objectAtIndex:indexPath.row];
} else {
detailViewController.title = [[Strains objectAtIndex:indexPath.row] objectForKey:#"Title"];
//YOU'RE ALREADY DOING IT :)
detailViewController.strainDetail = [Strains objectAtIndex:indexPath.row];
NSLog(#"%#", Strains);
}
[self.navigationController pushViewController:detailViewController animated:YES];
}
You're already sending a reference to the details view! This makes things easy. In your details view, whenever someone favorites a strain, set the favorite flag for the strainDetail object. This will also update the strainDetail object in the table view. It works like this because you're passing a reference (also referred to as a pointer) to the detail view. In other words, both of your strainDetail objects, aren't objects but pointers to a memory address. If you update either strainDetail, the the memory address for both strainDetail gets changed. Not the strainDetail pointer itself. Here's an link for further explanation https://softwareengineering.stackexchange.com/questions/17898/whats-a-nice-explanation-for-pointers
So your Details view handler will look something like this
- (IBAction)buttonpressed:(UIButton *)fave {
if (!checked) {
[checkedButton setImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateNormal];
checked = YES;
}
else if (checked) {
[checkedButton setImage:[UIImage imageNamed:#"unchecked.png"] forState:UIControlStateNormal];
checked = NO;
}
//updates in both the detail view and the table view
BOOL isItChecked = [[self.strainDetail objectForKey:#"checked"] boolValue];
[self.strainDetail setObject:[NSNumber numberWithBool:checked] forKey:#"checked"];
}
I also suggest creating a Strain class to hold all of your strain data. Working with dictionaries is not fun, is error prone, and takes forever to figure out what keys you need.
You can save "favorite" state in a global variable/singleton and retrieve it when needed.
This way, even if your table view is deallocated, you won't lose its cells' state.
I am facing a problem where the didselectrowatindexpath is not getting called.
My tableview is set in viewdidload as follows:
- (void)viewDidLoad
{
//[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.tableView reloadData];
self.detailViewController = (klViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
}
Now my cellForRowAtIndexPath gets called and I am reusing the cells as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"menuSelection1";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
AmbassadorInfoData * data = [self.LoadMenuItems objectAtIndex:indexPath.row];
cell.textLabel.text = data.treeName;
return cell;
}
I have checked other answers and mostly said that there could be the use of didDeselectRowAtIndexPath might be used. But this is not the case here.
Now my problem is in my split view controller whenever I select any option the corresponding detail view doesn't get displayed and is blank. None of my methods in the rootview controller (klViewController) gets called. What could be the reason for this?
Following are the all methods related to UITableView. It is implemented in the same class as in viewDidLoad
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.LoadMenuItems count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"menuSelection1";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
AmbassadorInfoData * data = [self.LoadMenuItems objectAtIndex:indexPath.row];
cell.textLabel.text = data.treeName;
return cell;
}
-(NSArray *)LoadMenuItems
{
NSMutableArray * menuData = [[NSMutableArray alloc] initWithCapacity:5];
AmbassadorInfoData * data = [[AmbassadorInfoData alloc] init];
data.treeName = #"Ambassador Info";
data.treeDescription = #"This is temp data.";
NSString * file = [[NSBundle mainBundle] pathForResource:#"oak" ofType:#"jpg"];
data.treePhoto = [UIImage imageWithContentsOfFile:file];
[menuData addObject:data];
data=nil;
data = [[AmbassadorInfoData alloc] init];
data.treeName = #"AmbassadorInternalList";
data.treeDescription = #"This is temp data.).";
file = [[NSBundle mainBundle] pathForResource:#"DouglasFir" ofType:#"jpg"];
data.treePhoto = [UIImage imageWithContentsOfFile:file];
[menuData addObject:data];
data=nil;
data = [[AmbassadorInfoData alloc] init];
data.treeName = #"Text 3";
data.treeDescription = #"This is temp data.";
file = [[NSBundle mainBundle] pathForResource:#"SugarMaple" ofType:#"jpg"];
data.treePhoto = [UIImage imageWithContentsOfFile:file];
[menuData addObject:data];
data=nil;
data = [[AmbassadorInfoData alloc] init];
data.treeName = #"Text 4";
data.treeDescription = #"This is temp data.";
file = [[NSBundle mainBundle] pathForResource:#"RedMaple" ofType:#"jpg"];
data.treePhoto = [UIImage imageWithContentsOfFile:file];
[menuData addObject:data];
data=nil;
data = [[AmbassadorInfoData alloc] init];
data.treeName = #"Text 5";
data.treeDescription = #"This is temp data.";
file = [[NSBundle mainBundle] pathForResource:#"Pine" ofType:#"jpg"];
data.treePhoto = [UIImage imageWithContentsOfFile:file];
[menuData addObject:data];
menuItems = [[NSArray alloc] initWithArray:(NSArray *)menuData];
data=nil;
return menuItems;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
AmbassadorInfoData * selectedItem =(AmbassadorInfoData *)[self.menuItems objectAtIndex:[self.tableView indexPathForSelectedRow].row];
detailObject = selectedItem;
[detailViewController setDetailItem:detailObject];
}
#end
It could be that your program is stuck in a long running operation because of the way you're using LoadMenuItems. Every time numberOfRowsInSection and cellForRowAtIndexPath is called, you're recreating the entire array -- this is bad. You should call LoadMenuItems once, in viewDidLoad perhaps, and once you've created that array, use it, not a call to LoadMenuItems when you need to get data out. I don't know if this is what is causing your problem, but you should definitely fix it, and see if it makes a difference.
Try these modifications:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"TableView methods are called! problem is getting AmbassadorInfoData object")
AmbassadorInfoData * selectedItem =(AmbassadorInfoData *)[self.menuItems objectAtIndex:indexPath.row];
if (!selectedItem) NSLog(#"Unable to get Ambassador object, check your menuItems");
[self.detailViewController setDetailItem:selectedItem]; //notice "self."
}
also, uncomment [super viewDidLoad];
EDIT:
it appears that you are using the code you don't understand, in this case i am suggesting to learn some basics....here's a great site that helped me understand things quite a few times
and this is a guide from Apple: Table View Programming Guide
I want to set the checkmark in front of the cell. When I click on the checkmark icon the correct function is called and the datasource is changed.
I call [table reloadData] and after that even cellForRowAtIndexPath is also called but it is always dequeing the old table cell and not reloading it with the new data source..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"CheckboxCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
NSLog(#"cellForRowAtIndexPath row: %d", indexPath.row);
// use CustomCell layout
CheckboxCell *checkboxCell;
if(cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CheckboxCell" owner:self options:nil];
for (id currentObject in topLevelObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]){
checkboxCell = (CheckboxCell *)currentObject;
break;
}
}
} else {
checkboxCell = (CheckboxCell *)cell; // return cell;
}
Observation *observation = [observations objectAtIndex:indexPath.row];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"dd.MM.yyyy";
[dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
NSString *nowString = [dateFormatter stringFromDate:observation.date];
[dateFormatter release];
NSString *amountString = [[NSString alloc] initWithFormat:#"%d", observation.amount];
checkboxCell.name.text = [observation.organism getNameDe];
checkboxCell.date.text = nowString;
checkboxCell.amount.text = amountString;
checkboxCell.latName.text = [observation.organism getLatName];
// Define the action on the button and the current row index as tag
[checkboxCell.checkbox addTarget:self action:#selector(checkboxEvent:) forControlEvents:UIControlEventTouchUpInside];
[checkboxCell.checkbox setTag:observation.observationId];
// Define the action on the button and the current row index as tag
[checkboxCell.remove addTarget:self action:#selector(removeEvent:) forControlEvents:UIControlEventTouchUpInside];
[checkboxCell.remove setTag:observation.observationId];
// Set checkbox icon
if(observation.submitToServer) {
NSLog(#"CHECKED");
checkboxCell.checkbox.imageView.image = [UIImage imageNamed:#"check_icon.png"];
} else {
NSLog(#"UNCHECKED");
checkboxCell.checkbox.imageView.image = [UIImage imageNamed:#"checkbox.gif"];
}
[amountString release];
[observation release];
return checkboxCell;
// Set checkbox icon
if(observation.submitToServer) {
NSLog(#"CHECKED");
checkboxCell.checkbox.imageView.image = [UIImage imageNamed:#"check_icon.png"];
} else {
NSLog(#"UNCHECKED");
checkboxCell.checkbox.imageView.image = [UIImage imageNamed:#"checkbox.gif"];
}
[amountString release];
[observation release];
return checkboxCell;
}
I think I'm doing something in the cellForRowAtIndexPath wrong.. Can anybody help me?
Edit:
The first problem could be fixed (Thanks to Maggie). Now the checkmark is changing the first time correctly. But somehow if I change it on another cell its crashing on the following line:
- (void) checkboxEvent:(UIButton *)sender
{
UIButton *button = (UIButton *)sender;
NSNumber *number = [NSNumber numberWithInt:button.tag];
for(Observation *ob in observations) {
if(ob.observationId == [number intValue]) { // <---- IT'S CRASHING HERE
ob.submitToServer = !ob.submitToServer;
NSLog(#"New value: %#", (ob.submitToServer) ? #"YES" : #"NO");
}
}
[table reloadData];
}
But the observation object isn't nil. Any ideas?
Replace
else {
return cell;
}
part with
else {
checkboxCell = (CheckboxCell *)cell;
}
and fill your checkboxCell with apropriate data.