Какао-привязки и КВО - PullRequest
       33

Какао-привязки и КВО

3 голосов
/ 17 апреля 2011

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

MyView class

@interface MyView : NSView {
@private
    NSArray *images;
}

@end

+ (void)initialize
{
    [self exposeBinding:@"images"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"Changed!");
}

My AppDelegate

@property (retain) NSArray *images;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{   
    images = [[NSMutableArray alloc] init];

    [view bind:@"images" toObject:self withKeyPath:@"images" options:nil];
    // [self addObserver:view forKeyPath:@"images" options:0 context:nil]; // !!!

    MyImage *img = [[MyImage alloc] ...];

    [self willChangeValueForKey:@"images"];
    [[self images] addObject:img];
    [self didChangeValueForKey:@"images"];
    [img release];
}

Без [self addObserver:view forKeyPath:@"images" options:0 context:nil]; метод observeValueForKeyPath: никогда не вызывается.

Нужно ли вызывать addObserver: при использовании bind:?bind: устанавливает КВО?А почему не работает переплет?

Ответы [ 2 ]

3 голосов
/ 04 февраля 2012

Вам нужен встроенный установщик для свойства images, как показано ниже. Наиболее распространенным вариантом использования этого является то, что вам нужно сделать недействительным чертеж и запросить перерисовку с -setNeedsDisplay:YES.

- (void)setImages:(NSArray *)newImages
{
  if(newImages != images) {
    [images release];
    images = newImages;
    [images retain];
  }

  [self setNeedsDisplay:YES]; // Addition and only difference to synthesized setter
}

Вы можете отбросить вызов -exposeBinding:, поскольку он влияет только на плагины для Interface Builder и те, которые были потеряны с введением Xcode 4.

Причина, по которой сообщение -observeValueForKeyPath:ofObject:change:context: не отправляется, заключается в том, что для привязки наблюдатель не является объектом привязки. На заднем плане есть еще один объект. (В форме стека точка останова вы можете видеть, что ее класс - NSEditableBinder.) Поэтому правильно зарегистрироваться в качестве наблюдателя из представления в свойстве представления @ "images".

Другой способ получить уведомление об изменении в представлении - переопределить метод -setValue:forKey:. Тогда вам нужно будет проверить строку ключа и посмотреть, равно ли оно @"images". Но так как существуют другие методы из протокола KVC, такие как -setValue:forKeyPath:, вам нужно быть очень осторожным, чтобы не нарушать работу механизма, то есть всегда вызывать super.

Э. Я просто понимаю, что мой ответ до сих пор предполагает более простой случай, когда вы заменяете весь массив. Ваш вопрос был о модификации массива. (Однако в вашем примере вы объявляете свойство неизменяемого массива, которое допускает только замену. Поэтому оставьте его как объявлено, и мой подход пока работает. Ниже я покажу другую альтернативу.)

Хорошо, давайте предположим, что вы делаете это в делегате приложения, замена:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{   
    [view bind:@"images" toObject:self withKeyPath:@"images" options:nil];

    MyImage *img = [[MyImage alloc] ...];

    self.images = [NSArray arrayWithObject:img];
    [img release];
}

Вам не нужно публиковать изменения (используя willChangeValueForKey: и didChangeValueForKey:, поскольку вы просматриваете объявленное свойство. Они делают это за вас.

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

[self mutableArrayValueForKey:@"images"] addObject:img];

Это приведет к изменению на отправляющей (связанной) стороне. Затем он будет перенесен в вид через механизм привязки и в конечном итоге настроен с использованием KVC.

Там, на принимающей стороне в представлении, вам нужно будет подобрать изменение свойства к @ "images". Это можно сделать, переписав метод (ы) метода доступа к коллекции и выполнив дополнительную работу, вместо того, чтобы просто принять изменение. Но это немного сложно, так как существует довольно много методов доступа (см. docs ). Или, проще, вы можете добавить еще одно отношение наблюдения из представления.

Для этого где-то при инициализации (например, -awakeFromNib:) представления:

[self addObserver:self forKeyPath:@"images" options:0 context:nil];

и затем:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
  [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];

  if([keyPath isEqualToString:@"images"]) {
    [self setNeedsDisplay:YES]; // or what else you need to do then.
  }
}

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

Это должно сработать.

0 голосов
/ 17 апреля 2011

Единственный способ вызвать observeValueForKeyPath - это позвонить addObserver.Привязка работает через другой механизм.

...