81
DISEÑO DE INTERFACES DE USUARIO CON XCODE XCode y el desarrollo de Aplicaciones para IPhone, IPad e IPod Diseño de Interfaces de Usuario con XCODE

diseño de interfaces de usuario

Embed Size (px)

DESCRIPTION

diseño en xcode

Citation preview

DISEÑO DE INTERFACES DE USUARIO CON XCODE XCode y el desarrollo de Aplicaciones para IPhone, IPad e IPod

Diseño de Interfaces

de Usuario con

XCODE

C O M P U T E R H U M A N I N T E R A C T I O N A N D C O L L A B O R A T I O N R E S E A R C H G R O U P

Diseño de Interfaces de Usuario con XCODE

AUTORES: Manuel Ortega Cantero

Miguel Gómez Ortiz Ana Isabel Molina Díaz

Francisco Jurado Monroy

CHICO – UCLM - JCCM Escuela Superior de Informática • UCLM Paseo Universidad 4 • 13071 Ciudad Real

Email: [email protected] ISBN: 978-84-615-0880-8

DISEÑO DE INTERFACES DE USUARIO CON XCODE por Manuel Ortega

Cantero et al is licensed under a Creative Commons Reconocimiento-NoComercial-CompartirIgual 3.0 Unported License.

Tabla de Contenidos

0. Introducción ......................................................................... 3  0.1 Propósito de este libro ............................................................. 3  0.2 Agradecimientos ...................................................................... 4  0.3 Bibliografía ............................................................................... 4  

1. Dispositivos y programa de desarrolladores de Apple ........ 7  1.1 Dispositivos: iPhone, iPad, iPod .............................................. 7  1.2 Desarrollo de Aplicaciones sobre IPhone ................................ 9  

1.2.1 Equipo de desarrollo .................................................................... 10  1.2.2 Certificado de desarrollador ........................................................ 10  1.2.3 Agregar dispositivos .................................................................... 12  1.2.4 Identificador de Aplicaciones (AppID) ......................................... 13  1.2.5 Perfiles ......................................................................................... 14  

2. Objective-C y XCode ......................................................... 17  2.1 Generalidades sobre el lenguaje ........................................... 17  2.2 Sentencias condicionales. ..................................................... 23  2.3 Bucles. ................................................................................... 24  

3. App1: Linterna ................................................................... 25  3.1 Introducción ........................................................................... 25  3.2 Instalaciones necesarias ....................................................... 25  3.3 Creando el proyecto .............................................................. 25  3.4 Desarrollando la interfaz ........................................................ 29  3.5 Añadiendo las conexiones ..................................................... 31  3.6 Desarrollando el código ......................................................... 33  3.7 Probando la aplicación en un iPhone .................................... 34  

4. App2: Calculadora ............................................................. 38  4.1 Introducción ........................................................................... 38  4.4   Desarrollando la interfaz ..................................................... 41  4.5   Añadiendo las conexiones .................................................. 43  4.6   Mejorando la interfaz .......................................................... 46  4.7   Desarrollando el código ...................................................... 47  4.8 Posibles mejoras ................................................................... 49  4.9 Probando la aplicación en un iPhone .................................... 49  

5. App3: Lista de la compra ................................................... 51  5.1 Introducción ........................................................................... 51  

G R U P O C H I C O - U C L M

2

5.2 Creando el proyecto ............................................................... 51  5.3 Desarrollando las interfaces ................................................... 56  

5.3.1. MainWindow ............................................................................... 56  5.3.2. CompraViewController ................................................................ 57  5.3.3. CarroViewController ................................................................... 60  5.3.4. AnadirViewController .................................................................. 61  

5.4 Añadiendo las conexiones ..................................................... 63  5.5 Código Fuente ........................................................................ 66  5.6 Probando la aplicación en un iPhone ..................................... 77  

0. Introducción En este capítulo vamos a introducir las convenciones usadas en este libro y a presentar en general el propósito de éste.

ntes de comenzar el capítulo vamos a explicar algunas convenciones utilizadas en este texto. Abajo a la izquierda podemos ver algunos iconos que nos servirán para remarcar partes de este libro.

Estos iconos nos van a ayudar a seleccionar la información que debe ser retenida por el alumno, nos permitirán hacer preguntas para conocer si se han aprendido los conceptos, Se propondrán ejercicios y se detallarán los conceptos explicados en revisiones finales.

A lo largo del libro hablaremos del IPhone pero nos estaremos refiriendo no solo a este dispositivo sino también

al IPod y al IPad, ya que el desarrollo de aplicaciones para estos tres dispositivos puede llevarse a cabo con mínimos cambios, solo teniendo en cuenta las especiales características de cada uno, que serán explicadas en el siguiente capítulo.

0.1 Propósito de este libro

El propósito de este libro es enseñar a construir Interfaces Gráficas de Usuario (GUI) mediante la herramienta XCODE de Apple.

Existen multitud de libros sobre XCODE y sobre el desarrollo de aplicaciones para IPhone, IPad y IPod. Algunas de ellas han sido seleccionadas en la Bibliografía de este Capítulo de Introducción. Todos ellos tienen información valiosa para los desarrolladores de aplicaciones para estos dispositivos, pero nuestra principal idea al redactar este libro es presentar las herramientas de desarrollo de Apple para el prototipado y la generación de Interfaces de Usuario.

El enfoque seguido en el libro es el de hacer una introducción al entorno XCode y a las principales características del lenguaje Objective-C para inmediatamente proceder a realizar pequeños proyectos de aplicaciones de complejidad creciente que nos irán introduciendo en los conceptos del desarrollo de interfaces de usuario para estos dispositivos. Es el enfoque que siguen los libros [10,11,12] de la bibliografía por lo que se sugiere la continuación del estudio de estos temas mediante estos libros.

Capítulo

0

A I C O N O S

Información importante

Preguntas

Ejercicio propuesto

Revisión

G R U P O C H I C O - U C L M

4

Hay tres libros en castellano [7,8,9] que sirven para profundizar en el desarrollo de aplicaciones mediante XCode, Cocoa y que incluyen un estudio avanzado de Objective-C. También se recomienda su lectura en paralelo con este libro, ya que tratan de forma mas pormenorizada la sintaxis del lenguaje y el framework Cocoa.

El libro puede ser utilizado en clases prácticas de asignaturas como las de Interfaces de Usuario o Interacción Persona – Ordenador. También en las de Introducción a la Programación, siempre que se utilice como lenguaje de programación C u Objective-C.

Este libro se adhiere a la licencia Creative Commons y puede ser reproducido siempre citando la fuente que se ha utilizado y no puede obtenerse beneficio alguno de su distribución.

0.2 Agradecimientos

Para la realización de este libro se ha contado con una ayuda del Departamento de Tecnologías y Sistemas de Información de la Universidad de Castilla – La Mancha (UCLM), de acuerdo a una subvención de la Junta de Comunidades de Castilla – La Mancha (JCCM). A estas tres entidades queremos agradecer la oportunidad de poder redactar esta herramienta que los alumnos de los estudios del Grado de Informática de la UCLM podrán utilizar a partir del curso escolar 2010-2011.

0.3 Bibliografía

1. John Ray y Sean Johnson, Sams teach yourself IPhone Application Development in 24 hours, Sams Publishing, 2010.

2. Apple Inc., Object-Oriented Programming with Objective-C, Apple Developer Publications, 2010.

3. Apple Inc., iOS Application Programming Guide, Apple Developer Publications, 2010.

4. Apple Inc., iOS Technology Overview, Apple Developer Publications, 2010.

5. Apple Inc., The Objective-C Programming Language, Apple Developer Publications, 2010.

6. Dan Pilone y Tracey Pilone, Head First iPhone Development, O’Really, 2010.

7. Bert Altenberg, Alex Clarke y Philippe Mougin, Become an Xcoder, versión 1.15, disponible en http://www.cocoalab.com. Disponible en castellano.

8. Fernando Lopez, El lenguaje Objective-C para programadores C++ y Java, Disponible en http://macprogramadores.org/

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

5

9. Fernando Lopez, Programación Cocoa con Foundation Framework, Disponible en http://macprogramadores.org/

10. David Mark, Joachim Bondo, Owen Goss, Peter Honeder, Ray Kiddy, Steve Finkelstein, Tom Harrington, Jonathan Saggau, Noel Llopis, Joe Pezzillo, Florian Pflug, Dylan Bruzenak, Ben Smith, iPhone advanced Projects, Apress, 2009.

11. Dave Mark y Jeff LaMarche, Beginning iPhone 3 Development: Exploring the iPhone SDK, Apress, 2009.

12. David Barnard, Joachim Bondo, Dan Burcaw, David Kaneda, Craig Kemper, Tim Novikoff, Chris Parrish, Brad Ellis, Keith Peters, Ju�rgen Siebert, Eddie Wilson, iPhone user interface design, Apress, 2009.

1. Dispositivos y programa de desarrolladores de Apple En primer lugar presentamos las principales características de los dispositivos que vamos a usar para el desarrollo de las Interfaces de Usuario (UI). Posteriormente describimos el programa de desarrolladores de Apple que necesitamos conocer para desarrollar aplicaciones en XCode.

1.1 Dispositivos: iPhone, iPad, iPod

Los dispositivos que vamos a utilizar para el desarrollo de interfaces de usuario son verdaderamente máquinas con grandes capacidades. A pesar de su pequeño tamaño, un IPhone es un computador con un sistema operativo capaz, UNIX, con una memoria que haría las delicias de cualquier mini de los años 80 y que en su última versión de sistema operativo permite multitarea en las aplicaciones, aunque de una manera limitada, ya que las restricciones para la conservación de la batería tienen que estar presentes en todo momento.

Un IPhone (3G/3GS) tiene una resolución de 320 x 480 píxeles (Figura 1). Esta restricción es una de las más importantes. Vamos a restringir nuestras interfaces de usuario a esta circunstancia en este libro, aunque el IPad o un IMac, nos permitirían interfaces de usuario más complejas. Un IPhone 4 tiene una resolución de 960 x 640, añadiendo una resolución envidiable que Apple ha denominado “retina display” y que supone para una pantalla semejante a las anteriores elevar a 326 píxeles por pulgada los anteriores 163. En cuanto al IPad, su resolución es de 1024 x 768 píxeles (Figura 2).

Podemos utilizar OpenGL ES para realizar gráficos 2D y 3D, y en el caso de los IPhone 3GS puede usarse OpenGL 2.0 gracias a un chipset 3D que integra este terminal.

El IPhone dispone de procesadores ARM a 412 y 600 MHz y una memoria de 128 Mb y 256 Mb para los modelos 3G y 3GS, respectivamente. Esta es una de las restricciones más importantes a tener en cuenta en el desarrollo de aplicaciones, ya que aunque los dispositivos tienen memorias de 16 y 32 Gb, el IPhone no maneja memoria dinámica.

Capítulo

1

G R U P O C H I C O - U C L M

8

La conectividad va desde la EDGE de más baja velocidad hasta la actual HSDPA 7.2 para acceso de pago y la conectividad WiFi y Bluetooth.

Figura 1. IPhone (Extraído del Simulador de XCode)

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

9

Figura 2. IPad (Extraído del Simulador de XCode)

