desarrolador ios

46
Curso de creación de aplicaciones de iOS Hardware y Software que “nos exigen” OSX (Snow Leopard al menos) actualizado a la última versión, para poder instalar el IDE de la manzanita. Nos quieren obligar a comprarnos un Mac, peeero, también podéis instalaros el VMWare Fusion en un PC (o el VMWare normal pero sin soporte OpenGL), u Oracle VM VirtualBox sobre Windows o GNU/Linux y descargar una imagen de OSX de la red para este software- emulador, e incluso…crearos un Hackintosh , que no es muy caro y están de moda jeje XCode 4 + iPhone SDK: podéis descargarlo de Internet si no tenéis dinero para la licencia de developer, aunque si queréis publicar pronto una aplicación podéis daros de alta en el programa de desarrolladores de la empresa , pagando claro!… iPhone/iPad/iPod: pues hombre, estaría bien tenerlo para poder probar lo que escribamos, ya que el simulador (no es un emulador!) que trae el IDE se comporta de manera algo distinta a como debería ser…pero bueno, para empezar tampoco vamos a tirar la casa por la ventana jeje Si tienes un dispositivo intenta instalarte el AppWizard para el siguiente paso -no te diré como conseguirlo- Hardware de los dispositivos iOS El iPhone 4 tiene una resolución de pantalla de 960×640 a 326 ppi. El iPhone 3 tiene una resolución de 480×320 píxels La resolución del iPad es de 1024×768 a 132 ppi Habilidades que necesitamos para programar aplicaciones de iOS Programar en Objective C: programar aplicaciones de iPhone en XCode es, básicamente, empezar a conocer cómo funciona Objective C y cómo asociar los objetos del Interface Builder a nuestro código. Para eso existen cientos de tutoriales, algunos más famosos que otros como “Masters of the Void “, pero lo mejor es seguir un libro gratuito de la propia empresa (en el apartado de desarrolladores) o ir a una biblioteca y pillarse libros como Objective C for dummies, iPhone application development for dummies, y luego algo más serio como iOs4 Programming Cookbook de O’Reilly, ya que contienen multitud de ejemplos y trucos para desarrollar todo lo que deseemos/necesitemos. Hay un libro de la web de MacProgramadores bastante bueno sobre Objective C con especial énfasis en su origen (NeXT) y XCode, lo han borrado de la web, pero seguro que lo encontráis…

Upload: kilfix

Post on 12-Aug-2015

190 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Desarrolador IOS

Curso de creación de aplicaciones de iOS

Hardware y Software que “nos exigen”

• OSX (Snow Leopard al menos) actualizado a la última versión, para poder instalar el IDE de la manzanita. Nos quieren obligar a comprarnos un Mac, peeero, también podéis instalaros el VMWare Fusion en un PC (o el VMWare normal pero sin soporte OpenGL), u Oracle VM VirtualBox sobre Windows o GNU/Linux y descargar una imagen de OSX de la red para este software-emulador, e incluso…crearos un Hackintosh, que no es muy caro y están de moda jeje

• XCode 4 + iPhone SDK: podéis descargarlo de Internet si no tenéis dinero para la licencia de developer, aunque si queréis publicar pronto una aplicación podéis daros de alta en el programa de desarrolladores de la empresa, pagando claro!…

• iPhone/iPad/iPod: pues hombre, estaría bien tenerlo para poder probar lo que escribamos, ya que el simulador (no es un emulador!) que trae el IDE se comporta de manera algo distinta a como debería ser…pero bueno, para empezar tampoco vamos a tirar la casa por la ventana jeje Si tienes un dispositivo intenta instalarte el AppWizard para el siguiente paso -no te diré como conseguirlo-

Hardware de los dispositivos iOS

• El iPhone 4 tiene una resolución de pantalla de 960×640 a 326 ppi. • El iPhone 3 tiene una resolución de 480×320 píxels • La resolución del iPad es de 1024×768 a 132 ppi

Habilidades que necesitamos para programar aplicaciones de iOS

• Programar en Objective C: programar aplicaciones de iPhone en XCode es, básicamente, empezar a conocer cómo funciona Objective C y cómo asociar los objetos del Interface Builder a nuestro código. Para eso existen cientos de tutoriales, algunos más famosos que otros como “Masters of the Void“, pero lo mejor es seguir un libro gratuito de la propia empresa (en el apartado de desarrolladores) o ir a una biblioteca y pillarse libros como Objective C for dummies, iPhone application development for dummies, y luego algo más serio como iOs4 Programming Cookbook de O’Reilly, ya que contienen multitud de ejemplos y trucos para desarrollar todo lo que deseemos/necesitemos.

• Hay un libro de la web de MacProgramadores bastante bueno sobre Objective C con especial énfasis en su origen (NeXT) y XCode, lo han borrado de la web, pero seguro que lo encontráis…

Page 2: Desarrolador IOS

Introducción a XCode e iOS4 SDK

Conceptos básicos:

XCode

• Xcode es el IDE que nos ofrece Apple para desarrollar aplicaciones de iOS. • El SDK (Software Development Kit) incorpora herramientas para el desarrollo

(entre ellas, el simulador de iPhone/iPad). • El lenguaje de programación es Objective-C

• Simulador de iOS: Permite simular tanto iPhone como iPad en nuestro Mac. • Interface Builder: Editor visual para diseñar interfaces de usuario para nuestras

aplicaciones. • Instruments: Herramienta de análisis que nos ayuda a optimizar y monitorizar

la aplicación

Sistema Operativo iOS

• El Sistema Operativo de los dispositivos de Apple (iOS), está formado por un conjunto de capas, que conforman el conjunto de servicios ofrecidos por el dispositivo. Arquitectura:

• Cada capa de la arquitectura está compuesta por un conjunto de frameworks • La capa Core OS es la base del sistema operativo. Se encarga de realizar la

gestión de memoria, el sistema de ficheros, conexión a la red y procesos que interactúan con el hardware

• Core Services nos permite el acceso a los servicios básicos, tales como la agenda, el acceso a la base de datos, preferencias, conexión a servidores y procesamiento de URLs, etc…

• La capa Media nos permite la ejecución de tareas multimedia. Entre ella el acceso al Audio, OpenGL, Imágenes y PDF, Animaciones, etc…

• Cocoa Touch nos permite acceder al acelerómetro, los eventos y controles táctiles, la jerarquía de vistas, alertas, etc…gestiona la interacción visual con el usuario

Page 3: Desarrolador IOS

• Novedades de iOS4:

• Multitarea: mientras ejecutamos una aplicación, al pulsar el botón “home” no se cierra sino que pasa a segundo plano, pueden continuar ejecutándose o pueden suspenderse

• Notificaciones Locales: completa el sistema de notificaciones push (desde un servidor remoto que nosotros mismos podemos crear)

• Core Motion: conjunto de interfaces para acceder a toda la información basada en el movimiento (giroscopio ,motion-based)

• Data protection: sistema integrado de encriptación • Soporte para pantalla de alta resolución (adaptación a la antigua)

• Leer más >

Frameworks

• Un framework es un conjunto de librerías que nos permite añadir una funcionalidad concreta a nuestra aplicación

• Por defecto, cuando creamos un proyecto, tenemos los frameworks esenciales para el funcionamiento básico

• Por ejemplo, para usar bases de datos SQLite usaremos su framework, simplemente con copiar el directorio descargado de su web, aunque en la versión de XCode4 ya viene integrado, también haremos lo mismo con Cocos2D

• Cocoa Touch: conjunto de frameworks orientados a objetos que permiten el desarrollo de aplicaciones nativas para iOS

Crear una aplicación

Pasos

1. Abrir XCode y crear un proyecto 2. Diseñar la interfaz de usuario 3. Escribir el código asociado de la aplicación 4. Compilar, ejecutar y probar (vuelta al paso 2) 5. Medir y ajustar el rendimiento de la aplicación 6. Producir y publicar la aplicación

Tipos de proyecto

• Cocos 2d: para crear juegos (necesita el framework Cocos2D),lo veremos en otro curso, de programación de videojuegos para iPhone

• Navigation-based : presentan la información de forma jerárquica usando múltiples vistas (diseño de pantallas)

• Open GL : igual que cocos, además de juegos podemos crear cualquier tipo de aplicación que represente imágenes, animaciones o gráficos 3D

• Split View-based : Aplicaciones enfocadas para iPad que muestran más de una vista en la pantalla al mismo tiempo (un control listado y una vista detalle de elementos normalmente)

Page 4: Desarrolador IOS

• Tab Bar : presentan una interfaz de “radio” que permite al usuario elegir entre varias opciones

• Utility : aplicaciones que presentan una vista principal y permiten que el usuario acceda a otra vista para realizar personalizaciones básicas

• View-based : una vista simple para implementar la interfaz • Window-based : plantilla universal (cualquier dispositivo con iOS) que sirve de

punto de partida con un delegado y una ventana. Útil para aplicaciones con jerarquía propia

Recordar que no todos los tipos de aplicaciones pueden ser universales (para cualquier iDevice) y las que lo son tienen diferentes diseños de interfaces de usuario en subdirectorios con su nombre.

Interfaz de XCode

XCode4: editor en una ventana

Page 5: Desarrolador IOS

XCode4: Interface Builder

XCode4: Git integrado

Tipos de ficheros de un proyecto

• Info.plist : como todos los plist, es un XML (introducido por NeXT) constituído de propiedades de configuración en UTF-8

• Ficheros.h: declaraciones de interfaces ,clases y atributos • Ficheros.m: implementación de clases y métodos definididos en los .h (aunque

no necesariamente requieren un .h) • Ficheros .pch: cabeceras precompiladas que se añaden a las clases • Ficheros de objetivo: resultado de la compilación de un conjunto de ficheros de

código, puede ser una librería o una aplicación. Un conjunto de objetivos forman un producto. Lo utilizaremos para publicar una aplicación en la tienda

Perfiles de compilación

• Depuración: incluye la opción de depuración al compilar con gcc, es más lenta pero encuentra errores lógicos. Se puede depurar en el editor de texto, mini-depurador, depurador y en la consola [(gdb)]. Opción: GCC_GENERATE_DEBUGGING_SYMBOLS. Podemos usar breakpoints. Mientras el programa está siendo depurado podemos modificarlo (como pasaba en VisualBasic y otros lenguajes! :D) : cambiar el nº y tipo de argumentos de una función o método que esté en la pila,el tipo del valor de retorno o el nombre

Page 6: Desarrolador IOS

de una función o método también en la pila, así como cambiar el tipo de las variables estáticas, un fichero NIB, añadir clases o cambiar sus métodos… Leer más >

• Lanzamiento: mayor rendimiento de la app, tamaño de objeto final compilado menor.

• Distribución : como el anterior pero necesita un certificado o firma de Distribución válida (con apple hemos topado xD, aunque podemos arreglar esto con algún que otro truco de Cydia…). Se añade meta-información necesaria para ser validada en la tienda.

Introducción a Objective C

¿Qué es?

• Pequeño set de extensiones de ANSI C • Sus añadidos a C están basados en Smalltalk, uno de los primeros lenguajes

orientados a objetos • Diseñado para dotar a C de toda la potencia de la orientación a objetos

Clases

• Sintáxis normal de creación de clases: interfaz (.h) + implentación (.m)

• Ejemplo de clase Persona.h:

@interface Persona : NSObject { NSString *nombre; NSString *apellidos; } -(NSString*) nombre; -(NSString*) apellidos;-(void) setNombre: (NSString *) _Nombre; -(void) setApellidos: (NSString *) _Apellidos;   +(NSString *) soyMetodoEstatico: (NSString *) mens aje; @end

Page 7: Desarrolador IOS

• Ejemplo de clase Persona.m:

#import "Persona.h" @implementation Persona -(NSString*) nombre { return self.nombre; } -(NSString*) apellidos { return self.apellidos; } -(void) setNombre: (NSString *) _Nombre { self.nombre = _Nombre; } -(void) setApellidos: (NSString *) _Apellidos{ self.apellidos = _Apellidos; } +(NSString) soyMetodoEstatico: (NSString *) mensaje { return mensaje; } @end

• Ejempo de función que toma datos de la clase padre de la que hereda:

- (id) init { if ( self = [super init] ) { [self setNombre:@"nombre por defecto"]; [self setApellidos:@"apellidos por defecto"]; }   return self; }

• self es la propia instancia del objeto y super es la clase de la que hereda • Declaración de un método:

• A través del corchete accedemos a un objeto, que es el primer parámetro, el

segundo es la llamada al método, es como hacer $this->método en PHP , o puntero_a_clase -> método , en C / C++,Java. Por ejemplo , un método sin entrada

[object method];

Page 8: Desarrolador IOS

• Con entrada:

[object methodWithInput:input];

• Con salida:

output = [object methodWithOutput];

• Con entrada y salida:

output = [object methodWithInputAndOutput:input];

• Recordar que si queremos especificar una función con varios parámetros se ponen dos nombres, uno para el nombre clave de llamada y otro para el nombre interno del parámetro, el primer nombre de nombre clave no se pone, ejemplo: -(void) miFuncion: (int)parametro1 nombre1: (bool) parametro2 nombre3:(NSString*) parametro3; a esta función accedemos así, ejemplo con datos: [miObjeto miFuncion:7 nombre1:true nombre3: @"cadena" Fijaros que una cadena siempre se precede de una arroba.

• Propiedades de una clase: (@property) permiten generar automáticamente métodos de acceso para los atributos de una clase, podemos sobrecargar los métodos setter & getter si no usamos las especificaciones por defecto de una propiedad, para saber cuál nos conviene primero debemos ver la gestión de memoria. Por defecto, el setter se añade automática a la propiedad definida como setNombre_variable (set+UCase(nombre_variable)), y el getter es el nombre de la variable atributo tal cual, en Persona.h el setter es setNombre y el getter nombre. Fácil. Para que podamos usar las propiedades es obligatorio inicializarlas con @synthesize en Persona.m (la implementación de la clase),este genera los setters y getters automáticamente. Una vez hecho además podemos especificar en esta línea de sintetización el nombre local del acceso o accesser, algo como @synthesize nombre = _nombre_local;

• Ver más >

Gestión de memoria!: de lo más importante en Objective C

• Con el método dealloc liberamos memoria utilizada en las instancias de nuestras clases, lo vamos a sobrecargar para hacer "release" de todas las instancias de objetos que hayamos creado

• Se debe liberar por completo el propio objeto también ,con [super dealloc] . • Debemos liberar las variables de tipado fuerte • Para aprovechar la memoria tenemos que crear nuestro propio “recolector de

basura”, algunos ven esto como muy buena idea y otros no son partícipes de que el lenguaje no tenga las bondades de Java…

Page 9: Desarrolador IOS

• Para gestionar qué variables están siendo utilizadas en memoria, tenemos lo que se llama el “contador de referencia” de Objective C, sólo tenemos que especificar qué hacer con las variables al asignarlas, crearlas, copiarlas, pasarlas por parámetro, etc. y la ejecución del programa hace el resto.

• Con alloc inicializamos memoria en función del tipo o clase de una variable, por ejemplo, cuando declaramos una variable de tipo NSNumber con una instrucción como esta: NSNumber *number = [[NSNumber alloc] initWithFloat:8.75]; lo que pasa internamente es que el contador de referencias se pone a 1.

• Con retain aumentamos el contador de referencias en uno, por ejemplo si deseamos liberar en un contexto la variable pero no queremos que se pierda la variable porque se usa en otro contexto, por ejemplo: -(void) setNombre: (NSString *) _Nombre { [self.nombre autorelease]; self.nombre = [_Nombre retain]; } Usando autorelease se libera al objeto de manera diferida, es decir, es posible usar la variable de la primera línea durante un tiempo más, le dice al compilador que la variable self.nombre se decremente al salir del contexto de la clase de la función setNombre, después le asigna un nuevo valor, el del parámetro, pero a este le dice que lo retenga aunque se borre fuera, para que sea gestionado desde la clase.

• Con release se decrementa el contador en una unidad, si tiene valor 1 ,se borra la variable, es decir, le decimos al sistema que no la necesitamos y éste llama a dealloc automáticamente (primer punto)

• Ojo entonces, si por ejemplo creamos un método en una clase para asignar un

valor y hacemos que use release para luego hacer retain con el nuevo valor que le pasamos, siendo este no una variable sino una llamada de una función, algo así: ________ - (void) setNombre: (NSString*) _Nombre { [self.nombre release]; self.nombre = [_Nombre retain]; } … persona* ego = [[persona alloc] init]; … [ego setNombre:[ego nombre]]; ________ al llamar a la función setNombre, primero se libera el objeto self.nombre antes

Page 10: Desarrolador IOS

de retenerlo, por lo que ya no hay espacio reservado en memoria para esta variable y aquella pasa a estado incoherente ya que el valor que le hemos pasado es precisamente el que hemos liberado, para solucionarlo ponemos autorelease en lugar de release.

• Ahora podremos saber qué tipo de gestión de memoria asignarle a una propiedad, por ejemplo para el nombre en nuestra clase Persona.h: @property (retain) NSString* nombre; al poner retain hemos declarado que el setter (función como setNombre pero automática) debe hacer retain sobre el valor de entrada. Pero podemos especificar con setter=setNombre nuestra propia función ;)

• A veces tenemos que tener cuidado con lo que liberamos de la memoria, no podemos dejar todo en autorelease porque al pasar la referencia de una variable entre funciones es posible que se libere antes de tiempo, sin embargo de esto nos daremos cuenta cuando el simulador haga CRASH! jaja

Variables

• Tipado débil: int, bool, float, etc. • Tipado fuerte: objetos como NSString, NSNumber, etc. • ejemplos de métodos con tipos distintos:

- (int) multiplica: (int)a por:(int)b { return a * b; }   - (NSString *) cadenaResultado: (int)resultado { NSString *res = [NSString stringWithFormat:@"El res ultado es: %d", resultado];

return res; } int resultado = [self multiplica:2 por:3]; NSString *cadena = [self cadenaResultado:resultado] ; NSLog(@"%@", cadena); //devuelve 6

• Un protocolo (@protocol) permite que la lista de métodos que contienen se puedan implementar por cualquier clase, puede especificarse al crear una clase en su definición de la clase que hereda de otra…

@protocol MyProtocol - (void)requiredMethod; @optional - (void)anOptionalMethod; - (void)anotherOptionalMethod; @required - (void)anotherRequiredMethod; @end  

Page 11: Desarrolador IOS

@interface ClassName : ItsSuperclass < protocol lis t >

• Otro método para depurar es usar el log, la función NSLog(cadena) usa el mismo formato que un printf, recordar el formato:

NSLog(@”La fecha y hora actual es %@”, [NSDate date] );

XCode Organizer

