Target-Action en Cocoa

La aproximación a la programación con componentes dentro de Cocoa es algo diferente a la que los programadores en Java estamos acostumbrados. Por ejemplo, en Java si queremos crear una nueva ventana, hacemos una clase que herede de JFrame y metemos en su constructor las llamadas necesarias para añadir los componentes, para atender a los eventos, debemos crear subclases que implementen las interfaces que imponen en el sistema de eventos de Swing. Todo este proceso es bastante laborioso, incluso para crear una ventana que tenga un botón que cambie el valor de un “label” lleva una carga en código bastante fuerte.
En Cocoa, este enfoque es algo diferente, es una de las cosas que tienes que “aprender de nuevo” cuando te enfrentas a cocoa con una mente “Javera” o “.NETera”. Para crear una ventana usaremos el Interface Builder, cosa que en el fondo tampoco difiere mucho de la parte Java, sin embargo las grandes diferencias se encuentran en la forma de tratar los eventos, para ello se usa el modelo conocido como target-action. En dicho modelo tendremos una clase que no tiene por que heredar de ninguna otra, la cual sera el “target” de la clase que lanza los eventos, y se invocara a un “action” de dicha clase “target”.
¿En que consiste el target-action?
Creo que podemos verlo mejor con un ejemplo, supongamos que tenemos el típico ejemplo que consta de una ventana con un botón, y queremos que al pulsar el botón, se cambie el texto del label.
Según el esquema de funcionamiento de cocoa, vamos a tener una clase llamada Controller la cual va a ser target del botón, y le diremos que el action de pulsar el botón se le asigna al método botonPulsado de dicha clase Controller. Ademas dentro de dicha clase Controller tenemos que tener una referencia al Label para poder acceder a sus métodos y cambiar el testo que muestra.
La clase controller quedaria asi:
Controller.h
#import <Cocoa/Cocoa.h>
@interface
class Controller : NSObject
{
IBOutlet NSTextField *textField;
}
– (IBAction) botonPulsado: (id)sender;
@end
Controller.m
#import <Controller.h>
@implementation
– (IBAction) botonPulsado: (id)sender
{
NSLog(@”Boton pulsado”);
Operar con el label.
}
@end
IBOutlet indica que la variable es realmente una referencia como comentabamos anteriormente por otro lado IBAction define que la llamada se trata de una respuesta a un evento. Por el resto, la clase es bastante sencilla.
La forma mas sencilla de realizar los enlaces entre los componentes es desde el Interface builder, para ello, arrastraremos una instancia de Object a la Ventana “Doc”, la asociaremos a la clase Controller, hacemos Ctrl-Click sobre nuestro objeto Controller en la ventana “Doc” y arrastraremos el label al propio label de la ventana. Para asociar el action del botón pincharemos arrastrando desde el botón al objeto en la ventana “Doc” y seleccionaremos el método botonPulsado.
Si Compilamos y ejecutamos veremos como al pinchar sobre el botón, sale el mensaje de log y se cambia el contenido del label.
Mirando debajo del capó. Selectors
Realmente esto es muy “bonito”, la parte de codificación es bastante pequeña y todo esta bastante aislado, fácilmente podríamos cambiar los targets de la ventana y sustituir la clase Controller por otra que haga otra cosa, sin tocar nada de la vista, pero ¿en que mecanismos se basa para funcionar?
En Objective C, un “selector” guarda la referencia de un método de una clase, de tal manera que podemos hacer referencia a dicho metodo de forma dinamica. En este concepto se apoya el modelo target-action, de tal manera que en tiempo de ejecución podemos indicarle cual sera el método que una acción deberá ejecutar.
En código hay varias formas de obtener un selector, una de ellas es mediante el uso de “@selector()” a la cual se le pasa directamente el método del cual queremos obtener el selector y la otra es mediante la función NSSelectorFromString() que recibe por parámetros una cadena con el nombre del método, por ejemplo
SEL selectorBotonPulsado;
selectorBotonPulsado = @selector(botonPulsado:);
selectorBotonPulsado = NSSelectorFromString(@”botonPulsado:”);
Ademas de esto, la clase NSControl tiene un metodo llamado “setAction” y otro “setTarget” que como puede suponerse sirven para indicar ambos elementos de forma manual.
Con todo esto, si queremos hacer los enlaces que hace el interface builder, pero haciendolo nosotros a mano, el código seria algo como:
[boton setTarget: self];
[boton setAction: selectorBotonPulsado];
Habiendo definido antes un IBOutlet NSButton *boton, en Controller.h