La característica más importante y lo que le ha convertido en un terminal revolucionario es la capacidad multitouch (usando hasta 4 dedos) de su pantalla. También integra un GPS (3G/3GS/4) y una brújula digital (3GS/4), capacidades que han supuesto su uso como terminal de “bajo coste” en aplicaciones de Realidad Aumentada.

Se añaden a las características de estos terminales un audio de alta calidad y el uso de vibración. Además disponemos de cámaras para realizar fotos de hasta 3 megapíxeles con capacidades para realizar y editar vídeo en las versiones superiores del dispositivo.

Muchas de las características presentadas se encuentran en los terminales iPod y iPad, pero no todas, aunque esto varía con el tiempo y es de prever que el IPad terminará teniendo cámara integrada tanto en el frontal como en el reverso del dispositivo lo que lo convertirá en el mejor instrumento para realizar a bajo coste aplicaciones de Realidad Aumentada.

1.2 Desarrollo de Aplicaciones sobre IPhone

Para desarrollar una aplicación y ejecutarla en el Simulador de IPhone solo es necesario descargar el fichero dmg correspondiente sobre un ordenador Apple con sistema operativo Mac OS X, y con preferencia la versión denominada Snow Leopard (10.6). Si se quieren subir aplicaciones a la tienda ITunes de aplicaciones (App Store) es necesario pagar a Apple 99 $ anuales. La Universidad de Castilla – La Mancha tiene un acuerdo con Apple España para desarrollar aplicaciones y poderlas ejecutar en IPhone de acuerdo a una licencia especialmente destinada a la formación en Universidades. Es de acuerdo a esta licencia como vamos a desarrollar las interfaces de usuario de este curso.

Los pasos pasa desarrollar, probar y distribuir las aplicaciones iPhone OS son los siguientes:

1. Crear un equipo de desarrollo en el iPhone Developer Program (http://developer.apple.com/iPhone/) o solicitar formar parte de alguno ya existente.

2. Solicitar un certificado de desarrollador y que éste sea aceptado por alguno de los administradores del equipo de desarrollo.

3. Agregar dispositivos (IPod Touch, IPhone o IPad) al equipo de desarrollo.

4. Asignar un App ID o identificador de aplicación para cada aplicación que se desarrolle.

G R U P O C H I C O - U C L M

10

5. Crear un perfil en el que se agrupe el certificado de desarrollador, el identificador del dispositivo donde se va a instalar la aplicación y el identificador de la aplicación que se va a probar.

1.2.1 Equipo de desarrollo Dentro del equipo de desarrollo del programa para universidades existen 3 roles posibles:

Member: Perteneciendo a este rol se podrá solicitar el certificado de

desarrollador. Para probar aplicaciones se necesitará descargar un perfil, los cuales sólo pueden ser creados por algún miembro de rango superior.

Admin: Además de los permisos de un member podrá invitar a personas al equipo, aprobar certificados de desarrollador, introducir dispositivos, crear AppID y crear perfiles.

Agent: Además de los permisos de un admin podrá activar un AppID para usar el servicio de notificaciones Push. Es el único rol que tiene contacto directo con el programa de desarrollo de iPhone.

1.2.2 Certificado de desarrollador Para crear un certificado en primer lugar se deben desactivar las opciones OCSP y CRL del llavero del ordenador Mac. Para esto se debe hacer clic en Acceso a llaveros > Preferencias > Certificados (Figura 3).

Figura 3. Configuración de certificados en MacOS X.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

11

A continuación se debe solicitar el certificado, para lo cual es necesario hacer clic en Acceso a llaveros > Asistente para certificados > Solicitar un certificado a una autoridad de certificación (Figura 4).

Figura 4. Solicitud de certificado.

G R U P O C H I C O - U C L M

12

Se escribe el correo electrónico así como el nombre del desarrollador, a continuación se elige la opción Guardar en el disco y se establece el tamaño de la clave en 2048 bits y Algoritmo RSA (Figura 5).

Figura 5. Instalando el certificado en nuestro ordenador.

En el siguiente paso se subirá el certificado al portal de desarrollo de tu equipo y se esperará a que éste sea aceptado por al menos un Admin o por el Agent. La aceptación/rechazo te será comunicada vía correo electrónico.

La reinstalación del SO del Mac puede dar problemas con

el certificado, por lo que es recomendable crear uno nuevo

o exportar el anterior antes de reinstalar.

1.2.3 Agregar dispositivos Se pueden agregar hasta 200 iPod Touch, iPad o iPhone para los que cada uno obtendrá automáticamente un identificador (ID) único.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

13

Figura 6. Añadiendo dispositivos en el portal de Apple Developer.

1.2.4 Identificador de Aplicaciones (AppID) Este identificador es necesario sólo cuando se usen algunas características del sistema operativo del iPhone, como las notificaciones Push o el intercambio de datos entre aplicaciones. El identificador esta formado por dos partes:

(ID generado por apple).(ID generado por el usuario) Existen dos tipos de identificadores:

Wildcard: Permite el intercambio de datos entre las aplicaciones que compartan la parte fija del AppID (antes del asterisco). Ejem: AB76TY.* ó U87UTR.es.uclm.*

Explicit: Permite a la aplicación usar el servicio de notificaciones Push. Ejem: G5R45E.es.uclm.burbuja

G R U P O C H I C O - U C L M

14

Figura 7. Gestión de Identificadores de Aplicaciones (App ID) en el portal de

desarrolladores.

1.2.5 Perfiles

Con el perfil se consigue agrupar todo lo necesario para probar las aplicaciones en un dispositivo real. Al crear el perfil se agrupa el certificado del autor de la aplicación con el identificador del dispositivo en el que vamos a probar y con el identificador de la aplicación que se va a instalar.

Figura 8. Gestión de Perfiles (Provisioning Profiles).

Una vez creado, en el portal de desarrollo del equipo, se descargará el perfil al dispositivo en el se que va a realizar la prueba. Antes de ejecutar la prueba, se debe

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

15

introducir en Xcode el App ID (si el elegido es de tipo Wildcard no será necesario) y seleccionar el dispositivo que está agregado en el perfil.

2. Objective-C y XCode Presentamos de manera sucinta la sintaxis de Objective-C. Además se explica el uso del entorno XCode de manera abreviada con vistas a compilar algunos programas muy simples.

2.1 Generalidades sobre el lenguaje

La sintaxis de Objective C es un superconjunto de C. Por tanto para conocer las principales sentencias de Objective C sobre convenciones en nombres de variables, instrucciones de selección, de control, etc. pueden seguirse cualquiera de los libros de consulta sobre C.

Solo vamos a detallar algunas características que es necesario saber bien porque pertenecen al superconjunto de C que no están en el C estándar o porque es ligeramente diferente del estándar.

Sobre las variables se usa la convención habitual de comenzar con letra minúscula y usar la nomenclatura camelCase. Objective C distingue mayúsculas de minúsculas, por lo que hay que tener cuidado en este sentido. Es mejor por tanto llamar a una variable como anchoRectangulo en la convención camelCase y saber que la variable anchoRECTANGULO es diferente a la anterior.

Los tipos de datos primitivos son los mismos de C, así como las operaciones matemáticas o los comentarios en el programa, que como en otros lenguajes de programación es muy recomendable utilizar.

Para imprimir resultados en la salida estándar podemos utilizar las funciones predeterminadas de C, pero en Objective-C además podemos utilizar funciones del entorno Cocoa como NSLog() que imprime en la consola (Console) del Entorno Integrado de Desarrollo (IDE) XCode. Podemos ver la consola en el menú [Run] (Cmd-Shift-R). Hemos usado la función NSLog que se diseñó para presentar mensajes de error en la consola, pero podemos utilizarla perfectamente para presentar información en la salida estándar, junto a otras funciones más específicas de esta tarea. También podemos hacer uso de printf() como en el siguiente código:

//[programa1] #include <stdio.h> int main() {

Capítulo

2

G R U P O C H I C O - U C L M

18

printf("Hola mundo\n"); return 0; } Al ejecutarlo, se muestra el texto "Hola mundo" en la Consola, que nunca a nadie se le había ocurrido como salida de un programa hasta el nacimiento de este libro. Si queremos utilizar los Strings en Objective-C tenemos que poner el texto entre @" y " (entre las comillas dobles).

La salida completa del compilador en la consola es algo de este estilo:

Figura 9. La Consola de XCode con el resultado de la ejecución del programa 1.

Para probar la función NSLog() vamos a utilizar el siguiente código:

//[programa 2] #import <Foundation/Foundation.h> int main (int argc, const char * argv[]) //línea 3 { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; //línea 5 // insert code here... NSLog(@"Hola mundo"); [pool drain]; //Línea 8 return 0; } NSLog() es parte de una librería denominada Foundation. Para poder utilizarla tenemos que poner la directiva del compilador siguiente:

#import <Foundation/Foundation.h>

Para escribir este programa tienes que hacer los siguientes pasos (Hemos utilizado XCode 3.2.3, puede ser ligeramente diferente en otras versiones):

1. Selecciona un nuevo Proyecto (File>New Project) y asegúrate de elegir “Command Line Tool” y Type “Foundation” (Figura 10).

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

19

2. Tras poner un nombre al programa , en este caso programa2, aparece un archivo denominado programa2.m que es el que albergará nuestro código Objective-C (Figura 11).

3. Abre programa2.m y podrás editar el código y añadir el que hemos propuesto. Verás que parte del código ya lo ha añadido automáticamente XCode, así que completa lo que falte.

4. Abre la Consola con Cmd-Shift-R (⌘R) para ver el resultado de la compilación y ejecución. Si el resultado es Program exited with status value:0. todo ha salido bien.

Figura 10. Elección de un nuevo Proyecto de Línea de Instrucciones.

G R U P O C H I C O - U C L M

20

Figura 11. El IDE XCode con nuestro nuevo programa.

En el programa que acabamos de ver es importante NSAutoreleasePool, así como pool y drain. Con la primera instrucción solicitamos memoria (línea 3) y con la instrucción en la línea 8 la liberamos.

Vamos a introducir un error en nuestro código para ver como aparecen los errores en XCode. Cambiemos el programa 2 eliminando el punto y coma final de la línea 8:

//[programa 3] #import <Foundation/Foundation.h> int main (int argc, const char * argv[]) //línea 3 { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; //línea 5 // insert code here... NSLog(@"Hola mundo"); [pool drain] //Línea 8, LE QUITAMOS el ; return 0; }

Clicamos en el botón de Build and Run:

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

21

Figura 12. Ventana de depuración mostrando el error.

El resultado de la compilación aparece en la fig. 12. En la línea de información, abajo a la derecha se nos informa de que la compilación ha fallado con un error. Claramente advierte de que falta el punto y coma al final de la línea. Para aprender más sobre el depurador recomendamos leer la documentación en línea de que dispone XCode, para ello en el menú Help busca “Debugging in the debugger”. El depurador de XCode es de una potencia extraordinaria por lo que se recomienda conocer en detalle todas sus características. A pesar de esta potencia su estudio en este libro excede los objetivos de un texto de introducción que es lo que presentamos aquí. Solo vamos a explicar algunas técnicas esenciales para el manejo del depurador.