• Una vez creada la aplicación, si queremos usarla en un iDevice, tenemos que desplegarla (necesita certificado), conectar el dispositivo por USB y probar! Pero también podemos usar el simulador ,aunque no refleja el comportamiento exacto (real) de la aplicación si que sabremos si funciona o no :)

Consejos

Diseñar como un ingeniero y producir como un artista: echad mano de vuestro amigo el arquitecto de información para encontrar la mejor manera para guardar los datos,… no es lo mismo una base de datos en SQLite que un conjunto de ficheros XML.

• Hay que conocer el flujo de la información en el sistema de aplicaciones, por ejemplo un sistema de etiquetas debe ser recurrente en cuanto a que hay tags que se repiten, la forma óptima de hacer esto es como lo hace WordPress o algunas aplicaciones web si lo prefieres. Debemos consultar a nuestro amigo experto en marketing para el nombre del programa, así será el dominio para publicarlo, también nos aconseja qué tipo de características son viables en cuanto a lo que el público demanda y nos da su perspectiva como experto lo bonito, barato y bueno. Efectivamente, necesitamos conocer cómo construir diagramas pero no son indispensables, aunque sería ideal diseñar previamente la app con UML

• Beta tester: antes de empezar si quiera a pensar en querer programar algo para iOS ,lo primero que debemos hacer es conocer los dispositivos, el iOS y OSX, todos los widgets del sistema operativo, así que al menos tenemos que haber probado todas las aplicaciones antes mencionadas, aunque sea en el ifón de un amigo, no vale haberlo visto en vídeo, tenemos que tocar la esencia de la magia de esta tecnología para sentir qué queremos hacer realmente.

Page 12: Desarrolador IOS

Otros tutoriales para conocer el IDE XCode 3: nos sirve para empezar.

• Tutorial 1 Introducción al iphone sdk • Tutorial 2 : INTERFACE BUILDER

Ahora que conocemos el IDE de la mano de Noemi gracias a sus tutoriales de Youtube, vamos a seguir un poco más adelante, tenemos la ayuda necesitamos aquí:

• Apple Dev Forum • iPhoneSDK (moderado por Erica Sadun) • iPhoneSDKForum • iPhoneDev Forums • iPhoneSB • SQLite para iPhone!: recordar cómo se creaban aplicaciones con XCode que

usen bases de datos SQLite, se trata de un único fichero que podemos fácilmente exportar ya que utiliza instrucciones SQL.

• Podemos seguir el siguiente tutorial de icodeblog. • Tutoriales para empezar a programar juegos con Cocos2D

Ejercicios (Deberes :)

1. Ejercicio 1: crear un proyecto tipo “View-based”, crear una clase Persona y otra clase Usuario, establecer propiedades en las clases asociadas a sus atributos, una clase Usuario debe tener un atributo que sea una clase Persona.

2. Ejercicio 2: Crear un NSArray e inicializarlo con clases NSNumber, luego mostrarlo con NSLog

3. Ejercicio 3: Crear un NSMutableArray e inicializarlo con clases Persona, añadir a dicha clase la función (sobrecargada) description para que NSLog pueda mostrar el contenido de todos sus atributos

Para corregirlo podéis enviármelo con el formulario de contacto.

Page 13: Desarrolador IOS

Conexión de la interfaz con el código: aplicaciones de iOS

Un poco de teoría: Diseño de la aplicación

Guía de estilo para la interfaz de usuario: PDF.

MVC & Eventos

• El patrón de ingeniería del software Modelo-Vista-Controlador actúa en base a clases que tienen la forma: el Modelo define las estructuras de datos, la Vista las ventanas, widgets, etc. que interactúan por medio de los Controladores que unifica los dos anteriores y en sus acciones determina cómo manejar los eventos.

• Estos eventos pueden ser de varios tipos: un gesto (o gesture: secuencia de

eventos desde que el usuario toca la pantalla hasta que deja de hacerlo), toque (touch: el dedo está en la pantalla) y el clásico click o tap que es cuando el usuario toca la pantalla por un instante. A los eventos se les captura por medio de una cadena de clases UIResponder,

Page 14: Desarrolador IOS

que responden una a una, esto se logra con la herencia (subclases de UIView, UIControl,etc.)

siendo la primera de las instancias que responden la que interactúa con el usuario. Lo que se hace una vez despachado el evento por el primer Responder es redirigirlo al siguiente respondedor a mano con la función

“nextResponder”. • Podemos controlar los eventos de tipo Touch, siendo estos pertenecientes a una

vista sobrecargando la función “touchesBegan”, capturando así el inicio de la interacción:

Page 15: Desarrolador IOS

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSUInteger numTaps = [[touches anyObject] tapCount]; NSUInteger numTouches = [touches count]; }

Podéis seguir investigando con métodos como touchesMoved, touchesEnded y touchesCancelled. Con locationInView obtenemos la posición del touch.

Más información >

• Tipos de gestos: swipe (sigue la acción del usuario en un eje determinado, puede implicar dragging o arrastrar y soltar -UIPanGestureRecognizer-) -> UISwipeGestureRecognizer, tapping (cualquier número de toques) -> UITapGestureRecognizer, pinching (acercar o alejar dedos para zoom por ejemplo), rotating (mover dedos en direcciones opuestas) -> UIRotationGestureRecognizer y long press (tocar y mantener) -> UILongPressGestureRecognizer. Todas estas clases reconocedoras son utilizables por medio de la declaración de una clase (entre paréntesis) asociada a la ventana UIWindow (que está asociada a una UIApplication al mismo tiempo) y hace posible la gestión de estos eventos

Page 16: Desarrolador IOS

• Los posibles estados con sus transiciones:

Evidentemente ,un gesto contínuo lanzará varios eventos para poder hacer un seguimiento del mismo, mientras que uno discreto sólo tendrá un evento.

• Los eventos de movimiento están encapsulados por el objeto

CMAccelerometerData que guarda en una estructura la aceleración sobre cada uno de los ejes espaciales. CMGyroData guarda datos sobre la velocidad y CMDeviceMotion encapsula varias medidas, incluyendo la postura, rotación y aceleración. El acelerómetro se ha de configurar antes de usarse, con la clase UIAccelerometer definimos los parámetros.

• La orientación se obtiene con la clase UIDevice invocando el método beginGeneratingDeviceOrientationNotifications, hay un begin y un end para gestionar la secuencia.

• El tipo de eventos que necesitan recoger información de los sensores del giroscopio y el acelerómetro deben ser los primeros en responder por lo que usamos la función canBecomeFirstResponder de una vista y si tenemos permiso entonces activamos con beginReceivingRemoteControlEvent,y desactivándolo cuando no se use la vista con resignFirstResponder.

Page 17: Desarrolador IOS

• Éste es el ciclo de vida de una aplicación, podemos ver como se enmarca la gestión de eventos justo en mitad del loop de eventos:

Recordemos MVC

• El modelo encapsula el estado de la aplicación, contiene los datos, notifica de cambios, etc. La vista representa en pantalla los modelos, pide actualizaciones de estos, envía gestos al controlador, permite al controlador seleccionar una vista, etc. Y el Controlador define el marco de la aplicación, selecciona una vista para la respuesta a una petición, etc.

ahora la versión de Cocoa:

Page 18: Desarrolador IOS

si combinamos el MVC con la aplicación, el modelo de datos y las vistas de cocoa junto con los controladores, esto es lo que obtenemos:

Con este patrón podemos separar el código en función de lo que hace.

• Como hemos visto, los controladores no deciden totalmente lo que hacer con las vistas sino que son estas las que les dicen cuáles pueden utilizar, lo cual no es totalmente un patrón MVC sino Modelo-Vista-Presentación, aquí tenéis el esquema real del funcionamiento actual de una aplicación en iOS:

Page 19: Desarrolador IOS

que se puede ver así representado :

Leer más acerca de la diferencia entre MVC y MVP »

Conexión del constructor de interfaces (I.B.) con las vistas: los controladores de las vistas y los modelos

Controladores de las Vistas

Sabemos que una instancia de una clase controlador tiene la lógica que une los datos de un modelo de una aplicación con las entidades visuales usadas para presentarlos al usuario en el dispositivo. En iOS ,los controladores de las vistas o View Controllers, son objetos que heredan de la clase genérica de Objective C llamada UIViewController.

Page 20: Desarrolador IOS

Gracias a los controladores de las vistas definimos el comportamiento de las interfaces de usuario, podemos encontrar tres tipos de controladores de vistas:

• Custom View Controller: para representar a “nuestra manera” la información, es algo más personalizado; se usa en listas de elementos, presentaciones de estos, propiedades, etc. ( ejemplos: de un UITableViewCell -> UIViewController)

• Container View Controller: sirven para embeber dentro a otros controladores de vistas, definiendo relaciones de navegación entre ellos, suelen utilizarse de forma automatizada los que trae el sistema por defecto (ejemplos: UITabBarController, UINavigationController), establecen relaciones entre controladores

• Modal View Controller : describen las relaciones entre controladores y cómo modifican la representación de los datos, puede ser utilizado por cualquier controlador (en realidad es un modo de funcionamiento, la ventana modal, que no permite continuar con el resto de la aplicación hasta que se ha cerrado la vista/ventana modal)

Por dónde empezar

• Hemos visto que los eventos se gestionan en mitad del ciclo de vida de una aplicación, lo que pasa antes y después se puede controlar por medio del denominado “Application Delegate”, que no es otra cosa que los eventos que ocurren antes ,durante y después del inicio de una aplicación. XCode genera por defecto los archivos {nombre_proyecto}AppDelegate.h y .m que contienen el código que se ejecuta cuando la aplicación se lanza y finaliza, aprovecharemos estas funciones para cargar datos, inicializar componentes, etc. Luego sólo cargar la aplicación, este es el esquema de funcionamiento de iOS:

