I'm building a simple checklist in a UITableView. I've added editing capability by placing the usual editing button in the navigation bar. The button turns on editing mode. Editing mode works great until I add custom check boxes (as buttons) in each cell's accessory view. I'm using this code to do it:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// put the tasks into the cell
[[cell textLabel] setText:[NSString stringWithFormat:#"%#", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[indexPath row]]]];
// put the checkbox into the cell's accessory view
UIButton *checkBox = [UIButton buttonWithType:UIButtonTypeCustom];
[checkBox setImage:[UIImage imageNamed:#"checkbox.png"] forState:UIControlStateNormal];
[checkBox setImage:[UIImage imageNamed:#"checkbox-checked.png"] forState:UIControlStateSelected];
checkBox.frame = CGRectMake(0, 0, 30, 30);
checkBox.userInteractionEnabled = YES;
[checkBox addTarget:self action:#selector(didCheckTask:) forControlEvents:UIControlEventTouchDown];
cell.accessoryView = checkBox;
// put the index path in the button's tag
checkBox.tag = [indexPath row];
}
return cell;
}
As you can see, I'm using the button's tag to pass the indexPath to my didCheckTask: method:
- (void)didCheckTask:(UIButton *)button
{
task = [[[CLTaskStore sharedStore] allTasks] objectAtIndex:button.tag];
task.didComplete = YES;
// toggle checkbox
button.selected = !button.selected;
[checkList reloadData];
}
The checkboxes and editing all seem to be working properly on the surface. However, a big problem arises when I enter editing mode, delete an item in the tableView and then try to use a checkbox. For example, if I delete the first item in the tableView and then try to check the last item's checkbox, the program crashes with:
2012-05-06 21:45:40.645 CheckList[16022:f803] * Terminating app due
to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM
objectAtIndex:]: index 4 beyond bounds [0 .. 3]'
I have been trying to figure out the source of this bug, but I'm having no luck. I could really use some help - I'm new to cocoa. Pertinent code follows.
CLTaskFactory.h
#import <Foundation/Foundation.h>
#interface CLTaskFactory : NSObject
{
NSString *taskName;
BOOL didComplete;
}
#property NSString *taskName;
- (void)setDidComplete:(BOOL)dc;
- (BOOL)didComplete;
#end
CLTaskFactory.m
#import "CLTaskFactory.h"
#implementation CLTaskFactory
#synthesize taskName;
- (void)setDidComplete:(BOOL)dc
{
didComplete = dc;
}
- (BOOL)didComplete
{
return didComplete;
}
- (NSString *)description
{
// override the description
NSString *descriptionString = [[NSString alloc] initWithFormat:#"%#", taskName];
return descriptionString;
}
#end
CLTaskStore.h
#import <Foundation/Foundation.h>
#class CLTaskFactory;
#interface CLTaskStore : NSObject
{
NSMutableArray *allTasks;
}
+ (CLTaskStore *)sharedStore;
- (NSMutableArray *)allTasks;
- (void)addTask:(CLTaskFactory *)task;
- (void)removeTask:(CLTaskFactory *)task;
- (void)moveTaskAtIndex:(int)from toIndex:(int)to;
#end
CLTaskStore.m
#import "CLTaskStore.h"
#implementation CLTaskStore
+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedStore];
}
+ (CLTaskStore *)sharedStore
{
static CLTaskStore *sharedStore = nil;
if (!sharedStore) {
sharedStore = [[super allocWithZone:nil] init];
}
return sharedStore;
}
- (id)init
{
self = [super init];
if (self) {
allTasks = [[NSMutableArray alloc] init];
}
return self;
}
- (NSMutableArray *)allTasks
{
return allTasks;
}
- (void)addTask:(CLTaskFactory *)task
{
[allTasks addObject:task];
}
- (void)removeTask:(CLTaskFactory *)task
{
[allTasks removeObjectIdenticalTo:task];
NSInteger taskCount = [allTasks count];
NSLog(#"Removed: %#, there are now %d remaining tasks, they are:", task, taskCount);
for (int i = 0; i < taskCount; i++) {
NSLog(#"%#", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:i]);
}
}
- (void)moveTaskAtIndex:(int)from toIndex:(int)to
{
if (from == to) {
return;
}
CLTaskFactory *task = [allTasks objectAtIndex:from];
[allTasks removeObjectAtIndex:from];
[allTasks insertObject:task atIndex:to];
}
#end
CLChecklistViewController.h
#import <Foundation/Foundation.h>
#class CLTaskFactory;
#interface CLCheckListViewController : UIViewController
{
CLTaskFactory *task;
}
- (void)didCheckTask:(UIButton *)button;
#end
CLCheckListViewController.m
#import "CLCheckListViewController.h"
#import "CLTaskFactory.h"
#import "CLTaskStore.h"
#implementation CLCheckListViewController
{
__weak IBOutlet UITableView *checkList;
}
- (id)init
{
self = [super init];
if (self) {
// add five sample tasks
CLTaskFactory *task1 = [[CLTaskFactory alloc] init];
[task1 setTaskName:#"Task 1"];
[task1 setDidComplete:NO];
[[CLTaskStore sharedStore] addTask:task1];
CLTaskFactory *task2 = [[CLTaskFactory alloc] init];
[task2 setTaskName:#"Task 2"];
[task2 setDidComplete:NO];
[[CLTaskStore sharedStore] addTask:task2];
CLTaskFactory *task3 = [[CLTaskFactory alloc] init];
[task3 setTaskName:#"Task 3"];
[task3 setDidComplete:NO];
[[CLTaskStore sharedStore] addTask:task3];
CLTaskFactory *task4 = [[CLTaskFactory alloc] init];
[task4 setTaskName:#"Task 4"];
[task4 setDidComplete:NO];
[[CLTaskStore sharedStore] addTask:task4];
CLTaskFactory *task5 = [[CLTaskFactory alloc] init];
[task5 setTaskName:#"Task 5"];
[task5 setDidComplete:NO];
[[CLTaskStore sharedStore] addTask:task5];
}
return self;
}
- (void)viewDidLoad
{
[[self navigationItem] setTitle:#"Checklist"];
// create edit button
[[self navigationItem] setLeftBarButtonItem:[self editButtonItem]];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[CLTaskStore sharedStore] allTasks] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// put the tasks into the cell
[[cell textLabel] setText:[NSString stringWithFormat:#"%#", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[indexPath row]]]];
// put the checkbox into the cell's accessory view
UIButton *checkBox = [UIButton buttonWithType:UIButtonTypeCustom];
[checkBox setImage:[UIImage imageNamed:#"checkbox.png"] forState:UIControlStateNormal];
[checkBox setImage:[UIImage imageNamed:#"checkbox-checked.png"] forState:UIControlStateSelected];
checkBox.frame = CGRectMake(0, 0, 30, 30);
checkBox.userInteractionEnabled = YES;
[checkBox addTarget:self action:#selector(didCheckTask:) forControlEvents:UIControlEventTouchDown];
cell.accessoryView = checkBox;
// put the index path in the button's tag
checkBox.tag = [indexPath row];
}
return cell;
}
- (void)didCheckTask:(UIButton *)button
{
task = [[[CLTaskStore sharedStore] allTasks] objectAtIndex:button.tag];
task.didComplete = YES;
// toggle checkbox
button.selected = !button.selected;
[checkList reloadData];
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
// set editing mode
if (editing) {
self.navigationItem.title = #"Edit Checklist";
[checkList setEditing:YES];
} else {
self.navigationItem.title = #"Checklist";
[checkList setEditing:NO];
}
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
// remove guest from store
if (editingStyle == UITableViewCellEditingStyleDelete) {
task = [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[indexPath row]];
[[CLTaskStore sharedStore] removeTask:task];
// remove guest from table view
[checkList deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
[[CLTaskStore sharedStore] moveTaskAtIndex:[sourceIndexPath row] toIndex:[destinationIndexPath row]];
}
#end
Thank you so much for your help and expertise!
edited:
I modified two methods with looping NSLogs to gain some insight. First, CLTaskStore:
- (void)removeTask:(CLTaskFactory *)task
{
[allTasks removeObjectIdenticalTo:task];
NSInteger taskCount = [allTasks count];
NSLog(#"Removed: %#, there are now %d remaining tasks, they are:", task, taskCount);
for (int i = 0; i < taskCount; i++) {
NSLog(#"%#, status: %#", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:i], [[[[CLTaskStore sharedStore] allTasks] objectAtIndex:i] didComplete]?#"YES":#"NO");
}
}
Second, CLTaskListViewController:
- (void)didCheckTask:(UIButton *)button
{
task = [[[CLTaskStore sharedStore] allTasks] objectAtIndex:button.tag];
task.didComplete = YES;
NSInteger taskCount = [[[CLTaskStore sharedStore] allTasks] count];
for (int i = 0; i < taskCount; i++) {
NSLog(#"%#, status: %#", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:i], [[[[CLTaskStore sharedStore] allTasks] objectAtIndex:i] didComplete]?#"YES":#"NO");
}
// toggle checkbox
button.selected = !button.selected;
[checkList reloadData];
}
I noticed two things. If I delete upwards, from bottom to top, there are no issues. I can check anything - everything works. However, if I delete the first row and then check the last row the program crashes. The NSLog from the deletion is clean, its working fine.
If I delete the first row and check the fourth row, the NSLog from CLTaskStore reports row 5 was checked.
This is the problem. The two are definitely out of sequence after the deletion.
Your entire problem stems from the bad idea of using tags to indicate what row a button is in. This is bad enough when you aren't deleting rows from the datasource, but when you are, this is the sort of problem you can run into.
Using the location of the tapped item in the table view, and getting the index path of the location from the table view, is far more robust and works with editable tables and multi-section tables. See sample code in my answer here.
If you do it that way there is no re-indexing necessary.
When the delete button is pressed after entering Edit mode for your tableView, you must remove the corresponding data item from the datasource. Your code shows that you have a removeTask: method, but I don't see where you are actually calling that method to delete the corresponding task entry from your datasource. A good place to do this would be in the tableview:commitEditingStyle:forRowAtIndexPath: method in your view controller.
Since you are deleting the corresponding item in the datasource, further study of the code shows that your checkbox tag values still have their original values. If you delete any tableView item before the last one, then try to check the last one, your didCheckTask method tries to access the original indexPath row value, which now does not exist and causes a bounds exception. If you delete the first two cells, then the last two tableView items will both cause exceptions, and so on.
It wouldn't work in the didCheckTask method, but in the removeTask: method, after you delete the object from your datasource, loop through the remaining objects and set each tag equal to its corresponding array index. In the moveTaskAtIndex:toIndex: method, after you move your array entries around due to the user reordering items, do the same thing -- loop through the array and set each tag equal to its index in the array.
Related
Hi friends I am new in IOS. I am creating a table view of state list. On button click I'm showing a TableView on popup. Code for creating state list is
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
selectedState = [NSMutableArray new];
UIView *contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth-50, screenHeight-50)];
mytableview = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, screenWidth-50, screenHeight-100)];
mytableview.delegate =self;
mytableview.dataSource=self;
[contentView addSubview:mytableview];
UIButton *save = [[UIButton alloc] initWithFrame:CGRectMake(0, screenHeight-100, screenWidth-50, 50)];
[save setTitle:#"SAVE" forState:UIControlStateNormal];
save.titleLabel.font = [UIFont systemFontOfSize:14];
[save setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[save addTarget:self action:#selector(saveActionForState:) forControlEvents:UIControlEventTouchUpInside];
[save setBackgroundColor:[UIColor colorWithRed:41.0/255.0 green:178.0/255.0 blue:165.0/255.0 alpha:1.0]];
[contentView addSubview:save];
[[KGModal sharedInstance] showWithContentView:contentView andAnimated:YES];
For table creation code is
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [stateList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CRTableViewCellIdentifier = #"cellIdentifier";
if(selectStateButton ==1){
CRTableViewCell *cell = (CRTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CRTableViewCellIdentifier];
if (cell == nil) {
cell = [[CRTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CRTableViewCellIdentifier];
}
NSString *text = [stateList objectAtIndex:[indexPath row]];
cell.textLabel.font = [UIFont systemFontOfSize:12.0];
cell.isSelected = [selectedState containsObject:text] ? YES : NO;
cell.textLabel.text = text;
return cell;
}
}
On selection of table cell checked the cell of IOS.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectStateButton ==1){
//[selectedStatesForSelect2 removeAllObjects];
NSString *text = [stateList objectAtIndex:[indexPath row]];
NSInteger indexings = [indexPath row]+1;
if ([selectedState containsObject:text]){
[selectedState removeObject:text];
[selectedStatesId removeObject:[NSString stringWithFormat:#"%ld", (long)indexings]];
}else{
//NSLog(#"count%lu indexing %#",(unsigned long)[selectedState count],selectedStatesId);
[selectedState addObject:text];
[selectedStatesId addObject:[NSString stringWithFormat:#"%ld", (long)indexings]];
[selectedStatesForSelect2 addObject:[NSString stringWithFormat:#"%ld", (long)indexings]];
NSLog(#"select%#count%lu",selectedStatesForSelect2,(unsigned long)[selectedState count]);
if([selectedState count] > 3){
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Hold On..." message:#"You can't select more than three states." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[selectedState removeObject:text];
[selectedStatesId removeObject:[NSString stringWithFormat:#"%ld", (long)indexings]];
}
}
}
That seen like this. When user select first time it works very good. But after closing the table view, when user again open the state list I want to show the previous selected item. Thats not working properly. I'm using a library for check "CRMultiRowSelector".Please help me.
I will give what you ask.I tried many times this.It works perfectly
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
#property (strong, nonatomic) IBOutlet UITableView *tableViewCheckMarkPreviousSelectionUpdate;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
{
NSMutableArray *arrProductSelection,*arrProductSelectDeSelectCheckMark;
NSArray *arrayFetchFromDefaults;
}
#end
#implementation ViewController
#synthesize tableViewCheckMarkPreviousSelectionUpdate;
- (void)viewDidLoad
{
[super viewDidLoad];
arrProductSelection = [[NSMutableArray alloc]initWithObjects:#"iPhone",#"iPad",#"iPod",#"iTV",#"iWatch",#"iMac",nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)viewWillAppear:(BOOL)animated
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
arrayFetchFromDefaults = [userDefaults objectForKey:#"selectedcheckmark"];
arrProductSelectDeSelectCheckMark = [[NSMutableArray alloc]initWithArray:arrayFetchFromDefaults];
if(arrProductSelectDeSelectCheckMark.count == 0)
{
arrProductSelectDeSelectCheckMark = [[NSMutableArray alloc]init];
for(int j=0;j<[arrProductSelection count];j++)
{
[arrProductSelectDeSelectCheckMark addObject:#"deselected"];
}
}
[tableViewCheckMarkPreviousSelectionUpdate reloadData];
}
#pragma mark - UITableViewDataSource Methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return arrProductSelection.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *strCell = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:strCell];
if(cell==nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strCell];
}
if([[arrProductSelectDeSelectCheckMark objectAtIndex:indexPath.row] isEqualToString:#"deselected"])
cell.accessoryType = UITableViewCellAccessoryNone;
else
cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.textLabel.text = [arrProductSelection objectAtIndex:indexPath.row];
return cell;
}
#pragma mark - UITableViewDelegate Methods
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
#try
{
CGPoint touchPoint = [cell convertPoint:CGPointZero toView:tableViewCheckMarkSelectionUpdate];
NSIndexPath *indexPath = [tableViewCheckMarkSelectionUpdate indexPathForRowAtPoint:touchPoint];
NSLog(#"%#",arrProductSelectDeSelectCheckMark);
if([arrProductSelectDeSelectCheckMark count]==0)
{
for(int i=0; i<[arrProductSelection count]; i++)
{
[arrProductSelectDeSelectCheckMark addObject:#"deselected"];
}
}
if([[arrProductSelectDeSelectCheckMark objectAtIndex:indexPath.row] isEqualToString:#"deselected"])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[arrProductSelectDeSelectCheckMark replaceObjectAtIndex:indexPath.row withObject:#"selected"];
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
[arrProductSelectDeSelectCheckMark replaceObjectAtIndex:indexPath.row withObject:#"deselected"];
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:arrProductSelectDeSelectCheckMark forKey:#"selectedcheckmark"];
[defaults synchronize];
}
#catch (NSException *exception) {
NSLog(#"The exception is-%#",exception);
}
}
#end
You have to save indexPaths and while creating the cell you have to again reassign state of cell. If it is selected or not.
In
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{}
in this method keep selected indexPaths and check for same indexPaths in cellPathForRowIndexPath, if there there show as checked otherwise unchecked.
This is a simple project that I am half copying from a book to teach myself. It's a to do list, with a table view, a text field and a button to add the task to the list. Then it saves the array of tasks to a directory (using a helper function) and when you close and reopen the application it retrieves the information. The thing runs on the computer and I thought it also worked on the device until I closed the app and reopened it to discover none of the elements of the array where there. As it works on the computer I have no idea what isn't right. Some of this code I understand, some I just copied for now (like the helper function), any suggestions greatly appreciated! P
Code:
h file
#import <UIKit/UIKit.h>
NSString *docPath();
#interface ViewController : UIViewController<UITableViewDataSource>
{
UITableView *taskTable;
UITextField *taskField;
UIButton *insertButton;
NSMutableArray *tasks;
}
- (void)addTask:(id)sender;
- (void)writeTasksToFile:(NSString *)path;
#end
m file
#import "ViewController.h"
NSString *docPath()
{
NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[pathList objectAtIndex:0] stringByAppendingString:#"data.td"];
}
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *plist = [NSArray arrayWithContentsOfFile:docPath()];
if(plist) {
tasks = [plist mutableCopy];
} else {
tasks = [[NSMutableArray alloc] init];
}
CGRect buttonFrame = CGRectMake(228,40,72,31);
insertButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[insertButton setFrame:buttonFrame];
insertButton.backgroundColor = [UIColor redColor];
[insertButton addTarget:self action:#selector(addTask:) forControlEvents:UIControlEventTouchUpInside];
[insertButton setTitle:#"Insert" forState:UIControlStateNormal];
[[self view] addSubview:insertButton];
CGRect tableFrame = CGRectMake(0, 80, 320, 380);
taskTable = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStylePlain];
[taskTable setSeparatorStyle:UITableViewCellSeparatorStyleNone];
taskTable.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
[self.view addSubview:taskTable];
[taskTable setDataSource:self];
CGRect fieldFrame = CGRectMake(20, 40, 200, 31);
taskField = [[UITextField alloc]initWithFrame:fieldFrame];
[taskField setBorderStyle:UITextBorderStyleRoundedRect];
[taskField setPlaceholder:#"type a task, enter"];
[taskField setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:taskField];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void) applicationDidEnterBackground:(UIApplication *)application {
[tasks writeToFile:docPath() atomically:YES];
}
- (void)addTask:(id)sender{
NSString *t = [taskField text];
if ([t isEqualToString:#""]) {return;}
[tasks addObject:t];
[taskTable reloadData];
[taskField setText:#""];
[taskField resignFirstResponder];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [tasks count];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[tasks removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *c = [taskTable dequeueReusableCellWithIdentifier:#"Cell"];
if (!c){
c = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
NSString *item = [tasks objectAtIndex:[indexPath row]];
[[c textLabel] setText:item];
return c;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)writeTasksToFile:(NSString *)path
{
[tasks writeToFile:path atomically:YES];
}
#end
edit------------------------------------------------------------------------------------------------------------------------------
Here is a debugger screenshot with a break point on the line that checks to see if there is a plist.
One thing I see that needs to be changed is that your docPath() is returning a bad path. You need to use stringByAppendingPathComponent instead of stringByAppendingString
return [[pathList objectAtIndex:0] stringByAppendingPathComponent:#"data.td"];
Or, if you want to use stringByAppendingString then it should be:
return [[pathList objectAtIndex:0] stringByAppendingString:#"/data.td"];
I am having an app in which I am showing some data in my tableview as shown in the ScreenShot1 below.
Screenshot1.
Now I am having an imageview On my each tableview cell.
If I select any of the cell from tableview, I want an image on that selected cell as shown in screenshot2 below.
Screenshot2.
Now If I select any other cell form tableview. I want to set my imageview's image only on those selected cells as shown in Screenshot3 below.
Below is the code I am doing.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
lblRecipe = [[UILabel alloc] initWithFrame:CGRectMake(50, 5, 600, 32)];
lblRecipe.backgroundColor = [UIColor clearColor];
lblRecipe.font = [UIFont systemFontOfSize:20.0f];
[cell.contentView addSubview:lblRecipe];
checkedimg=[[UIImageView alloc]initWithFrame:CGRectMake(40, 20, 500, 5)];
[cell.contentView addSubview:checkedimg];
lblRecipe.text = [array objectAtIndex:indexPath.row];
return cell;
}
On Tableview's didselect method, what should i keep to have this kind of a functionality?
I searched a lot and there are lots of questions for this but couldn't find one for this.
So Please help me on this.
Thanks in advance.
You may be better off just subclassing UITableViewCell, as this would let you change the way the cells draw, and add a property or something of the sort to indicate this state.
To create the subclass, create a new file in Xcode, and select UITableViewCell as its superclass.
Then, within that subclass, you could implement a method called setStrikeThrough: like this that you can call on the cell you dequeue from the table:
- (void) setStrikeThrough:(BOOL) striked {
if(striked) {
// create image view here and display, if it doesn't exist
} else {
// hide image view
}
}
Then, in the view controller that holds the table, you'd call this in viewDidLoad to register your subclass with the table view:
[_tableView registerClass:[YourAmazingTableCell class] forCellReuseIdentifier:#"AmazingCellSubclass"];
You would then alter your tableView:cellForRowAtIndexPath: to be something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"AmazingCellSubclass";
YourAmazingTableCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[YourAmazingTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// here you would look up if this row should be selected
[cell setStrikeThrough:YES];
// Set up the remainder of the cell, again based on the row
cell.textLabel.text = #"chilli";
return cell;
}
Try this,
Add a NSMutableArray checkedCellArray, as property and alloc init.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UILabel *labelRecipe = [[UILabel alloc] initWithFrame:CGRectMake(50, 5, 600, 32)];
labelRecipe.backgroundColor = [UIColor clearColor];
labelRecipe.font = [UIFont systemFontOfSize:20.0f];
labelRecipe.tag = kRecipeTag;//a tag to label
[cell.contentView addSubview:labelRecipe];
UIImageView *checkedimg = [[UIImageView alloc]initWithFrame:CGRectMake(40, 20, 500, 5)];
[checkedimg setImage:[UIImage imageNamed:#"checked.png"];
checkedimg.tag = kCheckedImageTag;//a tag to imageView
[cell.contentView addSubview:checkedimg];
checkedimg.hidden = YES;
}
UILabel *labelRecipe = (UILabel *)[cell.contentView viewWithTag:kRecipeTag];
UIImageView *checkedimg = (UIImageView *)[cell.contentView viewWithTag:kCheckedImageTag];
if ([self.checkedCellArray containsObject:indexPath]) {
checkedimg.hidden = NO;
} else {
checkedimg.hidden = YES;
}
labelRecipe.text = [array objectAtIndex:indexPath.row];
return cell;
}
and in didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (![self.checkedCellArray containsObject:indexPath]) {
[self.checkedCellArray addObject:indexPath];
}
NSArray* rowsToReload = [NSArray arrayWithObjects:indexPath, nil];
[UITableView reloadRowsAtIndexPaths:rowsToReload withRowAnimation:UITableViewRowAnimationNone];
}
Oky hear you can do like this
just subclass the tableview cell
in CustomCell.h file
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell
#property (nonatomic,retain)UIImageView *dashImageView; //add a property of image view
#end
in CustomCell.m file
#import "CustomCell.h"
#implementation CustomCell
{
BOOL isCellSelected; //add this to check
}
#synthesize dashImageView = _dashImageView; //synthesize it
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
isCellSelected = NO;//initially set it no
_dashImageView = [[UIImageView alloc]initWithFrame:CGRectMake(self.bounds.origin.x + 15, self.bounds.origin.y + 10, self.bounds.size.width, 15.0f)];
_dashImageView.backgroundColor = [UIColor redColor];
_dashImageView.tag = 12345;
_dashImageView.hidden = YES;
[self.contentView addSubview:_dashImageView];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if(selected)
{
UIImageView *dashImageVIew = (UIImageView *)[self viewWithTag:12345];
if(dashImageVIew)
{
if(!isCellSelected)
{
dashImageVIew.hidden = NO;
isCellSelected = YES;
}
else
{
dashImageVIew.hidden = YES;
isCellSelected = NO;
}
}
}
}
EDIT:2 *For selecting both section and row for the table view*
there is no change in the custom cell only change in the controller so that u hav to reflect what changes that u made to tableview cell i.e weather u select or deselect
since u want select section along with the selected row.(your requirement) there are various way to do that, but i go by using some model object which contains index section and row.
for that u need to create a subclass of NSObject and name it as MySelectedIndex then in
in MySelectedIndex.h file
#import <Foundation/Foundation.h>
#interface MySelectedIndex : NSObject<NSCoding>//to store and retrieve data like user defaults, for non-property list objects, confirms to NSCoding protocol
#property (nonatomic,retain) NSNumber *selectedRow;
#property (nonatomic,retain) NSNumber *selectedSection;
- (id)initWithSelectedRow:(NSNumber *)selRow andSelectedIndex:(NSNumber *)selSection;
#end
in MySelectedIndex.m file
#import "MySelectedIndex.h"
#implementation MySelectedIndex
#synthesize selectedSection = _selectedSection;
#synthesize selectedRow = _selectedRow;
- (id)initWithSelectedRow:(NSNumber *)selRow andSelectedIndex:(NSNumber *)selSection
{
self = [super init];
if(self)
{
_selectedRow = selRow;
_selectedSection = selSection;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_selectedRow forKey:#"ROW"];
[aCoder encodeObject:_selectedSection forKey:#"SECTION"];
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if(self)
{
_selectedRow = [aDecoder decodeObjectForKey:#"ROW"];
_selectedSection = [aDecoder decodeObjectForKey:#"SECTION"];
}
return self;
}
#end
now get back to controller where u are using the table
in .m file
import it #import "CustomCell.h"
and in the method
in viewController.m
#interface SampleViewController ()<UITableViewDataSource,UITableViewDelegate>
{
NSMutableArray *indexes;//to store in defaults
NSMutableArray *indexesRed;//to red from the defaults
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
//...other stuff's
indexes = [[NSMutableArray alloc]init];//initilise your arrays
indexesRed = [[NSMutableArray alloc]init];
//after initilizing your array
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"SELECTED_CELLL"];
if(data != nil)
{
indexes = [NSKeyedUnarchiver unarchiveObjectWithData:data];
[indexes retain];//you hav to retain the objects other wise u will get crash, make sure u will release it by proper memory mabagement (if u are not using ARC)
}
}
as u said u are using buttons to pop this viewcontroller so already u know where to place so put this in all the action methods(just replace where u are saving to user defaults )
and also read the comments that i put in the code
//put all these codes where u poping this viewcontroller
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:indexes];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"SELECTED_CELLL"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self.navigationController popViewControllerAnimated:YES];
replace the cellForRowAtIndexPath by following code (i added in the below method)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:( NSIndexPath *)indexPath
{
CustomTabedCell *Cell = [tableView dequeueReusableCellWithIdentifier:#"CELL"];
if(Cell == nil)
{
Cell = [[CustomTabedCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CELL"];
}
Cell.textLabel.text = #"Apple";
NSNumber *rowNum = [NSNumber numberWithInt:indexPath.row];
NSNumber *secNum = [NSNumber numberWithInt:indexPath.section];
__block BOOL isImageHidden = YES;
[indexes enumerateObjectsUsingBlock:^(MySelectedIndex *obj, NSUInteger idx, BOOL *stop)
{
if(obj.selectedRow == rowNum && obj.selectedSection == secNum)
{
isImageHidden = NO;
}
}];
Cell.dashImageView.hidden = isImageHidden;
[Cell.contentView bringSubviewToFront:Cell.dashImageView];//as i notised the red dash imageview appers below the text, but in your image it is appearing above the text so put this line to bring the imageview above the text if dont want commnent this
return Cell;
}
finally in the method didSelectRowAtIndexPath replace by following method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *selSection = [NSNumber numberWithInt:indexPath.section];
NSNumber *selRow = [NSNumber numberWithInt:indexPath.row];
if([indexes count] == 0)
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
else if ([indexes count] == 1)
{
MySelectedIndex *singleIndex = [indexes objectAtIndex:0];
if(singleIndex.selectedSection == selSection & singleIndex.selectedRow == selRow)
{
[indexes removeAllObjects];
}
else
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
}
else
{
__block BOOL addSelectedRow = NO;
__block int index = -1;
[indexes enumerateObjectsUsingBlock:^(MySelectedIndex *obj, NSUInteger idx, BOOL *stop) {
if(!((obj.selectedRow == selRow) & (obj.selectedSection == selSection)))
{
addSelectedRow = YES;
}
else
{
index = idx;
addSelectedRow = NO;
*stop = YES;
}
}];
if(addSelectedRow)
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
else
{
if(index >= 0)
{
[indexes removeObjectAtIndex:index];
}
}
}
NSLog(#"%#",indexes.description);
[tableView reloadData];
}
edit for not able to select more than 13 row's
in viewDidLoad
do like this
indexes = [[NSMutableArray alloc]init];
indexesRed = [[NSMutableArray alloc]init];
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"SELECTED_CELLL"];
if(data != nil)
{
indexes = [[NSKeyedUnarchiver unarchiveObjectWithData:data] retain]; //hear only u retain it
NSLog(#"%#",indexes.description);//check the description, how many objects are there in the array
NSLog(#"indexes->%d",[indexes count]);
}
//[indexes retain];//comment this
} //end of `viewDidLoad`
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTabedCell *Cell = [tableView dequeueReusableCellWithIdentifier:#"CELL"];
if(Cell == nil)
{
Cell = [[CustomTabedCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CELL"];
}
Cell.textLabel.text = #"Apple";
__block BOOL isImageHidden = YES; //remove that __block qualifier
// __block NSInteger currentRow = indexPath.row;
// __block NSInteger currentSection = indexPath.section;
// [self.indexes enumerateObjectsUsingBlock:^(MySelectedIndex *obj, NSUInteger idx, BOOL *stop)
// {
// NSInteger rowNum = [obj.selectedRow integerValue];
// NSInteger secNum = [obj.selectedSection integerValue];
// if(rowNum == currentRow && secNum == currentSection)
// {
// isImageHidden = NO;
// }
// }];
//comment block code and use simple for loop, looping through each obj slightly faster
for(int k = 0;k < [indexes count];k++)
{
MySelectedIndex *seletedIndex = [indexes objectAtIndex:k];
NSInteger row = [seletedIndex.selectedRow integerValue];
NSInteger sec = [seletedIndex.selectedSection integerValue];
if(row == indexPath.row & sec == indexPath.section)
{
isImageHidden = NO;
}
}
Cell.dashImageView.hidden = isImageHidden;
[Cell.contentView bringSubviewToFront:Cell.dashImageView];//as i notised the red dash imageview appers below the text, but in your image it is appearing above the text so put this line to bring the imageview above the text if dont want commnent this
return Cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *selSection = [NSNumber numberWithInt:indexPath.section];
NSNumber *selRow = [NSNumber numberWithInt:indexPath.row];
if([indexes count] == 0)
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
else if ([indexes count] == 1)
{
MySelectedIndex *singleIndex = [indexes objectAtIndex:0];
NSInteger rowNumInInt = [singleIndex.selectedRow integerValue];
NSInteger sectionInInt = [singleIndex.selectedSection integerValue];
if(sectionInInt == indexPath.section & rowNumInInt == indexPath.row)
{
[indexes removeAllObjects];
}
else
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
}
else
{
BOOL addSelectedRow = NO;
int index = -1;
for(int j = 0; j < [indexes count];j++)
{
MySelectedIndex *selectedObj = [indexes objectAtIndex:j];
NSInteger rowInt = [selectedObj.selectedRow integerValue];
NSInteger sectionInt = [selectedObj.selectedSection integerValue];
if(!(rowInt == indexPath.row && sectionInt == indexPath.section))
{
addSelectedRow = YES;
}
else
{
index = j;
addSelectedRow = NO;
// *stop = YES;
}
}
if(addSelectedRow)
{
MySelectedIndex *selectedIndx = [[MySelectedIndex alloc]initWithSelectedRow:selRow andSelectedIndex:selSection];
[indexes addObject:selectedIndx];
}
else
{
if(index >= 0)
{
[indexes removeObjectAtIndex:index];
}
}
}
// [tableView reloadData];
NSLog(#"indexes count->%d",[indexes count]);//check each time by selecting and deselectin weateher it is working or not
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
thats it simple .. :)
hope this helps u .. :)
Try This it work good
#import "ViewController.h"
#interface ViewController ()<UITableViewDataSource,UITableViewDelegate>
{
UITableView *menuTableView;
NSMutableArray *colorsArray,*tagvalueArray;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
menuTableView=[[UITableView alloc]initWithFrame:CGRectMake(0, 60, 320, 404) style:UITableViewStyleGrouped];
menuTableView.delegate=self;
menuTableView.dataSource=self;
menuTableView.separatorColor=[UIColor grayColor];
[self.view addSubview:menuTableView];
NSArray *serverResponseArray=[[NSArray alloc]initWithObjects:#"red",#"yellow",#"pink",#"none", nil]; // consider this array as the information you receive from DB or server
colorsArray =[[NSMutableArray alloc]init];
tagvalueArray =[[NSMutableArray alloc]init];
for (int i=0; i<serverResponseArray.count; i++) {
[colorsArray addObject:serverResponseArray[i]];
[tagvalueArray addObject:#"0"];
}
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return colorsArray.count;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 40;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
for (UIView *view in cell.contentView.subviews) {
[view removeFromSuperview];
}
cell.textLabel.text =colorsArray[indexPath.row];
if ([[tagvalueArray objectAtIndex:indexPath.row]intValue]==1) {
UIImageView *checkedimg=[[UIImageView alloc]initWithFrame:CGRectMake(40, 20, 500, 5)];
checkedimg.backgroundColor=[UIColor redColor];
[cell.contentView addSubview:checkedimg];
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%#",colorsArray[indexPath.row]);
[tagvalueArray replaceObjectAtIndex:indexPath.row withObject:#"1"];
[menuTableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I'm writing a simple checklist app. I have two UIViewControllers. The first displays the checklist in a UITableView. I'm using a UIBarButtonItem to push a second view onto the stack to add new tasks. All of the tasks are saved in an array.
Everything works great save for one thing.
If I enter edit mode and delete an item from the table view, the item is removed from the table view and the array--this part seems to be working fine. However, after deleting an item, if I tap the bar button item to add a new task, I run into a problem.
My NSLogs tell me that the new item is added to the array, but when I return to the table view, the deleted item shows up instead of the new item. The table view seems to be reusing the dequeued cell (not sure).
What am I doing wrong?
CLCheckListViewController.m
#import "CLCheckListViewController.h"
#import "CLTaskFactory.h"
#import "CLTaskStore.h"
#import "CLAddTaskViewController.h"
#implementation CLCheckListViewController
{
__weak IBOutlet UITableView *checkList;
}
- (id)init
{
self = [super init];
if (self) {
// add five sample tasks
CLTaskFactory *task1 = [[CLTaskFactory alloc] init];
[task1 setTaskName:#"Task 1"];
[task1 setDidComplete:NO];
[[CLTaskStore sharedStore] addTask:task1];
CLTaskFactory *task2 = [[CLTaskFactory alloc] init];
[task2 setTaskName:#"Task 2"];
[task2 setDidComplete:NO];
[[CLTaskStore sharedStore] addTask:task2];
CLTaskFactory *task3 = [[CLTaskFactory alloc] init];
[task3 setTaskName:#"Task 3"];
[task3 setDidComplete:NO];
[[CLTaskStore sharedStore] addTask:task3];
CLTaskFactory *task4 = [[CLTaskFactory alloc] init];
[task4 setTaskName:#"Task 4"];
[task4 setDidComplete:NO];
[[CLTaskStore sharedStore] addTask:task4];
CLTaskFactory *task5 = [[CLTaskFactory alloc] init];
[task5 setTaskName:#"Task 5"];
[task5 setDidComplete:NO];
[[CLTaskStore sharedStore] addTask:task5];
}
return self;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[checkList reloadData];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// create edit button
[[self navigationItem] setLeftBarButtonItem:[self editButtonItem]];
// create title
[[self navigationItem] setTitle:#"Checklist"];
// create add guest button
UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(pushAddTask)];
[[self navigationItem] setRightBarButtonItem:bbi];
}
- (void)pushAddTask
{
CLAddTaskViewController *advk = [[CLAddTaskViewController alloc] init];
[[self navigationController] pushViewController:advk animated:YES];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[CLTaskStore sharedStore] allTasks] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [checkList dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// put the tasks into the cell
[[cell textLabel] setText:[NSString stringWithFormat:#"%#", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[indexPath row]]]];
// put the checkbox into the cell's accessory view
UIButton *checkBox = [UIButton buttonWithType:UIButtonTypeCustom];
checkBox = [UIButton buttonWithType:UIButtonTypeCustom];
[checkBox setImage:[UIImage imageNamed:#"checkbox.png"] forState:UIControlStateNormal];
[checkBox setImage:[UIImage imageNamed:#"checkbox-checked.png"] forState:UIControlStateSelected];
checkBox.frame = CGRectMake(0, 0, 30, 30);
checkBox.userInteractionEnabled = YES;
[checkBox addTarget:self action:#selector(didCheckTask:) forControlEvents:UIControlEventTouchDown];
cell.accessoryView = checkBox;
}
return cell;
}
- (void)didCheckTask:(UIButton *)button
{
CGPoint hitPoint = [button convertPoint:CGPointZero toView:checkList];
hitIndex = [checkList indexPathForRowAtPoint:hitPoint];
task = [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[hitIndex row]];
if (task.didComplete) {
task.didComplete = NO;
} else {
task.didComplete = YES;
}
NSInteger taskCount = [[[CLTaskStore sharedStore] allTasks] count];
for (int i = 0; i < taskCount; i++) {
NSLog(#"%#, status: %#", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:i], [[[[CLTaskStore sharedStore] allTasks] objectAtIndex:i] didComplete]?#"YES":#"NO");
}
// toggle checkbox
button.selected = !button.selected;
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
// set editing mode
if (editing) {
self.navigationItem.title = #"Edit Checklist";
[checkList setEditing:YES];
} else {
self.navigationItem.title = #"Checklist";
[checkList setEditing:NO];
}
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
// remove task
if (editingStyle == UITableViewCellEditingStyleDelete) {
// remove task from CLTaskStore
task = [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[indexPath row]];
[[CLTaskStore sharedStore] removeTask:task];
// remove guest from table view
[checkList deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
// reload table view
//[checkList reloadData];
}
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
[[CLTaskStore sharedStore] moveTaskAtIndex:[sourceIndexPath row] toIndex:[destinationIndexPath row]];
}
#end
CLAddTaskViewController.m
#import "CLAddTaskViewController.h"
#import "CLTaskFactory.h"
#import "CLTaskStore.h"
#implementation CLAddTaskViewController
- (void)viewDidLoad
{
[[self navigationItem] setTitle:#"Add Task"];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// clear first responder
[[self view] endEditing:YES];
// create new task
CLTaskFactory *newTask = [[CLTaskFactory alloc] init];
[newTask setTaskName:[newTaskName text]];
// add new guest to RCGuestStore
[[CLTaskStore sharedStore] addTask:newTask];
}
#end
CLAddTaskFactory.m
#import "CLTaskFactory.h"
#implementation CLTaskFactory
#synthesize taskName;
- (void)setDidComplete:(BOOL)dc
{
didComplete = dc;
}
- (BOOL)didComplete
{
return didComplete;
}
- (NSString *)description
{
// override the description
NSString *descriptionString = [[NSString alloc] initWithFormat:#"%#", taskName];
return descriptionString;
}
#end
CLAddTaskStore.m
#import "CLTaskStore.h"
#import "CLTaskFactory.h"
#import "CLCheckListViewController.h"
#implementation CLTaskStore
+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedStore];
}
+ (CLTaskStore *)sharedStore
{
static CLTaskStore *sharedStore = nil;
if (!sharedStore) {
sharedStore = [[super allocWithZone:nil] init];
}
return sharedStore;
}
- (id)init
{
self = [super init];
if (self) {
allTasks = [[NSMutableArray alloc] init];
}
return self;
}
- (NSMutableArray *)allTasks
{
return allTasks;
}
- (void)addTask:(CLTaskFactory *)task
{
[allTasks addObject:task];
NSLog(#"Task added: %#", task);
}
- (void)removeTask:(CLTaskFactory *)task
{
// remove the item for the deleted row from the store
[allTasks removeObjectIdenticalTo:task];
NSInteger taskCount = [allTasks count];
NSLog(#"Removed: %#, there are now %d remaining tasks, they are:", task, taskCount);
for (int i = 0; i < taskCount; i++) {
NSLog(#"%#, status: %#", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:i], [[[[CLTaskStore sharedStore] allTasks] objectAtIndex:i] didComplete]?#"YES":#"NO");
}
}
- (void)moveTaskAtIndex:(int)from toIndex:(int)to
{
if (from == to) {
return;
}
CLTaskFactory *task = [allTasks objectAtIndex:from];
[allTasks removeObjectAtIndex:from];
[allTasks insertObject:task atIndex:to];
}
#end
Thanks for your help!
The table view seems to be reusing the dequeued cell
That's exactly what it does; it reuses cells whenever possible. If you had enough items to cause the table view to scroll, you'd also see the same problem.
In -tableView:cellForRowAtIndexPath: you must always set up the cell to display the correct content for the given row index, regardless of whether dequeueReusableCellWithIdentifier: returns a cell or not.
Basically:
If -dequeueReusableCellWithIdentifier: returns nil, create a new cell and add your checkbox button to it.
Then set the cell text and the button state on either this new cell or the cell returned from -dequeueReusableCellWithIdentifier:
Darren is correct. If you look at your code here:
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// put the tasks into the cell
[[cell textLabel] setText:[NSString stringWithFormat:#"%#", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[indexPath row]]]];
// put the checkbox into the cell's accessory view
UIButton *checkBox = [UIButton buttonWithType:UIButtonTypeCustom];
checkBox = [UIButton buttonWithType:UIButtonTypeCustom];
[checkBox setImage:[UIImage imageNamed:#"checkbox.png"] forState:UIControlStateNormal];
[checkBox setImage:[UIImage imageNamed:#"checkbox-checked.png"] forState:UIControlStateSelected];
checkBox.frame = CGRectMake(0, 0, 30, 30);
checkBox.userInteractionEnabled = YES;
[checkBox addTarget:self action:#selector(didCheckTask:) forControlEvents:UIControlEventTouchDown];
cell.accessoryView = checkBox;
}
your only setting up the cell if(cell == nil)
Change your code to
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// put the tasks into the cell
[[cell textLabel] setText:[NSString stringWithFormat:#"%#", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[indexPath row]]]];
// put the checkbox into the cell's accessory view
UIButton *checkBox = [UIButton buttonWithType:UIButtonTypeCustom];
checkBox = [UIButton buttonWithType:UIButtonTypeCustom];
[checkBox setImage:[UIImage imageNamed:#"checkbox.png"] forState:UIControlStateNormal];
[checkBox setImage:[UIImage imageNamed:#"checkbox-checked.png"] forState:UIControlStateSelected];
checkBox.frame = CGRectMake(0, 0, 30, 30);
checkBox.userInteractionEnabled = YES;
[checkBox addTarget:self action:#selector(didCheckTask:) forControlEvents:UIControlEventTouchDown];
cell.accessoryView = checkBox;
I am using customTableView.I want to reload my tableViewCell with new data from my NSMutableArray.How can i do that?Here is my current Code for the tableView :
tableView.m
//
// GameTableView.m
//
// Created by Mahmudul hasan on 4/6/11.
// Copyright 2011 JU. All rights reserved.
//
#import "GameTableView.h"
#import "BeginingCell.h"
#implementation GameTableView
#synthesize XmlManipulatorObject,QuestionMutableArray,dictionary;
#synthesize _tableView=tableView;
#pragma mark -
#pragma mark Table view data source
-(void)viewDidLoad
{
[super viewDidLoad];
XmlManipulatorObject=[[xmlManipulator alloc] init];
QuestionMutableArray=[[XmlManipulatorObject ReadXml] retain];
dictionary=[[NSMutableDictionary alloc] init];
dictionary = [QuestionMutableArray objectAtIndex:0];
}
-(IBAction) goBack:(id) sender{
[self.parentViewController dismissModalViewControllerAnimated:YES];
}
-(void) btnAction:(id) sender {
NSString *str = ((UIButton*)sender).titleLabel.text;
UIAlertView *alrt=[[UIAlertView alloc]initWithTitle:str message:str
delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:nil ];
[dictionary removeAllObjects];
[alrt show];
[alrt release];
if(dictionary==nil)
{
NSLog(#"dictionary is nill now");
}
else {
NSLog(#"dictionary is not nil");
}
}
//Method to iterate trough a NSMutableDictionaries in a NSMutableArray
-(NSMutableArray*)GetQuestionByKey:(NSInteger *)key :(NSMutableArray *)Array
{
NSMutableArray *arr;
for (dictionary in Array) {
if (dictionary!=nil) {
arr=[[NSMutableArray alloc] init];
[arr addObject:[dictionary objectForKey:#"option1"]];
}
}
return arr;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"%#",[dictionary objectForKey:#"question"]);
NSLog(#"%#",[dictionary objectForKey:#"option1"]);
NSLog(#"%#",[dictionary objectForKey:#"option2"]);
NSLog(#"%#",[dictionary objectForKey:#"option3"]);
NSLog(#"%#",[dictionary objectForKey:#"option4"]);
static NSString *CellIdentifier = #"BeginingCell";
BeginingCell *cell=(BeginingCell *)[_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects=[[NSBundle mainBundle] loadNibNamed:#"BeginingCell" owner:self options:nil ];
for(id CurrentObject in topLevelObjects)
{
if ([CurrentObject isKindOfClass:[BeginingCell class]]) {
cell=(BeginingCell *) CurrentObject;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
break;
}
}
}
// Configure the cell.
if(indexPath.row==0)
{
cell.myImageView.image = [UIImage imageNamed:#"man_kirstie_alley.jpg"];
cell.SectionTitle.text=[dictionary objectForKey:#"question"];
cell.Option1.text=[dictionary objectForKey:#"option1"];
cell.Option2.text=[dictionary objectForKey:#"option2"];
cell.Option3.text=[dictionary objectForKey:#"option3"];
cell.Option4.text=[dictionary objectForKey:#"option4"];
///For showing the option idntity
[cell.button1 setTitle:#"A." forState:UIControlStateNormal];
[cell.button2 setTitle:#"B." forState:UIControlStateNormal];
[cell.button3 setTitle:#"C." forState:UIControlStateNormal];
[cell.button4 setTitle:#"D." forState:UIControlStateNormal];
///For selecting the correct answer
[cell.ansBtn1 setTitle:#"A" forState:UIControlStateNormal];
[cell.ansBtn2 setTitle:#"B" forState:UIControlStateNormal];
[cell.ansBtn3 setTitle:#"C" forState:UIControlStateNormal];
[cell.ansBtn4 setTitle:#"D" forState:UIControlStateNormal];
[cell.ansBtn1 addTarget:self action:#selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
[cell.ansBtn4 addTarget:self action:#selector(goBack:) forControlEvents:UIControlEventTouchUpInside];
}
return cell;
}
-(void)viewDidUnload
{
[self dealloc];
}
#end
call
[tblView reloadData];
when you are updating your mutable array.