2

由于所有这些日历样式NSDatePicker占用大量空间,我决定将它们放在视图控制器中,使用按钮显示日期本身,并在单击时使用 anNSPopover在弹出窗口中显示日历。

然后我使用绑定将NSButton子类的date属性与NSArrayController当前选择的NSDate类型属性链接起来。然后传播到日历样式的日期选择器中,反之亦然。对于“其他方式”,我没有使用 KVO,而是简单地实现了自定义设置器,所以如果有人date在按钮或日历视图控制器上设置值(也许绑定到数组控制器设置按钮的值,所以按钮设置日历视图控制器的值;也许日历上的更改设置视图控制器的值;谁知道)——然后我传播更改。

一切都很好地结束了……除了一件事。

多选。

您会看到,在用户执行多项选择后,系统立即正确地决定告诉我的按钮选择现在nil。为了传播更改,我告诉所有绑定的观察者该值已更改为nil. 这些绑定的观察者之一是NSArrayController,它当前选择了多个项目。

有什么问题?将当前选择的绑定键路径设置为nil导致将选择中的所有日期设置为nil……将更改传播到数据库中。并且用户甚至没有将值设置为nil- 它是由于简单地进行多项选择而不是进行任何选择而传播的,并且它应该仅用于显示此值的目的。

谢天谢地,现在我不需要支持这些日历的多项选择。我只是NSMultipleSelectionMarker通过使用 ) 比较观察对象的观察到的关键路径的值来检测isEqual:。如果完成多项选择,我会将该值应用于 ivar,但我不会“双向”通知其他绑定对象的更改。

但是——如果我想支持用户选择多个文档,然后一次更改所有发布日期(因为用户可以使用标准等中完成的绑定实现NSDatePickerNSTextField——我将如何去做这样做没有创建多个选择的操作也会传播错误的更改,例如设置nil到所有选定的对象?

(注意:虽然写这个问题是为了反映我当前重用标准控件的需求:按钮、弹出框、日期选择器——它不限于这种重用。我可能希望明天实现一个完全自定义的函数图小部件,需要双向 Cocoa 绑定。)

以下是一些相关代码IRDatePopoverEditorButton

+ (void)initialize
{
    [super initialize];

    [self exposeBinding:@"date"];
    [self exposeBinding:@"minDate"];
    [self exposeBinding:@"maxDate"];
}
- (void)awakeFromNib
{
    [super awakeFromNib];

    // …        
    [self establishBindings];

}

- (void)establishBindings
{
    NSArray *keys = [NSArray arrayWithObjects:@"date", @"minDate", @"maxDate", nil];

    NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
                              [NSNumber numberWithBool:YES], NSContinuouslyUpdatesValueBindingOption,
                              [NSNumber numberWithBool:YES], NSConditionallySetsEnabledBindingOption,
                              [NSNumber numberWithBool:YES], NSValidatesImmediatelyBindingOption,
                              [NSNumber numberWithBool:NO], NSRaisesForNotApplicableKeysBindingOption,
                              [NSNumber numberWithBool:NO], NSAllowsEditingMultipleValuesSelectionBindingOption, 
                              nil];

    // This code allows creation of bindings via interface builder without actual
    // support for custom bindings in IB. I hijack the 'user defined runtime attributes'
    // and define binding keypath there, and I use an IBOutlet to define a binding
    // target.
    for(NSString *key in keys)
    {
        id bindingTarget = [self valueForKey:[key stringByAppendingString:@"BindingTarget"]];;
        id bindingKeyPath = [self valueForKey:[key stringByAppendingString:@"BindingKeyPath"]];;
        if (!bindingTarget || !bindingKeyPath)
            continue;

        [bindingTarget valueForKeyPath:bindingKeyPath];

        [self bind:key
          toObject:[self valueForKey:[key stringByAppendingString:@"BindingTarget"]]
       withKeyPath:[self valueForKey:[key stringByAppendingString:@"BindingKeyPath"]]
           options:options];

    }
}

// one of the custom setters
- (void)setDate:(NSDate *)date
{
    if(_date == date)
        return;

    [self willChangeValueForKey:@"date"];
    [date retain];
    [_date release];
    _date = date;
    [self didChangeValueForKey:@"date"];

    NSDictionary * infoForBinding = [self infoForBinding:@"date"];
    if(infoForBinding && [infoForBinding objectForKey:NSObservedObjectKey])
    {
        id observedObject = [infoForBinding objectForKey:NSObservedObjectKey];
        NSString * observedKeyPath = [infoForBinding objectForKey:NSObservedKeyPathKey];

        if([[observedObject valueForKeyPath:observedKeyPath] isEqual:NSMultipleValuesMarker])
        {
            // This is needed because, when performing multiple selection,
            // we get called with setDate:nil. Then we blindly apply this to
            // the entire selection.
            // For our needs, it's enough to block applying values to multiple
            // selection. But it's not a correct solution.

            // FIXME: we currently block editing of multiple selection.
            NSLog(@"Not applying value for keypath %@ to %@; currently multiple items are selected", observedKeyPath, observedObject);
        }
        else
        {
            [observedObject setValue:date forKeyPath:observedKeyPath];
        }
    }

    if(self.date)
        if(self.formatter)
            [self setTitle:[self.formatter stringForObjectValue:date]];
        else
            [self setTitle:[date description]];
    else
        [self setTitle:@"-"];
}
4

0 回答 0