Una de las características más interesantes en un depurador es la posibilidad de insertar puntos de ruptura del flujo de programación (breakpoints) donde podemos parar la ejecución del programa y observar los valores de variables seleccionadas, los resultados intermedios, etc. Para ello hacemos clic con el ratón secundario en el margen gris que está a la izquierda del código (donde aparecen los números de línea) y en la línea en la que queremos que el programa suspenda la ejecución. En ese punto Xcode insertará un "breakpoint" (punto de ruptura), que se representa mediante una flecha azul (Figura 13).

G R U P O C H I C O - U C L M

22

Figura 13. Ventana de depuración con 2 breakpoints.

Hemos colocado dos puntos de ruptura en el código en las líneas 5 y 9. Eliminamos el error en la línea 8 y lanzamos la compilación en este caso no sobre el icono anterior sino sobre el nuevo que aparece denominado "Build and Debug" (Compilar y depurar):

El botón de compilar y depurar.

La ventana del Depurador aparece en la figura 14.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

23

Figura 14. Depurador con la consola y el punto de break en la línea 5.

El Depurador de Xcode permite ejecutar el programa paso a paso y consultar el valor de las variables. El programa se ejecuta hasta que se alcanza el primer breakpoint. Los botones de Restart y Continue nos sirven para volver al principio y por tanto colocar el flujo en el primer breakpoint o para ir al siguiente punto de corte. No necesitamos nada más por el momento para escribir, depurar y ejecutar programas para Mac OS X.

2.2 Sentencias condicionales.

Las sentencias condicionales son igual que en C. Una sentencia if es del tipo:

if (volumen > 50) { NSLog(@"El volumen es mayor que 50."); }

G R U P O C H I C O - U C L M

24

Una sentencia if-else por el contrario tiene esta forma:

if (volumen > 50) { NSLog(@"El volumen es mayor que 50."); } else { NSLog(@"El volumen es menor que 50."); } Los operadores admitidos para comparar números son los siguientes:

== igual a > mayor que < menor que >= mayor o igual que <= menor o igual que != distinto de 2.3 Bucles. Podemos utilizar bucles for cuando conocemos el numero exacto de veces que tenemos que realizar el bucle:

int x; for (x = 1; x <= 5; x++) { NSLog(@"El valor de x es %d", x); }

Y utilizar los bucles while y do-while:

int cont = 1; while (cont <= 10) { NSLog(@"El valor del contador es %d", cont); contador = cont + 1; }

int cont = 1; do { NSLog(@"El valor del contador es %d", cont); contador = cont + 1;} while (cont <= 10);

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

25

3. App1: Linterna Vamos a desarrollar una aplicación que durante algún tiempo tuvo bastantes descargas en la AppStore de Apple: una linterna. Aunque será mucho mejor que la que se podía descargas por 0,79 $.

3.1 Introducción A través de este capitulo se explicarán las nociones básicas para realizar una aplicación sencilla para el iPhone.

En este caso se trata de una linterna, que tendrá un control de intensidad y una opción de auto off, para que se apague automáticamente pasados unos segundos.

Se comenzará explicando todas las tareas necesarias para empezar a programar en Objective-C, los programas imprescindibles y las nociones básicas para llevar a cabo el proyecto. Después se desarrollará la aplicación paso a paso y explicando lo más representativo de cada uno de ellos.

3.2 Instalaciones necesarias

Para la programación del iPhone o del iPod touch es necesaria la descarga del iPhone SDK with Xcode. Esta descarga se realiza a través de la Web del centro de desarrollo de Apple (http://developer.apple.com/iPhone/) para lo cual es necesario formar parte de algún equipo de desarrollo de Apple.

Una vez instalado el Xcode ya podemos empezar a programar nuestra aplicación para el iPhone/iPod Touch.

3.3 Creando el proyecto

Debemos lanzar el Xcode (por defecto se encuentra en /Developer/Applications/Xcode) y elegir la opción File > New Project.

Seleccionamos la opción Windows-based Application (Véase Figura 15). La plantilla Windows-based Application es la plantilla por defecto y está preparada para ser personalizada por el usuario y proporciona una ventana y el controlador de la aplicación. Se podría usar de igual modo la plantilla View-based Application que proporciona algunos elementos más, pero en este tutorial crearemos el resto de elementos que se necesiten para realizar la aplicación.

Capítulo

3

G R U P O C H I C O - U C L M

26

Figura 15: Elección del tipo de proyecto

Una vez seleccionada la plantilla Windows-based Application, se nos muestra una ventana en la que debemos dar nombre a nuestro proyecto, le daremos el nombre de Linterna y elegiremos la ruta donde queramos que se guarde la carpeta que contendrá todos los archivos de nuestro proyecto.

Después de crear el proyecto se nos muestra la pantalla principal de Xcode (Véase Figura 16). Si nunca hemos usado antes Xcode, dedicaremos el tiempo que consideremos necesario para explorar un poco la aplicación.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

27

Figura 16: Vista inicial del proyecto

En la parte superior izquierda podemos elegir el tipo de dispositivo en el cual queremos ejecutar nuestra aplicación. Por defecto aparece iPhone Simulator que es el que utilizaremos, si queremos probar la aplicación en un dispositivo real necesitaremos seguir los pasos marcados en el Apartado 3.7.

En la parte izquierda tenemos agrupados todos los archivos que forman parte de nuestro proyecto. Al hacer clic en cada uno de ellos se mostrará su contenido en la parte derecha, mientras que con doble clic se mostrará su contenido en una nueva ventana.

Ahora vamos a añadir la clase controladora. Esta será la responsable de mostrar, definir y controlar los elementos de la interfaz. Para añadirla pulsamos File > New File y dentro de Cocoa Touch Class seleccionamos UIViewController subClass (Véase Figura 17). Hay que asegurarse de que no esta seleccionada la opción With XIB for user interface. Le daremos el nombre de LinternaViewController y crearemos también la cabecera (Also create “LinternaViewController.h).

Al hacer clic en File > New File los archivos se crearán en la carpeta que tengamos seleccionada, por lo que deberíamos tener seleccionada previamente la carpeta Classes.

G R U P O C H I C O - U C L M

28

Figura 17: Añadiendo la clase controladora de la interfaz

El archivo MainWindow.xib (dentro de Resources) será el que defina la vista de nuestro controlador. Podríamos crear nuestro propio fichero xib, pero aumentaría la complejidad de la aplicación.

Los archivos xib, antiguamente nib, son ficheros que contienen una descripción de los componentes de la interfaz gráfica, se cargan al arrancar la aplicación y los usa Cocoa (API de programación de Mac OS X) para dibujar la ventana y sus componentes en pantalla. Se trata de ficheros XML de acuerdo a las tendencias actuales de separación de la Interfaz de Usuario y la lógica de la aplicación y a las posibilidades que las técnicas de Model-Based User Interface Design añaden a la generación automática de interfaces de usuario.

Una vez creados mediante el paso anterior los archivos LinternaViewController.h, LinternaViewController.m y teniendo MainWindow.xib vamos a añadir los principales métodos y variables a la cabecera. Para lo cual hacemos clic en LinternaViewController.h y añadimos:

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

29

#import <UIKit/UIKit.h> @interface LinternaViewController : UIViewController { IBOutlet UILabel*autooff; IBOutlet UILabel*segto; IBOutlet UIProgressView * progreso; } - (IBAction)sliderChanged:(id)sender; - (IBAction)segmentedChanged:(id)sender; - (void)contadorTiempo: (NSDecimalNumber *)intervalo; - (void)actualizarPorcentaje: (NSDecimalNumber *) porcentaje; @end

En este código se define la interfaz LinternaViewController que deriva de UIViewController, y a su vez se definen las variables necesarias. Entre estas, se encuentra la variable progreso que es de tipo UIProgressView y que nos mostrará el tiempo que resta para que se cierre la aplicación. Autooff y segto son dos etiquetas de texto. IBOutlet indica al compilador que ese elemento se enlazará con la herramienta Interface Builder, por lo que posteriormente habrá que conectar la variables con los objetos que añadamos en la interfaz mediante la herramienta Interface Builder.

El método sliderChanged servirá para ajustar la intensidad de la linterna. Cada vez que se modifique la posición del slider este método tomará el control. Cuando pulsemos algún segmento será el método segmentedChanged el que tomará el control y lanzará un hilo para programar el cierre de la aplicación. Los dos métodos restantes son dos métodos de apoyo que sirven para actualizar la barra de progreso y para controlar el tiempo restante hasta el cierre programado de la aplicación. IBAction indica al compilador que el método se lanzará con una acción de la herramienta Interface Builder, por ejemplo, pulsando un botón en la interfaz.

3.4 Desarrollando la interfaz

Antes de seguir debemos guardar nuestro proyecto (File > Save). Para comenzar con el desarrollo de la interfaz necesitaremos el Interface Builder. Este se puede abrir directamente desde nuestro proyecto en Xcode, para lo cual, haremos doble clic en el archivo MainWindow.xib.

Nos aparece la interfaz vacía y una ventana que muestra los elementos del archivo MainWindow.xib. Para añadir los elementos necesarios en la interfaz haremos clic en Tools > Library. En primer lugar arrastramos desde la librería Cocoa Touch – Controllers un objeto UIViewController a MainWindow.xib.

Debemos fijarnos en el objeto View Controller que acabamos de añadir, que será el responsable de controlar la vista. Para esto, debemos hacer clic sobre este y en Identity (pulsar Tools > Inspector) seleccionar nuestra clase LinternaViewController, que es la que va a controlar nuestra interfaz (Véase la Figura 18).

G R U P O C H I C O - U C L M

30

Figura 18: Contenido del archivo MainWindow.xib

Ahora tenemos que arrastrar a la ventana de la interfaz (doble clic sobre Window dentro de MainWindow.xib si no aparece) las dos etiquetas (UILabel) necesarias, una barra de segmentos (UISegmentedControl), una barra de progreso (UIProgressView) y un slider (UISlider). Todos se encuentran en Library - Cocoa Touch – Inputs and Values. Nos debe quedar algo similar a lo mostrado en la Figura 19.

Haciendo doble clic en cada etiqueta de texto podremos poner el texto que queramos dentro de esta.

Debemos pulsar Tools > Inspector y hacer clic en la barra de segmentos, para que quede correctamente configurado, dentro de Attributes el estilo debe ser Bar, tiene que tener 4 segmentos con los siguientes nombres: 10, 30, 60 y 120. Es muy importante que ninguno de ellos esté seleccionado de antemano, todos deben tener desmarcada la opción selected.

A continuación hacemos clic en el slider, establecemos el valor mínimo a 180, el máximo a 255 y el inicial también a 255. En la barra de progreso debemos seleccionar la opción Hidden y establecer el valor de progreso a 0.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

31

Figura 19: Interfaz de la linterna

3.5 Añadiendo las conexiones

Ya tenemos la interfaz en bruto, pero no está relacionada/conectada con ninguno de los métodos o variables que hemos definido en nuestra cabecera LinternaViewController.h.

A continuación vamos a conectar la vista del controlador Linterna View Controller con la interfaz que hemos desarrollado, con esto será nuestra clase LinternaViewController la que manejará los eventos producidos por la interfaz. Para manejar los eventos debemos hacer clic en el objeto Linterna View Controller, seleccionar connections y crear una conexión desde Outlet - view hasta Window de la ventana MainWindow.xib, como muestra la Figura 20.

G R U P O C H I C O - U C L M

32

Figura 20: Conexión entre la interfaz y el controlador

A continuación debemos crear una conexión entre las etiquetas de texto y las variables con su mismo nombre. Para esto haremos de nuevo clic sobre Linterna View Controller y en connections crearemos una conexión entre segto y Label (seg to) y entre autooff y Label (Auto OFF) como muestra la Figura 21. Para mostrar Label hay que desplegar el contenido de View dentro de MainWindow.xib.

Figura 21: Conexión entre autooff y Label (Auto OFF)

Nos quedan por conectar el slider, la barra de segmentos y la barra de progreso. Para este último debemos hacer doble clic de nuevo en el objeto Linterna View Controller y crear una conexión entre progreso y Progress View.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

33

Posteriormente hay que crear una conexión entre segmentedChanged y Bar Segmented Control (se encuentra dentro de Received Actions), de tipo Touch up Inside. Lo mismo para sliderChanged.

Ya tenemos conectado nuestro controlador Linterna con la vista de la interfaz, con las etiquetas, el slider, la barra de segmentos y la barra de progreso. Debemos guardar el archivo MainWindow.xib (File > Save) y ya podemos cerrar el Interface Builder.

3.6 Desarrollando el código

Debemos dotar de contenido a los métodos declarados en la cabecera, para lo cual añadiremos a LinternaViewController.m lo siguente:

- (IBAction)sliderChanged:(id)sender{ UISlider *slider = (UISlider *)sender; [[self view] setBackgroundColor:[UIColor colorWithRed:((int)(slider.value))/255.0 green:((int)(slider.value))/255.0 blue:((int)(slider.value))/255.0 alpha:1.0f]]; } - (IBAction)segmentedChanged:(id)sender{ [progreso setHidden:NO]; [segto setHidden:YES]; UISegmentedControl *segmen = (UISegmentedControl *)sender; [autooff setTextColor: [UIColor redColor]]; switch (segmen.selectedSegmentIndex) { case 0: [NSThread detachNewThreadSelector:@selector(contadorTiempo:) toTarget:self withObject:[NSDecimalNumber numberWithFloat: 0.1]]; break; case 1: [NSThread detachNewThreadSelector:@selector(contadorTiempo:) toTarget:self withObject:[NSDecimalNumber numberWithFloat: 0.3]]; break; case 2: [NSThread detachNewThreadSelector:@selector(contadorTiempo:) toTarget:self withObject:[NSDecimalNumber numberWithFloat: 0.6]]; break; case 3: [NSThread detachNewThreadSelector:@selector(contadorTiempo:) toTarget:self withObject:[NSDecimalNumber numberWithFloat: 1.2]]; break; } [segmen setHidden:YES]; } - (void) actualizarPorcentaje: (NSDecimalNumber *) porcentaje{ progreso.progress=[porcentaje floatValue]/100.0; } - (void) contadorTiempo: (NSDecimalNumber *) intervalo{ NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init]; for(int i=1; i<=100; i++){ [self performSelectorOnMainThread:@selector(actualizarPorcentaje:) withObject:[NSDecimalNumber numberWithInt: i] waitUntilDone:false]; [NSThread sleepForTimeInterval:[intervalo floatValue]]; } exit(0); [apool release];

G R U P O C H I C O - U C L M

34

} - (void)dealloc { [autooff release]; [segto release]; [progreso release]; [super dealloc]; }