Xcode Icon

La aproximación a la programación con componentes dentro de Cocoa es algo diferente a la que los programadores en Java estamos acostumbrados. Por ejemplo, en Java si queremos crear una nueva ventana, hacemos una clase que herede de JFrame y metemos en su constructor las llamadas necesarias para añadir los componentes, para atender a los eventos, debemos crear subclases que implementen las interfaces que imponen en el sistema de eventos de Swing. Todo este proceso es bastante laborioso, incluso para crear una ventana que tenga un botón que cambie el valor de un “label” lleva una carga en código bastante fuerte.

En Cocoa, este enfoque es algo diferente, es una de las cosas que tienes que “aprender de nuevo” cuando te enfrentas a cocoa con una mente “Javera” o “.NETera”. Para crear una ventana usaremos el Interface Builder, cosa que en el fondo tampoco difiere mucho de la parte Java, sin embargo las grandes diferencias se encuentran en la forma de tratar los eventos, para ello se usa el modelo conocido como target-action. En dicho modelo tendremos una clase que no tiene por que heredar de ninguna otra, la cual sera el “target” de la clase que lanza los eventos, y se invocara a un “action” de dicha clase “target”.

¿En que consiste el target-action?

Creo que podemos verlo mejor con un ejemplo, supongamos que tenemos el típico ejemplo que consta de una ventana con un botón, y queremos que al pulsar el botón, se cambie el texto del label.

Según el esquema de funcionamiento de cocoa, vamos a tener una clase llamada Controller la cual va a ser target del botón, y le diremos que el action de pulsar el botón se le asigna al método botonPulsado de dicha clase Controller. Ademas dentro de dicha clase Controller tenemos que tener una referencia al Label para poder acceder a sus métodos y cambiar el testo que muestra.

La clase controller quedaria asi:

Controller.h

#import <Cocoa/Cocoa.h>
@interface
class Controller : NSObject
{
IBOutlet NSTextField *textField;
}
- (IBAction) botonPulsado: (id)sender;
@end

Controller.m

#import <Controller.h>
@implementation
- (IBAction) botonPulsado: (id)sender
{
NSLog(@"Boton pulsado");
Operar con el label.
}
@end

IBOutlet indica que la variable es realmente una referencia como comentabamos anteriormente por otro lado IBAction define que la llamada se trata de una respuesta a un evento. Por el resto, la clase es bastante sencilla.

La forma mas sencilla de realizar los enlaces entre los componentes es desde el Interface builder, para ello, arrastraremos una instancia de Object a la Ventana “Doc”, la asociaremos a la clase Controller, hacemos Ctrl-Click sobre nuestro objeto Controller en la ventana “Doc” y arrastraremos el label al propio label de la ventana. Para asociar el action del botón pincharemos arrastrando desde el botón al objeto en la ventana “Doc” y seleccionaremos el método botonPulsado.

Si Compilamos y ejecutamos veremos como al pinchar sobre el botón, sale el mensaje de log y se cambia el contenido del label.

Mirando debajo del capó. Selectors

Realmente esto es muy “bonito”, la parte de codificación es bastante pequeña y todo esta bastante aislado, fácilmente podríamos cambiar los targets de la ventana y sustituir la clase Controller por otra que haga otra cosa, sin tocar nada de la vista, pero ¿en que mecanismos se basa para funcionar?

En Objective C, un “selector” guarda la referencia de un método de una clase, de tal manera que podemos hacer referencia a dicho metodo de forma dinamica. En este concepto se apoya el modelo target-action, de tal manera que en tiempo de ejecución podemos indicarle cual sera el método que una acción deberá ejecutar.

En código hay varias formas de obtener un selector, una de ellas es mediante el uso de “@selector()” a la cual se le pasa directamente el método del cual queremos obtener el selector y la otra es mediante la función NSSelectorFromString() que recibe por parámetros una cadena con el nombre del método, por ejemplo

SEL selectorBotonPulsado;

selectorBotonPulsado = @selector(botonPulsado:);

selectorBotonPulsado = NSSelectorFromString(@”botonPulsado:”);

Ademas de esto, la clase NSControl tiene un metodo llamado “setAction” y otro “setTarget” que como puede suponerse sirven para indicar ambos elementos de forma manual.

Con todo esto, si queremos hacer los enlaces que hace el interface builder, pero haciendolo nosotros a mano, el código seria algo como:

