I have retrieved my events from a shared google calendar i IOS, and are displaying them in a tableview. As for right now, they're simplty shown with the event title as the title, and the eventtime as the subtitle. I'd like to use the date (eventtime) as a section header in stead, so that all events on for instance march 31st where shown in a group, with the time as a "right detail"
My code looks like this:
#interface MasterViewController () {
NSArray *events;
}
#end
#implementation MasterViewController
-(void)viewDidAppear:(BOOL)animated
{
//show loader view
//[HUD showUIBlockingIndicatorWithText:#"Fetching JSON"];
//make HTTP call
NSString* searchCall = [NSString stringWithFormat:#"http://www.google.com/calendar/feeds/kao1d80fd2u5kh7268caop11o4%%40group.calendar.google.com/public/full?alt=json"];
[JSONHTTPClient getJSONFromURLWithString: searchCall
completion:^(NSDictionary *json, JSONModelError *err) {
//got JSON back
NSLog(#"Got JSON from web: %#", json);
if (err) {
[[[UIAlertView alloc] initWithTitle:#"Error"
message:[err localizedDescription]
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles: nil] show];
return;
}
//initialize the models
events = [CalendarModel arrayOfModelsFromDictionaries:
json[#"feed"][#"entry"]
];
if (events) NSLog(#"Loaded successfully models");
//show the videos
[self.tableView reloadData];
}];
}
;
#pragma mark - table methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return events.count;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CalendarModel* event = events[indexPath.row];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"yyyy-MM-dd'T'HH:mm:ss.SSSzzz";
NSDate *gmtDate = [formatter dateFromString: [[event.time objectAtIndex:0] startTime]];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"%#",
event.title
];
cell.detailTextLabel.text = [formatter stringFromDate: gmtDate];
return cell;
}
#end
I have found this code online, but he doesn't get data as JSON, but from an iPhone calendar, i believe.
#interface MasterViewController ()
#property (strong, nonatomic) NSMutableDictionary *sections;
#property (strong, nonatomic) NSArray *sortedDays;
#property (strong, nonatomic) NSDateFormatter *sectionDateFormatter;
#property (strong, nonatomic) NSDateFormatter *cellDateFormatter;
- (NSDate *)dateAtBeginningOfDayForDate:(NSDate *)inputDate;
- (NSDate *)dateByAddingYears:(NSInteger)numberOfYears toDate:(NSDate *)inputDate;
#end
#implementation MasterViewController
#synthesize sections;
#synthesize sortedDays;
#synthesize sectionDateFormatter;
#synthesize cellDateFormatter;
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
NSDate *now = [NSDate date];
NSDate *startDate = [self dateAtBeginningOfDayForDate:now];
NSDate *endDate = [self dateByAddingYears:1 toDate:startDate];
EKEventStore *eventStore = [[EKEventStore alloc] init];
NSPredicate *searchPredicate = [eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:nil];
NSArray *events = [eventStore eventsMatchingPredicate:searchPredicate];
self.sections = [NSMutableDictionary dictionary];
for (EKEvent *event in events)
{
// Reduce event start date to date components (year, month, day)
NSDate *dateRepresentingThisDay = [self dateAtBeginningOfDayForDate:event.startDate];
// If we don't yet have an array to hold the events for this day, create one
NSMutableArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
if (eventsOnThisDay == nil) {
eventsOnThisDay = [NSMutableArray array];
// Use the reduced date as dictionary key to later retrieve the event list this day
[self.sections setObject:eventsOnThisDay forKey:dateRepresentingThisDay];
}
// Add the event to the list for this day
[eventsOnThisDay addObject:event];
}
// Create a sorted list of days
NSArray *unsortedDays = [self.sections allKeys];
self.sortedDays = [unsortedDays sortedArrayUsingSelector:#selector(compare:)];
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)viewDidUnload
{
[super viewDidUnload];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#pragma mark - Date Calculations
- (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;
}
#pragma mark - UITableViewDataSource methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.sections count];
}
- (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];
return [self.sectionDateFormatter stringFromDate:dateRepresentingThisDay];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *reuseIdentifier = #"EventTitleCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
NSDate *dateRepresentingThisDay = [self.sortedDays objectAtIndex:indexPath.section];
NSArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
EKEvent *event = [eventsOnThisDay objectAtIndex:indexPath.row];
cell.textLabel.text = event.title;
if (event.allDay) {
cell.detailTextLabel.text = #"all day";
} else {
cell.detailTextLabel.text = [self.cellDateFormatter stringFromDate:event.startDate];
}
return cell;
}
#end
Is there a way for me to use my json event.startdate as a section header?
Personally, I would ignore the code you posted second there because its not what you are trying to do.
The part of the code where you set you header and subtitle are these two lines:
cell.textLabel.text = [NSString stringWithFormat:#"%#",event.title];
cell.detailTextLabel.text = [formatter stringFromDate: gmtDate];
So if you want the heading to be the date, just place it as the cell.textLabel.text value.
So if you are wanting to do sections.
Make sure the number of sections is greater then 1.
Then you might want to take a look at these table methods. Just depending on what your exact case and customization needs are you can change it accordingly.
(NSString*) tableView:(UITableView*)tableView titleForHeaderInSection:(NSInteger)section{
}
(CGFloat)tableView:(UITableView*)tableView heightForHeaderInSection:(NSInteger)section{
}
(UIView*)tableView:(UITableView*)table viewForHeaderInSection:(NsInteger)section{
}
I hope this helps. I normally would have an example I just didn't have the most available time currently, but if need be I can provide one.
Take Care :)
Related
I am trying to implement to show the calendar dates in TableViewCell. I am able to achieved the filled with current year. But As soon I hit the bottom of the table I need to populate next year and if I hit at top of TableView then previous year should be populated.
Pasting code which I have implemented.
- (void)fillDatesWithCalendarUnit:(NSCalendarUnit)unit withDate:(NSDate*)date
{
NSDate *today = date;
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSDate *beginning;
NSTimeInterval length;
[calendar rangeOfUnit:unit startDate:&beginning interval:&length forDate:today];
NSDate *end = [beginning dateByAddingTimeInterval:length-1];
[self fillDatesFromDate:beginning toDate:end];
}
- (void)fillDatesFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
{
NSAssert([fromDate compare:toDate] == NSOrderedAscending, #"toDate must be after fromDate");
NSDateComponents *days = [[NSDateComponents alloc] init];
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSInteger dayCount = 0;
while(YES){
[days setDay:dayCount++];
NSDate *date = [calendar dateByAddingComponents:days toDate:fromDate options:0];
if([date compare:toDate] == NSOrderedDescending) break;
[_dates addObject:date];
}
[self.tableView reloadData]; //_dates mutableArray
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dates.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
AgendaCustomCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.date.text = [NSString stringWithFormat:#"%#",_dates[indexPath.row]];
if (indexPath.row == _dates.count-1) {
NSLog(#"load more");
NSDate *tomorrow = [NSDate dateWithTimeInterval:(48*60*60) sinceDate:_dates[indexPath.row]];
NSLog(#"last daye - %# ,tomorrow - %#",_dates[indexPath.row],tomorrow);
[self fillDatesWithCalendarUnit:NSCalendarUnitYear withDate:_dates[indexPath.row]];
}
return cell;
}
I have implemented the logic for show infinite future dates on scrolling.
- (void)viewDidLoad {
[super viewDidLoad];
_dates = [NSMutableArray new];
[self fillCurrentYear];
}
- (void)fillCurrentYear
{
[self fillDatesWithCalendarUnit:NSCalendarUnitYear withDate:[NSDate new
] isLoadMore:NO];
}
pragma mark Private methods
- (void)fillDatesWithCalendarUnit:(NSCalendarUnit)unit withDate:(NSDate*)date isLoadMore:(BOOL)isBool
{
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSDate *beginning;
NSTimeInterval length;
if (isBool) {
beginning = _dates[[_dates count]-1];
}
[calendar rangeOfUnit:unit startDate:&beginning interval:&length forDate:date];
NSDate *end = [beginning dateByAddingTimeInterval:length-1];
[self fillDatesFromDate:beginning toDate:end];
}
- (void)fillDatesFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
{
NSAssert([fromDate compare:toDate] == NSOrderedAscending, #"toDate must be after fromDate");
// NSMutableArray *dates = [[NSMutableArray alloc] init];
NSDateComponents *days = [[NSDateComponents alloc] init];
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSInteger dayCount = 0;
while(YES){
[days setDay:dayCount++];
NSDate *date = [calendar dateByAddingComponents:days toDate:fromDate options:0];
if([date compare:toDate] == NSOrderedDescending) break;
[_dates addObject:date];
}
// _dates = dates;
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
AgendaCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.date.text = [NSString stringWithFormat:#"%#",_dates[indexPath.row]];
if (indexPath.row == _dates.count-1) {
NSLog(#"load more");
NSDate *tomorrow = [NSDate dateWithTimeInterval:(48*60*60) sinceDate:_dates[indexPath.row]];
NSLog(#"last daye - %# ,tomorrow - %#",_dates[indexPath.row],tomorrow);
[self fillDatesWithCalendarUnit:NSCalendarUnitYear withDate:tomorrow isLoadMore:YES];
}
return cell;
}
I want to get an EKEvent from an array iOS. But I have one problem. If I put my code lines in the viewDidLoad method, it works.
But if I make it in a button from a table view: (-(IBAction)button:(UiButton *)sender) it gets an EKEvent, but doesn't note the event.
Thats my code:
EKEvent *event =[eventArray objectAtIndex:0]
NSString *string = event.notes
Thats the complete code:
- (void)viewDidLoad {
selectedDate = [NSDate date];
[super viewDidLoad];
EKEventStore *store = [[EKEventStore alloc] init];
[store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted == NO) {
UIViewController *controller = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"needAccess"];
[self.view addSubview:controller.view];
[self addChildViewController:controller];
}
}];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *start = [calendar dateBySettingHour:0 minute:0 second:0 ofDate:selectedDate options:0];
NSDate *end = [calendar dateBySettingHour:0 minute:0 second:0 ofDate:[selectedDate dateByAddingTimeInterval:60*60*24] options:0];
NSPredicate *predicate = [store predicateForEventsWithStartDate:start endDate:end calendars:nil];
eventArray = [store eventsMatchingPredicate:predicate];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [eventArray count];
}
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [table dequeueReusableCellWithIdentifier: #"EventCell"];
EKEvent *event = [eventArray objectAtIndex:indexPath.row];
UIButton *headline = (UIButton *)[cell viewWithTag:1];
headline.layer.cornerRadius = headline.frame.size.width / 2;
headline.clipsToBounds = YES;
headline.tag = indexPath.row;
[headline addTarget:self action:#selector(headlineButton:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
-(void)headlineButton:(UIButton*)sender {
EKEvent *event = [eventArray objectAtIndex:0];
NSArray *notes = [event.notes componentsSeparatedByString:#"//"];
NSString *latitude = [notes objectAtIndex:1];
NSString *longitude = [notes objectAtIndex:2];
}
That's probably because neither the cell or the button on the cell have an EKEvent property.
Your cell is loaded from a model. The event data is likely in the model
If your button or cell needs to access event data, you have to decide how the data flows through your app. You also want to have a single source of truth for the event, so no matter what your app's state, you always are certain about details for an event, whether editing or updating or persisting or deleting or inserting an event.
These are questions you may want to tackle, so you have a really good understanding where and how you store these details, and how your cell button should access an event's note.
I've retrieved data from 2 different shared Google Calendars, and stored the data in an array. I need to sort the data into a sectioned UITableView, sorted by date.
Heres my code:
CalendarModel.h
#import "JSONModel.h"
#import "Time.h"
#interface CalendarModel : JSONModel
#property (strong, nonatomic) NSString* title;
#property (strong, nonatomic) NSArray<Time>* time;
#end
CalendarModel.m
#import "CalendarModel.h"
#implementation CalendarModel
+(JSONKeyMapper*)keyMapper
{
return [[JSONKeyMapper alloc] initWithDictionary:#{
#"gd$when": #"time",
#"title.$t": #"title",
}];
}
#end
Time.h
#import "JSONModel.h"
#protocol Time #end
#interface Time : JSONModel
#property (strong, nonatomic) NSString* startTime;
#property (strong, nonatomic) NSString* endTime;
#end
Time.m does nothing, as it's handled by JSONModel
SportsViewController.m
#import "SportsViewController.h"
#import "JSONModelLib.h"
#import "CalendarModel.h"
#import "Time.h"
#import "JSONValueTransformer.h"
#interface SportsViewController ()
#property (strong, nonatomic) NSMutableArray *events;
#property (strong, nonatomic) NSArray *music;
- (NSDate *)dateAtBeginningOfDayForDate:(NSDate *)inputDate;
- (NSDate *)dateByAddingYears:(NSInteger)numberOfYears toDate:(NSDate *)inputDate;
#end
#implementation SportsViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//make HTTP call
NSString* searchCall = [NSString stringWithFormat:#"http://www.google.com/calendar/feeds/kao1d80fd2u5kh7268caop11o4%%40group.calendar.google.com/public/full?alt=json"];
NSString* searchCall2 = [NSString stringWithFormat:#"http://www.google.com/calendar/feeds/3qag5m8iad46mtvsnnqbtrcjjg%%40group.calendar.google.com/public/full?alt=json"];
[JSONHTTPClient getJSONFromURLWithString: searchCall
completion:^(NSDictionary *json, JSONModelError *err) {
//got JSON back
NSLog(#"Got Sports JSON from web: %#", json);
if (err) {
[[[UIAlertView alloc] initWithTitle:#"Error"
message:[err localizedDescription]
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles: nil] show];
return;
}
//initialize the models
_events = [CalendarModel arrayOfModelsFromDictionaries:
json[#"feed"][#"entry"]
];
if (_events) NSLog(#"Loaded successfully sports models");
//show the Events
[_events addObjectsFromArray:_music];
NSLog(#"%#", _events);
[self.tableView reloadData];
}];
[JSONHTTPClient getJSONFromURLWithString: searchCall2
completion:^(NSDictionary *json2, JSONModelError *err2) {
//got JSON back
NSLog(#"Got Music JSON from web: %#", json2);
if (err2) {
[[[UIAlertView alloc] initWithTitle:#"Error"
message:[err2 localizedDescription]
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles: nil] show];
return;
}
//initialize the models
_music = [CalendarModel arrayOfModelsFromDictionaries:
json2[#"feed"][#"entry"]
];
if (_music) NSLog(#"Loaded successfully music models");
[_events addObjectsFromArray:_music];
//show the Events
[self.tableView reloadData];
}];
//[events addObjectsFromArray:music];
[self.tableView reloadData];
}
;
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - table methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _events.count;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CalendarModel* event = _events[indexPath.row];
NSString *dato = [[event.time objectAtIndex:0] startTime];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"yyyy-MM-dd'T'HH:mm:ss.SSSzzz";
NSDate *gmtDate = [formatter dateFromString: dato];
formatter.dateFormat = #"dd-MM-yyyy HH:mm";
dato = [formatter stringFromDate:gmtDate];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SportCell" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"%#",
event.title
];
cell.detailTextLabel.text = dato;
return cell;
}
#end
So basically all the data i need sorted resides in the _events array. I just do not get how i sort it by date into sections. The reason why startTime and endTime are NSStrings is that, that's what i am returned by the call to the shared calendar on Google
First group all your events - you'll have to take care of variable scoping (I'm using days and groupedEvents locally but you'll have to declare those as ivars/properties)
NSMutableArray *days = [NSMutableArray array];
NSMutableDictionary *groupedEvents = [NSMutableDictionary dictionary];
- (void)groupEventsIntoDays
{
for (CalendarModel *event in _events)
{
NSString *dato = [[event.time objectAtIndex:0] startTime];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"yyyy-MM-dd'T'HH:mm:ss.SSSzzz";
NSDate *gmtDate = [formatter dateFromString: dato];
if (![days containsObject:gmtDate])
{
[days addObject:gmtDate];
[groupedEvents setObject:[NSMutableArray arrayWithObject:event] forKey:gmtDate];
}
else
{
[((NSMutableArray*)[groupedEvents objectForKey:gmtDate]) addObject:event];
}
}
days = [[days sortedArrayUsingSelector:#selector(compare:)] mutableCopy];
}
Section headers:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0,0,tableView.frame.size.width, 30)];
UILabel *headerLabel = [[UILabel alloc] initWithFrame:header.bounds];
[header addSubview:headerLabel];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"dd-MM-yyyy HH:mm";
NSString *dateAsString = [formatter stringFromDate:[days objectAtIndex:section]];
[headerLabel setText:dateAsString];
return header;
}
I only touched the first two lines in your cellForRow - you can modify the rest as needed for display/formatting:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDate *date = [days objectAtIndex:indexPath.section];
CalendarModel *event = [((NSMutableArray*)[groupedEvents objectForKey:date]) objectAtIndex:indexPath.row];
NSString *dato = [[event.time objectAtIndex:0] startTime];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"yyyy-MM-dd'T'HH:mm:ss.SSSzzz";
NSDate *gmtDate = [formatter dateFromString: dato];
formatter.dateFormat = #"dd-MM-yyyy HH:mm";
dato = [formatter stringFromDate:gmtDate];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SportCell" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"%#",
event.title
];
cell.detailTextLabel.text = dato;
return cell;
}
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
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).
When you delete a row, you are removing an item from the currentShiftsList array. However, you are not using that array in your UITableViewDataSource related methods. The TableView is expecting there to be 1 less item from its DataSource, but you will be returning the original number of items instead.
Overall, things are very confusing because you have several collections that are trying to manage the same set of data (or copies of that data). Try looking into CoreData and the NSFetchedResultsController which is specifically made to managed a set of data and propagate any changes to a UITableView.
The YApp crashes whenever I press the Delete button from the simulator in iPhone. I have copied some code from my project here. How can I solve the delete bug?
I found a similar question, Error in the delete function of the alarm app in iPhone, but it does not answer my question.
My code is below.
#import "FirstPage.h"
#implementation FirstPage
#synthesize datePicker, eventText,tableview;
- (void) viewWillAppear:(BOOL)animated {
[self.tableview reloadData];
}
- (IBAction) scheduleAlarm:(id) sender {
[eventText resignFirstResponder];
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
// Get the current date
NSDate *pickerDate = [self.datePicker date];
// Break the date up into components
NSDateComponents *dateComponents = [calendar components:( NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit )
fromDate:pickerDate];
NSDateComponents *timeComponents = [calendar components:( NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit )
fromDate:pickerDate];
// Set up the fire time
NSDateComponents *dateComps = [[NSDateComponents alloc] init];
[dateComps setDay:[dateComponents day]];
[dateComps setMonth:[dateComponents month]];
[dateComps setYear:[dateComponents year]];
[dateComps setHour:[timeComponents hour]];
// Notification will fire in one minute
[dateComps setMinute:[timeComponents minute]];
[dateComps setSecond:[timeComponents second]];
NSDate *itemDate = [calendar dateFromComponents:dateComps];
[dateComps release];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
return;
localNotif.fireDate = itemDate;
localNotif.timeZone = [NSTimeZone defaultTimeZone];
// Notification details
localNotif.alertBody = [eventText text];
// Set the action button
localNotif.alertAction = #"View";
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
// Specify custom data for the notification
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:#"someValue" forKey:#"someKey"];
localNotif.userInfo = infoDict;
// Schedule the notification
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
[self.tableview reloadData];
}
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
}
return self;
}
*/
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
[eventText resignFirstResponder];
return YES;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
Array = [[NSMutableArray alloc]init];
eventText.delegate = self;
[super viewDidLoad];
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
int count = [Array count];
if(self.editing) count++;
//return count;
return [[[UIApplication sharedApplication] scheduledLocalNotifications] count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
NSArray *notificationArray = [[UIApplication sharedApplication] scheduledLocalNotifications];
// notificationArray = Array;
notif = [notificationArray objectAtIndex:indexPath.row];
[cell.textLabel setText:notif.alertBody];
[cell.detailTextLabel setText:[notif.fireDate description]];
return cell;
}
- (IBAction)DeleteButtonAction:(id)sender
{
[Array removeLastObject];
[tableview reloadData];
}
- (IBAction) EditTable:(id)sender
{
if(self.editing)
{
[super setEditing:NO animated:NO];
[tableview setEditing:NO animated:NO];
// [tableview reloadData];
// [self.navigationItem.leftBarButtonItem setTitle:#"Edit"];
// [self.navigationItem.leftBarButtonItem setStyle:UIBarButtonItemStylePlain];
}
else
{
[super setEditing:YES animated:YES];
[tableview setEditing:YES animated:YES];
// [tableview reloadData];
// [self.navigationItem.leftBarButtonItem setTitle:#"Done"];
// [self.navigationItem.leftBarButtonItem setStyle:UIBarButtonItemStyleDone];
}
}
// The editing style for a row is the kind of button displayed to the left of the cell when in editing mode.
- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
// No editing style if not editing or the index path is nil.
if (self.editing == NO || !indexPath) return UITableViewCellEditingStyleNone;
// Determine the editing style based on whether the cell is a placeholder for adding content or already
// existing content. Existing content can be deleted.
if (self.editing && indexPath.row == ([Array count]))
{
return UITableViewCellEditingStyleInsert;
} else
{
return UITableViewCellEditingStyleDelete;
}
return UITableViewCellEditingStyleNone;
}
// Update the data model according to edit actions delete or insert.
- (void)tableView:(UITableView *)aTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[Array removeObjectAtIndex:indexPath.row];
// [tableview reloadData];
} else if (editingStyle == UITableViewCellEditingStyleInsert)
{
}
}
#pragma mark Row reordering
// Determine whether a given row is eligible for reordering or not.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
// Process the row move. This means updating the data model to correct the item indices.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath
{
NSString *item = [[Array objectAtIndex:fromIndexPath.row] retain];
[Array removeObject:item];
[Array insertObject:item atIndex:toIndexPath.row];
[item release];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
datePicker = nil;
tableview = nil;
eventText = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
I think the problem is in the section listed below.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
int count = [Array count];
if(self.editing) count++;
//return count;
return [[[UIApplication sharedApplication] scheduledLocalNotifications] count];
}