El método sliderChanged será llamado cada vez que movamos el valor del slider y lo que hará es actualizar el color de fondo de pantalla, desde un blanco grisáceo hasta el blanco más brillante, que será el que más luz aporte.

El método segmentedChanged será llamado cuando hagamos clic en algún segmento que representa un intervalo de tiempo. Este abrirá un hilo que llamará al método contadorTiempo con un valor determinado. También se ocultará la barra de segmentos y la etiqueta segto, a la vez que se cambia el color de letra de la etiqueta autoOff. El motivo de usar un hilo es que gracias a este cuando activemos el temporizador podremos seguir regulando la intensidad y visualizando los cambios en la barra con el contador de tiempo. Sin el hilo, no se podría cambiar el valor de ningún objeto de la pantalla, ya que el programa se quedaría congelado hasta que terminase el bucle.

El método contadorTiempo se encargará de que la aplicación se apague cuando pase un intervalo concreto de tiempo y de llamar a actualizarPorcentaje que cambiará el valor de la barra que muestra el tiempo que falta para que la aplicación se cierre de manera automática. Tiene un bucle con 100 operaciones, en cada iteración se queda “dormido” un intervalo de tiempo que depende de la opción pulsada en la barra de segmentos. Además en cada iteración se actualiza la barra de progreso del contador de tiempo. Se han elegido 100 iteraciones para que los incrementos del valor de la barra de progreso sean lo más suaves posibles.

En dealloc debemos añadir las variables creadas para que las libere de la memoria.

3.7 Probando la aplicación en un iPhone Para probar la aplicación en un iPhone o en un iPod touch será necesario seguir esta serie de pasos:

1. Crear un equipo de desarrollo en el iPhone Developer Program o solicitar formar parte de alguno ya existente.

2. Solicitar un certificado de desarrollador. Para lo cual se debe crear un certificado en el llavero del Mac (Asistente para Certificados > Solicitar un certificado de una autoridad de certificación), guardarlo en el disco duro y posteriormente subirlo al portal de desarrollo del equipo. Este tendrá que ser aceptado por alguno de los administrares del equipo de desarrollo.

3. Agregar dispositivos (iPod touch o iPhone) al equipo de desarrollo, cada uno obtendrá un identificador único.

4. Asignar un App ID o identificador de aplicación para cada aplicación que se desarrolle. Únicamente será necesario en aquellas aplicaciones que usen características del sistema

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

35

operativo del iPhone, como las notificaciones Push o el intercambio de datos entre aplicaciones.

5. Crear un perfil en el que se agrupe el certificado de desarrollador, el identificador del dispositivo donde se va a instalar la aplicación y un identificador de aplicación válido. Este perfil se descargará al dispositivo en el que se vaya a probar la aplicación.

G R U P O C H I C O - U C L M

38

4. App2: Calculadora En este capitulo implementamos una calculadora. Si buscamos en la Appstore veremos que hay cientos de aplicaciones del mismo tipo que se venden entre 0 y 12,99 euros. Veremos que no es tan difícil hacer una por nosotros mismos.

4.1 Introducción

Vamos a implementar una calculadora formada por los diez dígitos; las operaciones sumar, restar, multiplicar, dividir y resultado; y la opción de cancelar la operación actual.

Debemos lanzar el Xcode (por defecto se encuentra en /Developer/Applications/Xcode) y elegir la opción File > New Project.

Seleccionamos la opción Windows-based Application (Véase Figura 22). La plantilla Windows-based Application es la plantilla por defecto, está preparada para ser personalizada por el usuario y proporciona una ventana y el controlador de la aplicación. Se podría usar de igual modo la plantilla View-based Application que proporciona algunos elementos más, pero en este tutorial crearemos el resto de elementos que se necesiten para realizar la aplicación.

Una vez seleccionada la plantilla Windows-based Application, se nos muestra una ventana en la que debemos dar nombre a nuestro proyecto, le daremos el nombre de Calculadora y elegiremos la ruta donde queremos que se guarde la carpeta que contendrá todos los archivos de nuestro proyecto.

Después de crear el proyecto se nos muestra la pantalla principal de Xcode (Véase Figura 23). Si nunca hemos usado antes Xcode, dedicaremos el tiempo que consideremos necesario para explorar un poco la aplicación.

Capítulo

4

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

39

Figura 22: Eleccion del tipo de proyecto

Figura 23: Vista inicial del proyecto

G R U P O C H I C O - U C L M

40

En la parte superior izquierda podemos elegir el tipo de dispositivo en el cual queremos ejecutar nuestra aplicación, por defecto viene iPhone Simulator que es el que utilizaremos, si queremos probar la aplicación en un dispositivo real necesitaremos seguir los pasos marcados en el Apartado 9.

En la parte izquierda tenemos agrupados todos los archivos que forman parte de nuestro proyecto, al hacer clic en cada uno de ellos se mostrará su contenido en la parte derecha, con doble clic se mostrará su contenido en una nueva ventana.

Ahora vamos a añadir un View Controller, este será el responsable de mostrar, definir y controlar los elementos de la interfaz. Para añadirlo pulsamos File > New File y dentro de Cocoa Touch Class seleccionamos UIViewController subClass (Véase Figura 24). Debe estar seleccionada la opción With XIB for user interface. Le daremos el nombre de Calcu y crearemos también la cabecera (Also create “Calcu.h”).

Al hacer clic en File > New File los archivos se crearán en la carpeta que tengamos seleccionada, por lo que deberíamos tener seleccionada previamente la carpeta Classes.

Figura 24: Añadiendo el controlador de la interfaz

El archivo Calcu.xib será el que defina la vista de nuestro controlador.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

41

Los archivos xib, antiguamente nib, son ficheros que contienen una descripción de los componentes de la interfaz gráfica, se cargan al arrancar la aplicación y los usa Cocoa (API de programación de Mac OS X) para pintar la ventana y sus componentes en pantalla.

Una vez creados mediante el paso anterior los tres archivos (Calcu.h, Calcu.m y Calcu.xib) vamos a añadir los principales métodos y variables a la cabecera. Para lo cual hacemos clic en Calcu.h y añadimos:

#import <UIKit/UIKit.h> @interface Calcu : UIViewController { float resultado; int operacionActual; float numeroActual; IBOutlet UILabel *pantallaCalculadora; BOOL continuarOperacion; } -(IBAction)cancelarOperacion; -(IBAction)botonNumeroPresionado:(id)sender; -(IBAction)botonOperacionPresionado:(id)sender; @end

En este código se define la interfaz Calcu que deriva de UIViewController, a su vez se definen las variables necesarias. Entre estas, se encuentra la variable pantallaCalculadora que muestra el resultado de la operación o el último número introducido. IBOutlet indica al compilador que ese elemento se enlazará con la herramienta Interface Builder, por lo que posteriormente habrá que conectar la variable con la etiqueta de texto que añadamos en la interfaz.

El método cancelarOperacion servirá para borrar el contenido de la pantalla (el botón AC de la calculadora). Los dos métodos restantes se lanzarán cuando se presione en la calculadora un dígito o una operación. IBAction indica al compilador que el método se lanzará con una acción de la herramienta Interface Builder, por ejemplo, pulsando un botón en la interfaz.

4.4 Desarrollando la interfaz

Antes de seguir debemos guardar nuestro proyecto (File > Save). Para comenzar con el desarrollo de la interfaz necesitaremos el Interface Builder. Este se puede abrir directamente desde nuestro proyecto en Xcode, para lo cual, haremos doble clic en el archivo Calcu.xib.

G R U P O C H I C O - U C L M

42

Figura 25. Contenido del archivo Calcu.xib

Nos aparece la interfaz vacía y una ventana (Véase Figura 25) que muestra los elementos del archivo Calcu.xib.

Para añadir los elementos necesarios en la interfaz haremos clic en Tools > Library y arrastramos los botones (Round Rect Button) necesarios y la etiqueta (Label) que mostrará el resultado de las operaciones o los dígitos marcados, ambos se encuentran en Library - Cocoa Touch – Inputs and Values.

