« Extensión por delegación | Inicio | Entresijos. Haciendo la web. El nuevo proyecto de Design-Nation »

Bindings, NSArrayController y Value Transformers

Parece el título de una mala película de acción, de esas que estiran hasta más allá de lo razonable y lo creíble la trama de un comic, pero es aún peor, porque es el título de la última aventura de El Gran Dragón.

Y es que Bruce le ha ordenado que construya una prueba de concepto, una aplicación funcional, pero no muy compleja, que presente en un listado unos cuantos iconos. En concreto, los que corresponden con NSComputer, NSFolderBurnable y NSFolderSmart, que como El Gran Dragón bien sabe, Apple ha expuesto para su uso a partir de Leopard.

El Gran Dragón sabe que su Gran Maestro gusta de las aplicaciones sencillas y elegantes, de tener un núcleo funcional desde etapas muy tempranas sobre el que ir realizando los refinamientos necesarios y, por eso, su primera idea llega rápida como el ataque de una serpiente: "voy a construir una aplicación con un controlador, que tenga agregado un array mutable, array que controlaré con una instancia de NSArrayController, de forma que, por bindings, asigne el contenido del listado que constituye el interfaz de la aplicación a partir del contenido de ese array".

No está mal. Una solución sencilla, elegante, que utiliza en su beneficio muchos de los frameworks y metodologías de desarrollo para Mac OS X y cuya arquitectura sigue las líneas maestras de lo que se consideran "buenas prácticas".

Por tanto, El Gran Dragón comienza por crear el proyecto, como siempre, y por crear el controlador de la aplicación, una clase llamada AppController, que agregará un array mutable. La cabecera de esa clase sería por tanto:

#import


@interface AppController : NSObject
{
NSMutableArray *iconsCollection;
}

@property(copy, readwrite) NSMutableArray *iconsCollection;

@end

El interfaz de la aplicación tampoco tiene mucha complicación. Basta con crear en Interface Builder una instancia de NSObject y asignarle su tipo al controlador de la aplicación, y construir un interfaz con un NSTableView con una única columna, sobre la que El Gran Dragón arrastra una instancia de NSImageCell, para de esa forma poder presentar imágenes en la tabla.

interfaz_transformer.png

Bien, se dice a sí mismo el Gran Dragón. "Como tengo un controlador de la aplicación responsable de crear un array mutable, si consigo colocar en ese array mutable las imágenes que quiero presentar en el interfaz, sólo tendría que asignar el binding correspondiente en Interface Builder para que mi aplicación presentara las imágenes".

Como ya era tarde, El Gran Dragón se marchó a la cama sonriente, pensando que había encontrado la solución a su problema. Sin embargo, a altas horas de la madrugada, su maestro se le apareció en sueños: "Gran Dragón, ¿acaso has olvidado todo lo que te he enseñado? De verdad crees que es necesario crear esas imágenes? ¿Y si quisieras serializar el modelo a disco? No entiendes que no es necesario crear tantas instancias de NSImage, sobe todo si las puedes crear en tiempo de ejecución? ¿No te parece?"

El Gran Dragón despertó sudoroso, pero con una mirada de gran determinación. "¡Es cierto! Puedo crear las instancias de NSImage al vuelo, lanzándole un mensaje como éste!":

[ NSImage imageNamed: @"NSComputer" ];

Por tanto, sólo necesitaría guardar en el modelo las cadenas de texto con los nombres de las imágenes a crear. Pero, ¿cómo hago para convertir esa cadena de texto en una instancia de NSImage, que es a lo que necesito bindar la columna de mi tabla?

Pues utilizando un Value Transformer. Un Value Transformer es una clase que ejerce como mediador en el mecanismo de bindings. Si esa clase existe, antes de pasarle al interfaz lo que debe dibujar, se le pasa al mediador el valor extraído del modelo, y es el mediador el que, tras hacer lo que deba con ese valor, quien le termina por dar al interfaz lo que éste necesita.