[boton setTarget: self];

[boton setAction: selectorBotonPulsado];

Habiendo definido antes un IBOutlet NSButton *boton, en Controller.h

6 respuestas a Target-Action en Cocoa

  1. Tyflos dice:

    Hola, el artículo está muy bien pero no indicas dónde debemos colocar las 2 sentencias para ajustar el target (setTarget) y el action (setAction), se coloca en el init del NSObject o dónde? He hecho pruebas y no me sale.

    Otra preguntas es,cuando hacemos un setAction a un button es para el evento press, cómo podemos elegir otro evento como un mouseOver o similar?

    gracias y enhorabuena por el artículo

  2. robjperez dice:

    Hola, antes de nada, gracias por comentar la entrada🙂

    Respecto a la primera pregunta, se puede colocar la llamada a setTarget y setAction en múltiples sitios, si el componente lo hemos puesto en la ventana con el Interface Builder, desde el propio programa podemos indicarle cual sera nuestro Target y nuestro Action. Para ello podemos pinchar con el Ctrl pulsado en el boton y arrastrar la flecha que sale hasta el Controller del Inspector de Objetos. O también hacer el enlace desde el Inspector, teniendo seleccionado el control.

    En código, se puede colocar en múltiples sitios, uno muy apropiado para colocarlo es en el mensaje awakeFromNib, que puedes implementar en tu controlador principal.

    Sobre la segunda pregunta, los componentes suelen tener una acción predeterminada, que es la se maneja con estos dos métodos, en el caso de un Botón, esta acción es cuando se presiona, en el caso de otro componente puede ser diferente. Para tratar esos tipos de eventos deberías hacerte una propia subclase del componente y sobrecargar esos métodos o bien en algunos casos usar delegates.

    Saludos.

  3. Tyflos dice:

    Hola, bueno, el problema es que con Interface builder no puedo hacerlo porque soy ciego y lo de drag and drop como que no puedo hacerlo con voiceOver. Espero no abusar pero estaría bien que explicases un poco cómo desarrollar una aplicación Cocoa y Cocoa touch sin usar interface builder ya que todos los tutoriales que he encontrado usan interface builder. Vamos,algo sencillito, un botón y un label, pulsas el botón y sale un texto en el label pero todo con xCode y sin interface builder.

    Ahora me tengo que poner a estudiar un poco Como va Cocoa y el tema de los delegados, que mucho en inglés y con muchos diagramas por lo que muchas veces me pierdo al no poder ver los diagramas.

    Fuera ahora del comentario, y para que no parezca peloteo🙂 tu blog está muy bien y te sigo un poco, aunque llevo poco tiempo en esto de programación para MacOS X, yo vengo de programar en Delphi y C# por lo que el paradigma es bastante distinto pero me ha encantado el mundo del mac y del iPhone por lo que quiero desarrollar apps para ellos y que sean accesibles

    • robjperez dice:

      Me parece muy buena idea la que me comentas, me apunto para que la siguiente entrada sea como empezar a hacer una aplicación sin usar Interface Builder🙂

      Nunca había pensado que usar esta herramienta fuera un problema, pero claro la mayoría de las veces no pensamos en todas las formas que tenemos las personas para interactuar con el ordenador.

      Gracias de nuevo por el comentario😉

  4. Juan dice:

    Hola, tyflos

    Estoy trabajando en temas de acceso a los beneficios de Internet para colectivos con distintos tipos de exclusión social y digital. Puedes ver una descripción del proyecto en http://www.emadrid2011.es . Si me permites, te sugeriría la posibilidad de publicar lo que estás haciendo para que más personas se beneficien de lo que vayas acumulando, tanto por tu cuenta como lo que llegues con robjperez.

    Nos gustaria ponernos en contacto contigo. Si te parece bien, mándanos un email a emadrid@onlineandoffline.net y hablamos.

    un saludo

  5. Tyflos dice:

    Gracias a ti por el futuro artículo. En mi blog ( http://programaraciegas.weblog.discapnet.es ) trato el problema de la accesibilidad y la tecnología, por si te interesa.

    Como programador y consultor, como comprenderás, me afecta mucho que una herramienta de desarrollo no sea accesible ya que, al ser ciego, se me cierran puertas en mi trabajo diario, por eso mi interés en buscar alternativas y soluciones a las barreras de accesibilidad.

    De nuevo, gracias por tu atención y tu labor

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: