Наблюдение за тем, как NSUserDefaultsController не работает, когда установлены связанные свойства

Вот настройка: у меня есть подкласс IKImageBrowserView, свойство zoomValue которого связано с ключом VFBrowserZoomValue в общем NSUserDefaultsController. У меня есть NSSlider, привязка value которого привязана к тому же ключу.

Это отлично работает, чтобы изменить zoomValue браузера с ползунка.

Я пытаюсь реализовать -magnifyWithEvent: в своем подклассе IKImageBrowserView, чтобы разрешить масштабирование браузера с помощью жеста сжатия на трекпадах.

Вот моя реализация:

-(void)magnifyWithEvent:(NSEvent *)event
{
  if ([event magnification] > 0) {
    if ([self zoomValue] < 1) {
      [self setZoomValue: [self zoomValue] + [event magnification]];
    }
  } 
  else if ([event magnification] < 0) {
    if ([self zoomValue] + [event magnification] > 0.1) {
      [self setZoomValue: [self zoomValue] + [event magnification]];
    }
    else {
      [self setZoomValue: 0.1];
    }
  }
}

Это правильно изменяет zoomValue браузера. Проблема в том, что NSUserDefaults не обновляется новым значением.

В другом месте приложения у меня есть реализация -observeValueForKeyPath:ofObject:change:context:, которая наблюдает за браузером zoomValue. Если я запишу значения масштабирования браузера, значение ползунка и ключ в значениях по умолчанию в этом методе, я увижу, что значение zoomValue браузера не было помещено в NSUserDefaults, а ползунок не обновился.

Я пытался окружить метод -magnifyWithEvent: вызовами -{will,did}ChangeValueForKey, но безрезультатно.


person Fraser Speirs    schedule 25.01.2010    source источник
comment
Если вы создаете наблюдатель масштабирования браузера с помощью NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld в качестве параметров, содержит ли словарь изменений, переданный вObservValueForKeyPath:ofObject:change:context:, правильный NSKeyValueChangeNewKey и NSKeyValueChangeOldKey?   -  person hatfinch    schedule 25.01.2010


Ответы (3)


Поток KVO для привязок не является ортогональным; привязка не является свойством, это ссылка на свойство. Это сокращение для запоминания того, как работают привязки:

  • KVO используется для передачи изменений от модели к контроллеру и представлению.
  • KVC используется для передачи изменений из представления в контроллер и модель.

Таким образом, когда представление с привязками обрабатывает события, ему необходимо распространять изменения свойств, на которые ссылаются его привязки.

Вот как может выглядеть ваш код с служебным методом для выполнения тяжелой работы по распространению изменений через привязки:

- (void)magnifyWithEvent:(NSEvent *)event
{
  if ([event magnification] > 0) {
    if ([self zoomValue] < 1) {
      [self setZoomValue: [self zoomValue] + [event magnification]];
    }
  } 
  else if ([event magnification] < 0) {
    if ([self zoomValue] + [event magnification] > 0.1) {
      [self setZoomValue: [self zoomValue] + [event magnification]];
    }
    else {
      [self setZoomValue: 0.1];
    }
  }

  // Update whatever is bound to our zoom value.
  [self updateValue:[NSNumber numberWithFloat:[self zoomValue]]
         forBinding:@"zoomValue"];
}

Немного жаль, что ImageKit требует использования @"zoomValue" для ссылки на привязку Zoom Value IKImageBrowserView, большинство привязок в AppKit имеют свою собственную глобальную строковую константу, например NSContentBinding.

А вот этот универсальный служебный метод для распространения изменения через привязку:

- (void)updateValue:(id)value forBinding:(NSString *)binding
{
  NSDictionary *bindingInfo = [self infoForBinding:binding];

  if (bindingInfo) {
    NSObject *object = [bindingInfo objectForKey:NSObservedObjectKey];
    NSString *keyPath = [bindingInfo objectForKey:NSObservedKeyPathKey];
    NSDictionary *options = [bindingInfo objectForKey:NSOptionsKey];

    // Use options to apply value transformer, placeholder, etc. to value
    id transformedValue = value; // exercise for the reader

    // Tell the model or controller object the new value
    [object setValue:transformedValue forKeyPath:keyPath];
  }
}

На самом деле применение заполнителей, преобразователей значений и т.п. остается в качестве упражнения для читателя.

person Chris Hanson    schedule 25.01.2010

Боюсь, это ожидаемое поведение, см. раздел часто задаваемых вопросов документации по привязке. Вам нужно нажать это вручную.

person Yuji    schedule 25.01.2010

Когда вы создаете совместимый с привязками элемент управления (или, в вашем случае, создаете подкласс), элемент управления должен уведомлять контроллер об изменении его значения. Итак, что вы хотите сделать, это переопределить

- (void)bind:(NSString *)binding toObject:(id)observableController withKeyPath:(NSString *)keyPath options:(NSDictionary *)options

и следите за интересующей вас привязкой. Следите за observableController и keyPath (а также за словарем options, если будут использоваться какие-либо преобразователи значений). Когда вы обновите значение своего элемента управления, вам нужно будет отправить

[observableController setValue:newValue forKeyPath:keyPath];

чтобы обновить контроллер с новым значением. Наблюдение за ключевой ценностью — это улица с односторонним движением, а в привязках контроль — это наблюдатели.

person Alex    schedule 25.01.2010