Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 在自定义控件上正确实现自定义绑定_Objective C_Macos_Cocoa Bindings_Foundation - Fatal编程技术网

Objective c 在自定义控件上正确实现自定义绑定

Objective c 在自定义控件上正确实现自定义绑定,objective-c,macos,cocoa-bindings,foundation,Objective C,Macos,Cocoa Bindings,Foundation,由于所有这些日历样式NSDatePickers占用了大量空间,我决定将它们放在视图控制器中,使用按钮显示日期本身,然后单击使用nspover在弹出窗口中显示日历 然后,我使用绑定将NSButton子类的date属性与NSArrayController当前选择的NSDate类型属性链接起来。然后传播到日历样式的日期选择器中,反之亦然。对于“另一种方式”,我没有使用KVO,而是简单地实现自定义设置器,因此如果有人在按钮或日历视图控制器上设置date的值(可能是绑定到数组控制器设置了按钮的值,因此按钮

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

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

最后一切都很顺利…除了一件事

多重选择

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

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

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

但是--如果我想支持用户选择几个文档,然后立即更改它们的所有发布日期(用户可以使用在标准
NSDatePicker
NSTextField
等中完成的绑定实现)--如果不创建多个选择并向所有选定对象传播错误更改(如设置
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:@"-"];
}