2008-12-14 17 views
6

Tengo una subclase NSView que tiene propiedades que quiero que sean enlazables. He implementado el siguiente en la subclase:¿Es necesario anular bind: toObject: withKeyPath: options: en una subclase NSView para implementar el enlace?

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]]; 
    } 
} 

entonces crear la unión a un arrayController en -[AppController awakeFromNib]:

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

¿Es esta la forma correcta de implementar el enlace? Se trata de un código de placa de caldera que me hace pensar que estoy haciendo algo mal.

Pensé que NSObject implementaría automágicamente lo que he hecho manualmente en -bind:toObject:withKeyPath:options:, pero este no parece ser el caso. Si hago un comentario en mi -bind:toObject:withKeyPath:options:, nunca se llama al método setRepresentedObjects.

Información adicional: He investigado un poco más y he llegado a la conclusión de que mi enfoque original es correcto y que tiene que sobrepasar -bind:toObject:withKeyPath:options:. Aquí hay una cita de Cocoa Bindings Programming Topics: How Do Bindings Work?:

En su aprieto: toObject: withKeyPath: Opciones: Método de un objeto debe, como mínimo, lo siguiente:

  • Determinar el que la unión se está estableciendo
  • Grabar lo objeto que se está obligado a usar lo keypath y con qué opciones
  • Registro como un observador de la ruta de acceso clave del objeto al que está unido de forma que reciba la notificación de cambios

El ejemplo de código en el Listado 2 muestra una implementación parcial del enlace de Joystick: toObject: withKeyPath: options: método que trata solo del enlace de ángulo.

Listado 2 aplicación parcial de la bind: toObject: Método de opciones para la clase Joystick:: withKeyPath

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... 

Esto muestra claramente que la clase Joystick (que es una subclase NSView) debe sobrescribir -bind:toObject:withKeyPath:options:.

Me parece sorprendente. Estaba escéptico de esta conclusión ya que no he encontrado otras muestras de código que hagan esto. Sin embargo, como la documentación oficial de Apple dice que debería andar sobre -bind:toObject:withKeyPath:options:, concluyo que es el enfoque correcto.

¡Estaría muy feliz si alguien pudiera probar que estoy equivocado!

+0

Pregunta relacionada con más actividad, en vinculaciones con actualizaciones bidireccionales: http://stackoverflow.com/questions/1169097/can-you-manually-implement-cocoa-bindings – paulmelnikow

Respuesta

1

No, no debería necesitar ese código de pegamento.

¿Qué quiere decir con "no parece ser el caso"? ¿Qué pasa si lo omites?

0

Definitivamente debe implementar -bind:toObject:withKeyPath:options: en una vista personalizada si desea implementar enlaces en esa vista. Su implementación en myView.m es bastante acertada.

+3

No, no es así. Solo necesita llamar a exposeBinding e implementar accesos compatibles con KVC y KVO para cada propiedad enlazable. @property y @synthesize hacen la última parte por ti. –

+0

Interesante, tienes razón. Siempre lo he hecho de manera prolija, así que me voy a borrar un código. –

1

No, no es necesario anular bind:.

Como Peter Hosey escribió en el comentario de la respuesta anterior, puede llamar al exposeBinding: e implementar accesos y arreglos compatibles con KVC y 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]]; 
} 

entonces se puede establecer la unión mediante programación:

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

Para establecer la unión en Interface Builder, ho sin embargo, debes crear una paleta personalizada.

Cuestiones relacionadas