开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