una vez cargada, puede pasar al estado en segundo plano, de modo que quedaría así:

si,ahora es cuando deberíamos programar los eventos de la clase {loquesea}ApplicationDelegate para descargar memoria o realizar tareas de sincronización mientras trabaja en segundo plano…ya hablaremos de eso más adelante en el capítulo de multitarea…podéis leer más acerca de la implementación de funciones en el

Page 21: Desarrolador IOS

marco común de comportamiento de la aplicación aquí. Ahora debemos reemplazar el esquema de ciclo de aplicación que vimos antes en los eventos por el nuevo esquema en segundo plano:

y si aún no os queda muy claro, podéis echar un vistazo a este esquema:

sacado de cocoanetics: understanding iOS 4 Backgrounding and delegate messaging

Creación de interfaces con el Interface Builder

Conceptos básicos

• Las interfaces se guardan en ficheros .XIB que es un XML y puede editarse a mano aunque para eso está el editor visual :) Cuando se compilan se genera un NIB que puede usarse para inicializar una vista.

• La manera en que utilizamos los widgets, ventanas, vistas y demás objetos en el código es a través de un outlet (llamado IBOutlet en el código), tenemos que definir una variable con este apóstrofe y luego el tipo que sea, normalmente se llaman UI___ por lo que es fácil distinguir las clases de la interfaz. Al posponer la palabra clave IBOutlet a una variable no modifica ni su contenido ni su comportamiento, pero avisa al compilador y al I.B. de que puede ser conectada a un objeto de interfaz incluído en el XIB asociado a dicha clase

Page 22: Desarrolador IOS

• Para definir acciones, es decir, funciones que se puedan activar a través de un evento generado por un elemento de la interfaz (de una vista), utilizamos las bien denominadas IBAction en el código, en este caso el prefijo que se utiliza para definir una función es como un tipo de dato, por lo que si afecta a la declaración de una función ( y no se pueden utilizar dos tipos seguidos en la declaración, recordemos Objective C ) . IBAction indica al editor de interfaces que la función se utiliza por objetos para eventos concretos, como pueda ser, pulsar un botón o cualquier otro…

Interface Builder: ¿qué es?

• Editor que provee de una paleta de objetos para el interfaz de programación con Objective C

• Objetos asociados: cada .xib tiene una clase asociada, este objeto dentro de la ventana de edición de interfaz se llama “File’s owner”, y si recordamos lo que era el first responder, pues este objeto representa al que el usuario tiene asociado para interaccionar, lo mismo pasa con View, que es la vista principal de la clase. Para realizar las asociaciones usamos el botón derecho del ratón y arrastrando y soltando asignamos los outlets y actions que hayamos definido en el código ( Ver ejemplo de asociación )

…simple

• Gracias a la ventana de la biblioteca de objetos podremos arrastrar y soltar los widgets, vistas, etc. que necesitemos y por medio del inspector editamos los atributos de estos, podemos escribir una clase que luego sea la que controle un objeto (aquí la definimos)

Ejercicio:

Escribe un proyecto nuevo View-Based, abre la vista principal y crea una label, desde el código crea una asociación con un atributo -> propiedad con el prefijo IBOutlet, asóciala en el I.B. y luego en el evento de recién cargada la aplicación (appdelegate -> didlaunch with options) accede a dicho objeto de la interfaz para establecer el texto “Hola mundo”, algo sencillo, puedo corregírtela si me la envías en el formulario de contacto.

Page 23: Desarrolador IOS

En esta tercera entrega se explica brevemente como crear un modelo de datos usando XCode (internamente la gente de la manzana se ha apropiado de SQLite para hacerlo, han aprendido de la comunidad otra vez,al igual que en la época NeXT jeje) y cómo crear un servidor en la Google App Engine con las herramientas de Google Web Toolkit que nos proporcionará mediante servicios web la comunicación con una base de datos externa que desplegará datos a todos los dispositivos…lo que quiere decir es que puedes replicar un cambio desde cualquier lugar hacia todos a la vez de forma atómica…apasionante no? Como no todo en esta vida es un camino de rosas, tratar con punteros y tipos de datos complejos tiene sus cosas, en XCode, o mejor dicho, con la versión de Objective C privativa que usamos para compilar nuestras aplicaciones para los dispositivos, tratar con la memoria es como jugar con granadas, desde que lanzas la granada al aire (reservas memoria) hasta que la recoges (liberas memoria), la parábola que surca en el aire podría ser un tanto extraña, también puede perderse la granada o ser reemplazada, meterse entre nubes o chocar con un avión que pasa jaja…con lo cual, nuestra tarea consiste en tratarla como a una mujer,delicadamente…para que no nos explote en la cara…Jugando con la memoriaConceptos básicos para almacenar datos en iOS:

Core Data Foundation

• Es un framework que garantiza la persistencia de los datos de nuestra aplicación • Genera una abstracción con clases que facilitan la implementación final del

almacenamiento • Conecta la parte lógica con el modelo físico (ficheros, etc.)

Core Data Stack

• El objeto que utilizaremos para gestionar colecciones de objetos es el Managed Object Context, también conocido en la jerga NeXT como NSManagedObjectContext, y representa el espacio de un único objeto, es decir, vamos a utilizar este objeto para recuperar todo lo que tiene que ver con una entidad (una entidad es una tabla de la base de datos, también puede contener atributos en relaciones cuyos tipos sean otras entidades o tablas)

• Con Managed Object Model (esquema de base de datos) guardaremos una colección de descripciones de entidades (nombres de tablas) pertenece a la clase

Page 24: Desarrolador IOS

NSManagedObjectContext que relaciona Managed Object Context con el siguiente objeto que hace de puente:

• Persistent Store Coordinator: guarda una colección de almacenes (Persistent Object Stores)

en un conjunto de ficheros de datos persistentes (los ficheros de la base de datos, como en MySQL), su clase es NSPersistentStoreCoordinator y asocia los objetos de la aplicación con los registros de la BD.

Guía de uso del Modelo de Datos de iOS:

1. Para hacer una consulta primero necesitamos crear las tablas, para ello lo que hacemos es crear un proyecto de tipo datos o bien añadimos un fichero al actual proyecto que sea XCode -> Core Data -> Data Model, en la pantalla de creación de entidades añadimos una tabla o entidad y la llamamos “Event”, por ejemplo. Dentro añadimos sus atributos con distintos tipos. Veréis que podéis usar un tipo “Transformable” que sirve para guardar Arrays, siempre y cuando los tipos que guarde el array sean soportamos por el protocolo del modelo de datos, además podemos crear relaciones como atributos que se conectan con otras entidades, aunque esto es más complejo, por ahora sólo veremos los casos simples. (ver más información >>)

Podéis ver que el editor trae un soporte para la vista de un bonito Diagrama de Entidad/Relación de nuestra BD. Una vez creado el modelo vamos a New file -> Core Data -> NSManagedObject subclass (si lo que queremos es una clase que se encargue de realizar todas las operaciones setter & getter por nosotros y así asociemos el

Page 25: Desarrolador IOS

contenido al modelo de datos fácilmente con funciones, aunque también podemos hacerlo aún más automático con un framework como este: Fremont)

2. Para crear objetos es necesario invocar a NSEntityDescriptor con el tipo de objeto y el contexto, veamos un ejemplo:(os recomiendo siempre escribir el código el inglés ;) . Supongamos que nuestra tabla (modelo de datos) event tiene los campos, longitud y latitud de tipo Float, un título (NSString -> String en Modelo de Datos), y una fecha de tipo date:

//Crear y configurar una instancia de una entidad E vent - (void) createEvent { Event *event = (Event *) [NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext: managedObjectContext]; //Crear un tipo de dato coordenada para mapas de go ogle, así nos acostumbramos a usarlos: CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(37.123123, -3.321321); //Ahora lo tenemos muy fácil ,sólo tenemos que usar los setters & getters generados por XCode: [event setLatitude:[NSNumberWithFloat:coordinate.la titude]]; [event setLongitude:[NSNumberWithFloat:coordinate.l ongitude]]; [event setCreationDate:[NSDate date]]; //"date" es la fecha de hoy [event setTitle:@"Mi primer evento"]; }

8. y eso sería todo el código, ahora pasamos a grabar los datos para hacerlos persistentes

9. Para guardar un objeto se utiliza el método save de NSManagedObjectContext, si ocurre algún error se guardará la información del mismo (el motivo por ejemplo) en la variable NSError dispuesta a tal efecto.

NSError *error; if ([managedObjectContext save:&error]){ //prestar esp ecial atención al ampersand...referencia de memoria! //Gestión del error aquí }

11. Podemos intentar capturar errores con una captura de excepciones (@try { } @catch (NSException *exception) { } @finally { } ) pero esto no es recomendable, además de que debemos recordar la cadena de respondedores, aquí se aplica el mismo cuento y tendríamos que ir hacia arriba en la lógica de la programación para capturar la verdadera excepción de la pila de llamadas…cosa que es bastante tediosa, por eso es mejor pensar las cosas bien y hacerlas mejor jeje, con la práctica todo se consigue ;)

Page 26: Desarrolador IOS

Fetch Request -> execute!

12. Si queremos recuperar el objeto no tenemos más que usar, como se hace en php

