Как обеспечить правильное поведение KVO для моего пользовательского элемента управления, подобного NSSlider? - PullRequest
0 голосов
/ 30 июня 2010

Допустим, у вас есть пользовательский элемент управления, похожий на NSSlider, но с поддержкой выбора диапазона значений вместо одного значения. Очевидный выбор свойств - minValue, maxValue, leftValue, rightValue или как вы хотите их назвать.
Возможно, вы также захотите убедиться, что leftValue и rightValue всегда лежат между minValue и maxValue. То же самое для minValue и maxValue. Вы не хотите, чтобы они потенциально сделали недействительными существующие leftValue и rightValue, например, установив maxValue на значение ниже текущего rightValue.

Довольно прямолинейно.

Что, однако, вы делаете в случае обеспечения надлежащего доступа / соответствия КВО? Вы просто не можете гарантировать, что KVO устанавливает свойства в правильном порядке (это сначала установка минимального и максимального пределов, а затем других значений).

У меня такой элемент пользовательского интерфейса, и я просто не могу понять, как его запустить, не открывая двери для ложно установленных значений.

Пользовательский элемент управления предназначен для привязки к объекту модели. Если я сейчас создаю такую ​​модель со значениями (мин: 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 открыла источник какао. Или хотя бы некоторые элементы управления для более глубокого осмотра.

Как бы вы внедрили такой элемент управления?

Или более общий: Каков наилучший способ реализации класса, который имеет перекрестно-зависимые свойства при соблюдении KVO?

Ответы [ 2 ]

1 голос
/ 30 июня 2010

Ничего себе. Это сложная проблема. Как вы уже определили, вы ничего не можете сделать, чтобы сообщения 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 сэкономят вам много усилий.

0 голосов
/ 30 июня 2010

Не совсем уверен, что я понимаю, что вы пытаетесь сделать, но ваша проблема не имеет ничего общего с КВО.

При первом вызове setLeftValue: после подготовки к установке 14.0 вы увидите следующее со значениями переменных в паретизе:

- (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, они не влияют на этот код.

...