objective-c building my own custom calendar (nsarraycontroller observer problems)
I have for some time now being building my own calendar because the standard mac cal sucks in terms of layout.
What i have done is to make an NSCollectionView that holds all my dates, and then in my code i add each date to an array controller.
So far all works fine and the cal is showing!
Take a look at my ss here: http://cl.ly/0Z3a2i12242b2d1m3Z1n
I have added an observer to my NSArrayController to know whenever a user clicks a date, this allows me to send a message to my parent class telling it to update my table-list under the cal.
My observer looks like this:
// observing our array - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if([keyPath isEqualTo:@"selectionIndexes"])
{
if([[self.arrayController selectedObjects] count] > 0)
{
if ([[self.arrayController selectedObjects] count] == 1)
{
CalendarDate* obj = [[self.arrayController selectedObjects] objectAtIndex:0];
if(obj.dateobj != nil)
{
if(obj.isOld)
{
self.currentMonth = [self moveMonth:(NSInteger)-1];
self.selectedDate = obj.dateobj;
[self buildArrayController];
}
else {
self.selectedDate = obj.dateobj;
[self callParent];
}
}
}
}
}
}
My big problem is that when the user presses a date before this current month (look at the SS, iam talking about the grayed-out dates) then the observer gets called twice and my therefor my array is sent back 2 months, iam not sure how to counterfight this and now i have been struggling for days..
My code for building the array controller looks like this:
- (void) buildArrayController {
NSLog(@"Building array controller");
// remove observer, all objects and set selection to -1
[arrayController removeObserver:self forKeyPath:@"selectionIndexes"];
[[self.arrayController content] removeAllObjects];
[self.arrayController setSelectionIndex:-1];
// getting current month
NSDateComponents *nowComps = [self.currentCal components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSWeekdayCalendarUnit) fromDate:self.currentMonth];
// figure out how many days the current month have
NSRange daysInMonth = [self daysInMonth:self.currentMonth];
// save current month int for later
int month = [nowComps month];
// getting days to add before this month
NSDateComponents *weekdayComponents = [self.currentCal components:NSWeekdayCalendarUnit fromDate:self.currentMonth];
int weekdaysToAdd = (([weekdayComponents weekday] == 1) ? 8 : [weekdayComponents weekday])-2;
// add days befroe the current month, ie when 1st day of month is on a wednesday f.ex.
NSDate* lastMonth = [self moveMonth:-1];
NSRange daysInLastMonth = [self daysInMonth:lastMonth];
NSDateComponents *lastMonthComps = [self.currentCal components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSWeekdayCalendarUnit) fromDate:lastMonth];
for (int i2 = 0; i2 < weekdaysToAdd; i2++)
{
[lastMonthComps setDay:daysInLastMonth.length-weekdaysToAdd+i2+1];
CalendarDate* obj = [[CalendarDate alloc] init];
obj.dateobj = [self.currentCal dateFromComponents:lastMonthComps];
obj.showDot = [coreEditor checkDate:[self.currentCal dateFromComponents:lastMonthComps]];
obj.textColor = [NSColor lightGrayColor];
obj.isOld = YES;
[self.arrayController addObject:obj];
[obj release];
}
// adding actually month dates
for (int i = 1; i < daysInMonth.length+1; i++)
{
[nowComps setDay:i];
CalendarDate* obj = [[CalendarDate alloc] init];
obj.dateobj = [self.currentCal dateFromComponents:nowComps];
obj.showDot = [coreEditor checkDate:[self.currentCal dateFromComponents:nowComps]];
[self.arrayController addObject:obj];
[obj release];
}
// add days after the current month
NSDate* nextMonthobj = [self moveMonth:1];
NSDateComponents *nextMonthComps = [self.currentCal components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSWeekdayCalendarUnit) fromDate:nextMonthobj];
for (int i3 = 1; i3 < 7-weekdaysToAdd+1; i3++)
{
[nextMonthComps setDay:i3];
CalendarDate* obj = [[CalendarDate alloc] init];
obj.dateobj = [self.currentCal dateFromComponents:nextMonthComps];
obj.showDot = [coreEditor checkDate:[self.currentCal dateFromComponents:nextMonthComps]];
obj.textColor = [NSColor lightGrayColor];
obj.isOld = YES;
[self.arrayController addObject:obj];
[obj release];
}
// add observer for our array controller
[self.arrayController addObserver:self
forKeyPath:@"selectionIndexes"
options:NSKeyValueObservingOptionNew
context:nil];
// now figure out what object should be selected
NSDateComponents *selectedComp = [self.currentCal components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSWeekdayCalendarUnit) fromDate:self.selectedDate];
int selectedMonth = [selectedComp month];
int selectedDay = [selectedComp day]-1;
if(selectedMonth == month)
{
[self.arrayController setSelectionIndex:selectedDay+weekdaysToAdd];
}
else {
[self.arrayController setSelectionIndex:-1];
}
}
Thats it! Any suggestions?
(btw, if you have any heads up on making my code better please tell me, this is my first project in objective-c ever :))
Update I 'feel' its not really the observer that is the problem but somehow, when i press the before date in the cal and my method rebuilds the array it somehows re-send/click on the next view when the items are updated, does htis make senes? Could i temporary disable click events on the collection view?
update2 Okay, i tried adding this
self.selectedDate = obj.dateobj;
self.currentMonth = [self moveMonth:(NSInteger)-1];
[self.collectionView se开发者_C百科tSelectable:NO];
[self buildArrayController];
[self.collectionView setSelectable:YES];
removing selectiable for the collecitonview but it does not work, so it must be the observer that shomehow gets called twice.. :t
It looks like your observer will be called twice:
- The user selects the new date, before the current month. This causes a change in "selectionIndexes" and causes your observer method to be called.
- In buildArrayController, you remove yourself as an observer, make your changes then reactivate yourself as an observer. After re-adding yourself as an observer, you call 'setSelectionIndex' on the array controller, which causes a second observation callback.
On the second callback, obj.isOld will be false, so luckily you don't enter an infinite loop of this. Try moving the call to
[self.arrayController addObserver:self
forKeyPath:@"selectionIndexes"
options:NSKeyValueObservingOptionNew
context:nil];
to the very end of buildArrayController.
精彩评论