y mysql una petición tipo “fetch”, es decir, con NSFetchRequest especificamos la entidad, aquí tenéis un ejemplo completo. En resumidas cuentas, hay que crear un objeto NSFetchRequest, reutilizamos el objeto de la descripción de una entidad y asociamos esta al primero con setEntity:

NSFetchRequest *request = [[NSFetchRequest alloc] init]; //cuidado que esto no se libera. lo hace sólo...:O NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext: managedObjectContext]; [request setEntity:entity]; //Para realizar un ORDER BY fecha típico usamos un descriptor de ordenación: NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"creationDate" ascending:NO]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; [request setSortDescriptors:sortDescriptors]; //ahora sí, liberamos los descriptores [sortDescriptor release]; [sortDescriptors release]; //Con todo configurado, vamos a ejecutar la consult a y guardar el resultado en una matriz modificable: NSError *error1; //Atención al parámetro "mutableCopy"!! NSMutableArray *results = [[managedObjectContext executeFetchRequest:request &error1] mutableCopy]; if ( results == nil ){ //Manejar el error! } //Para borrar necesitamos un NSError igual que cuan do guardamos con save NSError *error2; [managedObjectContext deleteObject:objetoEventoPara Borrar]; if (![managedObjectContext save:&error2){ //error... }

Page 27: Desarrolador IOS

24. Nunca os asustéis de un error como éste: significa que no habéis asociado bien el tipo de dato al diccionario usado para realizar una inserción en masa, ánimo!

Que los robots escriban código por nosotros: Servicios Web

Recordamos del curso de servicios web que escribir XML y código de un servicio web no es tarea de humanos, para eso existen frameworks que harán el trabajo duro por nosostros, Fremont es la parte cliente, en Objective C, para la parte del servidor tenemos los transformadores que ya vimos, BPEL, etc.

Gracias al uso de un servidor asociado a Google App Engine, crearemos un conjunto de servicios usando Google Web Toolkit 2.3.0 , que una vez probados en red local podremos desplegar en el servidor Java de GAE asociado a nuestra cuenta de usuario.

Pasos para la creación de un servidor de datos por medio de servicios web con Eclipse:

1. Descargar el IDE Eclipse (Hellios SR2 por ejemplo) y el plugin para GWT . 2. Crear un proyecto de prueba y ejecutarlo

Ir a File -> New - > Project ... -> Google -> Web Application Project . Le dáis un nombre al proyecto y al package sencillos, os recomiendo usar un área de trabajo nueva para no mezclar, aseguraos de que la opción de Google Web Toolkit está marcada y que estamos usando el SDK de GWT - 2.3.0 asó como el Google App Engine con su SDK (el que sea, en mi caso el 1.5.0) y marcad que genere el código de ejemplo para que todo fluya :) . Si todo ha ido bien debéis poder desplegar la aplicación en vuestra cuenta de google sobre el App Engine y ver el producto generado...ya podéis ser un 3% más felices hoy jeje

3. Crear un paquete de servicios para nuestro servidor de aplicaciones de iPhone.

• Pasos: o Crear paquete

En el directorio src pinchamos con el segundo botón y hacemos New ->

Page 28: Desarrolador IOS

Package ,de nombre le damos servicios_iOS, por ejemplo. El primer servicio que vamos a crear es un servicio simple, que muestre una lista de eventos, pero antes debemos crear el modelo de datos que ha de persistir (guardarse) en el Data Store de Google Web Toolkit.

o Crear modelo de datos Dentro de nuestro paquete hacemos click con el botón secundario del ratón y -> New -> Class, de nombre "Evento", claro ,jeje...me gusta escribir todo el código de un servidor en inglés ya que es algo que veremos poco y puede que solamente nosotros...como opciones pues, es una clase pública, etc. Dentro de la clase añadimos ,como os imaginaréis, private Key id; private String titulo; private float latitud; private float longitud; y luego usaremos los generadores de setters y getters que tanto nos gustan de Java en Eclipse, así como los constructores.

Hay un caso curioso, como veréis he puesto Key, en lugar de un int o un Long para la llave principal, esto es debido a que si queremos usar el objeto como algo persistente y almacenable dentro de otra clase, esta llave le permite indexar al objeto por lo que puede serializarse, convertirse en una cadena y quedar guardado en las bases de datos de Google. A cada campo le debéis añadir los protocolos antes de su declaración "@Persistent", y a la llave principal le añadimos @Primary Key , y también: @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY). A la clase Evento además le añadimos el siguiente prefijo: @PersistenceCapable(indetityType = IdentityType.APPLICATION)

o Añadir complejidad al modelo de datos Para aquellos que no se contentan con un modelo de datos simple y quieren asociar una lista de cosas (vaya usté a saber qué más cosas...) pueden usar el prefijo: @Persistent(embedded="true") para variables del tipo private List<TipoDeDatosPropio> miListaDeCosas; Sin olvidarse,claro, de que el tipo de datos propio ha de llevar nuestro querido prefijo @PersistenceCapable(indetityType = IdentityType.APPLICATION) y que ha de contener una llave o Key id para poder serializarse y empaquetarse dentro de un atributo persistente de otra clase persistente

o Crear servicios que usen el modelo de datos Tenemos un modelo donde almacenar datos, utilizaremos un fichero

Page 29: Desarrolador IOS

HTML en el directorio /war/ sencillo donde colocar un formulario con los campos título, longitud,latitud, fecha y cuya acción (o action, con method="POST") irá a "/nombreProyecto/nombreAccionServicio". Para crear el servicio que atenderá la petición vamos a nuestro paquete y -> new -> class -> "nombreAccionServicio". A la clase, le ponemos el prefijo @ServiceName(value="nombreAccionServicio") y hereda (con la palabra clave extends) de la clase HttpServlet, de modo que debemos sobrecargar la función public doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, DatastoreFailureException { //... }, para que atienda la petición enviada desde el fichero HTML por el método POST. Igual que en PHP. Importante que la respuesta se haga de una manera parecida a esta:

resp.setContentType("text/html"); PrintWriter out = resp.getWriter(); out.println("<!DOCTYPE HTML PUBLIC \"-W3C//DTD..... "); //etc

o Una vez que hayamos creado nuestras inicializaciones de datos de nuestro modelo (instanciado las clases oportunas), debemos guardarlos usando un gestor de Persistencia, lo que viene a ser una clase de JDOHelper.getPersistenceManagerFactory("transactions-optional"); que por medio de la función makePersistent(clase_instanciada) se almacenará (después debemos hacer un close(), claro jeje)

o Añadir las rutas al war/WEB-INF/web.xml --> este es el enrutador de acciones, asocia un nombre de ruta en una url (con un patrón) a un servicio web (con una clase que extiende de HttpServlet). Para ello,abrimos el fichero y creamos dentro del web-app un hijo servlet que especifica la asociación con el programa usando la palabra clave servlet-name:nombreServicio y servlet-class: nuestra clase nombreAccionServicio. Por último, para el mapeado, es decir, la forma en que un patrón o expresión regular asocia una ruta url a nuestro servlet, añadimos un hijo de "web-app" llamado servlet-mapping con el mismo servlet-name:nombreServicio y url-pattern:/nombreAccionServicio

• Repetir operación para crear servicios que generen listas de datos, en nuestro caso nos hará falta que se genere un listado XML de todos los <eventos> con sus etiquetas para cada campo que tiene el modelo de datos. Para ello, añadimos una función a la clase Evento que genere la cadena XML como un string en UTF8 y un Servlet que la muestre por pantalla, en este caso la cabecera ha de ser text/xml ;)

• Más información en este tutorial >>

Page 30: Desarrolador IOS

Análisis sintáctico: XML - Servicio Web - iOS

Como ya hemos visto antes el funcionamiento de la persistencia de datos en iOS con un Modelo de Datos, pasaré directamente al análisis de un XML , aunque esto debería hacerse automáticamente con un framework como Fremont, lo importante es que os quedéis con que las clases que permiten estas tareas son NSURL, NS/Mutable\URLRequest,y NSURLConnection. Estas descargan los datos y podemos realizar el análisis sintáctico con varios tipos de "parser", SAX: envío de notificaciones a medida que el analizador sintáctico va leyendo la cadena XML recibida por NSURL, o bien DOM: es un analizador que lee toda la cadena XML y construye su representación completa. Para utilizar estos analizadores debemos incluir al proyecto el fichero libxml2 de las librerías del SDK (tanto SAX como DOM), o bien NSXMLParser (sólo SAX). Aunque existen alternativas como TBXML, TouchXML, KissXML, TinyXML, GDataXML, etc. --> ver comparativa >>

Pasos para utilizar NSXMLParser:

1. Crear el parser con la información recibida de NSURL.

NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData]; //recibidos por NSURL

2. Asignar un delegado (delegate), una clase que se encarga de recibir los eventos,i.e., que hereda de <NSXMLParserDelegate>

[parser setDelegate:self];

3. Comenzar a parsear con [parser parse];

A partir de aquí, se trata de usar los eventos del delegado (eventos de NSXMLParserDelegate) que son: didStartElement -> empieza un elemento, didEndelement, didStartDocument, etc. de forma que al principio del documento inicializamos los datos (un array modificable, por ejemplo), y cada vez que encuentra un elemento de tipo titulo pues añade un elemento al array y luego al terminar un elemento de tipo evento, pues añade el array de propiedades de un evento al array de eventos…fácil…Importante implementar también la función:

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError; ya que nunca se sabe qué errores se pueden cometer, por ejemplo que el documento no llegue completo, y la aplicación pende de un hilo por una mala gestión de este tipo de errores…algo que no da buena imagen por lo que hay que pulir estos detalles…

