Desarrollo de una aplicación para Mac OSX: dado
Hay dos cosas que nos gustan particularmente en design-nation: los dados, y las aplicaciones para Mac OSX. Así que hemos desarrollado una pequeña aplicación ( lo has adivinado, un dado ) con la finalidad de enseñar aunque sea por encima cómo es el proceso de desarrollo en el Mac. Lo haremos utilizando XCode y escribiendo código en Objective-C
En todo caso, como casi cualquier otro tutorial sobre XCode que se pueda escribir, el nuestro queda oscurecido por el que hay en el Apple Developer Connection. Léelo!
Pero antes, echa un vistazo al resultado final:
Antes de empezar a picar código como locos, parémonos a pensar cómo vamos a implementar la aplicación. Generaremos un número entre 1 y 6, y después cargaremos un png que contendrá la cara del dado que corresponda a ese número y lo mostraremos en el interfaz.
Siempre es bueno desarrollar cualquier aplicación siguiendo el paradigma MVC, pero en este caso, veremos cómo XCode y el Interface Builder hacen esa implementación fácil e intuitiva.
Bien, empecemos. en primer lugar, abre XCode ( si no lo encuentras, está escondido en la carpeta /Developer/Applications ). Después selecciona File-> New Project, y cuando te pregunte, elige "Cocoa Application". Entonces, escribe el nombre y elige la carpeta en disco donde crear los archivos del proyecto. Tras hacer eso, se abrirá la ventana del proyecto ( "Project Window" )
No nos vamos a meter a explicar qué son todos los archivos y carpetas que se han creado. Además, hay varios tutoriales ( incluso la documentación de XCode ) que lo explican. Por tanto, pasemos directamente a trabajar en el interface.
¿Pero cómo?. Nuestra aplicación estará basada en el MVC. eso significa que el interfaz de usuario ( la vista ) será totalmente independiente de la lógica de la aplicación ( el modelo ). Y XCode y el Interface Builder harán un excelente papel a la hora de facilitar esa implementación.
Para empezar, por tanto, haz doble click en el archivo MainMenu.nib. De ese modo se abrirá el Interface Builder.
El Interface Builder es una herramienta visual que permite, como su nombre indica, construir el interfaz de la aplicación. Pero no sólo eso, sino que nos asegura que, si seguimos las guías que nos proporciona, cumpliremos con las User Interface Guidelines de Apple. Veamos cómo.
En primer lugar, adapta el tamaño de la ventana del programa a tus necesidades. Para hacerlo, puedes arrastrar la esquina inferior derecha, o abrir el Inspector, seleccionar Size, e introducir los valores a mano. En nuestro ejemplo, hemos seleccionado un ancho de 240 y un alto de 280. En la sección de Attributes se pueden cambiar el nombre de la ventana, fondo, y otras propiedades.
Ahora, añade los elementos de interfaz que vamos a necesitar. Akade un NSImageView ( de la pestaña Cocoa-Containers ) y un Button ( de la pestaña Cocoa-controls ).
Arrastra la instancia del NSImageView, y suéltala sobre la ventana. Mientras la arrastras, verás cómo el Interface Builder dibuja unas líneas que ayudan a posicionarlo. Seguir esas guías asegurará que se están cumpliendo las User Interface Guidelines de Apple.
A continuación añade el botón al interfaz. Arrastra el botón de la paleta a la ventana de la aplicación ( recuerda, deja que las guías te guíen ), y tras soltarlo, mientras aún está seleccionado, presiona Option y muévelo usando los cursores. Verás cómo el Interface Builder muestra la distancia entre el botón y el elemento del interfaz que esté debajo del cursor del ratón. Por tanto, coloca el cursor sobre el NSImage View, y centra el botón respecto a él.
Ahora, asigna el texto del botón, haciendo doble click sobre él, y tras hacerlo, vete a la sección de Attributes del Info panel, y selecciona el atajo de teclado para el botón ( en este caso, Enter ).
Para asegurarnos que el botón tendrá el foco cuando se arranque la aplicación, setearemos el initialFirstresponder. Para ello, hay que arrastrar ( con Control presionado ) una línea de conexión desde la instancia de window en la ventana MainMenu.nib, hasta el botón. Una vez hecho, hay que seleccionar en el Info panel initialFirstResponder, y hacer click en "Connect"
El interfaz ya casi está terminado. Sólo falta conectarlo con el modelo de la aplicación, lo que haremos a través del controlador.
Pero antes de hacerlo, vamos a ver rápidamente qué son los "outlets" y los "actions". Los outlets son variables de instancia de una clase que apuntan a otro objeto. Dicho de otra forma, un outlet es una referencia que un objeto mantiene a otro objeto distinto. Sin embargo, una acción ( action ) es similar a un método púclico de una clase. Mejor dicho, es la forma de encapsular el envío de un mensaje a un target determinado.
En todo caso, el Interface Builder facilita el proceso de crear outlets y actions
En primer lugar, tendremos que crear la clase que haga el papel de controlador. Para ello, en el panel Classes de la ventana MainMenu.nib, hay que hacer click, en el panel más a la izquierda, en NSObject. A continuación, debe presionarse Return. Entonces, hay que nombrar la nueva clase como DiceController. De ese modo, hemos creado una subclase de NSObject ( la clase base de toda la jerarquía ).
Ahora, definiremos los outlets para esa clase. DiceController es el controlador, por lo que tendrá un outlet al NSImageView del interfaz, y otro al modelo ( una clase que no hemos creado aún ). Por tanto, selecciona "DiceController", y en el Info Panel, selecciona Attributes. A continuación asegúrate que el panel Outlets está seleccionado, y haz click en add. Llama el outlet imageView, y asígnalo el tipo NSImageView ( un poco de tipado no viene mal ).
Ahora añade otro outlet, que será el que apunta al modelo, y llámalo dice.
Para crear la acción que será ejecutada cuando se haga click en el botón, selecciona el panel de acciones ( en el Info Panel ), haz click en add y da a la acción el siguiente nombre: roll.
Ya hemos descrito el controlador, incluyendo sus outlets y actions. Ahora es el momento de conectar este controlador con el interfaz. Para hacerlo, en primer lugar habrá que crear una instancia de la clase DiceController, seleccionando la clae en la ventana MainMenu.nib, y a continuación seleccionando Instantiate en el menú Classes. La nueva instancia aparecerá en el panel de instancias. Para conectarla con el interfaz, Control-Drag una línea desde la instancia de la clase hasta el NSIMageView del interfaz. Cuando sueltes el botón, selecciona el outlet que estará asignado a esa conexión ( en nuestro ejemplo inageView ), y haz click en connect. Para conectar el botón en la vista con el controlador, Control-Drag desde el botón hasta la instancia de DiceController. Al soltar el botón, selecciona roll en la columna de las acciones y haz click en connect.
Para crear el modelo ( Dice ), define una nueva subclase de NSObject y crea una instancia de esa clase. A continuación, crea una conexión de outlet entre DiceController y Dice. ¡Y ya está todo conectado!
Una vez que hemos concluido con el trabajo en el interfaz, es el momento de que XCode genere el código necesario. Para ello, selecciona una de las dos clases en la ventana MainMenu.nib, y selecciona Create classes en el menú Classes. A continuación haz lo mismo con la otra clase.
Mira el código generado. Verás que hay un método sin implementar en DiceController.m. Implementémoslo.
@implementation DiceController
- (IBAction)roll:(id)sender
{
[ imageView setImage: [ dice image ] ];
}
@end
El código es muy sencillo. Simplemente, setearemos en contenido de imageView ( que es uno de los outlets de esta clase ), con una imagen que obtendremos del modelo ( el otro outlet ).
Por tanto, ahora concluiremos la implementación del modelo. Esta clase tendrá un método que generará un número aleatorio entre 1 y 6, y cargará, dependiendo de ese valor, un png diferente de disco. Después le pasará la imagen cargada al controlador.
Por tanto, el constructor del modelo será:
-( id ) init
{
self = [ super init ];
image = nil;
srand( time( NULL ) );
return self;
}
Para evitar agujeros de memoria, se liberarán los recursos al liberar la clase:
- ( void ) dealloc
{
[ image release ];
[ super dealloc ];
}
Los png estarán en el bundle de la aplicación. Por tanto, lo primero será obtener una referencia a ese bundle:
-( NSImage* ) image
{
NSBundle *bundle = [ NSBundle bundleForClass: [ self class ] ];
NSString *imgName = [ bundle pathForResource: [ self generateImageName ] ofType: @"png" ];
image = [ [ NSImage alloc ]
initWithContentsOfFile: imgName ];
return image;
}
El código completo de Dice.m será, finalmente:
#import "Dice.h"
@implementation Dice
-( id ) init
{
self = [ super init ];
image = nil;
srand( time( NULL ) );
return self;
}
- ( void ) dealloc
{
[ image release ];
[ super dealloc ];
}
-( NSString* ) generateImageName
{
int random = ( rand( ) % 6+ 1 );
return [ NSString stringWithFormat: @"%i", random ];
}
-( NSImage* ) image
{
NSBundle *bundle = [ NSBundle bundleForClass: [ self class ] ];
NSString *imgName = [ bundle pathForResource: [ self generateImageName ] ofType: @"png" ];
image = [ [ NSImage alloc ]
initWithContentsOfFile: imgName ];
return image;
}
@end
Ya sólo queda compilar la aplicación. Bueno, y otro paso más. Habrá que empaquetarla para distribuirla. Para ello, crearemos un archivo dmg que contendrá la aplicación y un archivo de texto con las instrucciones de instalación.
Crearemos ese dmg con la Utilidad de Discos ( /Aplicaciones/Utilidades/Utilidad de discos ). Abre la utilidad de discos, crea una nueva imagen de disco, asigna el espacio de dicha imagen, y se creará y montará. Arrastra dentro de la imagen el bundle de la aplicación y el resto de archivos a incluir en la distribución, y ¡listo!
Hay que ver lo atrevida que es la ignorancia...
Comentarios
Te han faltado cosas, como qué es lo que va en Diche.h y DiceController.h así como echar los pngs en el proyecto, etc. Al final se queda uno con las ganas de ver rodar la criaturita, pero vamos, gracias de todas formas, hacen falta tutoriales sencillos, más sencillos aún. La programación en Cocoa ya es rara de por sí y el entorno llega casi al esoterismo.
Publicado por: ende | Junio 30, 2005 02:19 PM
Bueno, comprobaré el empaquetado del proyecto, pero las cabeceras debería estar ahí, al igual que los png.
Luego lo echaré un vistazo, y lo actualizo si es necesario.
Publicado por: Cesar Tardaguila | Junio 30, 2005 02:51 PM
FILE > PRINT > OK !
Gracias por esta introduccion !
Publicado por: afrael | Junio 30, 2005 09:36 PM
A pesar que lo haces ver fácil, se me ha hecho muy difícil aprender a usar el Xcode y el CodeWarrior, podrías sugerirme algun sitio con documentación o libros para la iniciación en Xcode y/o CodeWarrior, los lenguajes los conozco, pero no se usar los editores
Publicado por: Teddy Dennis | Julio 4, 2005 05:23 AM
Pues no sé de ningún libro sobre XCode. Lo único que se me ocurre es utilizar la ayuda del propio XCode ( Comando-? ), donde está el manual del programa.
Publicado por: Cesar Tardaguila | Julio 4, 2005 08:43 AM
Pues gracias por el tuto, pero la verdad es que me perdí varias veces. El problema que tengo es el mismo que el camarada de arriba, no se usar XCode.
Aún así se agradece el esfuerzo.
Publicado por: Sergio De Luna | Agosto 5, 2005 07:48 AM