I have UITableView cell for display UITextView text. I need to delete some cell rows text using UITableViewCellEditingStyleDelete. When i use edit button for delete some text i got error.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSMutableArray* myMutableArrayAgain = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"save"]];
// NSLog(#"array is %#",myMutableArrayAgain);
return [myMutableArrayAgain count];
}
Delete function:
-(void)editButtonPressed:(UIButton *)button{
NSLog(#">>> Entering %s <<<", __PRETTY_FUNCTION__);
[tableView setEditing:![tableView isEditing] animated:YES];
NSString *buttonTitle = ([tableView isEditing]) ? #"Done" : #"Edit";
[editButton setTitle:buttonTitle forState:UIControlStateNormal];
NSLog(#"<<< Leaving %s >>>", __PRETTY_FUNCTION__);
}
- (void)tableView:(UITableView *)tableView1
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#">>> Entering %s <<<", __PRETTY_FUNCTION__);
NSMutableArray* myMutableArrayAgain = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"save"]];
if (editingStyle == UITableViewCellEditingStyleDelete)
{
//first delete this from the db of favorites table
NSLog(#"art id is %#",[myMutableArrayAgain objectAtIndex:indexPath.row]);
NSLog(#"indexpath is %d", indexPath.row);
[myMutableArrayAgain removeObjectAtIndex:indexPath.row];
NSLog(#"remove %d",indexPath.row);
NSArray *indexPathsToRemove = [NSArray arrayWithObject:indexPath];
NSLog(#"indextoremove %#",indexPathsToRemove);
[tableView1 deleteRowsAtIndexPaths:indexPathsToRemove withRowAnimation:UITableViewRowAnimationRight];
}
NSLog(#"<<< Leaving %s >>>", __PRETTY_FUNCTION__);
}
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath
{
NSLog(#">>> Entering %s <<<", __PRETTY_FUNCTION__);
NSMutableArray* myMutableArrayAgain = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"save"]];
NSString *contentsToMove = [[myMutableArrayAgain objectAtIndex:[fromIndexPath row]] retain];
[myMutableArrayAgain removeObjectAtIndex:[fromIndexPath row]];
[myMutableArrayAgain insertObject:contentsToMove atIndex:[toIndexPath row]];
[contentsToMove release];
NSLog(#"<<< Leaving %s >>>", __PRETTY_FUNCTION__);
}
Error in Nslog:
2013-10-07 14:29:41.939 WorkSa[3689:c07] indexpath is 0
2013-10-07 14:29:41.940 WorkSa[3689:c07] remove 0
2013-10-07 14:29:41.940 WorkSa[3689:c07] indextoremove (
"<NSIndexPath 0x8b44fa0> 2 indexes [0, 0]"
)
2013-10-07 14:29:41.941 WorkSa[3689:c07] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:1046
2013-10-07 14:29:41.942 WorkSafeACT[3689:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (7) must be equal to the number of rows contained in that section before the update (7), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
You need to add synchronize statement after deleting, change your function as shown below.
- (void)tableView:(UITableView *)tableView1
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#">>> Entering %s <<<", __PRETTY_FUNCTION__);
NSMutableArray* myMutableArrayAgain = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"save"]];
if (editingStyle == UITableViewCellEditingStyleDelete)
{
//first delete this from the db of favorites table
NSLog(#"art id is %#",[myMutableArrayAgain objectAtIndex:indexPath.row]);
NSLog(#"indexpath is %d", indexPath.row);
[myMutableArrayAgain removeObjectAtIndex:indexPath.row];
//Add these 2 lines - Similarly you will need to do in moving items.
[[NSUserDefaults standardUserDefaults] setObject:myMutableArrayAgain forKey:#"save"];
[[NSUserDefaults standardUserDefaults] synchronize];
//Add these 2 lines
[myMutableArrayAgain removeObjectAtIndex:indexPath.row];
NSLog(#"remove %d",indexPath.row);
NSArray *indexPathsToRemove = [NSArray arrayWithObject:indexPath];
NSLog(#"indextoremove %#",indexPathsToRemove);
[tableView1 deleteRowsAtIndexPaths:indexPathsToRemove withRowAnimation:UITableViewRowAnimationRight];
}
NSLog(#"<<< Leaving %s >>>", __PRETTY_FUNCTION__);
}
Also you can use following code to redefine NSLog and have just print variables or strings which will give you method names and all, define these anywhere in appdelegate
#ifdef DEBUG
# define NLog(fmt, ...) NSLog((#"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
Check this for more information on NSLog formatting
ok try this,
i am used the normal string values, u better check if the array is correctly loading from the user defaults
#interface ViewController ()<UITableViewDataSource , UITableViewDelegate>
{
NSMutableArray *myMutableArrayAgain; //member mut_array this is the array
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//for example somewhere u are setting the values in the array
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arrayObj = [[NSMutableArray alloc] init];
for(int i = 0 ; i<10 ; i++) {
[arrayObj addObject:[NSString stringWithFormat:#"hello %d",i]];
}
[standardDefaults setObject:arrayObj forKey:#"save"]; //for key save
[arrayObj release];
//fetch the data once becz chancge's that u made to array to be reflected in tableview
//i am fetching the contents hear
myMutableArrayAgain = [[NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"save"]] retain]; //hear u are getting the contents of array
// myMutableArrayAgain = [[NSMutableArray arrayWithObjects:#"hello",#"world",#"happy",#"coding", nil]retain];//insted of ur values i use some string values , insted u can use alloc init method if confused of retain
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// NSMutableArray* myMutableArrayAgain = [NSMutableArray arrayWithArray: [[NSUserDefaults standardUserDefaults] objectForKey:#"save"]]; //dont fech the values to be displayed it is alreday in "myMutableArrayAgain" array
// NSLog(#"array is %#",myMutableArrayAgain);
return [myMutableArrayAgain count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// NSMutableArray* myMutableArrayAgain = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"save"]]; //dont fetch
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [myMutableArrayAgain objectAtIndex:indexPath.row]; //use the member array
[cell.textLabel setFont:[UIFont fontWithName:#"Arial-BoldMT" size:14]];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
- (void)tableView:(UITableView *)tableView1 commitEditingStyle (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#">>> Entering %s <<<", __PRETTY_FUNCTION__);
// NSMutableArray* myMutableArrayAgain = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"save"]];
if (editingStyle == UITableViewCellEditingStyleDelete)
{
//first delete this from the db of favorites table
// NSLog(#"art id is %#",[myMutableArrayAgain objectAtIndex:indexPath.row]);
NSLog(#"indexpath is %d", indexPath.row);
[myMutableArrayAgain removeObjectAtIndex:indexPath.row];
[tableView1 beginUpdates];
NSLog(#"remove %d",indexPath.row);
// NSArray *indexPathsToRemove = [NSArray arrayWithObject:indexPath];
// NSLog(#"indextoremove %#",indexPathsToRemove);
NSIndexPath *path=[NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
[tableView1 deleteRowsAtIndexPaths:[NSArray arrayWithObject:path] withRowAnimation:UITableViewScrollPositionBottom];
// [tableView1 deleteRowsAtIndexPaths:indexPathsToRemove withRowAnimation:UITableViewRowAnimationRight];
[tableView1 endUpdates];
}
NSLog(#"<<< Leaving %s >>>", __PRETTY_FUNCTION__);
}
- (void)dealloc
{
[_aTableView release];
[_aButton release];
[super dealloc];
}
- (IBAction)whenButtonTapped:(id)sender
{
NSLog(#">>> Entering %s <<<", __PRETTY_FUNCTION__);
[self.aTableView setEditing:![self.aTableView isEditing] animated:YES];
NSString *buttonTitle = ([self.aTableView isEditing]) ? #"Done" : #"Edit";
[self.aButton setTitle:buttonTitle forState:UIControlStateNormal];
NSLog(#"<<< Leaving %s >>>", __PRETTY_FUNCTION__);
}
#end
Related
I have a tableview. I want to create string from selected cell names. For example My Cell names : Row No 1 - Row No 2 - Row No 3 - Row No 4 - Row No 5. When selected Row No 1 - Row No 2 - Row No 5 mystring value = Selected Items Row No 1 , Row No 2 , Row No 5. When i deselect Row No 1 mystring value = Selected items Row No 2 , Row No 5 or Row No 5 , Row No 2. It does not matter.
Is it possible? If possible how can i do? Thanks.
//EDIT
My code is here ;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
tableData = [[NSMutableArray alloc] init];
[tableData retain];
for (int i=1; i<=10; i++)
{
NSString *rowInString= [[NSString alloc ]initWithFormat:#"Row No %d",i];
[tableData addObject:rowInString];
rowInString=nil;
}
_mytable.allowsMultipleSelectionDuringEditing = YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"self.Data count: %lu", (unsigned long)[tableData count]);
return tableData.count ;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellidentifier=#"cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellidentifier];
if(cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellidentifier];
}
NSString *cellValue;
cellValue=[tableData objectAtIndex:[indexPath row]];
[_mytable setEditing: YES];
cell.textLabel.text=cellValue;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *selectedText = #"Selected Items";
NSArray *selectedRows = [_mytable indexPathsForSelectedRows];
NSLog(#"%lu" , (unsigned long)selectedRows.count);
}
- (void)dealloc {
[_mytable release];
[super dealloc];
}
#end
First add in your tableview.
self.myTableView.allowsMultipleSelection = YES;
then
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//you need an array in which you can add and remove strings
//adding the selected row string to array.
[stringArray addObject:[tableData objectAtIndex:[indexPath row]];
NSString *string;
for(NSString *str in stringArray)
{
string = [NSString stringWithFormat:"%#%#",string,str];
}
NSLog(#"Selected Row %#"string);
}
- (void)tableView:(UITableView *)tableView didDeSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//removing the selected string from array
for(NSString *stringInArray in stringArray)
{
if([stringInArray isEqualToString:[tableData objectAtIndex:[indexPath row]])
{
[stringArray removeObject:stringInArray];
}
}
NSString *string;
for(NSString *str in stringArray)
{
string = [NSString stringWithFormat:"%#%#",string,str];
}
NSLog(#"Selected Row %#"string);
}
Here is how you can do it:
Create a NSString *selectedText=#"Selected Items";
Now when you press a item in tableview, didselectitematibdexpath will be called.
Using the indexpath get the cell's name. Now compare if that text exist in the selectedText string. If it exist remove it from string
NSRange check = [selectedText rangeOfString:#" one"];
if (check.location != NSNotFound) {
selectedText = [selectedText stringByReplacingCharactersInRange:check withString:#""];
}
else add it to string using
selectedText=[selectedText stringWithFormat:#" %#",cellName];
Here is your code(i have changed as per your requirement):
NSString *selectedText = #"Selected Items";
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
tableData = [[NSMutableArray alloc] init];
[tableData retain];
for (int i=1; i<=10; i++)
{
NSString *rowInString= [[NSString alloc ]initWithFormat:#"Row No %d",i];
[tableData addObject:rowInString];
rowInString=nil;
}
_mytable.allowsMultipleSelectionDuringEditing = YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"self.Data count: %lu", (unsigned long)[tableData count]);
return tableData.count ;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellidentifier=#"cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellidentifier];
if(cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellidentifier];
}
NSString *cellValue;
cellValue=[tableData objectAtIndex:[indexPath row]];
[_mytable setEditing: YES];
cell.textLabel.text=cellValue;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSRange check = [selectedText rangeOfString:[tableData objectAtIndex:[indexPath row]]];
if (check.location != NSNotFound) {
selectedText = [selectedText stringByReplacingCharactersInRange:check withString:#""];
} else {
selectedText = [NSString stringwithFormat:#"%# %#",selectedText, [tableData objectAtIndex:[indexPath row]]];
}
NSArray *selectedRows = [_mytable indexPathsForSelectedRows];
NSLog(#"%lu" , (unsigned long)selectedRows.count);
}
- (void)dealloc {
[_mytable release];
[super dealloc];
}
#end
I can't seem to find too much on this. I'm currently trying to create a swipable delete button that will delete the row that is swiped, and if that row is now empty from the section header it will delete the section header as well. For example, "Bread" is swiped to delete, and there is nothing else under the section header "B". Then this will delete both Bread, and the "B" section header. My code is below.
#interface ChoicesTableViewController () <UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UITableView *myTableView;
#property (strong, nonatomic) NSMutableArray *items;
#property (strong, nonatomic) NSMutableDictionary *alphabetizedItems;
#end
#implementation ChoicesTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
self.items = [[NSMutableArray alloc] init];
[self.items addObject:#"Apples"];
[self.items addObject:#"Bread"];
self.alphabetizedItems = [self alphabetizeItems:self.items];
}
//Segue if the item is tapped
//- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
//{
// MyDataChoices *currentRow = self.arrayNames[indexPath.row];
// self.mySelectedCell = currentRow.myNameChoices;
//
// [self performSegueWithIdentifier:#"unwindSegueAction" sender:self];
//
//}
////unwind segue from add choice
- (IBAction)unwindSegueToChoices:(UIStoryboardSegue *)segue
{
AddChoiceViewController *sourceVC = segue.sourceViewController;
NSString *myNewItem = sourceVC.myTextField.text;
//NSString *myFinalString = [[myNewItem substringToIndex:1] capitalizedString];
NSString *stringCapitalized = [myNewItem capitalizedString];
[self.items addObject:stringCapitalized];
self.alphabetizedItems = [self alphabetizeItems:self.items];
//[self.arrayNames addObjectsFromArray:#[[MyDataChoices itemWithNewName:stringCapitalized]]];
[self.tableView reloadData];
}
//titles for talble view
#pragma mark Helper Methods
- (NSMutableDictionary *)alphabetizeItems:(NSArray *)items {
NSMutableDictionary *buffer = [[NSMutableDictionary alloc] init];
// Put Fruits in Sections
for (int i = 0; i < [items count]; i++) {
NSString *fruit = [items objectAtIndex:i];
NSString *firstLetter = [[fruit substringToIndex:1] uppercaseString];
if ([buffer objectForKey:firstLetter]) {
[(NSMutableArray *)[buffer objectForKey:firstLetter] addObject:fruit];
} else {
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithObjects:fruit, nil];
[buffer setObject:mutableArray forKey:firstLetter];
}
}
// Sort Fruits
NSArray *keys = [buffer allKeys];
for (int j = 0; j < [keys count]; j++) {
NSString *key = [keys objectAtIndex:j];
[(NSMutableArray *)[buffer objectForKey:key] sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
}
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:buffer];
return result;
}
#pragma mark title indexing
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSArray *keys = [[self.alphabetizedItems allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [keys objectAtIndex:section];
return key;
}
# pragma mark main table view
-(NSInteger) numberOfSectionsInTableView:(UITableView *) tableView
{
NSArray *keys = [self.alphabetizedItems allKeys];
return [keys count];
}
-(NSInteger) tableView:(UITableView *) tableView numberOfRowsInSection:(NSInteger)section
{
//return self.arrayNames.count;
NSArray *unsortedKeys = [self.alphabetizedItems allKeys];
NSArray *sortedKeys = [unsortedKeys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [sortedKeys objectAtIndex:section];
NSArray *fruitsForSection = [self.alphabetizedItems objectForKey:key];
return [fruitsForSection count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//MyDataChoices *currentRow = self.arrayNames[indexPath.row];
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:#"mainCell2" forIndexPath:indexPath];
//cell.textLabel.text = currentRow.myNameChoices;
NSArray *unsortedKeys = [self.alphabetizedItems allKeys];
NSArray *sortedKeys = [unsortedKeys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [sortedKeys objectAtIndex:[indexPath section]];
NSArray *fruitsForSection = [self.alphabetizedItems objectForKey:key];
NSString *fruit = [fruitsForSection objectAtIndex:[indexPath row]];
[cell.textLabel setText:fruit];
return cell;
}
# pragma Mark delete slide button
//Delete Swipe Button
// 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
int index = indexPath.row;
//[self.items removeObjectAtIndex:index];
[self.alphabetizedItems removeObjectForKey:indexPath];
[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
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
The basic approach is to see how many rows are in the section for the row being deleted. If the section has two or more rows, simply delete the row as you are doing now. If the section only has one row (the one being deleted), then remove the section from the data model and then delete the section from the table instead of deleting the row.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSArray *unsortedKeys = [self.alphabetizedItems allKeys];
NSArray *sortedKeys = [unsortedKeys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [sortedKeys objectAtIndex:[indexPath section]];
NSArray *fruitsForSection = [self.alphabetizedItems objectForKey:key];
if (fruitsForSection.count == 1) {
// Delete the whole section
[self.alphabetizedItems removeObjectForKey:key];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
} else {
// Delete the row from the data source
NSInteger index = indexPath.row;
//[self.items removeObjectAtIndex:index];
[self.alphabetizedItems removeObjectForKey:indexPath];
[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
}
}
I have a problem with my UITableView where deleting the last row in the section terminates the app with an NSInternalInconsistencyException:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
My UITableView is populated with MPMediaItems from an MPMediaItemCollection (self.detailCollection). When the last one gets deleted I want to show a "No results found" label in a blank cell.
Here is my cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
if ([[self.detailCollection items] count] == 0) {
[tableView numberOfRowsInSection:1];
cell.textLabel.text = #"No results found";
//return cell;
} else {
MPMediaItem *song = (MPMediaItem *)[[self.detailCollection items] objectAtIndex:[indexPath row]];
if (song) {
cell.textLabel.text = [song valueForProperty:MPMediaItemPropertyTitle];
MPMediaItemArtwork *art = [song valueForProperty:MPMediaItemPropertyArtwork];
cell.imageView.image = [art imageWithSize:CGSizeMake(64, 64)];
}
}
return cell;
}
Here is my code for deleting the rows:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
PlaylistData *pData = [PlaylistData getInstance];
NSMutableArray *tempArray = [[self.eventDictionary valueForKey:[NSString stringWithFormat:#"%#", pData.selectedEvent]] mutableCopy];
NSMutableArray *newArray = [[NSMutableArray alloc] init];
if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView beginUpdates];
// Delete the row from the data source
[tempArray removeObjectAtIndex:indexPath.row];
[self.eventDictionary setValue:tempArray forKey:[NSString stringWithFormat:#"%#", pData.selectedEvent]];
[[NSUserDefaults standardUserDefaults] setValue:self.eventDictionary forKey:#"Playlist Items"];
[[NSUserDefaults standardUserDefaults] synchronize];
if ([tempArray count] == 0) {
[tableView numberOfRowsInSection:1];
}
for (int i=0; i<[tempArray count]; i++) {
NSString *pID = [NSString stringWithFormat:#"%#", [tempArray objectAtIndex:i]];
unsigned long long ullvalue = strtoull([pID UTF8String], NULL, 0);
NSNumber *UniqueID = [NSNumber numberWithUnsignedLongLong:ullvalue];
MPMediaQuery *cellQuery = [[MPMediaQuery alloc] init];
[cellQuery addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:UniqueID forProperty:MPMediaItemPropertyPersistentID]];
for (MPMediaItem *item in [cellQuery items]) {
[newArray addObject:item];
}
[cellQuery release];
}
if (![newArray count] == 0) {
self.detailCollection = [[MPMediaItemCollection alloc] initWithItems:newArray];
[tableView numberOfRowsInSection:[self.detailCollection count]];
} else {
[tableView numberOfRowsInSection:1];
[tableView reloadData];
}
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
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
}
}
And here is my numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if ([[self.detailCollection items] count] == 0 || [self.detailCollection items] == nil || [self.detailCollection items] == NULL) {
return 1;
}
return [[self.detailCollection items] count];
}
My question is: Why isn't it creating the "No results found" cell when self.detailCollection is == 0?
I think you want something to the effect of:
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
if ([newArray count] == 0) {
[tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
[tableView endUpdates];
However, a simpler solution would be to just add a label to your table view. Unless there is some specific reason that you need an actual UITableViewCell to display "No results found".
UILabel *label = [[UILabel alloc] init];
CGRect frame = CGRectMake(0.0, 0.0, 320.0, 44.0);
label.frame = frame;
label.text = #"No results found";
[self.tableView addSubview:label];
One solution that I would recommend is to use a table footer view rather than a new cell. Basically, add a footer to your table that is only visible when the cell count is 0.
You can override the method
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
to get a footer.
When deleting and adding objects, check the new count and then adjust the visibility of the footer from there.
You are calling numberOfRowsInSection in a couple of places. You should never be calling it, it's a call back hook that you implement and the system calls.
The cleanest solution to do this would be to set self.tableView.tableFooterView when rows = 0
#interface UITableView : UIScrollView <NSCoding> {
...
#property(nonatomic,retain) UIView *tableFooterView;
// accessory view below content. default is nil. not to be confused with section footer
When editing the UITableView, the app usually crashes with this error after someone presses the "delete" button on the uitableviewcell. This usually happens for the first item in the table view, but has happened to others. I'm sorry for being so vague, I can provide any additional infomation. I'm just very confused on why this happens and why it happens when it does.
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = self.editButtonItem;
}
-(void)viewWillAppear:(BOOL)animated{
[_matchIDS removeAllObjects];
_matchIDS = [[NSMutableArray alloc]init];
_matchIDS = [[NSUserDefaults standardUserDefaults] valueForKey:#"allMatchIDS"];
[self.tableView reloadData];
}
-(void)viewWillDisappear:(BOOL)animated{
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:_matchIDS forKey:#"allMatchIDS"];
[defaults synchronize];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _matchIDS.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = _matchIDS[indexPath.row];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[_matchIDS removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
The error results from trying to remove an element from the _matchIDS array, which is immutable.
[_matchIDS removeObjectAtIndex:indexPath.row];
You attempt to make the array mutable here:
-(void)viewWillAppear:(BOOL)animated{
[_matchIDS removeAllObjects];
_matchIDS = [[NSMutableArray alloc]init];
_matchIDS = [[NSUserDefaults standardUserDefaults] valueForKey:#"allMatchIDS"]; // <---
[self.tableView reloadData];
}
but the line marked above replaces _matchIDS, discarding the NSMutableArray you instantiated. You probably want to use the mutableCopy method instead, resulting in something like:
_matchIDS = [[[NSUserDefaults standardUserDefaults] valueForKey:#"allMatchIDS"] mutableCopy];
I'm new to ios development, and I'm using checkmark for cells in a UITableView.
I want storing checked cell in a NSUserDefaults database, When I reload the app, the checked cell that previously selected will be display, I'm try to defferent ways, but still failed to implement it.
Anybody can help me? I would be highly appreciate it. Sorry, my english is not good.
My code is below:
#import "FirstRowSubview.h"
#interface FirstRowSubview ()
#end
#implementation FirstRowSubview
#synthesize array = _array;
#synthesize lastIndexPath = _lastIndexPath;
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *list = [[NSMutableArray alloc] initWithObjects:#"ffgfgh", #"564654", #"56548", #"fgmjfgmf", #"ggkdj", nil];
self.array = list;
[list release];
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.array = nil;
}
#pragma mark -
#pragma mark - TableView Datasource Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *subviewCells = #"Cells";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:subviewCells];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:subviewCells];
}
NSUInteger oldRow = [_lastIndexPath row];
cell.accessoryType = (indexPath.row == oldRow && _lastIndexPath != nil) ?
UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
cell.textLabel.text = [_array objectAtIndex:indexPath.row];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSNumber numberWithInt:_lastIndexPath.row] forKey:#"lastIndexPath"];
[defaults synchronize];
return cell;
}
#pragma mark -
#pragma mark - TableView Delegate Methods
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int newRow = [indexPath row];
int oldRow = (_lastIndexPath != nil) ? [_lastIndexPath row] : -1;
if (newRow != oldRow) {
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:_lastIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
[indexPath retain];
[_lastIndexPath release];
_lastIndexPath = indexPath;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
}
It can be easy to store and retrieve checked cell(s) information with NSUserDefaults.
NSUserDefaults can store the data interms of Key / Value pair. Here in your case value could be boolean and key could combination of NSString & indexpath.row from UITableView.
You can make a functions like,
- (NSString *)getKeyForIndex:(int)index
{
return [NSString stringWithFormat:#"KEY%d",index];
}
- (BOOL) getCheckedForIndex:(int)index
{
if([[[NSUserDefaults standardUserDefaults] valueForKey:[self getKeyForIndex:index]] boolValue]==YES)
{
return YES;
}
else
{
return NO;
}
}
- (void) checkedCellAtIndex:(int)index
{
BOOL boolChecked = [self getCheckedForIndex:index];
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:!boolChecked] forKey:[self getKeyForIndex:index]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Here, you can check for previously checked cells like
static NSString *subviewCells = #"Cells";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:subviewCells];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:subviewCells];
}
cell.textLabel.text = [_array objectAtIndex:indexPath.row];
if([self getCheckedForIndex:indexPath.row]==YES)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
//Use checkedCellAtIndex for check or uncheck cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self checkedCellAtIndex:indexPath.row];
if([self getCheckedForIndex:indexPath.row]==YES)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
P.S. Please, note that, this method will only useful and suggested if order of data from your NSArray will always in same order. Other then NSUserDefaults you've options like SQLite Database or plist file or any other option! Thanks :)
This is a basic example of how to create an array of NSIndexPaths based on the current selection of the table and then saving that array into NSUserDefaults.
-(void)viewDidLoad
{
[super viewDidLoad];
for (NSIndexPath *indexPath in [[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"mySavedMutableArray"]) {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
Here you can add objects to the array as they are selected
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"mySavedMutableArray"] addObject:indexPath];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Here you'll remove the deselected object from the array.
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"mySavedMutableArray"] removeObject:indexPath];
[[NSUserDefaults standardUserDefaults] synchronize];
}
The simplest way to get your code working is just put one line in view did load method as follow:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if([defaults valueForKey:#"lastIndexPath"])
_lastIndexPath = [defaults valueForKey:#"lastIndexPath"];
This will solve your problem :)
Happy Coding :)