Page 31: Desarrolador IOS

Uso de caché La utilización de caché tanto para datos persistentes como para datos dinámicos es importante a la hora de realizar aplicaciones para móviles ya que está muy penalizado el uso de conexiones a Internet, es más rentable llegar a un equilibrio de carga de datos entre el servicio web y los usuarios de los dispositivos, por eso os presento las dos tareas a realizar en un proyecto serio…

Caché de imágenes con ASI Http Framework

En esta dirección encontraréis un framework sorprendente para realizar tareas con análisis sintácticos de XML, descarga de datos a través de urls, caché, etc.

Es una maravilla ver como funcionan los ejemplos, hay uno,especial de caché en el que se dispone una serie de descargas de imágenes y a cada una de ellas se le asocia un puesto en una clase cola-de-espera que tiene asociada una barra de progreso por lo que va actualizando el estado conforme se van descargando los datos…impresionante :)

Caché de datos con SQlite

Lo que podéis hacer es utilizar una caché para guardar la información en un modelo de datos, durante unos días en el iDevice, pasados esos días, vuelve a sincronizarse con el servicio de Google App Engine y reescribimos toda la estructura de datos del programa con nueva información, así nos ahorramos realizar peticiones al servidor continuamente, lo que ralentizaría mucho la carga y si la aplicación es muy usada puede generar un cuello de botella que ha de evitarse.

Para hacer esto nos viene bien los datos de configuración de un usuario, lo veremos en la próxima entrega del curso de aplicaciones para iOS, aquí mismo.

Recordar que podéis utilizar recursos como http://wiki.gnustep.org y http://stackoverflow.com/ para solucionar vuestras dudas tanto con Objective C como con Java Google App Engine y GWT.

En cualquier caso, los ejercicios de esta entrega están claros cuáles son: desplegar una aplicación en GAE con GWT y pasar los datos a un modelo de datos de XCode (SQlite)…hay cientos de tutoriales en internet sin embargo si tenéis cualquier duda, mandadme el fichero y lo intentaré corregir.

Page 32: Desarrolador IOS

En la entrega anterior del Curso de Aplicaciones de iOSvimos como guardar datos persistentes, pero ¿qué pasa si lo que queremos guardar son de muchos tipos y en distintas partes del código?, ¿escribimos una clase con métodos estáticos que acceda a un modelo de datos configuración con valores transformables?…vale, esto se podría hacer pero tenemos una opción más sencilla y que además se puede utilizar en la interfaz de configuración del propio iOS especialmente dedicada a nuestra aplicación.

Preferencias de la aplicación iOS

Un mundo en un diccionario

Estamos hablando de las preferencias por defecto del usuario. En otras palabras, vamos a usar la clase NSUserDefaults, que accede las preferencias que creemos o bien por código o bien con el diseñador de “Settings Bundle”, estas quedan almacenadas en un fichero plist (xml), donde se define cada elemento como un diccionario que contiene los diferentes parámetros en tipos de datos que son diccionarios internamente.

Ejemplo de creación: File -> New File -> Resource -> Settings Bundle.

Normalmente el nombre que se le suele dar al fichero es “Root.plis”, la edición dentro de XCode de este tipo de ficheros de configuración viene a ser algo así:

Estos diccionarios tienen unos tipos de atributos, los siguientes:

• Type: tipo de la preferencia • Title: título de la preferencia, se utiliza para codificar las traducciones también

(en lugar de Key)

Page 33: Desarrolador IOS

• Key: clave para recuperar la preferencia desde el código • DefaultValue: valor por defecto • IsSecure: se utiliza para campos de texto como contraseñas • KeyboardType: tipo de teclado, puede ser Alphabet, NumbersAndPunctuation,

NumberPad, URL y EmailAddress (al igual que en las preferencias de Interface Builders para campos de texto)

• AutocapitalizationType: se especifica como None, Sentences o Words y convierte lo elegido a mayúsculas

• AutocorrectionType: si se pone como Yes entonces se utiliza el corrector automático de iOS en el campo

Veréis que existen varios tipos de datos a la hora de añadir un diccionario que contiene un elemento de preferencias, estos tipos pueden ser:

• PSTextFieldSpecifier: campo de texto, para un usuario o password, o cualquier otro posible uso

• PSTitleValueSpecifier: es un título, de sólo lectura, puede preceder a otro campo que no tenga título propio

• PSToggleSwitchSpecifier: elemento ON/OFF, es decir, puede servir como booleano

• PSSliderSpecifier: campo sin título, sirve para establecer un valor numérico con una escala, tiene un máximo y un mínimo, además del valor por defecto también podemos especificar una imagen para cada lado de los límites

• PSMultiValueSpecifier: sirve para mostrar una lista de valores entre los que podemos elegir

• PSGroupSpecifier: una manera elegante de separar cada zona de configuración haciendo un grupo (o subgrupo)

• PSChildPaneSpecifier: al pinchar en este campo se abrirá una nueva página de configuraciones asociada (otro plist)

Aquí un ejemplo de las opciones:

En el caso que utilicemos un panel hijo el esquema sería algo parecido a esto:

Page 34: Desarrolador IOS

