0

假设您有一个类似于 NSSlider 的自定义控件,但支持选择一系列值,而不是单个值。属性的明显选择是 minValue、maxValue、leftValue、rightValue,或者你想命名它们。
您可能还想确保 leftValue 和 rightValue 始终位于 minValue 和 maxValue 之间。minValue 和 maxValue 相同。您不希望它们潜在地使现有的 leftValue 和 rightValue 无效,例如通过将 maxValue 设置为低于当前 rightValue 的值。

到目前为止非常直截了当。

但是,在确保适当的 KVO 可访问性/合规性的情况下,您会怎么做?您根本无法确保 KVO 以正确的顺序设置属性(即首先设置最小和最大限制,然后设置其他值)。

我碰巧有这样一个 UI 元素,如果不打开错误设置值的大门,我根本无法弄清楚要让它运行起来。

自定义控件旨在绑定到模型对象。如果我现在使用值创建这样的模型(最小值:0.00 最大值:42.00 左侧:14.00 右侧:28.00)我最终在自定义控件中进行以下设置(最小值:0.00 最大值:42.00 左侧:1.00 右侧:1.00)

这样做的原因是,它不是先调用 minValue 和 maxValue,而是先调用 leftValue 和 rightValue,这导致两个值都以 1.0(这是默认的 maxValue)结束。(然后将 maxValue设置为适当的值。)

//[self prepare] gets called by both, [self initWithCoder:] and [self initWithFrame:]
- (void)prepare
{
    minValue = 0.0;
    maxValue = 1.0;
    leftValue = 0.0;
    rightValue = 1.0;       
}

- (void)setMinValue:(double)aMinValue
{
    if (aMinValue < maxValue && aMinValue < leftValue) {
        minValue = aMinValue;
        [self propagateValue:[NSNumber numberWithDouble:minValue] forBinding:@"minValue"];
        [self setNeedsDisplay:YES];
    }   
}

- (void)setMaxValue:(double)aMaxValue
{
    if (aMaxValue > minValue && aMaxValue > rightValue) {
        maxValue = aMaxValue;
        [self propagateValue:[NSNumber numberWithDouble:maxValue] forBinding:@"maxValue"];
        [self setNeedsDisplay:YES];
    }
}

- (void)setLeftValue:(double)aLeftValue
{
    double newValue = leftValue;
    if (aLeftValue < minValue) {
        newValue = minValue;
    } else if (aLeftValue > rightValue) {
        newValue = rightValue;
    } else {
        newValue = aLeftValue;
    }
    if (newValue != leftValue) {
        leftValue = newValue;
        [self propagateValue:[NSNumber numberWithDouble:leftValue] forBinding:@"leftValue"];
        [self setNeedsDisplay:YES];
    }
}

- (void)setRightValue:(double)aRightValue
{
    double newValue = leftValue;
    if (aRightValue > maxValue) {
        newValue = maxValue;
    } else if (aRightValue < leftValue) {
        newValue = leftValue;
    } else {
        newValue = aRightValue;
    }
    if (newValue != rightValue) {
        rightValue = newValue;
        [self propagateValue:[NSNumber numberWithDouble:rightValue] forBinding:@"rightValue"];
        [self setNeedsDisplay:YES];
    }

当你希望 Apple 开放 Cocoa 的来源时,正是在这种时候。或者至少是一些用于更深入检查的控件。

您将如何实施这样的控制?

或更笼统地说:在遵守 KVO 的同时实现具有交叉依赖属性的类的最佳实践是什么?

4

2 回答 2

1

哇。这是一个棘手的问题。正如您已经确定的那样,您无法确保以任何特定顺序发送 KVO 消息。

这里有一个想法:在你的setLeftValue:setRightValue:访问器中,为什么不只存储传入的值,然后在访问时修剪值,而不是修剪值。

例如:

- (void)setLeftValue:(double)value {
    leftValue = value;
    [self propagateValue:[NSNumber numberWithDouble:self.leftValue] forBinding:@"leftValue"]; // Note the use of the accessor here
    [self setNeedsDisplay:YES];
}

- (double)leftValue {
    return MAX(self.minValue, MIN(self.maxValue, leftValue));
}

这样,一旦minValuemaxValue被设置,leftValue并且rightValue将代表正确的值。而且,通过使用自定义访问器,您可以确保minValue <= leftValue/rightValue <= maxValue保留您的约束。

此外,对于您正在编写的代码,MINMAX预处理器宏将为您节省大量精力。

于 2010-06-30T14:37:55.390 回答
0

不完全确定我理解您的尝试,但您的问题与 KVO 无关。

在准备将其设置为 14.0 后第一次setLeftValue:调用时,您会看到以下内容以及括号中的变量值:

- (void)setLeftValue:(double)aLeftValue(14.0)
{
    double newValue = leftValue(0.0);
    if (aLeftValue(14.0) < minValue(0.0)) {
        newValue = minValue(0.0);
    } else if (aLeftValue(14.0) > rightValue(1.0)) { // always evaluates true for all aleftValue>1
        newValue = rightValue(1.0); // now newValue==1.0
    } else {
        newValue = aLeftValue(14.0);
    }
    if (newValue(1.0) != leftValue(0.0)) {
        leftValue = newValue(1.0); // now leftvalue==1.0
        [self propagateValue:[NSNumber numberWithDouble:leftValue] forBinding:@"leftValue"];
        [self setNeedsDisplay:YES];
    }
}

总而言之 aLeftvalues>1,此代码将始终设置leftValue==1.0

由于您从不调用 minValue 和 maxValue 设置器,因此它们对此代码没有影响。

于 2010-06-30T14:37:11.553 回答