假设您有一个类似于NSSlider的自定义控件,但支持选择一系列值,而不是单个值。明显的属性选择是minValue,maxValue,leftValue,rightValue,或者你想要命名它们。
您可能还想确保leftValue和rightValue始终位于minValue和maxValue之间。对于minValue和maxValue也是如此。您不希望它们可能使现有的leftValue和rightValue无效,例如by-let说 - 将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的情况下实现具有交叉依赖属性的类的最佳实践是什么?
答案 0 :(得分: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));
}
这样,设置minValue
和maxValue
后,leftValue
和rightValue
将代表正确的值。而且,通过使用自定义访问器,您可以确保保留minValue <= leftValue/rightValue <= maxValue
的约束。
此外,对于您正在编写的代码,MIN
和MAX
预处理器宏将为您节省大量精力。
答案 1 :(得分:0)
我不确定我的理解是什么,但你的问题与KVO无关。
第一次在准备将其设置为14.0后调用setLeftValue:
时,您会在parethesis中看到以下变量值:
- (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设置器,因此它们对此代码没有影响。