En este caso, el interfaz (la columna de la tabla) necesita instancias de NSImage, mientras que lo que El Gran Dragón va a guardar en el modelo con cadenas de texto. Por tanto, el Value Transformer será el que cree las instancias de NSImage necesarias a partir de ese valor de texto.

Lo primero es crear la entidad básica del modelo. El Gran Dragón ha decidido, en un alarde de originalidad, llamarla AppEntity:

#import


@interface AppEntity : NSObject
{
NSString *iconName;
}
@property(copy, readwrite) NSString *iconName;
@end


La implementación:

#import "AppEntity.h"


@implementation AppEntity
@synthesize iconName;
@end


Ahora, El Gran Dragón escribe la implementación de su controlador para que, al crearse, éste cree las tres instancias de esa entidad que El Gran Maestro le ha pedido. Por tanto, AppController.m será:

#import "AppController.h"
#import "AppEntity.h"
#import "DNIconValueTransformer.h"


@implementation AppController
@synthesize iconsCollection;
-(id) init
{
self = [ super init ];

NSValueTransformer *iconTransformer = [ [ DNIconValueTransformer alloc ] init ];
[ NSValueTransformer setValueTransformer: iconTransformer forName:@"IconImageTransformer"];

iconsCollection = [ [ NSMutableArray alloc ] init ];

AppEntity *entityOne = [ [ AppEntity alloc ] init ];
entityOne.iconName = @"NSComputer";
[ iconsCollection addObject: entityOne ];

AppEntity *entityTwo = [ [ AppEntity alloc ] init ];
entityTwo.iconName = @"NSFolderBurnable";
[ iconsCollection addObject: entityTwo ];

AppEntity *entityThree = [ [ AppEntity alloc ] init ];
entityThree.iconName = @"NSFolderSmart";
[ iconsCollection addObject: entityThree ];

return self;
}
@end

Como puede verse, al inicializarse el controlador también se registra el Value Transformer, asignándole como nombre IconImageTransformer.

El siguiente paso de El Gran Dragón, por tanto, es escribir esa clase, que tendrá la siguiente cabecera:

#import


@interface DNIconValueTransformer : NSValueTransformer
{

}

@end

Como se puede ver, el único misterio que tiene es que extiende de NSValueTransformer.

La implementación tiene un poco más de miga:

#import "DNIconValueTransformer.h"


@implementation DNIconValueTransformer

+ (Class)transformedValueClass
{
return [ NSImage class ];
}

+ (BOOL)allowsReverseTransformation
{
return YES;
}

- ( id ) transformedValue:( id )value
{
NSLog( @"transformador %@", value );

if( value != nil )
{
return [ NSImage imageNamed: value ];
}
else
{
NSLog( @"pasa por el transformador" );
return nil;
}
}

@end

Aunque tampoco es muy complicada. El método transformedValueClass devuelve el nombre del tipo de retorno del método que va a realizar la transformación: transformedValue. En este caso, lo que se va a crear son imágenes, por tanto devuelve NSImage.

El método de transformación en sí tampoco es muy complejo. Simplemente se crea la instancia de NSImage a partir del nombre de la imagen que se le pasa al transformador desde el modelo.

Ahora, de vuelta en Interface Builder, hay que crear una instancia de NSArrayController, y encargarla manejar la colección de iconos:

controlador_Array.png

El último paso es asignar el binding de la columna de la tabla.

table_column_bindings.png

Como se puede ver, se binda al valor de la propiedad iconName de la entidad del modelo, pero utilizando la transformación que El Gran Dragón registró previamente como IconImageTransformer.

De esa forma, el resultado final es el que se pretendía conseguir:

icons_final.png

Y se ha conseguido con un modelo ligero, sin gran consumo de memoria ni de recursos del sistema, que puede ser serializado fácilmente.

Es un paso más para El Gran Dragón en el largo camino de la sabiduría.

El proyecto completo puede descargarse de este enlace

Comentarios

Me parece espectacular toda la información que das

Publicar un comentario

(Si no dejó aquí ningún comentario anteriormente, quizás necesite aprobación por parte del dueño del sitio, antes de que el comentario aparezca. Hasta entonces, no se mostrará en la entrada. Gracias por su paciencia).