I have a tableview grouped by sections, and sorted by dates, every section is basically a day, every row is an appointment/event on that day. I get my events from an nsmutablearray, and they have a "startDate" property by which i sort the table. I'm having trouble trying to delete a row from a sections.
-If i have 1 section with 5 rows , it's ok i can delete each of them individually and the last one will also delete the section.
-If i have more than 1 section , when i try to delete the last row in a section, it crashes.
Here is my code:
@interface AgendaViewController ()
@property (nonatomic,strong) NSString *path;
@property (strong, nonatomic) NSMutableDictionary *sections;
@property (strong, nonatomic) NSMutableArray *sortedDays;
@property (strong, nonatomic) NSDateFormatter *sectionDateFormatter;
@property (strong, nonatomic) NSDateFormatter *cellDateFormatter;
@end
@implementation AgendaViewController
@synthesize events = _events;
-(IBAction)menuButtonTapped:(id)sender{
[self.slidingViewController anchorTopViewToRightAnimated:YES];
}
-(IBAction)unwindToAgenda:(UIStoryboardSegue *)segue{
AddAgendaEntryViewController *source = [segue sourceViewController];
AgendaEntry *event = source.agendaEntry;
if (event){
[self.events addObject:event];
[self saveList];
[self createSections];
[self.tableView reloadData];
}
}
- (void)saveList
{
[NSKeyedArchiver archiveRootObject:self.events toFile:self.path];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
self.path = [self.path stringByAppendingPathComponent:@"TodoList.txt"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:self.path]) {
self.events = [[NSMutableArray alloc] init];
} else {
self.events = [[NSKeyedUnarchiver unarchiveObjectWithFile:self.path] mutableCopy];
}
[self setMenuGesture];
[self fetchEvents];
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
self.tableView.separatorColor = [UIColor colorWithRed:224.0/255.0 green:224.0/255.0 blue:224.0/255.0 alpha:1.0f];
[self createSections];
//date formatteri
self.sectionDateFormatter = [[NSDateFormatter alloc] init];
[self.sectionDateFormatter setDateStyle:NSDateFormatterLongStyle];
[self.sectionDateFormatter setTimeStyle:NSDateFormatterNoStyle];
self.cellDateFormatter = [[NSDateFormatter alloc] init];
[self.cellDateFormatter setDateStyle:NSDateFormatterNoStyle];
[self.cellDateFormatter setTimeStyle:NSDateFormatterShortStyle];
}
-(void)createSections{
self.sections = nil;
self.sections = [[NSMutableDictionary alloc]init];
for (AgendaEntry *entry in self.events){
NSDate *dateRepresentingThisDay = [self dateAtBeginningOfDayForDate:entry.startDate];
NSMutableArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
if(eventsOnThisDay == nil){
eventsOnThisDay = [NSMutableArray array];
[self.sections setObject:eventsOnThisDay forKey:dateRepresentingThisDay];
}
[eventsOnThisDay addObject:entry ];
NSArray *unsortedDays = [self.sections allKeys];
self.sortedDays = [[unsortedDays sortedArrayUsingSelector:@selector(compare:)] mutableCopy];}
NSLog(@"NUMBER OF SECTIONS IN TABLEVIEW: %lu",(unsigned long)[self.sections count]);
// [self.tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.sections count];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
-(void)fetchEvents{
//Apel catre webserver pentru eventuri.
}
-(void)setEvents:(NSMutableArray *)events{
_events = events;
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSDate *dateRepresentingThisDay = [self.sortedDays objectAtIndex:section];
NSArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
return [eventsOnThisDay count];
}
/*- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSDate *dateRepresentingThisDay = [self.sortedDays objectAtIndex:section];
NSString *title = [self.sectionDateFormatter stringFromDate:dateRepresentingThisDay];
return title;
}*/
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger) section{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 15)];
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(5, 5, self.view.frame.size.width, 15)];
lbl.backgroundColor = [UIColor clearColor];
lbl.textAlignment = NSTextAlignmentCenter;
lbl.font = [UIFont systemFontOfSize:12];
NSDate *dateRepresentingThisDay = [self.sortedDays objectAtIndex:section];
NSString *title = [self.sectionDateFormatter stringFromDate:dateRepresentingThisDay];
lbl.text = title;
[view addSubview:lbl];
[view setBackgroundColor:[UIColor colorWithRed:166/255.0 green:177/255.0 blue:186/255.0 alpha:0.3]]; //your background color...
return view;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Event Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell..
//AgendaEntry *event = [self.events objectAtIndex:indexPath.row];
NSDate *dateRepresentingThisDay = [self.sortedDays objectAtIndex:indexPath.section];
NSArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
AgendaEntry *event = [eventsOnThisDay objectAtIndex:indexPath.row];
UIImageView *mappin = (UIImageView *)[cell viewWithTag:4] ;
mappin.image = [UIImage imageNamed:@"map_pin"];
UILabel *companyName = (UILabel *)[cell viewWithTag:1];
companyName.text = event.companyName;
UILabel *eventType = (UILabel *) [cell viewWithTag:3];
eventType.text = event.eventType;
UILabel *dateOfEvent = (UILabel *) [cell viewWithTag:2];
dateOfEvent.text = [self.cellDateFormatter stringFromDate:event.startDate];
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[self.events removeObjectAtIndex:indexPath.row];
[self createSections];
[self saveList];
[tableView beginUpdates];
NSDate *dateRepresentingThisDay = [self.sortedDays objectAtIndex:indexPath.section];
NSArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
if ([eventsOnThisDay count] > 0){
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}else{
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]
withRowAnimation:UITableViewRowAnimationFade];
}
[tableView endUpdates];
}
[self.tableView reloadData];
}
-(NSMutableArray *)sortedDays{
if (!_sortedDays) _sortedDays = [[NSMutableArray alloc]init];
return _sortedDays;
}
-(void)setMenuGesture{
id<ECSlidingViewControllerDelegate> transition = self.zoomAnimationController ;
self.slidingViewController.delegate = transition;
self.slidingViewController.topViewAnchoredGesture = ECSlidingViewControllerAnchoredGestureTapping | ECSlidingViewControllerAnchoredGesturePanning;
self.slidingViewController.customAnchoredGestures = @[];
// [self.navigationController.view addGestureRecognizer:self.slidingViewController.panGesture];
}
- (ZoomAnimationController *)zoomAnimationController {
if (_zoomAnimationController) return _zoomAnimationController;
_zoomAnimationController = [[ZoomAnimationController alloc] init];
return _zoomAnimationController;
}
-(NSMutableArray *)events{
[self sortEvents];
return _events;
}
/*- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 30;
}*/
- (NSDate *)dateAtBeginningOfDayForDate:(NSDate *)inputDate
{
// Use the user's current calendar and time zone
NSCalendar *calendar = [NSCalendar currentCalendar];
NSTimeZone *timeZone = [NSTimeZone systemTimeZone];
[calendar setTimeZone:timeZone];
// Selectively convert the date components (year, month, day) of the input date
NSDateComponents *dateComps = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:inputDate];
// Set the time components manually
[dateComps setHour:0];
[dateComps setMinute:0];
[dateComps setSecond:0];
// Convert back
NSDate *beginningOfDay = [calendar dateFromComponents:dateComps];
return beginningOfDay;
}
-(void)sortEvents{
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"startDate" ascending:TRUE];
[_events sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
}
@end
The error i get:
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'`
It seems like the logic in deleteForRowAtIndexPath
causes the issue.
What you are doing is just deleting from self.events
based on indexPath.row
. But your self.events
and the eventsOnThisDay
array may not be holding same index when you have more than 1 section.
I recommend you to delete the item in same way as you are loading it to tableView. For example, in deleteRowAtIndexPath
,
NSDate *dateRepresentingThisDay = [self.sortedDays objectAtIndex:indexPath.section];
NSArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
[eventsOnThisDay removeObjectAtIndex:indexPath.row];
//If there are no more events on this day, then just delete the section also.
if([eventsOnThisDay count] == 0){
[self.sections removeObjectForKey:dateRepresentingThisDay];
[self.sortedDays removeObjectAtIndex:indexPath.section];
}
[self.tableView reloadData];
//You should also update your self.events list also accordingly.
Hope this helps.