Haciendo doble clic en cada botón podremos poner el texto que queramos dentro de este. Nos debe quedar algo similar a lo mostrado en la Figura 26, la colocación de los botones y de la etiqueta, es indiferente.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

43

Figura 26. Interfaz de la calculadora

Un dato a tener en cuenta es que el texto que le hemos añadido a cada botón no lo identifica, es decir, por ahora no podríamos saber que botón se ha pulsado ya que no tienen ningún identificador individual. Para solucionar esto vamos a añadir un identificador a cada uno de los botones. Debemos pulsar Tools > Inspector, ahora hacemos clic en cada uno de los botones y en Attributes tenemos la etiqueta (Tag) en la que debemos introducir el identificador de cada botón.

Para los dígitos, hacemos clic en cada uno de ellos y ponemos el identificador igual al número que representan (tag=1 para el número 1, tag=2 para el 2…). Para las operaciones: 1 para la suma, 2 para la resta, 3 para la multiplicación, 4 para la división y 5 para el igual.

Para el botón AC, no es necesaria ninguna etiqueta porque será una conexión única con su método, no cómo el caso de los dígitos que van conectados los diez botones al mismo método (botonNumeroPresionado), por lo que necesitamos poder saber cual es el botón que se ha pulsado concretamente.

4.5 Añadiendo las conexiones

Ya tenemos la interfaz en bruto, pero no está relacionada/conectada con ninguno de los métodos o variables que hemos definido en nuestra cabecera Calcu.h.

G R U P O C H I C O - U C L M

44

Debemos fijarnos en el objeto File´s Owner que será el responsable de controlar la vista. Para esto, debemos hacer doble clic sobre este y en Identity seleccionar nuestra clase Calcu, que es la que va a controlar nuestra interfaz.

A continuación vamos a conectar la vista del controlador Calcu con la interfaz que hemos desarrollado, con esto será nuestra clase Calcu la que manejará los eventos producidos por la interfaz. Para esto debemos hacer doble clic en el objeto File´s Owner, seleccionar connections y crear una conexión (si no está creada) desde Outlet - view hasta el View de la ventana Calcu.xib, como muestra la Figura 27.

Figura 27. Conexión entre la interfaz y el controlador Calcu

A continuación debemos crear una conexión entre la etiqueta que mostrará los resultados y la variable pantallaCalculadora que hemos creado anteriormente. Para esto haremos de nuevo doble clic sobre File´s Owner y en connections crearemos una conexión entre pantallaCalculadora y Label (Véase Figura 28). Para mostrar Label hay que desplegar el contenido de View dentro de Calcu.xib.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

45

Figura 28. Conexión entre pantallaCalculadora y la etiqueta de la interfaz

Nos quedan por conectar todos los botones, las operaciones, los dígitos y la operación AC. Para esto debemos hacer doble clic de nuevo en el objeto File´s Owner e ir creando conexiones de tipo Touch up Inside entre botonNumeroPresionado y cada uno de los dígitos (Véase Figura 29). Luego haremos lo mismo entre botonOperacionPresionado y cada uno de los botones de operación.

G R U P O C H I C O - U C L M

46

Figura 29. Conexión entre el método botonNumeroPresionado y el Button 1

Por ultimo, relacionamos el método cancelarOperacion con el botón AC.

Ya tenemos conectado nuestro controlador Calcu con la vista de la interfaz, con la etiqueta que muestra el resultado y con todos los botones. Debemos guardar el archivo Calcu.xib (File > Save) y ya podemos cerrar el Interface Builder.

4.6 Mejorando la interfaz

No es muy difícil darle un toque de color y mejorar el diseño de nuestra interfaz. Aquí vamos a mostrar un ejemplo de cómo podría quedar la interfaz aplicando un par de mejoras.

Empezamos diseñando nuestros propios botones en alguna herramienta de diseño gráfico (GIMP, Seashore, PhotoShop, Paint, etc). Una vez que tengamos las imágenes correspondientes a cada botón/operación debemos agregarlas a nuestro proyecto, para lo cual crearemos una carpeta en Xcode, haciendo clic en Proyect > New Group, a la que llamaremos botones.

Haciendo clic derecho sobre la nueva carpeta y pulsando Add > Existing Files… podremos adjuntar a nuestro proyecto las imágenes que necesitemos.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

47

Ahora, de nuevo en el Interface Builder, haremos doble clic sobre cada botón, y en Background seleccionaremos la imagen que le corresponda. La agregamos a Background y no a Image porque queremos añadir un efecto de resaltado al pulsar cada botón, si estuviese en Image no sería visible. Para esto, debemos seleccionar la opción Shows Touch On Highlight, dentro de los atributos del botón.

Para poner el fondo de pantalla negro, pulsaremos este y en Background seleccionamos el color negro. Lo mismo haremos para cambiar el color de la etiqueta que muestra el resultado. Con estos cambios podríamos obtener una interfaz similar a la que se muestra continuación.

Figura 30: Interfaz mejorada

4.7 Desarrollando el código

Para que al ejecutar nuestro proyecto se lance la interfaz creada debemos añadir el siguiente código en los archivos AppDelegate, estos se encargan de la gestión de memoria del programa y de lanzar el controlador de nuestra interfaz.

En CalculadoraAppDelegate.h debemos añadir:

#import "Calcu.h"

G R U P O C H I C O - U C L M

48

@class Calcu; En CalculadoraAppDelegate.m, dentro del método applicationDidFinishLaunching, debemos añadir:

Calcu *viewController = [[Calcu alloc] initWithNibName:@"Calcu" bundle:[NSBundle mainBundle]]; [window addSubview:[viewController view]]; Por último nos falta dotar de contenido a los métodos declarados en la cabecera, para lo cual añadiremos a Calcu.m lo siguiente:

-(IBAction)botonNumeroPresionado:(id)sender { numeroActual = numeroActual*10 + (float)[sender tag]; pantallaCalculadora.text = [NSString stringWithFormat:@"%.3f",numeroActual]; continuarOperacion=FALSE; } -(IBAction)cancelarOperacion { numeroActual = 0; pantallaCalculadora.text = @"0"; operacionActual = 0; } -(IBAction)botonOperacionPresionado:(id)sender { if (operacionActual == 0){ resultado = numeroActual; } else { switch (operacionActual) { case 1: resultado = resultado + numeroActual; break; case 2: resultado = resultado - numeroActual; break; case 3: resultado = resultado * numeroActual; break; case 4: resultado = resultado / numeroActual; break; case 5: if(continuarOperacion) numeroActual = resultado; else resultado = numeroActual; break; } } continuarOperacion=TRUE;

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

49

numeroActual = 0; pantallaCalculadora.text = [NSString stringWithFormat:@"%.3f",resultado]; operacionActual = [sender tag]; } La variable continuarOperacion se usa para distinguir si el usuario de la calculadora después de realizar una operación usa el resultado como primer operando de la siguiente, o por el contrario inicia una nueva operación.

El método botonNumeroPresionado será llamado cada vez que pulsemos algún botón de los que hemos conectado anteriormente con este y se le pasará el identificador del botón (lo que hayamos escrito en Tag). Por lo tanto, lo mostrará en pantalla y lo guardará en la variable numeroActual. La multiplicación por 10 es necesaria para que al pulsar varios números seguidos los concatene, por ejemplo, si pulsamos 1 y luego 2, debe escribir 12 = 1*10 + 2.

El método botonOperacionPresionado será llamado al pulsar algún botón operación y realizará esta.

Esta calculadora escribirá todos los resultados con 3 decimales, por lo que de aquí podemos sacar una futura mejora de la aplicación.

4.8 Posibles mejoras

- Añadir un icono a la aplicación

- Manejo óptimo de la memoria

- Eliminar decimales cuando no sean necesarios

- Controlar excepciones matemáticas

- Introducir nuevas operaciones

4.9 Probando la aplicación en un iPhone Para probar la aplicación en un iPhone o en un iPod touch será necesario seguir esta serie de pasos:

1. Crear un equipo de desarrollo en el iPhone Developer Program o solicitar formar parte de alguno ya existente.

2. Solicitar un certificado de desarrollador. Para lo cual se debe crear un certificado en el llavero del Mac (Asistente para Certificados > Solicitar un certificado de una autoridad de certificación), guardarlo en el disco duro y posteriormente subirlo al portal de desarrollo del equipo. Este tendrá que ser aceptado por alguno de los administrares del equipo de desarrollo.

G R U P O C H I C O - U C L M

50

3. Agregar dispositivos (iPod touch o iPhone) al equipo de desarrollo, cada uno obtendrá un identificador único.

4. Asignar un App ID o identificador de aplicación para cada aplicación que se desarrolle. Únicamente será necesario en aquellas aplicaciones que usen características del sistema operativo del iPhone, como las notificaciones Push o el intercambio de datos entre aplicaciones.

5. Crear un perfil en el que se agrupe el certificado de desarrollador, el identificador del dispositivo donde se va a instalar la aplicación y un identificador de aplicación válido. Este perfil se descargará al dispositivo en el que se vaya a probar la aplicación.

5. App3: Lista de la compra En este capitulo implementamos una lista de la compra. Es una aplicación compleja que ya implica el uso de gran parte de las posibilidades que tienen los dispositivos de Apple.

5.1 Introducción En este capitulo tratamos una aplicación que da soporte a una lista de la compra en la que se pueden añadir productos desde el iPhone o desde una base de datos externa. Cuando se visualice el carro de la compra se añadirán a este los productos que estén seleccionados en la lista. Al cerrar la aplicación, se actualizará la base de datos, borrando los productos que ya hayan sido comprados y añadiendo los nuevos.

Además de esto, con esta aplicación se explicará al lector el uso de algunas animaciones básicas, el trabajo con tipos de letra no incluidos en el iPhone, la multiselección de elementos de una lista, el control del UITabBarController y UIPickerView, etc.

Se comenzará explicando brevemente todas las tareas necesarias para programar en Objective-C, los programas y las nociones básicas para llevar a cabo el proyecto. Después se desarrollará la aplicación paso a paso y explicando lo más representativo de cada uno de ellos.

5.2 Creando el proyecto

Debemos lanzar el Xcode (por defecto se encuentra en /Developer/Applications/Xcode) y elegir la opción File > New Project.

Seleccionamos la opción Windows-based Application (Véase la Figura 31). La plantilla Windows-based Application es la plantilla por defecto, está preparada para ser personalizada por el usuario y proporciona una ventana y el controlador de la aplicación. Se podría usar de igual modo la plantilla View-based Application que proporciona algunos elementos más, pero en este tutorial crearemos el resto de elementos que se necesiten para realizar la aplicación.

Capítulo

5

G R U P O C H I C O - U C L M

52

Figura 31. Elección del tipo de proyecto

Una vez seleccionada la plantilla Windows-based Application, se muestra una ventana en la que debemos dar nombre a nuestro proyecto, le daremos el nombre de ListaCompra y elegiremos la ruta donde queramos que se guarde la carpeta que contendrá todos los archivos de nuestro proyecto.

A continuación vamos a añadir la clase Producto junto a su cabecera, la cual contendrá los siguientes métodos y variables:

Producto.h // // Producto.h // ListaCompra // // Created by Miguel on 26/02/10. // Copyright 2010 chico. All rights reserved. // #import <UIKit/UIKit.h> #import <sqlite3.h> #import "ListaCompraAppDelegate.h"

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

53

@interface Producto : NSObject { BOOL estaSeleccionado; NSInteger productoID; NSString *productoName; } @property BOOL estaSeleccionado; @property (nonatomic, readonly) NSInteger productoID; @property (nonatomic, copy) NSString *productoName; + (void) getInitialDataToDisplay:(NSString *)dbPath; + (void) finalizeStatements; - (id) initWithPrimaryKey:(NSInteger)pk; - (void) deleteProducto; - (void) addProducto; - (void) saveAllData; @end

Con esta clase conseguiremos crear objetos de tipo Producto, estos se crearán leyendo la base de datos o introduciendo el producto desde la propia aplicación. Estos objetos tendrán un identificador único, un nombre y un valor booleano que nos marcará si está seleccionado en la lista de productos.

Los métodos definidos en la clase Producto servirán para obtener la lista inicial de productos a mostrar, cerrar las distintas sentencias SQL, asignar a cada producto un primary key, borrar/añadir productos en la base de datos y para guardar los cambios efectuados. Con esta clase obtendremos un array de productos en el caso de que haya productos almacenados en la base de datos, en caso contrario, estará vacía.

El siguiente paso será la creación de la base de datos que almacenará los productos de nuestra lista de la compra. En este caso estará formada por un campo INTEGER llamado ID y por un campo VARCHAR llamado Producto (Véase la Figura 32). No será necesario el campo booleano para saber si un producto está o no seleccionado, ya que inicialmente no hay ningún elemento marcado en la lista y cuando se cierre la aplicación (que será cuando actualicemos de nuevo la base de datos) los campos que estén marcados no se guardaran en la base de datos, porque ya son productos comprados y deben ser borrados de la lista de productos a comprar.

Figura 32. Estructura de la tabla vista desde SQLite Manager

G R U P O C H I C O - U C L M

54

Para la creación de esta base de datos sencilla se recomienda el uso del plugin de FireFox, SQLite Manager. La base de datos se llamará Compras.sqlite y se deberá adjuntar al proyecto. Para crearla hay que ejecutar la siguiente sentencia SQL:

CREATE TABLE "listaCompra" ("ID" INTEGER PRIMARY KEY

NOT NULL ,"Producto" VARCHAR)

Posteriormente es necesario añadir al proyecto el Framework libsqlite3.0.dylib. La base de datos añadida al proyecto será copiada al dispositivo, por lo tanto tendremos una base de datos interna, para poder seguir guardando los productos una vez esté instalada la aplicación en el dispositivo.

A continuación vamos a añadir los métodos y variables necesarios a la clase ListaCompraAppDelegate y a su cabecera:

ListaCompraAppDelegate.h

// // ListaCompraAppDelegate.h // ListaCompra // // Created by Miguel on 22/02/10. // Copyright chico 2010. All rights reserved. // #import <UIKit/UIKit.h> @class Producto; @interface ListaCompraAppDelegate : NSObject <UIApplicationDelegate> { UIWindow *window; IBOutlet UITabBarController *rootController; IBOutlet UILabel * cargando; IBOutlet UIActivityIndicatorView * indicador; // Lista con los productos NSMutableArray *productoArray; // Variable que indica que los productos seleccionados se han añadido al carro // y por lo tanto los debemos borrar de la lista. BOOL borrarSeleccionados; NSTimer *tiempo; } @property(nonatomic,retain) NSTimer *tiempo; @property (nonatomic, retain) IBOutlet UILabel * cargando; @property (nonatomic, retain) IBOutlet UIActivityIndicatorView * indicador; @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) UITabBarController *rootController; @property (nonatomic, retain) NSMutableArray *productoArray; @property BOOL borrarSeleccionados;

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

55

- (void) removeProducto:(Producto *)productoObj; - (void) addProducto:(Producto *)productoObj; - (void) copyDatabaseIfNeeded; - (void) quitarCargando; - (void)cargarVista; - (NSString *) getDBPath; @end

Como podemos ver, tendremos un controlador de tipo UITabBarController (Véase la Figura 33) que será el controlador principal de nuestra aplicación. Este será el encargado de llamar a cada una de las tres vistas distintas.

Figura 33. Vistas controladas desde el UITabBarController

Las variables cargando e indicador, sirven para mostrar al usuario que la aplicación se encuentra en proceso de carga (Véase la Figura 34). La visibilidad de estas variables será desactivada una vez iniciada la aplicación. Esto se consigue con la llamada al método quitarCargando.

G R U P O C H I C O - U C L M

56

Figura 34. Animación del proceso de carga

También es necesario un array de productos, este nos servirá para rellenar la tabla de productos en tiempo de ejecución. Desde esta clase se llamará al método getInitialDataToDisplay de la clase Producto que cargará el contenido correspondiente en el array. La variable booleana borrarSeleccionados nos indicará si hemos pulsado en la vista Ver Carro, por lo que se deben borrar de la lista los elementos marcados puesto que ya han sido comprados.

En los métodos addProducto y removeProducto será necesario añadir/borrar el producto del array y de la base de datos, para esto llamaremos a los métodos correspondientes de la clase Producto. Los métodos fadeScreen y finishedFading son necesarios para reproducir el efecto que permite que la aplicación vaya apareciendo lentamente al iniciarse.

En este punto del desarrollo de la aplicación nos falta crear las clases controladoras de las tres vistas que posee nuestra interfaz: la lista de la compra con la opción de borrar, la vista para añadir nuevos productos y por último la vista que nos permite ver los productos que tenemos añadidos al carro, es decir los que han sido seleccionados en la lista de la compra.

5.3 Desarrollando las interfaces

Antes de continuar, debemos guardar nuestro proyecto (File > Save). Para comenzar con el desarrollo de las interfaces necesitaremos también el programa Interface Builder.

5.3.1. MainWindow

El Interface Builder se puede abrir directamente desde nuestro proyecto en Xcode, para lo cual, haremos doble clic en el archivo MainWindow.xib.

Nos aparece la interfaz vacía y una ventana que muestra los elementos del archivo MainWindow.xib. En primer lugar arrastramos desde la librería un objeto de tipo UITabBarController a la ventana de MainWindow.xib.

Dentro de la ventana Window, añadimos un UIImageView, un UILabel y un UIActivityIndicatorView. Está ventana será la primera que cargará la aplicación, y por tanto, donde se mostrará al usuario que se está cargando la aplicación. Una vez cargada la aplicación esta ventana se quedará en segundo plano (de fondo). Usaremos un grado del 95% de transparencia en las demás interfaces, ya que al ir cargadas sobre la ventana Window se verá levemente la imagen principal de esta (Véase Figura 5) como parte del fondo de la aplicación.

Dentro del UITabBarController debemos añadir los 3 UITabBarItem con sus imágenes correspondientes. Luego debemos asignar un NIB Name a cada UITabBarItem, que se

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

57

corresponderá con cada una de las 3 vistas que controla el UITabBarControler. Finalmente deberá quedar como muestra la Figura 35.

Figura 35. MainWindow.xib

5.3.2. CompraViewController

La primera vista que vamos a crear, junto a su controlador, será CompraViewController, esta mostrará la lista con todos los productos, añadirá una marca a cada producto que seleccionemos y permitirá borrar toda la tabla mediante un botón.

Para crearla, dentro de Xcode, hacemos clic en File > New File > UIViewController subClass. Se nos crean los archivos CompraViewControler.xib, CompraViewControler.m y CompraViewControler.h, cuyo contenido debe quedar así:

CompraViewController.h // CompraViewController.h // ListaCompra // Created by Miguel on 22/02/10.

G R U P O C H I C O - U C L M

58

// Copyright 2010 chico. All rights reserved. #import <UIKit/UIKit.h> #import "ListaCompraAppDelegate.h" @class Producto; @interface CompraViewController : UIViewController { ListaCompraAppDelegate * appDelegate; IBOutlet UITableView * vistaTabla; NSMutableArray * seleccionados; IBOutlet UIImageView * image; } @property(nonatomic, retain) NSMutableArray * seleccionados; - (IBAction) clic_borrar:(id)sender; - (void) loadFonts; - (void) actualizarTabla; @end

Tendremos una tabla con los productos que queremos comprar, un array que almacenará los elementos que hemos seleccionado (los que ya han sido comprados) y una imagen de un post-it sobre la cual vendrá insertada nuestra tabla. Para conseguir este efecto, debemos poner el fondo de la tabla de color transparente y ajustarla a los márgenes de la imagen (Véase la Figura 36).

En lo que a métodos se refiere, clic_borrar lanzará una alerta para que el usuario confirme si desea borrar, si este ratifica su decisión se borraran todos los objetos del array, a la vez se inicia una animación que arranca el post-it actual y pone uno nuevo. Para esto se usará una transición de tipo UIViewAnimationTransitionCurlUp aplicada a la vista de la imagen. Usamos otro método para cargar un tipo de fuente especial que simula la escritura manual (Véase la Figura 36), la cual se debe añadir ya que no está incluida en los tipos de fuentes preinstalados en el iPhone.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

59

Figura 36. Vista de la lista de productos

Debemos implementar todos los métodos necesarios para el control de tablas. Para conseguir añadir/quitar una marca al producto que pulsemos es necesario añadir a los métodos correspondientes un modificador del tipo de accesorio de celda, es decir añadir un UITableViewCellAccessoryCheckmark o quitarlo. El manejo será el siguiente: si pulsamos un producto que no tiene marca se la añadimos y si pulsamos un producto con marca, la quitaremos.

Para que funcione de manera correcta la multiselección de productos se ha creado un array que contiene los productos seleccionados. Cabe destacar que esto es necesario ya que sólo están en memoria las celdas visibles en pantalla, el resto no existen como tal y debido a esto, no se guarda la información de si estaban marcadas mediante el UITableViewCellAccessoryCheckmark. Con el array de productos seleccionados se va a permitir que al subir y bajar dentro de la tabla de productos la aplicación sepa, al mostrar cada celda, si le debe añadir una marca o no.

Para añadir los elementos necesarios a la vista debemos volver al Interface Builder y dentro de CompraViewController.xib añadir: un UITableView, que mostrará la lista de productos; un UIButton, que permitirá borrar la lista completa; por último, un UIImageView que contenga la imagen del post-it sobre la que se mostrará la tabla (Véase la Figura 36).

G R U P O C H I C O - U C L M

60

5.3.3. CarroViewController

El siguiente controlador de vista será CarroViewController, este mostrará un UIPickerView con los productos añadidos al carro y realizará una animación del texto. Para crearlo, dentro de Xcode, haremos lo mismo que para la creación de la anterior, quedando la cabecera así:

CarroViewController.h

// CarroViewController.h // ListaCompra // // Created by Miguel on 22/02/10. // Copyright 2010 chico. All rights reserved. #import <UIKit/UIKit.h> #import "ListaCompraAppDelegate.h" @interface CarroViewController : UIViewController { IBOutlet UIPickerView * listaCarrito; NSMutableArray * contenido; IBOutlet UILabel * productosenel; IBOutlet UILabel * carro; IBOutlet UIImageView *image; ListaCompraAppDelegate * appDelegate; } @property(nonatomic, retain) UIPickerView * listaCarrito; @property(nonatomic, retain) NSMutableArray * contenido; @end

Tendremos dos etiquetas de texto, la imagen de un carro que será parte de la animación y el UIPickerView que mostrará el contenido del array de producto comprados. Al iniciar la carga de esta vista, se guardarán en el array los elementos de la lista de la compra que estén seleccionados, se mostraran en el UIPickerView del carro y posteriormente se eliminarán de la lista de la compra. Con esto, al regresar a la vista de la lista de la compra, sólo se mostrarán los productos que faltan por comprar, ya que el resto de productos están en la vista del carro.

Para añadir los elementos necesarios a la interfaz, dentro del archivo CarroViewController.xib en Interface Builder se añadirá un UIPickerView, la etiqueta que muestra el texto “productos en el” y el UIImageView que muestra la palabra “carro”. El resultado deber ser similar a lo mostrado en la Figura 37.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

61

Figura 37. CarroViewController.xib

5.3.4. AnadirViewController

La última vista (junto a su controlador) será AnadirViewControler, esta mostrará un UITextField y un botón que nos permitirán añadir elementos a la lista de la compra. Para crearla dentro de Xcode, haremos lo mismo que en las dos anteriores, quedando la cabecera así:

AnadirViewController.h

// // AnadirViewController.h // ListaCompra // // Created by Miguel on 23/02/10. // Copyright 2010 chico. All rights reserved. // #import <UIKit/UIKit.h> #import "ListaCompraAppDelegate.h" @interface AnadirViewController : UIViewController { IBOutlet UITextField * producto; IBOutlet UILabel * label; ListaCompraAppDelegate * appDelegate; }

G R U P O C H I C O - U C L M

62

- (IBAction) clic_teclado_return: (id) sender; - (IBAction) clic_anadir: (id) sender; @end

En esta vista tenemos dos métodos. El primero se encargará de añadir el producto a la lista de la compra, así como de mostrar una alerta en el caso de pulsar el botón sin que haya texto escrito. Para esto crea un objeto de tipo producto al que añade el nombre escrito por el usuario y posteriormente llama al método addProducto de la clase ListaCompraAppDelegate la cual se encarga de añadir el producto al array y a la base de datos. El otro método se encarga de quitar el teclado de pantalla cuando se pulse el botón Return dentro de este.

Tanto en este como en el resto de controladores no se permite mostrar la pantalla de forma horizontal, si se desease permitir rotar la orientación junto al movimiento del móvil habría que cambiar la devolución del método shouldAutorotateToInterfaceOrientation.

De nuevo en el Interface Builder, dentro de AnadirViewController.xib hay que añadir un UIButton, un UILabel y un UITextField, que deberán quedar como muestra la Figura 38.

Como en el resto de vistas, recordemos que tendrá un nivel de transparencia del 95%, para dejar ver la imagen con carros de la compra que contiene el MainWindow.xib.

Figura 38. AnadirViewController.xib

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

63

5.4 Añadiendo las conexiones

Ya tenemos los controladores y las interfaces, pero estas aún no están relacionadas/conectadas con ninguno de los métodos o variables que han sido definidos en sus correspondientes controladores.

De nuevo en el Interface Builder y dentro de MainWindow.xib debemos crear las siguientes conexiones:

• Entre el delegate del File´s Owner y ListaCompraAppDelegate.

• Dentro de Lista Compra App Delegate, habría que conectar la variable cargando con su UILabel en Window, indicador con el UIActivityIndicatorView y window del App Delegate con Window. Por último rootController con el Tab Bar Controller (Véase la Figura 39).

Figura 39. Conexiones necesarias en MainWindow.xib

En CompraViewController.xib debemos crear las siguientes conexiones en el File´s Owner (Véase Figura 40):

• Conectar ambas vistas, el View con el view del controlador.

• La variable image con el UIImageView del post-it.

• La variable vistaTabla con el UITableView.

• El método clic_borrar con el UIButton borrar todo, con una acción de tipo Touch Up Inside.

• DataSource y delegate con el UITableView.

G R U P O C H I C O - U C L M

64

Figura 40. Conexiones necesarias en CompraViewController.xib

En CarroViewController.xib debemos crear las siguientes conexiones en el File´s Owner (Véase Figura 41):

• Conectar ambas vistas, View con el view del controlador.

• La variable image con el UIImageView de la vista.

• La variable listaCarrito con el UIPickerView.

• La etiqueta productosenel con el UILabel.

• DataSource y delegate con el UIPickerView.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

65

Figura 41. Conexiones necesarias en CarroViewController.xib

En AnadirViewController.xib debemos crear las siguientes conexiones en el File´s Owner (Véase Figura 42):

• Conectar ambas vistas, View con el view del controlador.

• La variable label con el UILabel que muestra producto.

• La variable producto con el UITextField, que permite insertar el nombre de un producto.

• El método clic_anadir con el UIButton de añadir a la lista.

• El método clic_teclado_return con el UITextField, para permitir mostrar y ocultar el teclado.

G R U P O C H I C O - U C L M

66

Figura 42. Conexiones necesarias en AnadirViewController.xib

5.5 Código Fuente

A continuación se muestra el código fuente de las clases que posee la aplicación:

ListaCompraAppDelegate.m

// // ListaCompraAppDelegate.m // ListaCompra // // Created by Miguel on 22/02/10. // Copyright chico 2010. All rights reserved. // #import "ListaCompraAppDelegate.h" #import "CompraViewController.h" #import "Producto.h" @implementation ListaCompraAppDelegate @synthesize window, tiempo, cargando, indicador, rootController, borrarSeleccionados, productoArray; - (void)applicationDidFinishLaunching:(UIApplication *)application { //Si no esta la base de datos en el dispositivo, la copia [self copyDatabaseIfNeeded]; //Inicializa el array de productos NSMutableArray *tempArray = [[NSMutableArray alloc] init]; self.productoArray = tempArray; [tempArray release]; //Obtiene la primera pantalla que se mostrará

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

67

[Producto getInitialDataToDisplay:[self getDBPath]]; //Llamamos al fadeScreen para que inicie la aplicación con animacion tiempo = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(fadeScreen) userInfo:nil repeats:NO]; [self performSelector:@selector(cargarVista) withObject:nil afterDelay:0.0]; } - (void)cargarVista { [window addSubview:[rootController view]]; [window makeKeyAndVisible]; } - (void)fadeScreen { [UIView beginAnimations:nil context:nil]; // empieza la carga de la imagen [UIView setAnimationDuration:0.0]; // pones la duracion [UIView setAnimationDelegate:self]; // pones la propia imagen de delegate [UIView setAnimationDidStopSelector:@selector(finishedFading)]; // llamas a finishedfading cuando termine la imagen rootController.view.alpha = 0.0; [UIView commitAnimations]; } - (void) finishedFading { [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:2.2]; rootController.view.alpha = 1.0; [UIView commitAnimations]; [self performSelector:@selector(quitarCargando) withObject:nil afterDelay:1.5]; } - (void) quitarCargando{ [self.indicador setHidden:YES]; [self.cargando setHidden:YES]; } - (void)applicationWillTerminate:(UIApplication *)application { //Salva todos los productos modificados y libera la memoria [self.productoArray makeObjectsPerformSelector:@selector(saveAllData)]; [Producto finalizeStatements]; } - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { //Salva todos los productos modificados y libera la memoria [self.productoArray makeObjectsPerformSelector:@selector(saveAllData)]; } - (void)dealloc { [tiempo release]; [cargando release]; [indicador release]; [window release]; [rootController release]; [productoArray release]; [super dealloc]; }

G R U P O C H I C O - U C L M

68

