i have one table view with left side check button. for first 6 data its working well. when i add three more data to my table view after that when i check my first data my 7 the data also getting check. like wise when i check one data some other data also repeatng with check mark.....
i am using core data....if any one could answer this ......
#interface ViewController ()
{
NSDateFormatter *formatter;
}
#property (strong) NSMutableArray *notes;
#end
#implementation ViewController
#synthesize tableView;
#synthesize addButton;
#synthesize catefetchedResultsController;
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.title = #"My Notes";
tableView.dataSource = self;
tableView.delegate = self;
[self.view addSubview:tableView];
formatter = [[NSDateFormatter alloc] init];
formatter.doesRelativeDateFormatting = YES;
formatter.locale = [NSLocale currentLocale];
formatter.dateStyle = NSDateFormatterShortStyle;
formatter.timeStyle = NSDateFormatterNoStyle;
CATransition *animation = [CATransition animation];
[animation setDuration:2.0];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromTop];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
[[addButton layer] addAnimation:animation forKey:#"SwitchToDown"];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Notes"];
NSError *error = nil;
self.notes = [[managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
NSSortDescriptor *titleSorter= [[NSSortDescriptor alloc] initWithKey:#"mod_time" ascending:NO];
[self.notes sortUsingDescriptors:[NSArray arrayWithObject:titleSorter]]
;
NSLog(#"Your Error - %#",error.description);
[tableView reloadData];
}
#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.notes.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//UIButton *testButton;
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
UIButton *testButton = [[UIButton alloc]initWithFrame:CGRectMake(5, 5, 40, 40)];
[testButton setImage:[UIImage imageNamed:#"oval"] forState:UIControlStateNormal];
[testButton setImage:[UIImage imageNamed:#"tick"] forState:UIControlStateSelected];
[testButton addTarget:self action:#selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:testButton];
[cell setIndentationLevel:1];
[cell setIndentationWidth:45];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
// Configure the cell...
NSManagedObject *note = [self.notes objectAtIndex:indexPath.row];
NSDate *date = [note valueForKey:#"mod_time"];
NSString *dateString = [formatter stringFromDate:date];
cell.textLabel.text = [note valueForKey:#"title"];
cell.detailTextLabel.text = dateString;
return cell;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
//cell.textLabel.font = [UIFont fontNamesForFamilyName:#"Avenir"];
cell.textLabel.font = [UIFont fontWithName:#"Avenir" size:19.0];
cell.detailTextLabel.font=[UIFont fontWithName:#"Avenir" size:15.0];
}
-(void)buttonTouched:(id)sender
{
UIButton *btn = (UIButton *)sender;
if( [[btn imageForState:UIControlStateNormal] isEqual:[UIImage imageNamed:#"oval"]])
{
[btn setImage:[UIImage imageNamed:#"tick"] forState:UIControlStateNormal];
}
else
{
[btn setImage:[UIImage imageNamed:#"oval"] forState:UIControlStateNormal];
}
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = (UITableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (IBAction)addButtonPressed:(id)sender {
AddNoteViewController *addNoteVC = [AddNoteViewController new];
// to remove unused warning....
#pragma unused (addNoteVC)
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)cTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSManagedObjectContext *context = [self managedObjectContext];
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete object from database
[context deleteObject:[self.notes objectAtIndex:indexPath.row]];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't Delete! %# %#", error, [error localizedDescription]);
return;
}
// Remove device from table view
[self.notes removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (IBAction)btnClick:(id)sender {
}
#end
okay i saw your code and yes the issue is reusability just change this and it will solve your problem
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//UIButton *testButton;
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
UIButton *testButton = [[UIButton alloc]initWithFrame:CGRectMake(5, 5, 40, 40)];
[testButton setImage:[UIImage imageNamed:#"oval"] forState:UIControlStateNormal];
[testButton setImage:[UIImage imageNamed:#"tick"] forState:UIControlStateSelected];
[testButton addTarget:self action:#selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:testButton];
[cell setIndentationLevel:1];
[cell setIndentationWidth:45];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// Configure the cell...
NSManagedObject *note = [self.notes objectAtIndex:indexPath.row];
NSDate *date = [note valueForKey:#"mod_time"];
NSString *dateString = [formatter stringFromDate:date];
cell.textLabel.text = [note valueForKey:#"title"];
cell.detailTextLabel.text = dateString;
return cell;
}
As cells get reused, you have to prepare them for reuse, or your button will show the previous image when set by another row.
You have to reset the button's (selected) state to match its state for the current row.
You can do this either in tableView:cellForRowAtIbdexPath: or the cell's prepareForReuse method.
Update:
The preferred way to do this is to subclass UITableViewCell and add a UIButton property. This is much better than adding a button to the cell's (contentView, not) view.
In your custom cell, it would look like:
#interface ButtonCell : UITableViewCell
{
#property (weak) IBOutlet UIButton *toggleButton;
}
You can then hookup IBOutlets and IBActions between your storyboard custom cell at its ButtonCell class.
Then it's a simple matter to access the button in your cell by property:
[cell.toggleButton setImage:[UIImage imageNamed:#"oval"] forState:UIControlStateNormal];
[cell.toggleButton setImage:[UIImage imageNamed:#"tick"] forState:UIControlStateSelected];
If each row is supposed to start with that default state where the image is an oval, you would make sure to call those lines from tableView:cellForRowAtIndexPath:
This overwrites any previous toggle state left over from the other row the cell had been used for, so you won't see a tick when you expect an oval :)
Update 2:
Since you have a custom cell you've subclassed that already has a button on it, you no longer have to create or add a button to the cell's (content) view. So, this code from your old approach no longer applies:
[cell addSubview:toogleButton]; // <-- remove this
Next, a UITableViewCell doesn't have a toggleButton property. You need to use your subclassed ButtonCell, which does have a toggleButton property. Dequeue a custom cell (which you've specified and hooked up in the storyboard's dynamic prototype cell).
ButtonCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
Since that method always returns a cell, you can eliminate all the cell == nil conditional code.
Now that you have a custom cell, you set its image.
Since cellForIndexPath is not the place where you toggle its state, you don't want any conditional code there to change it between an oval and a tick.
Your present approach is trying to make the button keep track of its own ticked/oval state, by changing the image it displays. But the cell's button will be reused as cells scroll off the screen. This means the button can't keep track of whether its previous row was ticked or not.
Whenever you need to remember things like whether a row is ticked or not, you want to keep track of this in (an array in) the model.
So, conditionally set your custom button's image, based on the model.
if ([self.noteState objectAtIndex:indexPath.row]) // An array of flags indicating whether a row is checked or not
{
[cell.toggleButton setImage:[UIImage imageNamed:#"tick"] forState:UIControlStateSelected];
}
else
{
[cell.toggleButton setImage:[UIImage imageNamed:#"oval"] forState:UIControlStateSelected];
}
Now your table rows will appropriately be checked off or not.
Since your model has checked state, you would also modernize your buttonTouched code to similarly update the model and toggle the button.
I leave that as an exercise for you. If you've understood everything else up to this point, you won't have any problem applying what you've learned.
Your goal should be to learn and understand what a block of code is meant to do, instead of simply copying and pasting it from StackOverflow.
I appreciate that you want to fix problems with the app you're developing but unless you understand how or why something works, you'll struggle with trying to fix someone else's code.
If you have specific questions, please ask in the comments, and I'll try to answer them!
Related
I have a tableviewcontroller that has dynamic controls created in cells. If it's a dropdown type, I take the user to a different tableviewcontroller to select the value. Once selected, I pop back and reload the data, but when I do that it overwrites the cells on top of one another. I know this is because I'm reusing the cells, but I cannot seem to figure out how to prevent it.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
EWHInboundCustomAttribute *ca = [visibleCustomAttributes objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.tag=indexPath.row;
if (ca.CustomControlType == 1) {
cell.detailTextLabel.hidden=true;
cell.textLabel.hidden=true;
UITextField *caTextField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 185, 30)];
caTextField.adjustsFontSizeToFitWidth = YES;
caTextField.textColor = [UIColor blackColor];
caTextField.placeholder = ca.LabelCaption;
if (ca.ReadOnly) {
[caTextField setEnabled: NO];
} else {
[caTextField setEnabled: YES];
}
caTextField.text=nil;
caTextField.text=ca.Value;
caTextField.tag=indexPath.row;
caTextField.delegate=self;
[cell.contentView addSubview:caTextField];
} else if (ca.CustomControlType == 4) {
cell.detailTextLabel.text=ca.Value;
cell.textLabel.text=ca.LabelCaption;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
cell.detailTextLabel.hidden=true;
cell.textLabel.hidden=true;
UITextField *caTextField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 185, 30)];
caTextField.adjustsFontSizeToFitWidth = YES;
caTextField.textColor = [UIColor grayColor];
caTextField.placeholder = ca.LabelCaption;
[caTextField setEnabled: NO];
caTextField.text = ca.Value;
caTextField.tag=indexPath.row;
caTextField.delegate=self;
[cell.contentView addSubview:caTextField];
}
return cell;
}
Instead of creating the UITextfield each time I would suggest at least using [UIView viewWithTag:tag] to capture the same UITextField object.
I'd suggest you to create custom UITableViewCell subclass and put all subviews related logic there.
Next, in order to reset/clear cell before reuse - you should override prepeareForReuse function.
Swift:
override func prepareForReuse() {
super.prepareForReuse()
//set cell to initial state here
}
First,I suggest you to use custom cells.If not and your cells are not so many,maybe you can try unique cell identifier to avoid cell reuse:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// unique reuseID
NSString *cellReuseID = [NSString stringWithFormat:#"%ld_%ld", indexPath.section, indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellReuseID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellReuseID];
// do something
}
return cell;
}
Hope it's helpful.
Here I am trying to do dynamic UITableViewCell with UIPickerView.
Step 1:
In Custom cell, I took 1 label and 1 UITextField.
Step 2:
used downpickerview library for data displaying and data fetching.
Step 3:
using below code I can able to select data, but after that, if I scroll UITableView data will be miss placing.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"Cell";
customCell *cell1=[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell1 == nil)
{
cell1 = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
cell1.productTitleLabel.text =[arrProductTitle objectAtIndex:indexPath.row];
self.downPicker = [[DownPicker alloc] initWithTextField:cell1.productvalueTextfield withData:arrProductVal];
[self.downPicker addTarget:self action:#selector(measurementSelected:) forControlEvents:UIControlEventValueChanged];
[cell1.contentView addSubview:self.downPicker];
return cell1;
}
Please help me on this.
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"DETAILS";
_dict = [[NSMutableDictionary alloc]init];
arrProductTitle = [[NSMutableArray alloc]initWithObjects:#"title0",#"title1",#"title2",#"title3",#"title4",#"title5",#"title6",#"title7",#"title8",#"title9",#"title10",#"title11",#"title12",#"title13",#"title14",#"title15", nil];
arrProductVal = [[NSMutableArray alloc]initWithObjects:#"0",#"1",#"2",#"3",#"4",#"5",nil];}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return arrProductTitle.count-1;}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *MyIdentifier = #"Cell";
customCell *cell1=[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell1 == nil)
{
cell1 = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
cell1.productTitleLabel.text =[arrProductTitle objectAtIndex:indexPath.row];
NSLog(#"%#",_dict);
NSLog(#"%#",[NSString stringWithFormat:#"%ld",(long)indexPath.row]);
if (_dict[[NSString stringWithFormat:#"%ld",(long)indexPath.row]]) {
cell1.productvalueTextfield.text =[_dict valueForKey:[NSString stringWithFormat:#"%ld",(long)indexPath.row]];
}
else {
cell1.productvalueTextfield.text = #"";
self.downPicker = [[DownPicker alloc] initWithTextField:cell1.productvalueTextfield withData:arrProductVal];
self.downPicker.tag = indexPath.row;
[self.downPicker addTarget:self action:#selector(measurementSelected:) forControlEvents:UIControlEventValueChanged];
[cell1.contentView addSubview:self.downPicker];
}
return cell1;}
-(void)measurementSelected:(id)dp {
NSString* selectedValue = [dp text];
NSString* selectedIndex = [NSString stringWithFormat:#"%ld",(long)[dp tag]];
[_dict setValue:[dp text] forKey:selectedIndex];
NSLog(#"_dict: %#",_dict);
NSLog(#"SELECTED TAG:::::::%ld",[dp tag]);
NSLog(#"SELECTED VALUE:::::::%#",selectedValue);
NSLog(#"SELECTED INDEX VALUEEEEEEEEEEE:::::::%ld",[dp selectedIndex]);}
https://github.com/gvniosdev/Dynamic-UItableview-with-Picker-Selection
UITableViewCell values are misplacing because you haven't set the data for other cells, You need to store the values in an array and just update the values from there and it will work. :)
Something is not right with your code:
cell1.productTitleLabel.text =[arrProductTitle objectAtIndex:indexPath.row];
self.downPicker = [[DownPicker alloc] initWithTextField:cell1.productvalueTextfield withData:arrProductVal];
[self.downPicker addTarget:self action:#selector(measurementSelected:) forControlEvents:UIControlEventValueChanged];
[cell1.contentView addSubview:self.downPicker];
Bear in mind that in iOS, a tableView will reuse its cells. So when you scroll your UITableView, it will reuse the old cells which were created before, and
[cell1.contentView addSubview:self.downPicker]; Will be executed everytime a cell is reused, as a result, you will end up having many downPicker objects in one cell.
I struggle for few days and searched before.
I have button in UITableViewCell for favorite cell.
How to save state of button (selected button load image "fav.png" and normal button load "unfav.png") after application close?
// BirdsTableViewController.m
// iranbirdtest2
//
// Created by Mehdi on 9/27/15.
// Copyright (c) 2015 Mehdi.n13. All rights reserved.
// after 15 azar-21 mehr
#import "BirdsTableViewController.h"
#import "Bird.h"
#import "GeneralViewController.h"
#import "FavoriteTableViewController.h"
#import "MyManager.h"
//NSMutableArray *favoritesArray;
#interface BirdsTableViewController (){
}
#property (strong, nonatomic) UITabBarController *myTabbarController;
#property (strong, nonatomic) GeneralViewController *myFirstViewController;
#end
#implementation BirdsTableViewController
{
}
- (IBAction)buttonTouchDown:(UIButton *)sender {
sender.selected = !sender.selected; //to switch from selected to unselected
//OR in IBaction we can use:
/*
if ([sender isSelected]) {
[sender setImage:[UIImage imageNamed:#"unfav.png"] forState:UIControlStateNormal];
[sender setSelected:NO];
} else {
[sender setImage:[UIImage imageNamed:#"fav.png"] forState:UIControlStateSelected];
[sender setSelected:YES];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey: #"someKey"];
}
*/
}
- (void) viewDidLoad {
[super viewDidLoad];
for (NSIndexPath *indexPath in [[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"mySavedMutableArray"]) {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
self.title=#"پرندگان ایران";
self.tableView.delegate = self;
self.tableView.dataSource = self;
//create array
birds=[[NSMutableArray alloc]init];
// UIButton* myButton;
Bird *bird=[[Bird alloc]init];
bird.name=#"زنبور خوار";
bird.filename=#"bird1";
bird.detail=#"این قسمت مربوط به توضیح می باشد";
[birds addObject:bird];
bird=[[Bird alloc]init]; //dont forget reuse
bird.name=#"زاغ";
bird.filename=#"bird2";
bird.detail=#"توضیحات مربوط به شماره ۲";
[birds addObject:bird];
bird=[[Bird alloc]init];
bird.name=#"طوطی";
bird.filename=#"bird3";
bird.detail=#"توضیحات مربوط به شماره سومی";
[birds addObject:bird];
//add more later
MyManager *sharedManager = [MyManager sharedManager];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
//this is for page view controller:
self.parentViewController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"common_bg"]];
self.tableView.backgroundColor = [UIColor clearColor];
UIEdgeInsets inset = UIEdgeInsetsMake(5, 0, 0, 0);
self.tableView.contentInset = inset;
[self.tableView setSeparatorStyle:UITableViewCellSelectionStyleNone]; //delete sepreate line odf tables
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) viewWillDisappear:(BOOL)animated{
}
#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 birds.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
// Configure the cell...
tableView.allowsSelection=YES;
Bird *current=[birds objectAtIndex:indexPath.row];
UIImageView *birdImageView = (UIImageView *)[cell.contentView viewWithTag:100];
birdImageView.image = [UIImage imageNamed:current.filename];
UILabel *name = (UILabel *)[cell.contentView viewWithTag:101];
name.text=[current name];
//button code in table view
UIButton *button=(UIButton *) [cell.contentView viewWithTag:103];//fav
[button setImage:[UIImage imageNamed:#"unfav.png"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"fav.png"] forState:UIControlStateSelected];
button.frame = CGRectMake(0,0, 50, 50);
button.tag = indexPath.row;
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button]; // add the button to the cell
[cell.contentView bringSubviewToFront:button];
// Assign our own background image for the cell
UIImage *background = [self cellBackgroundForRowAtIndexPath:indexPath];
UIImageView *cellBackgroundView = [[UIImageView alloc] initWithImage:background];
cellBackgroundView.image = background;
cell.backgroundView = cellBackgroundView;
UIImageView *imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"sepratortable.png"]];
[imgView sizeToFit];
[cell.contentView addSubview:imgView];
return cell;
}
-(void)buttonPressed:(UIButton *)sender
{
NSLog(#"Button Pressed");
MyManager *sharedManager = [MyManager sharedManager];
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell*)sender.superview.superview];
if([sharedManager.favoritesArray containsObject:[birds objectAtIndex:indexPath.row]])
{
[sharedManager.favoritesArray removeObject:[birds objectAtIndex:indexPath.row]];
[self.tableView reloadData];
}
else
{
[sharedManager.favoritesArray addObject:[birds objectAtIndex:indexPath.row]];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Do you want to say hello?" message:#"More info..." delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Say Hello",nil];
[alert show];
; //we can remove later
}
//save favorite array in plist.
[NSKeyedArchiver archiveRootObject:sharedManager.favoritesArray toFile:#"/Users/Mehdi/Desktop/Project/Backup/21 mehr/fav.plist"];
NSLog(#"Favoritearray : %d",sharedManager.favoritesArray.count);
/*
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setValue:birds forKey:#"key"];
[[NSUserDefaults standardUserDefaults] synchronize];
*/
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (UIImage *)cellBackgroundForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger rowCount = [self tableView:[self tableView] numberOfRowsInSection:0];
NSInteger rowIndex = indexPath.row;
UIImage *background = nil;
if (rowIndex == 0) {
background = [UIImage imageNamed:#"cell_top.png"];
} else if (rowIndex == rowCount - 1) {
background = [UIImage imageNamed:#"cell_bottom.png"];
} else {
background = [UIImage imageNamed:#"cell_middle.png"];
}
return background;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#pragma mark - Navigation
/*
In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
UITabBarController *pvc=[segue destinationViewController];
// Pass the selected object to the new view controller.
//what row selected?
NSIndexPath *path=[self.tableView indexPathForSelectedRow];
Bird *c =birds[path.row];
}
*/
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
/* if ([segue.identifier isEqualToString:#"ShowGeneralView"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
GeneralViewController *destViewController = segue.destinationViewController;
destViewController.currentbird = [birds objectAtIndex:indexPath.row];
}
*/
self.myTabbarController = (UITabBarController*) [segue destinationViewController];
self.myFirstViewController = [self.myTabbarController.viewControllers objectAtIndex:0];
NSIndexPath *path=[self.tableView indexPathForSelectedRow];
Bird *c =birds[path.row];
_myFirstViewController.currentbird=c;
}
#end
I have no problem for save favorite row after application close.
Problem is with button state that can't save and retrieve.
I know I must use NSUserDefaults, but how?
It seems as though your favorites button is in a cell, and when the button is pressed, you add or remove the favorites from the birds array. You even seem to be storing the plist for a file for persistent storage between app launches. Unless I'm missing something, you need to set the state of the button in the cellForRowAtIndexPath when you build the row. Simply check the favorites array for the cell you are building and if it is in there, set the image to the correct state.
One thing to watch out for is that you need to set it regardless of what you think the default state for the image is. This is because cells are reused (for memory efficency). So let's say you build your cell with a default image state of a gray cell. The user clicks the favorite icon and you set it to the red heart. Then when the user scrolls the cell off the screen, iOS will reuse the cell and the image will still be set to the red image. If you don't explicitly set it to gray if the row is not in your favorites, it will stay red.
Also, as you allude to in your question, you could use NSUserDefaults to store your favorites array, rather than a file (and it would probably be simpler). It would also allow you to use iCloud to sync the user favorites across devices (with a bit more work to handle merge conflicts). There are plenty of resources for how to store data in NSUserDefaults.
In your cellForRowAtIndexPath, you would want to do something like the following:
if([sharedManager.favoritesArray containsObject:[birds objectAtIndex:indexPath.row]])
{
[button setSelected:NO];
} else {
[button setSelected:YES];
}
Also, in your buttonPressed method, you should be toggling the selected state of your button using
sender.selected = !sender.selected;
I would get rid of the (IBAction)buttonTouchDown: method and put all the button handling logic in the buttonPressed method. Having that logic in two places will cause confusion.
I have UITableviewCell and i placed 4 buttons in cell. When i click one button i need to change its background color to red.
Soo right now i have written code for this and when i click one button then that button background color is not changing instead of that same button in some other row changing background color.
Use case:
1.I have 10 rows in UITableView and each cell contains 4 buttons named as
"Good","Better","Best","Worst".
When i click on "Good" button in first row am expecting it should change color to red.
3.Right now if i click "Good " button in first row then its not changing color instead while scrolling down but i can see "Good" button in 4th and 8th is changed to red .
So some mistake in my code for changing color.
Please check my tableview code and Button click code
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 4;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int sectionCount;
if(section==0)
{
sectionCount=4;
}else if(section==1){
sectionCount=4;
}else if (section==2){
sectionCount=3;
}else if(section==3){
sectionCount=1;
}
return sectionCount;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
sharedManager=[Mymanager sharedManager];
static NSString *CellIdentifier = #"cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[questioncell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:CellIdentifier];
}
cell.excellentButton.tag=indexPath.row;
cell.goodButotn.tag=indexPath.row;
cell.fineButton.tag=indexPath.row;
cell.dorrButton.tag=indexPath.row;
if(indexPath.section==0){
cell.question.text=[questions objectAtIndex:indexPath.row];
} else if(indexPath.section==1){
cell.question.text=[section1 objectAtIndex:indexPath.row];
}else if(indexPath.section==2){
cell.question.text=[section2 objectAtIndex:indexPath.row];
}else if(indexPath.section==3){
cell.question.text=[section3 objectAtIndex:indexPath.row];
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 200;
}
Button click code
- (IBAction)goodButton:(id)sender {
UIButton *button = (UIButton *)sender; // first, cast the sender to UIButton
NSInteger row = button.tag; // recover the row
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
NSString *key=[NSString stringWithFormat:#"%ld",(long)indexPath.row];
[sharedManager.ratingDic setValue:#"Good" forKey:key];
cell.goodButotn.layer.backgroundColor=[[UIColor colorWithRed:39/255.0 green:174/255.0 blue:96/255.0 alpha:100] CGColor ];
cell.betterButton.layer.backgroundColor=[[UIColor clearColor] CGColor ];
cell.bestButton.layer.backgroundColor=[[UIColor clearColor] CGColor ];
cell.worstButton.layer.backgroundColor=[[UIColor clearColor] CGColor ];
}
Please help me to clear this issue
UIButton *button = (UIButton *)sender; <-- this is already your reference to the button. I see there 2 ways to do it
create a custom UITableViewCell and implement methods for resetting all colors and setting the correct color. This is the clean way. Here you can implement more logic and have always the correct corresponding data. Here every of your buttons call an own method and there you have also a clear method.
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if( self = [super initWithStyle:style reuseIdentifier:reuseIdentifier] )
{
[excellentButton addTarget:self action:#selector(clickedExcellentButton) forControlEvents:UIControlEventTouchDown]; // or TouchUp, how ever you like
[goodButton addTarget:self action:#selector(clickedGoodButton) forControlEvents:UIControlEventTouchDown];
[fineButton addTarget:self action:#selector(clickedFineButton) forControlEvents:UIControlEventTouchDown];
[dorrButton addTarget:self action:#selector(clickedWoButton) forControlEvents:UIControlEventTouchDown];
}
return self;
}
-(UIColor *)selectionColor
{
return [UIColor colorWithRed:39/255.0 green:174/255.0 blue:96/255.0 alpha:100];
}
-(void)resetSelection
{
excellentButton.backgroundColor = [UIColor clearColor];
goodButton.backgroundColor = [UIColor clearColor];
fineButton.backgroundColor = [UIColor clearColor];
dorrButton.backgroundColor = [UIColor clearColor];
}
-(void)clickedExcellentButton
{
[self resetSelection];
excellentButton.backgroundColor = [self selectionColor];
NSString *key=[NSString stringWithFormat:#"%ld",(long)indexPath.row];
[sharedManager.ratingDic setValue:#"Excellent" forKey:key]; // if you have your sharedManager object here. If you cannot access it from here, you have to forward it or give the cell a reference to it
}
-(void)clickedGoodButton
{
[self resetSelection];
goodButton.backgroundColor = [self selectionColor];
NSString *key=[NSString stringWithFormat:#"%ld",(long)indexPath.row];
[sharedManager.ratingDic setValue:#"Good" forKey:key];
}
...
or
- (IBAction)goodButton:(id)sender {
UIButton *button = (UIButton *)sender; // this is the button that was clicked
// [...]
for(UIButton *ctrl in [button.superview subviews]) // button.superview get the view that holds him. Subviews all the others in his layer
{
if([ctrl isKindOfClass:[UIButton class]])
{
ctrl.backgroundColor = [UIColor clearColor];
}
}
button.backgroundColor = [UIColor colorWithRed:39/255.0 green:174/255.0 blue:96/255.0 alpha:100];
I create a custom tableview cell, and those four button use the same IBAction when the button been pressed. Then change the background color of that button. It works.
TableView
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 2;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellID = #"cell";
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[NSBundle mainBundle] loadNibNamed:#"TableViewCell" owner:self options:nil][0];
}
return cell;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
Button click code
- (IBAction)actionButtonPressed:(UIButton *)sender {
[sender setBackgroundColor:[UIColor redColor]];
}
You don't need to use the layer of the buttons it is enough to set the backgroundColor ( i assume that you are able to access your buttons over cell.Button)
cell.goodButton.backgroundColor = [[UIColor colorWithRed:39/255.0 green:174/255.0 blue:96/255.0 alpha:100] CGColor ];
...
cell.betterButton.backgroundColor = [UIColor clearColor];
cell. bestButton.backgroundColor = [UIColor clearColor];
cell. worstButton.backgroundColor = [UIColor clearColor];
I'm using a PFQueryTableViewController with Parse in my IOS 8 Objective-c iPhone app.
My list consists of a label and a UIImageView where both the label text and image are downloaded from a row in my Parse core. I'm using this code to achieve this:
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:#"Story"];
return query;
}
#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 objects] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
// Download the header image from parse
PFFile *imageFile = [object objectForKey:#"Image"];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *cellImage = [UIImage imageWithData:imageData];
// Set the cellImage to the cell if it's not nil
if (cellImage == nil) {
// nil - do nothing
NSLog(#"nil");
} else {
NSLog(#"not nil");
// Set the image
UIImageView *cellImageView = (UIImageView *)[cell viewWithTag:40];
cellImageView.image = cellImage;
}
}
}];
// Configure the cell
UILabel *nameLabel = (UILabel*) [cell viewWithTag:10];
nameLabel.text = [object objectForKey:#"Title"];
nameLabel.textColor = [UIColor whiteColor];
// Make the cell transparent
cell.backgroundColor = [UIColor clearColor];
cell.backgroundView = [UIView new];
cell.selectedBackgroundView = [UIView new];
// Resize the cell
[cell sizeToFit];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Hide the tabBar and show the readButton
[self hideTabBar:self.tabBarController];
// Segue over to the viewing page
[self performSegueWithIdentifier:#"detailSegue" sender:self];
// Get the tapped cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *title = ((UILabel*) [cell viewWithTag:10]).text;
// Set selectedStory
MyManager *sharedManager = [MyManager sharedManager];
sharedManager.selectedStory = title;
// Set openedStory to YES as we opened a story
openedStory = YES;
}
This code works good, but the scrolling is a bit laggy, which I think is because it's downloading the image whenever the cell is shown. I thought of created a simple solution by creating an array of images locally and have them only download once, but it has to load 1 time minimum when the app launches. I need to somehow run the download method asynchronously (or another solution that would work).
How can I achieve this?
(I'm using storyboards)
EDIT
Thanks in advance!
Erik
EDIT 2:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (![self.shownIndexes containsObject:indexPath]) {
[self.shownIndexes addObject:indexPath];
UIView *weeeeCell = [cell contentView];
weeeeCell.layer.transform = self.initialTransform;
weeeeCell.layer.opacity = 0.8;
[UIView animateWithDuration:1.25 delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:0.5 options:0 animations:^{
weeeeCell.layer.transform = CATransform3DIdentity;
weeeeCell.layer.opacity = 1;
} completion:^(BOOL finished) {}];
}
}
and
if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) {
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation: UITableViewRowAnimationAutomatic];
}
Your hypothesis about the problem is right, and your idea about a solution is right, too. The additional requirement that you mention about preloading the images is a little fuzzy.
Must they be loaded before the table appears? If they are loaded asynchronously, which they should be, then you'll need to block user's access to the table until the requests are complete. You're replace the poor experience of not seeing the images right away with the worse experience of not seeing the table at all.
I think the better answer is to just load lazily. The outline of the solution is:
Declare a dictionary of images (to be indexed by the indexPaths) and be sure to initialize it to an empty dictionary...
#interface MyViewController () // replace 'MyViewController' with your class
#property(strong,nonatomic) NSMutableDictionary *images;
#end
Use that collection in cellForRowAtIndexPath...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *simpleTableIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
UIImageView *cellImageView = (UIImageView *)[cell viewWithTag:40];
UIImage *cachedImage = self.images[indexPath];
if (cachedImage) {
cellImageView.image = cachedImage;
} else {
cellImageView.image = // put a place holder image here
// load lazily, but read on. the code in the callback should assume
// nothing about the state of the table when it runs
PFFile *imageFile = [object objectForKey:#"Image"];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
// what if this gets run a second time before the first request finishes?
// no worries, check for that here:
if (!error && !self.images[indexPath]) {
UIImage *cellImage = [UIImage imageWithData:imageData];
self.images[indexPath] = cellImage;
// this is important: don't refer to cell in here, it may be
// scrolled away and reused by the time this closure runs
// the code we just wrote to init the cellImageView works just fine
// call that using reload
if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) {
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
}];
}
// Configure the cell
UILabel *nameLabel = (UILabel*) [cell viewWithTag:10];
nameLabel.text = [object objectForKey:#"Title"];
nameLabel.textColor = [UIColor whiteColor];
// Make the cell transparent
cell.backgroundColor = [UIColor clearColor];
cell.backgroundView = [UIView new];
cell.selectedBackgroundView = [UIView new];
// Resize the cell
[cell sizeToFit];
return cell;
}
Edit -- don't bother with this for now, but -- if you really do have the opportunity to prepare the view before its shown (like maybe this view controller is in a tab bar container and not the default tab). You could use the table view helper methods to do a pre-fetch of the visible rows...
- (void)prepareToBeShown {
NSArray indexPaths = [self.tableView indexPathsForVisibleRows];
[self.tableView reloadRowsAtIndexPaths:indexPaths];
}
EDIT 2:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (![self.shownIndexes containsObject:indexPath]) {
[self.shownIndexes addObject:indexPath];
UIView *weeeeCell = [cell contentView];
weeeeCell.layer.transform = self.initialTransform;
weeeeCell.layer.opacity = 0.8;
[UIView animateWithDuration:1.25 delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:0.5 options:0 animations:^{
weeeeCell.layer.transform = CATransform3DIdentity;
weeeeCell.layer.opacity = 1;
} completion:^(BOOL finished) {}];
}
}
Have you thought about using a PFImageView instead of a UIImageView?
All you have to do it set it's file and tell it to load in the background. I've never had any lag when using them in my tableviews.