Необходимо переопределить bind: toObject: withKeyPath: options: в подклассе NSView для реализации связывания? - PullRequest
6 голосов
/ 14 декабря 2008

У меня есть подкласс NSView, у которого есть свойство, которое я хочу привязать. В подклассе я реализовал следующее:

myView.h:

@property (readwrite, retain) NSArray *representedObjects;

myView.m:

@synthesize representedObjects;

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


-(void)bind:(NSString *)binding toObject:(id)observableController withKeyPath:(NSString *)keyPath options:(NSDictionary *)options
{
    if ([binding isEqualToString:@"representedObjects"]) {
        [observableController addObserver: self forKeyPath:@"arrangedObjects" options:NSKeyValueChangeNewKey context:nil];
    } else {
        [super bind: binding toObject:observableController withKeyPath:keyPath options: options];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"arrangedObjects"]) {
        [self setRepresentedObjects: [object arrangedObjects]];
    }
}

Затем я создаю привязку к массиву ControlCler в -[AppController awakeFromNib]:

[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];

Это правильный способ реализации связывания? В нем много кода, который заставляет меня думать, что я делаю что-то не так.

Я думал, что NSObject будет автоматически реализовывать то, что я сделал вручную в -bind:toObject:withKeyPath:options:, но, похоже, это не так. Если я закомментирую мой -bind:toObject:withKeyPath:options:, метод setRepresentedObjects никогда не будет вызван.

Дополнительная информация: Я провел еще несколько расследований и пришел к выводу, что мой первоначальный подход верен, и вам нужно переиграть -bind:toObject:withKeyPath:options:. Вот цитата из Темы программирования привязок Какао: Как работают привязки? :

В привязке: toObject: withKeyPath: options: метод объект должен как минимум сделать следующее:

  • Определите, какая привязка устанавливается
  • Запишите, к какому объекту он привязан, с помощью какой траектории и с какими параметрами
  • Зарегистрировать в качестве наблюдателя путь к ключу объекта, с которым он связан, чтобы получать уведомления об изменениях

Пример кода в листинге 2 демонстрирует частичную реализацию привязки джойстика: toObject: withKeyPath: options: метод, работающий только с привязкой угла.

Листинг 2 Частичная реализация метода bind: toObject: withKeyPath: option для класса Joystick:

static void *AngleBindingContext = (void *)@"JoystickAngle";

- (void)bind:(NSString *)binding
 toObject:(id)observableObject
 withKeyPath:(NSString *)keyPath
 options:(NSDictionary *)options
{
 // Observe the observableObject for changes -- note, pass binding identifier
 // as the context, so you get that back in observeValueForKeyPath:...
 // This way you can easily determine what needs to be updated.

if ([binding isEqualToString:@"angle"])
 {
    [observableObject addObserver:self
                   forKeyPath:keyPath
                  options:0
                  context:AngleBindingContext];

    // Register what object and what keypath are
    // associated with this binding
    observedObjectForAngle = [observableObject retain];
    observedKeyPathForAngle = [keyPath copy];

    // Record the value transformer, if there is one
    angleValueTransformer = nil;
    NSString *vtName = [options objectForKey:@"NSValueTransformerName"];
    if (vtName != nil)
    {
        angleValueTransformer = [NSValueTransformer
            valueTransformerForName:vtName];
    }
 }
 // Implementation continues...

Это ясно показывает, что класс Joystick (который является подклассом NSView) должен переопределить -bind:toObject:withKeyPath:options:.

Я нахожу это удивительным. Я скептически отнесся к такому выводу, так как не нашел других примеров кода, которые делают это. Однако, как говорится в официальной документации Apple, я должен переиграть -bind:toObject:withKeyPath:options:. Я пришел к выводу, что это правильный подход.

Я был бы очень рад, если бы кто-то мог доказать, что я не прав!

Ответы [ 3 ]

1 голос
/ 13 сентября 2011

Нет, нет необходимости переопределять bind:.

Как написал Питер Хоси в комментарии к предыдущему ответу, вы можете позвонить exposeBinding: и реализовать KVC- и KVO-совместимые методы доступа и сеттеры.

MyView.h:

@interface MyView : NSView {
    NSArray *_representedObjects;
}

// IBOutlet is not required for bindings, but by adding it you can ALSO use
// an outlet
@property (readonly, retain) IBOutlet NSArray *representedObjects;

@end

MyView.m:

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

// Use a custom setter, because presumably, the view needs to re-draw
- (void)setRepresentedObjects:(NSArray *)representedObjects {
    [self willChangeValueForKey:@"representedObjects"];
    // Based on automatic garbage collection
    _representedObjects = representedObjects;
    [self didChangeValueForKey:@"representedObjects"];

    [self setNeedsDisplayInRect:[self visibleRect]];
}

Затем вы можете установить привязку программно:

[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];

Однако, чтобы установить привязку в Интерфейсном Разработчике, необходимо создать собственную палитру.

1 голос
/ 15 декабря 2008

Нет, вам не нужен этот клеевой код.

Что вы подразумеваете под «не похоже на дело»? Что произойдет, если вы пропустите это?

0 голосов
/ 08 января 2009

Вам определенно нужно реализовать -bind:toObject:withKeyPath:options: в пользовательском представлении, если вы хотите реализовать привязки в этом представлении. Ваша реализация в myView.m в значительной степени соответствует.

...