- (void) copyDatabaseIfNeeded { NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error; NSString *dbPath = [self getDBPath]; BOOL success = [fileManager fileExistsAtPath:dbPath]; if(!success) { NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Compras.sqlite"]; success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error]; if (!success) NSAssert1(0, @"Failed to create writable database file with message '%@'.", [error localizedDescription]); } } - (NSString *) getDBPath { //Busca el directorio raiz de la base de datos Compras y lo devuelve NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); NSString *documentsDir = [paths objectAtIndex:0]; return [documentsDir stringByAppendingPathComponent:@"Compras.sqlite"]; } - (void) removeProducto:(Producto *)productoObj{ //Borra el producto de la base de datos [productoObj deleteProducto]; //Borra el producto del array [productoArray removeObject:productoObj]; } - (void) addProducto:(Producto *)productoObj { //Añade el producto de la base de datos [productoObj addProducto]; //Añade el producto del array [productoArray addObject:productoObj]; } @end

CompraViewController.m

// // CompraViewController.m // ListaCompra // // Created by Miguel on 22/02/10. // Copyright 2010 chico. All rights reserved. // #import "CompraViewController.h" #import <dlfcn.h> #import "Producto.h" @implementation CompraViewController

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

69

@synthesize seleccionados; - (void) viewWillAppear:(BOOL)animated { if([appDelegate borrarSeleccionados]){ // Si los hemos guardado en el carro, borramos las selecciones self.seleccionados=[[NSMutableArray alloc] init];; } [super viewWillAppear:animated]; // Ponemos a NO la variable que indica que debemos borrar los seleccionados [appDelegate setBorrarSeleccionados:NO]; [vistaTabla reloadData]; } // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { // Cargamos los tipos de letra añadidos al proyecto [self loadFonts]; vistaTabla.backgroundColor = [UIColor clearColor]; appDelegate = (ListaCompraAppDelegate *)[[UIApplication sharedApplication] delegate]; self.seleccionados=[[NSMutableArray alloc] init];; [super viewDidLoad]; } // Override to allow orientations other than the default portrait orientation. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return NO; } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [appDelegate.productoArray count] ; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell";

G R U P O C H I C O - U C L M

70

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleNone; } Producto *productoObj = [appDelegate.productoArray objectAtIndex:indexPath.row]; // Guarda en la celda el nombre del producto y le asigna un tipo de letra [cell.textLabel setText: productoObj.productoName]; [cell.textLabel setFont:[UIFont fontWithName:@"BoBsFrAnTiC-TrueType" size:20.0]]; cell.accessoryType = UITableViewCellAccessoryNone; // Si el objeto está en la lista de seleccionados le ponemos el check mark for (id obj in seleccionados){ if([obj isEqual:[NSNumber numberWithInt: indexPath.row]]){ cell.accessoryType = UITableViewCellAccessoryCheckmark; } } return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Evitamos que se quede la celda entera seleccionada [tableView deselectRowAtIndexPath:[tableView indexPathForSelectedRow] animated:YES]; UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; Producto *productoObj = [appDelegate.productoArray objectAtIndex:indexPath.row]; if (cell.accessoryType == UITableViewCellAccessoryNone) { // Marcamos el producto como seleccionado, lo añadimos a la lista y le ponemos la marca productoObj.estaSeleccionado=YES; [seleccionados addObject: [NSNumber numberWithInt: indexPath.row]]; cell.accessoryType = UITableViewCellAccessoryCheckmark; } else { // Desmarcamos el producto productoObj.estaSeleccionado=NO; cell.accessoryType = UITableViewCellAccessoryNone; // Lo borramos de la lista de seleccionados for (int i=0; i<[seleccionados count];i++){ if([[seleccionados objectAtIndex:i] isEqual:[NSNumber numberWithInt: indexPath.row]]){ [seleccionados removeObjectAtIndex:i]; } } } } - (void) loadFonts{ NSUInteger newFontCount = 0; NSBundle *frameworkBundle = [NSBundle bundleWithIdentifier:@"com.apple.GraphicsServices"]; const char *frameworkPath = [[frameworkBundle executablePath] UTF8String];

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

71

if (frameworkPath) { void *graphicsServices = dlopen(frameworkPath, RTLD_NOLOAD | RTLD_LAZY); if (graphicsServices) { BOOL (*GSFontAddFromFile)(const char *) = dlsym(graphicsServices, "GSFontAddFromFile"); if (GSFontAddFromFile) for (NSString *fontFile in [[NSBundle mainBundle] pathsForResourcesOfType:@"ttf" inDirectory:nil]){ newFontCount += GSFontAddFromFile([fontFile UTF8String]); } } } } - (IBAction) clic_borrar:(id)sender{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"¿Borrar la lista completa?" message:nil delegate:self cancelButtonTitle:@"Cancelar" otherButtonTitles:@"Aceptar" ,nil]; [alert show]; [alert release]; } - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { // Si presionamos el boton de aceptar, borramos la lista if(buttonIndex==1){ // Animacion para borrar [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:1.5]; [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:image cache:YES]; [UIView commitAnimations]; while([appDelegate.productoArray count]>0){ Producto *productoObj = [appDelegate.productoArray objectAtIndex:0]; [appDelegate removeProducto:productoObj ]; } [self performSelector:@selector(actualizarTabla) withObject:nil afterDelay:0.6]; } } - (void) actualizarTabla{ [vistaTabla reloadData]; } - (void)dealloc { [image release]; [appDelegate release]; [vistaTabla release]; [seleccionados release]; [super dealloc]; } @end

G R U P O C H I C O - U C L M

72

CarroViewController.m

// // CarroViewController.m // ListaCompra // // Created by Miguel on 22/02/10. // Copyright 2010 chico. All rights reserved. // #import "CarroViewController.h" #import "Producto.h" #import "CompraViewController.h" @implementation CarroViewController @synthesize listaCarrito, contenido; // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { [super viewDidLoad]; appDelegate = (ListaCompraAppDelegate *)[[UIApplication sharedApplication] delegate]; [productosenel setFont:[UIFont fontWithName:@"--unknown--2--" size:50.0]]; // Iniciamos la variable contenido NSMutableArray *tempArray = [[NSMutableArray alloc] init]; self.contenido = tempArray; [tempArray release]; } - (void) viewWillAppear:(BOOL)animated { // Retrasamos la posicion de la imagen para que tras la animación quede en su sitio image.frame = CGRectMake(image.frame.origin.x - 300, image.frame.origin.y, image.frame.size.width, image.frame.size.height ); // Los elementos marcados en la lista de la compra son añadidos al carro for(int i=0; i<[appDelegate.productoArray count]; i++){ Producto *productoObj = [appDelegate.productoArray objectAtIndex:i]; if(productoObj.estaSeleccionado){ // Lo añadimos al carro [contenido addObject: productoObj]; } } // Borramos de la lista los seleccionados que ya están en el carro for(int i=0; i<[contenido count]; i++){ [appDelegate removeProducto: [contenido objectAtIndex:i]]; } // Animacion mujer con el carro [UIView beginAnimations: @"moveField"context: nil]; [UIView setAnimationDelegate: self]; [UIView setAnimationDuration: 2.0]; [UIView setAnimationCurve: UIViewAnimationCurveEaseInOut]; image.frame = CGRectMake(image.frame.origin.x + 300, image.frame.origin.y, image.frame.size.width, image.frame.size.height ); [UIView commitAnimations]; // Tambien debemos borrar los check marks de la lista

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

73

[appDelegate setBorrarSeleccionados:YES]; [super viewWillAppear:animated]; [listaCarrito reloadComponent:0]; } // Override to allow orientations other than the default portrait orientation. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return NO; } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } - (NSInteger) numberOfComponentsInPickerView:(UIPickerView *)pickerView{ return 1; } - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{ return [contenido count]; } - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{ return [[contenido objectAtIndex:row] productoName]; } - (void)dealloc { [image release]; [listaCarrito release]; [productosenel release]; [carro release]; [contenido release]; [appDelegate release]; [super dealloc]; } @end

G R U P O C H I C O - U C L M

74

AnadirViewController.m

// // AnadirViewController.m // ListaCompra // // Created by Miguel on 23/02/10. // Copyright 2010 chico. All rights reserved. // #import "AnadirViewController.h" #import "Producto.h" @implementation AnadirViewController - (void)viewDidLoad { [super viewDidLoad]; [label setFont:[UIFont fontWithName:@"--unknown--2--" size:55.0]]; } // Override to allow orientations other than the default portrait orientation. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return NO; } - (IBAction) clic_anadir: (id) sender{ if([producto.text length]>0){ appDelegate = (ListaCompraAppDelegate *)[[UIApplication sharedApplication] delegate]; // Creamos un objeto producto Producto *productoObj = [[Producto alloc] initWithPrimaryKey:0]; productoObj.productoName = producto.text; // Añadimos el objeto [appDelegate addProducto:productoObj]; // Volvemos a la lista [self.tabBarController setSelectedIndex:0]; } else { // Si no añade texto, mostramos un mensaje de advertencia UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Debe introducir un producto" message:nil delegate:self cancelButtonTitle:@"Aceptar" otherButtonTitles:nil ,nil]; [alert show]; [alert release]; } } - (IBAction) clic_teclado_return: (id) sender { [producto resignFirstResponder]; } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use.

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

75

} - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } - (void)dealloc { [appDelegate release]; [producto release]; [label release]; [super dealloc]; } @end

Producto.m

// // Producto.m // ListaCompra // // Created by Miguel on 26/02/10. // Copyright 2010 chico. All rights reserved. // #import "Producto.h" @implementation Producto static sqlite3 *database = nil; static sqlite3_stmt *deleteStmt = nil; static sqlite3_stmt *addStmt = nil; static sqlite3_stmt *detailStmt = nil; static sqlite3_stmt *updateStmt = nil; @synthesize productoID, productoName, estaSeleccionado; + (void) getInitialDataToDisplay:(NSString *)dbPath { ListaCompraAppDelegate *appDelegate = (ListaCompraAppDelegate *)[[UIApplication sharedApplication] delegate]; if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) { const char *sql = "SELECT ID, Producto FROM listaCompra"; sqlite3_stmt *selectstmt; if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) { while(sqlite3_step(selectstmt) == SQLITE_ROW) { NSInteger primaryKey = sqlite3_column_int(selectstmt, 0); Producto * productoObj = [[Producto alloc] initWithPrimaryKey:primaryKey];

G R U P O C H I C O - U C L M

76

if(sqlite3_column_text(selectstmt, 1) != nil){ productoObj.productoName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)]; } productoObj.estaSeleccionado=NO; // Añadimos el producto construido [appDelegate.productoArray addObject:productoObj]; [productoObj release]; } } } else sqlite3_close(database); //Si falla, cerramos la base de datos }; + (void) finalizeStatements { if (database) sqlite3_close(database); if (deleteStmt) sqlite3_finalize(deleteStmt); if (addStmt) sqlite3_finalize(addStmt); if (detailStmt) sqlite3_finalize(detailStmt); if (updateStmt) sqlite3_finalize(updateStmt); } - (id) initWithPrimaryKey:(NSInteger) pk { [super init]; productoID = pk; return self; } - (void) deleteProducto { if(deleteStmt == nil) { const char *sql = "DELETE FROM listaCompra WHERE ID = ?"; if(sqlite3_prepare_v2(database, sql, -1, &deleteStmt, NULL) != SQLITE_OK) NSAssert1(0, @"Error while creating delete statement. '%s'", sqlite3_errmsg(database)); } sqlite3_bind_int(deleteStmt, 1, productoID); if (SQLITE_DONE != sqlite3_step(deleteStmt)) NSAssert1(0, @"Error while deleting. '%s'", sqlite3_errmsg(database)); sqlite3_reset(deleteStmt); } - (void) addProducto { if(addStmt == nil) { const char *sql = "INSERT INTO listaCompra(Producto) VALUES(?)"; if(sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) != SQLITE_OK) NSAssert1(0, @"Error while creating add statement. '%s'", sqlite3_errmsg(database)); } sqlite3_bind_text(addStmt, 1, [productoName UTF8String], -1, SQLITE_TRANSIENT); if(SQLITE_DONE != sqlite3_step(addStmt)) NSAssert1(0, @"Error while inserting data. '%s'", sqlite3_errmsg(database));

D E S A R R O L L O D E I N T E R F A C E S D E U S U A R I O C O N X C O D E

77

else productoID = sqlite3_last_insert_rowid(database); // Reseteamos el add statement sqlite3_reset(addStmt); } - (void) saveAllData { if(updateStmt == nil) { const char *sql = "UPDATE listaCompra SET Producto = ? WHERE ID = ?"; if(sqlite3_prepare_v2(database, sql, -1, &updateStmt, NULL) != SQLITE_OK) NSAssert1(0, @"Error while creating update statement. '%s'", sqlite3_errmsg(database)); } sqlite3_bind_text(updateStmt, 1, [productoName UTF8String], -1, SQLITE_TRANSIENT); sqlite3_bind_int(updateStmt, 2, productoID); if(SQLITE_DONE != sqlite3_step(updateStmt)) NSAssert1(0, @"Error while updating. '%s'", sqlite3_errmsg(database)); sqlite3_reset(updateStmt); // Liberamos memoria [productoName release]; productoName = nil; } - (void)setProductoName:(NSString *)newValue { [productoName release]; productoName = [newValue copy]; } - (void) dealloc { [productoName release]; [super dealloc]; } @end

5.6 Probando la aplicación en un iPhone Para probar la aplicación en un iPhone o en un iPod touch será necesario seguir esta serie de pasos:

1. Crear un equipo de desarrollo en el iPhone Developer Program o solicitar formar parte de alguno ya existente.

2. Solicitar un certificado de desarrollador. Para lo cual se debe crear un certificado en el llavero del Mac (Asistente para Certificados > Solicitar un certificado de una autoridad de certificación), guardarlo en el disco duro y posteriormente subirlo al portal de

G R U P O C H I C O - U C L M

78

desarrollo del equipo. Este tendrá que ser aceptado por alguno de los administrares del equipo de desarrollo.

3. Agregar dispositivos (iPod touch o iPhone) al equipo de desarrollo, cada uno obtendrá un identificador único.

4. Asignar un App ID o identificador de aplicación para cada aplicación que se desarrolle. Únicamente será necesario en aquellas aplicaciones que usen características del sistema operativo del iPhone, como las notificaciones Push o el intercambio de datos entre aplicaciones.

5. Crear un perfil en el que se agrupe el certificado de desarrollador, el identificador del dispositivo donde se va a instalar la aplicación y un identificador de aplicación válido. Este perfil se descargará al dispositivo en el que se vaya a probar la aplicación.

Con esto hemos terminado este capitulo. Como dijimos en la Introducción si se quiere avanzar en el conocimiento de las técnicas que hemos desarrollado en este libro existen varios manuales que siguen la misma filosofía de este libro.