Siempre podemos acceder a las opciones a través de la clase NSUserDefaults ,de la misma manera que funciona un diccionario (como un NSMutableSet), los objetos que guardemos tienen que ser serializables, como ocurría con el modelo de datos para poder utilizarse como un valor. En concreto, las preferencias se guardan desde NSUserDefaults -> standardUserDefaults, si creamos campos de preferencias para usuario,nivel, nombre y otro de password el código a usar es así:` NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *strUser = [defaults objectForKey:@"usuario"]; int level = [[defaults objectForKey:@"nivel"] intValue] ; NSString *strName = [defaults objectForKey:@"nombre"]; NSString *strPass = [defaults objectForKey:@"password"];

Si cargamos la aplicación por primera vez, debemos guardar los valores, igual que antes pero usando la función

[[NSUserDefaults standardUserDefaults] setValue:(id) forKey:(NSString*)];

[NSUserDefaults standardUserDefaults] synchronize]; //imprescindible!

,evidentemente, si los tipos de datos son numéricos, tenemos que encapsularlos en una clase NSNumber, etc.

Para reiniciar los valores en el simulador (esto debemos hacerlo también para cuando cambiemos un fichero traducido de Interface Builder) , borramos el directorio ~/Library/Application Support/iPhone Simulator/User/Applications o bien, abrimos el simulador, y borramos la aplicación o por último, en el menú del simulador – > Restablecer contenidos y ajustes. Esta última opción borrará todas las compilaciones de XIB a NIB (ficheros de I.B.).

Info.plist

Este fichero, según la documentación de XCode, contiene la información de configuración de la aplicación (en un bundle), puede utilizarse para especificar el icono de la aplicación, incluso una cadena “audio” en la variable UIBackgroundModes para cuando la aplicación pasa a segundo plano y estamos haciendo streaming ,que no se corte.

Page 35: Desarrolador IOS

O se puede indicar con la cadena “voip” para que se use un socket para VoIP (luego hace falta que se invoque al método setKeepAliveTimeout: handler para especificar la frecuencia con la que despertar la aplicación para que funcione bien la aplicación, por ejemplo, para cargar el buffer que se está reproduciendo).

Más información >

Traducciones de aplicaciones de iOS

Más diccionarios en ficheros de texto

Para realizar las traducciones utilizamos dos aplicaciones de XCode que extraen las cadenas de texto a traducir a todos los idiomas: ( ver recursos de traducción> ),

• genstrings: extrae de los ficheros .m todos los usos de la función NSLocalizedString(@”LLAVE”, @”COMENTARIO PARA SABER DE QUÉ VA LA TRADUCCIÓN DE LA LLAVE EN SU SITIO”). En el código reemplazamos todas las cadenas a traducir @”Mi Cadena” , por NSLocalizedString(@”miCadenaX”, “Mi Cadena”)…fácil… Ejemplo de uso:, desde ~directorioDeMiProyecto$

genstrings -o es.lproj *.m

• toma como entrada todos los ficheros de implementación y busca NSLocalizedString, creando un fichero codificado con el juego de caracteres UTF-16 con líneas así: /* Comentario de la llave */ “llave” = “Texto para la llave”; Una vez generados los ficheros para los .m en cada idioma, pues se traducen y luego se deben añadir los directorios al proyecto, si véis que no coge las cadenas de texto, quitad los directorios (en.lproj) sin borrarlos ,sólo la referencia, y luego añadirlos de nuevo

• Truco para traducir los títulos de las preferencias (la configuración) de la aplicación: cuando creamos nuestro Settings.bundle ,dentro aparece un fichero llamado Root.plist , pues bien, vamos a crear un nuevo fichero en el proyecto ,de tipo Resource -> Strings File, le llamamos, claro, Root.plist, cuando nos pregunte si queremos sobreescribir le diremos que sí, después, en la ventana de inspección, en la pestaña de Localization añadimos los idiomas.

• ibtools: Haremos lo mismo que en con el truco anterior, en la pestaña de Localization añadimos los idiomas a utilizar, después podemos usar una instrucción como esta para extraer todas las cadenas de texto (si no queremos traducir todo a mano con el editor del Interface Builder,claro), una vez añadidos los idiomas:

ibtool --generate-strings-file en.lproj/MiFichero.strings en.lproj/MiFichero.xib

Page 36: Desarrolador IOS

• En el fichero strings estarán las cadenas de texto a traducir, una vez hecho, las escribimos de nuevo al .xib:

ibtool --write MiFichero.strings MiFichero.xib

• Más información de XCode 4 y ibtool >

Los strings se pueden enviar a empresas de traducción para que hagan su trabajo y luego con esos pasos ya está todo hecho. Para probar que los idiomas funcionan sólo hay que ir a la configuración del iDevice y cambiar en los Ajustes Generales el idioma en Internacional. En este capítulo de programación del Curso de Aplicaciones de iOS me gustaría hablar sobre la localización en dispositivos que usan iOS así como de la multitarea ya que son cosas muy relacionadas, por eso remataremos el tema con los avisos, es decir, notificaciones locales, esos mensajitos que los programas lanzan, y como poner un número en nuestra icono de nuestra aplicación, también llamado “badge”; que por cierto también lo tiene la clase UITabBarItem, o sea, un botón de una barra de botones tipo pestañas o tab’s… ;-)

Multitarea en las aplicaciones de iOS Las plantillas generadas por el IDE XCode 4 ya traen las funciones que toda aplicación debe utilizar cuando la aplicación pasa entre los diferentes estados que iOS le permite. Cuando usamos el botón Home desde nuestra aplicación hay un evento, otro cuando este ha terminado, otro cuando se vuelve a la aplicación, etc.

Debemos utilizar la menor cantidad de recursos posible durante la multitarea ya que si nos pasamos el SO decidirá cerrar nuestra app aunque la mayoría de apps no ejecutan ningún código durante su tiempo en modo de segundo plano.

Esta multitarea, como sabemos está disponible sólo para dispositivos con firmware a partir de la versión 4, que fué cuando se introdujo dicha característica, para ello existe la función del SDK multitaskingSupported que nos informará de cada caso.

UIDevice* device = [UIDevice currentDevice]; BOOL backgroundSupported = NO; if ([device respondsToSelector:@selector(isMultitaskin gSupported)]) backgroundSupported = device.multitaskingSupported;

Lo primero que debemos hacer es incluir la opción UIBackgroundModes en nuestro Info.plist, y se debe especificar los valores: audio, location y/o voip para disponer de estas características durante el trabajo en segundo plano.

En nuestra clase Delegate de la aplicación tenemos las funciones siguientes:

• applicationdidFinishLaunchingWithOptions • applicationDidBecomeActive • applicationWillResignActive • applicationDidEnterBackground

Page 37: Desarrolador IOS

• applicationWillEnterForeground • applicationWillTerminate

No se recomienda utilizar llamadas a OpenGL (tampoco sería muy lógico) durante el segundo plano, debemos cancelar todos los servicios Bonjour antes de pasar a este estado así como no mostrar mensajes si hay errores de conexión, guardar datos, etc. Si tenemos una aplicación que muestra muchos datos visuales lo mejor es liberarlos hasta que se vuelva del segundo plano, pero sí debemos responder a las notificaciones de conexión y desconexión.

Para realizar la inclusión de una tarea en segundo plano existe la función llamada beginBackgroundTaskWithExpirationHandler, que pide al sistema un tiempo extra para completar una tarea larga y para finalizarla tenemos endBackgroundTask, sabemos el tiempo que lleva ejecutándose gracias a la propiedad backgroundTimeRemaining de nuestra UIApplication. Deberíamos usar estas funciones y propiedades si estamos descargando archivos de datos, configurando o guardando información sensible.Un ejemplo:

-(void)applicationDidEnterBackground:(UIApplication *) application { UIApplication* app = [UIApplication sharedApplicati on];

//Pedir permiso para ejecutar en background. Provee r de un manejador por //si la tarea requiere más tiempo NSAssert (bgTask == UIBackgroundTaskInvalid, nil); bgTask = [app beginBackgroundTaskWithExpirationHand ler:^{ dispactch_async(dispatch_get_main_queue(), ^{

if (bgTask != UIBackgroundTaskInvalid) { [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; } }) }]; dispatch_async(dispatch_get_global_queue(DISPATCH_Q UEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^{ if (bgTask != UIBackgroundTaskInvalid) { [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; } }); }); }

Page 38: Desarrolador IOS

Notificaciones:

No son más que tareas en segundo plano pero que usan la clase UILocalNotification, los tipos existentes son: sound (sonido como los de whatsapp o un sms),alert (el texto de un sms, etc.) y badge (el número que hay sobre el icono de la app).

Se pueden programar hasta 128 notificaciones simultáneas, cada una de ellas se puede repetir en un intervalo prefijado por el programador.

Veamos un ejemplo de una alarma con un archivo de sonido donde se comprueba si está en segundo plano:

- (void) scheduleAlarmForDate: (NSDate *) theDate { UIApplication* app = [UIApplication sharedApplicati on]; NSArray* oldNotifications = [app scheduledLocalNoti fications]; if ([oldNotifications count] > 0) [app cancelAllLocalNotifications];

UILocalNotification* alarm = [[[UILocalNotification alloc] init] autorelease];

if (alarm) { alarm.fireDate = theDate; alarm.timeZone = [NSTimeZone defaultTimeZone]; alarm.repeatInterval = 0; alarm.soundName = @"alarmsound.caf"; alarm.alertBody = @"Time to wake up!"; [app scheduleLocalNotification:alarm]; } }

Para el audio se indica en nuestro Info.plist, una cadena “audio” en UIBackgroundModes como hemos visto, cuando la aplicación esté en segundo plano ,limitaremos que se ejecute más de lo necesario…En el caso de “voip” se debe configurar además un socket, por medio de la función setKeepAliveTimeout: handler: para especificar la frecuencia con la que despertar la aplicación para el correcto funcionamiento del servicio.

Para las notificaciones por posicionamiento, sólo se realizan con cambios significativos de localización, aunque pueden seguir usando los servicios de localización en segundo plano no conviene abusar a menos que desarrollemos un GPS como Tomtom.

Activación:

CLLocationManager *locManager = [[[CLLocationManage r] alloc] init ];

Page 39: Desarrolador IOS

[locManager startMonitoringSignificantLocationChang es];

Podemos además usar la característica de despertar y relanzar nuestra aplicación si nos llega una notificación push.

Location Framework El posicionamiento es posible usarlo a través del SDK de Apple con un iDevice gracias al Location Framework, que nos proporciona la localización del usuario, información que podemos utilizar para mostrar recursos cercanos, orientar en rutas, realizar un seguimiento o tracking del desplazamiento, dibujar zonas o áreas sobre el mapa con líneas y otras primitivas geométricas, etc. Además el Location Framework da acceso a la brújula para mejorar la experiencia a la hora de realizar una orientación más realista, por ejemplo se usa en la aplicación Mapas de Google al pulsar el icono de brújula

Configurando el método de posicionamiento

iOS nos permite conseguir la localización actual con diferentes métodos de posicionamiento:

• Por cambio significativo: es un método para bajo consumo de batería • Servicios de posicionamiento estándar: es más configurable • Monitorización por región: para registrar cambios en una zona definida

Para aplicaciones que utilicen CoreLocation.framework ( #import <CoreLocation/CoreLocation.h>) además debemos añadir al Info.plist (recordar cómo se configura una aplicación en la anterior entrega de este curso de apps) de nuestra aplicación el campo UIRequiredDeviceCapabilities y la llave “location-services” si sólo necesitamos posicionamiento general (aproximado, por ejemplo con una triangulación por antenas de telefonía bastaría) o bien especificamos además la llave “gps” para usar el hardware del iPhone y el iPad (1,2,etc).

Una vez seleccionado el método y configurada la aplicación, pasamos a inicializar el servicio de posicionamiento, para ello utilizamos la clase CLLocationManager,

Page 40: Desarrolador IOS

Obtener la posición del usuario (por defecto)

Nada mejor que un ejemplo para mostrar el funcionamiento:

- (void)startStandardUpdates { // Crea el objeto location manager si no existe if (nil == locationManager){ locationManager = [[CLLocationManager alloc] init]; } //Especificamos la clase actual como clase que maneje los eventos del localizador locationManager.delegate = self; //la clase actual debe heredar de CLLocationManagerDelegate,claro //Ahora especificamos una precisión locationManager.desiredAccuracy = kCLLocationAccura cyKilometer;

// para los movimientos entre eventos que suceden: locationManager.distanceFilter = 500; //inicializar ya! [locationManager startUpdatingLocation]; }

Con desiredAccuracy (kCLLocationAccuracyBest) se establece la precisión del posicionamiento, con distanceFilter (kClDistanceFilterNode) se configura la distancia mínima que es necesario que se aprecie en un desplazamiento del dispositivo para que se produzca un evento de actualización de la localización actual del usuario.

Obtener la posición del usuario por cambio significarivo

- (void)startSignificantChangeUpdates { // Crear el objeto the location manager si no exist e: if (nil == locationManager){ locationManager = [[CLLocationManager alloc] init]; }

Page 41: Desarrolador IOS

locationManager.delegate = self; //igual que antes //esto es lo nuevo, inicialización con actualiz ación por cambio significativo [locationManager startMonitoring SignificantLocatio nChanges]; }

Crear una clase para las “chinchetas” o pins sobre el mapa, esta clase almacena información relevante sobre lo que queremos mostrar al pinchar sobre ella, desde el propio pin o chincheta (situación, color, imagen, animación,etc.) además del pequeño título y subtítulo que se muestra y la posterior acción a realizar al extender la información de la misma.

¿Qué es un “pin”?: Podemos extender la clase (por herencia) de una MkAnnotation y luego en la clase controlador de la vista (UIViewController) extender esta al delegado de un mapa (UIVIewController <MkMapViewDelegate>) para poder recoger el evento de cuando se pinta un pin o chincheta y decirle al objeto MkMapView cómo mostrar la información que almacena la clase anotación, es en este evento controlado donde cambiamos la imagen del pin asociado al MkAnnotation, el botón de acción o cualquier otra cosa que queramos a voluntad.

Como observación, recordad que cuando MKMapView carga podemos utilizar la posición del usuario que viene integrada como opción (UseUserLocation, el framework CoreLocation nos proporciona otra, debemos saber cuál utilizar en qué caso ya que no podemos tener siempre cargado un mapa, o sincronizar los valores de diferentes clases cada vez que la posición cambia, etc. Si ocurre un error al recibir la información de localización el evento locationManagerdidFailWithError nos informará de ello, a veces, el gestor de posicionamiento devuelve información en una caché así primero debemos comprobar en qué momento se generó mediante la propiedad timestamp de las posiciones recibidas.

Para consumir menos batería debemos desactivar los servicios de posicionamiento por medio de una configuración de usuario o bien cuando no se necesiten; usar el servicio de cambios significativos en lugar del servicio estándar, y un valor pequeño para distanceFilter (igual que en los videojuegos ,mientras mayor es el búfer, más memoria y procesamiento se necesita por lo que consume más batería). Por último, desactivar los eventos de posicionamiento si la precisión (diferencia entre cambios de posición) no mejora en un corto período de tiempo.

Información geolocalizada

Lo interesante de poder disponer de un framework de Google MapKit es que podemos extraer información a partir del campo longitud y latitud, un ejemplo que he utilizado en una de mis aplicaciones es el siguiente:

- (void) searchBarTextDidEndEditing:(UISearchBar *) _searchBar { [searchBar resignFirstResponder]; NSError *error; NSString *urlString = [NSString stringWithFormat:@" http://maps.google.es/maps/geo?q=%@&output=csv ",[_searchBar.text

Page 42: Desarrolador IOS

stringByAddingPercentEscapesUsingEncoding:NSUTF8Str ingEncoding]]; NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString] encoding:NSISOLatin2String Encoding error:&error]; NSArray *listItems = [locationString componentsSeparatedByString:@","];

if ([listItems count]>=4 && [[listItems objectAtIndex: 0] isEqualToString:@"200"]){ if (birthLocation!=nil){ [mk_mapView removeAnnotation:birthLocation ]; [birthLocation moveAnnotation:CLLocationCo ordinate2DMake( [[listItems objectAtIndex:2] doubleValue] , [[listItems objectAtIndex:3] doubleValue])]; [birthLocation setTitle:_searchBar.text ]; [birthLocation setSubtitle:[NSString stringWithFormat:@"%f,%f", [[listItems objectAtIndex:2] doubleValu e], [[listItems objectAtIndex:3] doubleValu e]]]; } else { birthLocation = [[MapAnnotation alloc] ini tWithCoordinate: CLLocationCoordinate2DMake([[listItems objectAtIndex:2] doubleValue], [[listItems objectAtIndex:3] doubleVa lue]) title:_searchBar.text subtitle:[NSS tring stringWithFormat:@"%f,%f", [[listItems objectAtIndex:2] double Value], [[listItems objectAtIndex:3] double Value]] ]; } [mk_mapView addAnnotation:birthLocation]; } else { [self AlertWithMessage:@"No se pudo encontrar la direcció n"]; }

}

Este código se aplica a una vista MkMapView con una barra de búsqueda, además he creado una clase para anotaciones básica MapAnnotation que lógicamente hereda de MkMapAnnotation (no instanciable a menos que se cree otra de NSObject que herede

Page 43: Desarrolador IOS

de esta).

Debemos guardar una pequeña caché de anotaciones para hacer más eficiente la aplicación, podemos utilizar el array de la propia vista del mapa de MkMapView.

En el caso en que queramos desarrollar un evento que nos vaya actualizando la información encontrada sobre una región haciendo una geolocalización inversa podemos crear una clase parecida a ésta:

@implementation MyGeocoderViewController (CustomGeocodingAdditions) - (void)geocodeLocation:(CLLocation*)location forAnnotation:(MapLocation*)annotation { MKReverseGeocoder* theGeocoder = [[MKReverseGeocode r alloc] initWithCoordinate:location.coordinate];

theGeocoder.delegate = self; [theGeocoder start]; }

// Delegate methods - (void)reverseGeocoder:(MKReverseGeocoder*)geocode r didFindPlacemark:(MKPlacemark*)place { MapLocation* theAnnotation = [map annotationForCoordinate:place.coordinate];

if (!theAnnotation) return;

Page 44: Desarrolador IOS

// Associate the placemark with the annotation. theAnnotation.placemark = place; // Add a More Info button to the annotation's view. MKPinAnnotationView* view = (MKPinAnnotationView*)[ map viewForAnnotation:annotation];

if (view && (view.rightCalloutAccessoryView == nil)) { view.canShowCallout = YES; view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; } }

- (void)reverseGeocoder:(MKReverseGeocoder*)geocode r didFailWithError:(NSError*)error { NSLog(@"Could not retrieve the specified place info rmation.\n"); } @end @implementation MKMapView (GeocoderAdditions) - (MapLocation*)annotationForCoordinate:(CLLocation Coordinate2D)coord { // Iterate through the map view's list of coordinat es // and return the first one whose coordinate matche s // the specified value exactly. id theObj = nil;

for (id obj in [self annotations]) { if (([obj isKindOfClass:[MapLocation class]])) { MapLocation* anObj = (MapLocation*)obj; if ((anObj.coordinate.latitude == coord.latitude) && (anObj.coordinate.longitude == coord.longitude)) { theObj = anObj; break; } } }

return theObj; } @end

Page 45: Desarrolador IOS

En el framework MapKit de Google se utiliza la proyección Mercator, que es un tipo específico de proyección cilíndrica para todo el globo. Esto es muy útil para poder realizar una navegación fácil por las imágenes de un mapa tanto en longitud y latitud como en altura, esto se especifica con los tipos de datos para coordenadas que se especifican con CClocationCoordinate2D y las áreas con MKCoordinateSpan y MKCoordinateRegion. Un punto del mapa es un par de valores (x, y) en la proyección Mercator. Se utilizan para simplificar los cálculos matemáticos entre puntos y áreas. Los puntos se especifican con MKMapPoint y las áreas con MKMapSize y MKMapRect. Un punto es una unidad gráfica asociada con el sistema de coordenadas de un objeto UIView. Por lo tanto, se puede asociar directamente la vista del mapa con la interfaz. Los puntos se especifican mediante CGPoint y las áreas mediante MKMapSize y MKMapRet.

Si queremos pasar al sistema de posicionamiento de un GPS estándar, podéis usar esta función de una de mis aplicaciones:

- (NSString*) toDegreesMinutesSeconds:(CLLocationCo ordinate2D) nLocation { //Longitud: N, Latitud: W int degreesW = nLocation.latitude; double decimalW = fabs(nLocation.latitude - degreesW); int minutesW = decimalW * 60; int degreesN = nLocation.longitude; double decimalN = fabs(nLocation.longitude - degreesN); int minutesN = decimalN * 60; return [NSString stringWithFormat:@"%dN%d;%dW%d",degreesW,minutesW,d egreesN,minutesN]; }

Por último sólo me queda recordaros los conceptos básicos de la geolocalización y posicionamiento con CoreLocation y MapKit de Google:

• Se puede añadir un MapView mediante el Interface Builder. • De manera programática, se debe crear una instancia de MKMapView e inicializarlo mediante el método initWithFrame: y añadirlo como una subvista dentro de la jerarquía de vistas.

• Propiedades principales de MapView • region (tipo MKCoordinateRegion). • Define la parte visible del mapa. Es posible cambiar esta propiedad en cualquier momento, asignando a ésta un valor. • centerCoordinate. Define la posición central del mapa

• Anotaciones 1) Definir un objeto de anotación concreto. • Es posible utilizar MKPointAnnotation para crear una anotación simple. Contiene un popup con un título y un subtítulo. • Definir un objeto personalizado que siga el protocolo MKAnnotation 2) Definir una vista de anotación para presentar la información en la pantalla. • Si la anotación se puede representar con una imagen estática, se debe crear una instancia de MKAnnotationView y asignar la imagen a la propiedad image. • Si se desea crear la anotación de chincheta, crear una instancia de

Page 46: Desarrolador IOS

MKPinAnnotationView. • Si la imagen estática es insuficiente, se puede crear una subclase de MKAnnotationView e implementar el código de dibujado para representarla. 3) Implementar el método mapView:viewForAnnotation: en el delegate del MapView. 4) Añadir la anotación usando el método addAnnotation: o addAnnotations:.

• Overlays • 1) Definir el Overlay apropiado (MKCircle, MKPolygon, MKPolyline o una subclase de MKShape o MKMultiPoint). • 2) Definir una vista para representar el overlay en la pantalla. • 3) Implementar el método mapView:viewForOverlay en el MapView delegate. • 4) Añadir el objeto al mapa mediante addOverlay:.

Y hasta aquí la entrega de este curso, los ejercicios de esta entrega son seguir el tutorial siguiente: introducción a MapKit .

http://www.programadorphp.org/blog/cursos/curso-de-creacion-de-aplicaciones-para-ios-iphone-ipad-ipod/