Barks Barks - 17 days ago 7
iOS Question

Getting a SIGABRT From Deleting a Row in a Table View

I am trying to have a table view list a number of objects that are stored in an array and it's working fine other than when I try to delete a row, it throws a SIGABRT. I checked at it is correctly updating the array. It seems that the SIGABRT has something to do with the number of sections in the table. Here is the code for the table view controller:

CalendarViewController.h

#import <UIKit/UIKit.h>
#import "Job.h"
#import "Shift.h"
#import "AppDelegate.h"

@interface CalendarViewController : UITableViewController

@property (nonatomic, strong) NSMutableArray *currentShiftsList;
@property (nonatomic, strong) AppDelegate *dataCenter;

@end


CalendarViewController.m

#import "CalendarViewController.h"

@interface CalendarViewController ()

@property (nonatomic, strong) NSMutableDictionary *sections;
@property (nonatomic, strong) NSArray *sortedShifts;
@property (strong, nonatomic) NSDateFormatter *sectionDateFormatter;
@property (strong, nonatomic) NSDateFormatter *cellDateFormatter;

@end

@implementation CalendarViewController

@synthesize sections, sortedShifts, currentShiftsList, dataCenter, sectionDateFormatter, cellDateFormatter;

- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}

- (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;
}

- (NSDate *)dateByAddingYears:(NSInteger)numberOfYears toDate:(NSDate *)inputDate
{
// Use the user's current calendar
NSCalendar *calendar = [NSCalendar currentCalendar];

NSDateComponents *dateComps = [[NSDateComponents alloc] init];
[dateComps setYear:numberOfYears];

NSDate *newDate = [calendar dateByAddingComponents:dateComps toDate:inputDate options:0];
return newDate;
}

- (void)viewDidLoad
{
[super viewDidLoad];

dataCenter = (AppDelegate *)[[UIApplication sharedApplication] delegate];
currentShiftsList = [[NSMutableArray alloc] initWithArray:dataCenter.shiftsList];

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];

// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;

// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;

self.sections = [NSMutableDictionary dictionary];
for (Shift *shift in dataCenter.shiftsList) {

// Reduce event start date to date components (year, month, day)
NSDate *dateRepresentingThisDay = [self dateAtBeginningOfDayForDate:shift.startDate];

// If we don't yet have an array to hold the events for this day, create one
NSMutableArray *shiftsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
if (shiftsOnThisDay == nil) {
shiftsOnThisDay = [NSMutableArray array];

// Use the reduced date as dictionary key to later retrieve the event list this day
[self.sections setObject:shiftsOnThisDay forKey:dateRepresentingThisDay];
}

// Add the event to the list for this day
[shiftsOnThisDay addObject:shift];
}

// Create a sorted list of days
NSArray *unsortedShifts = [self.sections allKeys];
self.sortedShifts = [unsortedShifts sortedArrayUsingSelector:@selector(compare:)];
}



- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [self.sections count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSDate *dateRepresentingThisDay = [self.sortedShifts objectAtIndex:section];
NSArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
return [eventsOnThisDay count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *reuseIdentifier = @"shifts";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];

NSDate *dateRepresentingThisDay = [self.sortedShifts objectAtIndex:indexPath.section];
NSArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
Shift *shift = [eventsOnThisDay objectAtIndex:indexPath.row];

cell.textLabel.text = shift.jobtitle;

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"h:mm a"];

NSString *start = [formatter stringFromDate:shift.startDate];
NSString *end = [formatter stringFromDate:shift.endDate];

NSString *detail = [[NSString alloc] initWithFormat:@"%@ to %@", start, end];
cell.detailTextLabel.text = detail;
return cell;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSDate *dateRepresentingThisDay = [self.sortedShifts objectAtIndex:section];
return [self.sectionDateFormatter stringFromDate:dateRepresentingThisDay];
}

/*
// 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

[currentShiftsList removeObjectAtIndex:indexPath.row];
dataCenter.shiftsList = currentShiftsList;

[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}


/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/

/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}

@end

Answer

This doesn't make sense to me.

[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

Why isn't it just:

[tableView deleteRowsAtIndexPaths:indexPath.row withRowAnimation:UITableViewRowAnimationFade];

Also remember a reload TableView after delete (if you want it).