242
Repaso 1

iOS Avanzado

Embed Size (px)

Citation preview

Page 1: iOS Avanzado

Repaso 1

Page 2: iOS Avanzado

QUE NECESITO PARA HACER UNA APP IOS

Hardware Software Conocimiento Ideas Tiempo

Page 3: iOS Avanzado

HARDWARE NECESARIO

Mínimo

Recomendado

Page 4: iOS Avanzado

SOFTWARE NECESARIO

Mínimo

Recomendado

SDK PIXELMATOR SMULTRON

SDK PHOTOSHOP DREAMWEAVER SMARTSVN NAVICAT

Page 5: iOS Avanzado

CONOCIMIENTO NECESARIO PARA HACER APPS

Diseño gráfico y de interfacesObjective - CCocoaQuartzC/C++OpenGL ES 1.5/2.0GLSLHTML5JavascriptCSSPHPSQLiteMySQL

Page 6: iOS Avanzado

¿QUE VAMOS A APRENDER EN ESTE CURSO?

Objective CCocoaQuartz

HTML5Javascript

CSS

OpenGL ES 1.5OpenGL ES 2.0

GLSL

PHPMySQLSQLite

Page 7: iOS Avanzado

1.Introducción

- Vamos a crear una aplicación de ejemplo y repasaremos algunos aspectos básicos

- Daremos un repaso a Objective - C

- Gestión de memoria con ARC

- Delegation and Core Location

Page 8: iOS Avanzado

2.Patrones básicos en cocoa: Patrón MVC

El modelo-vista-controlador es un patrón arquitectónico. Se emplea para estructurar las interfaces de los programas de una manera en la que separamos tareas diferentes en diferentes capas. Por un lado tenemos la presentación, por otro la lógica de aplicación y por otro la información del dominio.

Page 9: iOS Avanzado

2.Patrones básicos en cocoa: Delegation

El mecanismo de delegados consiste en que un clase A implementa l o s métodos de o t r a c l a se B pa r a hace r se ca r go de l comportamiento de esta, cuando esos métodos son llamados. El objeto A se llama delegado de B

Page 10: iOS Avanzado

3.Aplicación de ejemplo

- Ver diferentes templates de proyectos XCode- Iconos- Imágenes de Lanzamiento- Cómo creamos las interfaces desde Interface Builder- Declaración de variables- Declaración de métodos - Creando conexiones de variables y objetos de IB- Enlazando objetivos y acciones- Ejecución en el simulador- Ejecución en el dispositivo- Debugger- Instruments

Page 11: iOS Avanzado

4.Gestión de vistas

- Las diferentes vistas se gestionan como diferentes capas, pudiendo superponer unas a otras y soportando transparencias.

Page 12: iOS Avanzado

5.Ciclo de vida

Delegate Controlador Vista

Inicio

Fin

Botón {Preferencias()}

Inicio {AbrirTitulo()}

AbrirPreferencias() Preferencias()

AbrirTitulo()

Page 13: iOS Avanzado

6.Objective-C

- Objective-C es una extensión del lenguaje C

- Las aplicaciones iOS se desarrollan utilizando Objective-C y las librerías de Cocoa Touch

- Cocoa Touch está escrito en Objective-C y es un conjunto de APIs que nos permiten programar las aplicaciones

Page 14: iOS Avanzado

6.Clases

Coche

int numRuedas; //Atributo

-(int) getNumRuedas; //Método-(void) setNumRuedas:(int)n;

- Clase: Define una estructura y comportamiento.- Objeto: Es una instancia a una clase.- Atributo: Propiedad de una clase.- Método: Define un comportamiento- Evento: Interacción del usuario con la máquina.- Mensaje: comunicación dirigida a un objeto ordenándole que ejecute uno de sus métodos.

Page 15: iOS Avanzado

7.Instancias

Para crear una instancia de una clase primero debemos pedir memoria

NSMutableArray *listaPersonas = [NSMutableArray alloc];

Pero para ser usado ese objeto, necesita ser inicializado, para ello llamaremos a su método constructor.

[listaPersonas init];

Se suele usar mediante llamadas anidadas:

NSMutableArray *listaPersonas = [[NSMutableArray alloc] init];

Page 16: iOS Avanzado

8.Mensajes

- Mensaje : Comunicación dirigida a un objeto para que ejecute uno de sus métodos

NSMutableArray *listaPersonas = [[NSMutableArray alloc] init]; [listaPersonas addObject:@""];

Page 17: iOS Avanzado

9.Listas y bucles

- NSMutableArray es como NSArray pero la gestión de memoria es dinámica, podemos añadir y quitar elementos y el compilador gestionará el tamaño en memoria de la lista

NSMutableArray *listaPersonas = [[NSMutableArray alloc] init]; [listaPersonas addObject:@"Carlos"]; [listaPersonas addObject:@"David"]; [listaPersonas addObject:@"Juan"]; for (int i=0;i<[listaPersonas count];i++) { NSLog(@"Posición %d de la lista es %@",i,[listaPersonas objectAtIndex:i]); }

- Usamos NSArray para crear listas de objetos. NSArray tiene un tamaño fijo, como una constante, definida cuando se crea

Page 18: iOS Avanzado

10.NSString

- NSString es una clase de Objective-C usada para representar cadenas.

NSString *cadena1 = @"Reserva de memoria implícita"; NSString *cadena2 = [[NSString alloc] initWithString:@"Reserva de memoria con reference counting"];[cadena2 release]; NSString *cadena3 = [[[NSString alloc] initWithString:@"Reserva de memoria autorelease"] autorelease];

Page 19: iOS Avanzado

11.Heredar de una clase

- Todas las clases de Objective-C tiene una superclase, excepto NSObject

- Una clase hereda el comportamiento de su superclase

Page 20: iOS Avanzado

12.Variables y métodos de instancia

@interface Persona : NSObject { NSString *nombre; NSString *dni; int edad;}

@property (nonatomic, retain) NSString *nombre;@property (nonatomic, retain) NSString *dni;@property (nonatomic) int edad;

@end

- Para acceder a las variables de instancia hace falta tener una instancia de esa clase.

Page 21: iOS Avanzado

12.Variables y métodos de instancia

Propiedades: @property (atributos) tipo nombre;

Lectura/escritura: - readwrite: opción por defecto en la definición de las propiedades, y permite que se pueda leer y escribir su contenido. - readonly: Esta opción indica que la propiedad es solo de lectura.

Tipo de asignación: - assign: opción por defecto. Se realiza la asignación sin invocar a retain. Este tipo se suele utilizar para propiedades de tipos que no son objetos. - retain: Se realiza la asignación invocando a retain. Este tipo solo se puede utilizar para objetos. - copy: se le asigna a la propiedad una copia del objeto pasado al setter.

Page 22: iOS Avanzado

12.Variables y métodos de instancia

Atomicidad: - atomic: es la opción por defecto y su función es bloquear la asignación para que ningún hilo pueda modificar la propiedad mientras lo está haciendo otro hilo. - nonatomic: en esta opción no hay bloqueo, por lo que cualquier hilo puede modificar el valor sin tener que esperar a que termine el otro.

Métodos para acceder: - getter=nombre: por defecto, si no se indica nada, el getter tendrá el mismo nombre que el identificador de la propiedad. Por ejemplo: getName. - setter=nombre: por defecto, si no se indica nada, el setter tendrá como nombre al identificador de la propiedad precedido de la palabra set. Por ejemplo: setName.

Page 23: iOS Avanzado

13.Métodos de clase vs métodos de instancia

Método estático de la clase:

+ (tipo) nombre; + (tipo) nombre:(tipo)param1 ... nombreArgN:(tipo)paramN;

Método de la clase:

- (tipo) nombre; - (tipo) nombre:(tipo)param1 ... nombreArgN:(tipo)paramN;

Page 24: iOS Avanzado

14.Gestión de memoria

Implícito

NSString *cadena = [NSString stringWithString:@”Hola”]; //sin alloc

Manual

alloc +1retain +1release -1

NSString *cadena = [[NSString alloc] initWithString:@”Hola”];[cadena release];

Automático

NSString *cadena = [[NSString alloc] initWithString:@”Hola”];[cadena autorelease];

Page 25: iOS Avanzado

15.Gestión de memoria con ARC

- ARC: Automatic Reference Counting

- Evitamos tener que liberar memoria de forma manual y el tiempo que perdemos buscando memory leaks.

- ARC es un paso de pre-compilación que añade retain/release/autorelease sin tener que añadirlo manualmente.

- A pesar de eso, no nos podemos olvidar de la gestión de memoria por completo.

Page 26: iOS Avanzado

15.Gestión de memoria con ARC

Page 27: iOS Avanzado

15.Gestión de memoria con ARC

- Cuando habilitamos ARC nuestro código queda de la siguiente manera:

NSObject *obj = [[NSObject alloc] init];

- Esto no significa que el contador de referencias desaparezca, sino que es automático.

- Cuando el precompilador considera que el objeto no se va a usar más, añade automáticamente una línea para liberar ese objeto:

[obj release];

Page 28: iOS Avanzado

16.Referencias fuertes

Se utiliza el atributo strong.

Especifica que hay una relación de propiedad (fuerte) sobre el objeto de destino.

Sin ARC:@property(retain) NSObject *obj;

Con ARC:@property(strong) NSObject *obj;

@property(weak) NSObject *obj;

Page 29: iOS Avanzado

17.Referencias débiles

Se utiliza el atributo weak.

Especifica que hay una relación de no-propiedad (débil) sobre el objeto de destino. Contamos con la ventaja de que si el objeto en cuestión resulta desalojado de la memoria (dealloc), entonces el valor de la propiedad se ajustará automáticamente a nil y, por tanto, en ningún caso se producirá un cuelgue del programa dado que el lenguaje permite enviar mensajes a nil.

Page 30: iOS Avanzado

Repaso 2

Page 31: iOS Avanzado

1.Introducción

- Cadenas- Propiedades comunes de los objetos- Labels- Imágenes- Slider- AlertView- MapKit- Text Input- UIView- UIScrollView- UIWebView- Múltiples controladores - AppDelegate

Page 32: iOS Avanzado

2.Cadenas

Componemos la cadena con el texto de los labels y textfields

! NSString *cadena = [[NSString alloc] initWithString:@"La cadena es : "];! cadena = [cadena stringByAppendingFormat:label1.text];! cadena = [cadena stringByAppendingFormat:textField1.text];!! cadena = [cadena stringByAppendingFormat:label2.text];! cadena = [cadena stringByAppendingFormat:textField2.text];! !

Page 33: iOS Avanzado

3.Propiedades comunes de los objetos

UIView *objeto = [[UIView alloc] initWithFrame:CGRectMake:(0,0,300,300)];

Posición: [objeto setCenter:CGPointMake(30,30)];

Tamaño: [objeto setBounds:CGRectMake(0,0,300,300)];

Transparencia: [objeto setAlpha:0.5];

Hidden: [objeto setHidden:TRUE];

...

Page 34: iOS Avanzado

4.Label

Cambiar texto de un UILabel:

[label setText:@”Texto deseado”];

Page 35: iOS Avanzado

5.Imágenes

Instanciar UIImageView y añadirlo a la vista

UIImageView *imagen = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,100,100)]; [imagen addSubview:self.view];

Cambiar imagen de un UIImageView:

UIImage *im = [UIImage imageNamed:@”imagen.jpg”];imagen.image=im;

Page 36: iOS Avanzado

6.Slider

Método que se invocará cuando el slider cambie de valor

-(IBAction)valueChange:(id)sender{! int valor = (int)slider.value;! NSString *cadena = [NSString stringWithFormat:@"El valor es %d",valor];! [label setText:cadena];}

Configuración del slider en la carga de la vista

- (void)viewDidLoad { [slider setMinimumValue:1.0];! [slider setMaximumValue:10.0];! [slider setValue:5.0]; [super viewDidLoad];}

Page 37: iOS Avanzado

7.UISegmentedControl

Detectar cambio en un UISegmentedControl.

- (IBAction)segmentAction:(id)sender{! switch ([sender selectedSegmentIndex]) !{! ! case 0: //Hacer algo para la primera pestaña! ! ! break;! ! case 1: //Hacer algo para la primera pestaña! ! ! break;! }}

Page 38: iOS Avanzado

8.AlertView

Mostramos un AlertView

UIAlertView *alert = [[UIAlertView alloc]! ! ! initWithTitle: @"Titulo"! ! ! message: @"Texto aquí"! ! ! delegate: nil! ! ! cancelButtonTitle:@"OK"! ! ! otherButtonTitles:nil];

[alert show];[alert release];

Page 39: iOS Avanzado

9.MapKit

Configuración del mapa en la carga de la vista

- (void)viewDidLoad {!! [mapa setMapType:MKMapTypeHybrid]; ! [mapa setZoomEnabled:YES]; ! [mapa setScrollEnabled:YES]; ! [mapa setShowsUserLocation:YES];!! [super viewDidLoad];}

Debemos añadir los frameworks de MapKit

Page 40: iOS Avanzado

10.Text Input

- Cajas de introducir texto, permiten seleccionar diferentes tipos de teclados

- Para liberar teclado implementar el protocolo UITextFieldDelegate y sobreescribir el método de su delegado

- (BOOL)textFieldShouldReturn:(UITextField *)tf{ [tf resignFirstResponder]; return YES;}

Page 41: iOS Avanzado

11.UIView

- Uno de los elementos más usados

- Podemos tener vistas dentro de otras vistas de manera jerárquica

- Podemos sobreescribir el método drawRect para que la vista pinte algo específico

Page 42: iOS Avanzado

12.UIScrollView

- Crearemos una app de tipo Single View Application

- Crear un UIScrollView programáticamente y añadirlo a la vista

- Implementar UIScrollViewDelegate en nuestro controlador

- Crear un UIImageView y asignarle una imagen, añadirlo al UIScrollView

- Sobreescribir los métodos adecuados para que nos permita hacer zoom sobre la imagen

Page 43: iOS Avanzado

12.UIScrollView

- Creación y ajuste de elementos

//Instanciar ScrollView sv = [[UIScrollView alloc] initWithFrame: CGRectMake(0,0,self.view.bounds.size.width, self.view.bounds.size.height)]; [self.view addSubview:sv]; [sv release]; //Instanciar UIImageView iv = [[UIImageView alloc] initWithFrame: CGRectMake(0,0,self.view.bounds.size.width*2, self.view.bounds.size.height*2)]; [sv addSubview:iv]; [iv release]; //Asignar propiedades UIImageView [iv setImage:[UIImage imageNamed:@"wallpaper.jpg"]]; //Asignar propiedades UIScrollView [sv setContentSize:CGSizeMake(iv.bounds.size.width, iv.bounds.size.height)]; [sv setMinimumZoomScale:0.5]; [sv setMaximumZoomScale:2.5]; [sv setZoomScale:1.0 animated:TRUE]; [sv setDelegate:self];

Page 44: iOS Avanzado

12.UIScrollView

- Devolver vista sobre la que se realizará zoom

-(UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView{ return iv;}

Page 45: iOS Avanzado

13.UIWebView

Cargar web en un UIWebView

-(IBAction)irAPagina:(id)sender{ ! //Obtain the url! NSString *urlAddress = textField.text;!! //Create an NSURL Object! NSURL *url = [NSURL URLWithString:urlAddress];!! //URL Request Object! NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];!! //Load the request in the UIWebView.! [webView loadRequest:requestObj];}

Page 46: iOS Avanzado

14.UITabBar

Seleccionar tabButton

//Seleccionar el primer elemento   [tabBar setSelectedItem:[tabBar.items objectAtIndex:0]];     Recoger pulsación

- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {! switch (item.tag) {! ! case 0:! ! ! //Pestaña1! ! ! break;! ! case 1:! ! ! //Pestaña2! ! ! break;! }! ! !}

Page 47: iOS Avanzado

15.Múltiples controladores

Cargar un controlador y añadirlo a una vista

! controlador1=[[Controlador1 alloc] initWithNibName:@"Controlador1" bundle:nil]; ! [vistaControladores addSubview:controlador1.view];

Quitar la vista de un controlador y liberarlo

[controlador1.view removeFromSuperview]; [controlador1 release]; controlador1=nil;

Page 48: iOS Avanzado

16.AppDelegate

#import “ClaseD.h”

ClaseD *mainDelegate = (ClaseD*) [[UIApplication sharedApplication] delegate];

[mainDelegate cambiarAVista2];

- Para controlar el ciclo de vida utilizaremos el AppDelegate.

- En AppDelegate instanciaremos nuevos controladores y eliminaremos los que no usemos.

- Desde los controladores podremos acceder al AppDelegate para invocar un método

Page 49: iOS Avanzado

Repaso 3

Page 50: iOS Avanzado

1.Introducción

- UITableView y UITableViewController- Edición de UITableView- UINavigationController- UIPopoverController - Vistas Modales

Page 51: iOS Avanzado

2.UITableView

- Podemos trabajar con este elemento haciendo subclassing de UITableViewController o i m p l e m e n t a n d o e l d e l e g a t e d e UITableViewControllerDelegate en otro controlador

- Vamos a crear un Single View Application

- Mostrar una lista de los meses en un UITableView

Page 52: iOS Avanzado

2.UITableView

- Implementar el protocolo UITableViewDelegate en nuestro controlador

- Escribir los métodos del delegate

- Rellenar array

lista = [[NSMutableArray alloc] initWithCapacity:12]; [lista addObject:@"Enero"]; [lista addObject:@"Febrero"]; [lista addObject:@"Marzo"]; [lista addObject:@"Abril"]; [lista addObject:@"Mayo"]; [lista addObject:@"Junio"]; [lista addObject:@"Julio"]; [lista addObject:@"Agosto"]; [lista addObject:@"Septiembre"]; [lista addObject:@"Octubre"]; [lista addObject:@"Noviembre"]; [lista addObject:@"Diciembre"];

Page 53: iOS Avanzado

2.UITableView

- Métodos para rellenar contenido

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [lista count];}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"]; if (!cell) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"] autorelease]; }

[[cell textLabel] setText:[lista objectAtIndex:indexPath.row]]; return cell;}

Page 54: iOS Avanzado

2.UITableView

- Método para modo de editar

- (IBAction)toggleEditingMode:(id)sender{ if ([tableView isEditing]) { [sender setTitle:@"Editar"]; [tableView setEditing:NO animated:YES]; } else { [sender setTitle:@"Hecho"]; [tableView setEditing:YES animated:YES]; }}

Page 55: iOS Avanzado

2.UITableView

- Método para mover celdas

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { if (fromIndexPath.row == toIndexPath.row) { return; }

//Quitamos elemento de posición from NSString *e1 = [lista objectAtIndex:fromIndexPath.row]; [e1 retain]; [lista removeObjectAtIndex:fromIndexPath.row]; //Lo añadimos a posición to [lista insertObject:e1 atIndex:toIndexPath.row]; [e1 release];}

Page 56: iOS Avanzado

2.UITableView

- Método para eliminar celdas

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { //Eliminamos el elemento del array [lista removeObjectAtIndex:indexPath.row]; //Y del tableView con una animación [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES]; }}

Page 57: iOS Avanzado

2.UITableView

- Método para cuando seleccionemos una celda

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ NSLog(@"Ha seleccionado el mes %@",[lista objectAtIndex:indexPath.row]);}

Page 58: iOS Avanzado

3.UINavigationController

- Cambiemos algo de código en AppDelegate para añadir un UINavigationController

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after application launch.

UIViewController *vc = [[[ViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease]; self.window.rootViewController = [[[UINavigationController alloc] initWithRootViewController:vc] autorelease]; [self.window makeKeyAndVisible]; return YES;}

Page 59: iOS Avanzado

3.UINavigationController

- Configurar barra de navegación

//Poner título[self.navigationItem setTitle:@"Meses"]; //Añadir botón editarUIBarButtonItem *editar = [[UIBarButtonItem alloc] initWithTitle:@"Editar" style:UIBarButtonItemStylePlain target:self action:@selector(toggleEditingMode:)];

self.navigationItem.rightBarButtonItem = editar;[editar release];

Page 60: iOS Avanzado

4.UIPopoverController

- Vamos a hacer un nuevo ejemplo pa r a iPad y u sa remos un UIPopoverController, que sólo está disponible en estos dispositivos y no en iPhone e iPod Touch

- Crear proyecto Single View Application

- Añadir otro controlador y programarlo con la vista que queramos mostrar

Page 61: iOS Avanzado

4.UIPopoverController

1. Implementar protocolo UIPopoverControllerDelegate

2. Crear puntero en la cabecera de nuestro controlador

UIPopoverController *mesesPopover;

3. Escribir métodos del delegate UIPopoverController

4. Invocar popOver cuando corresponda

Page 62: iOS Avanzado

4.UIPopoverController

- Método del delegate

- Nos permitirá responder al evento de cerrar esta vista modal

- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController{ [popOver autorelease]; popOver = nil;}

Page 63: iOS Avanzado

4.UIPopoverController

- Método que lo despliega

-(IBAction)verPopover:(id)sender{ if (popOver==nil) { Otro *otro = [[[Otro alloc] initWithNibName:@"Otro" bundle:nil] autorelease];

popOver = [[UIPopoverController alloc] initWithContentViewController:otro]; [popOver setDelegate:self]; [popOver presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } else { [popOver dismissPopoverAnimated:YES]; [popOver autorelease]; popOver = nil; }

}

Page 64: iOS Avanzado

5.Vistas Modales

- Las vistas modales son ViewControllers predefinidos que nos proporciona el sistema y que nos permiten hacer cosas habituales como seleccionar una foto, enviar un mail, elegir un contacto, etc..

Page 65: iOS Avanzado

5.Vistas Modales

- En nuestro ejemplo vamos a añadir un botón nuevo que nos permita enviar un mail

1. Implementar el protocolo

MFMailComposeViewControllerDelegate

2. Añadir el framework

MessageUI.framework#import <MessageUI/MessageUI.h>#import <MessageUI/MFMailComposeViewController.h>

3. Sobreescribir mensajes del delegate implementado

4. Crear mensaje lanzador

Page 66: iOS Avanzado

5.Vistas Modales

- Sobreescribir método del delegate implementado

- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {!! [self dismissModalViewControllerAnimated:YES];}

- Nos permitirá responder al evento de cerrar esta vista modal

Page 67: iOS Avanzado

5.Vistas Modales

- Invocar vista modal

Class mailClass = (NSClassFromString(@"MFMailComposeViewController"));! if (mailClass != nil)!{! ! if ([mailClass canSendMail]) {! ! ! MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];! ! ! picker.mailComposeDelegate = self;! ! ! picker.navigationBar.barStyle = UIBarStyleBlack;! ! !! ! ! [picker setSubject:@"Título del mensaje"];! ! !! ! ! // Añadir destinatarios por defecto! ! ! NSArray *toRecipients = [NSArray arrayWithObject:@"[email protected]"]; ! ! !! ! ! [picker setToRecipients:toRecipients];! ! !! ! ! // Rellenar contenido del mail por defecto! ! ! NSString *emailBody = @"Introduce aquí tu texto...";! ! ! [picker setMessageBody:emailBody isHTML:NO];! ! !! ! ! [self presentModalViewController:picker animated:YES];! ! ! [picker release];!!! ! } ! }

Page 68: iOS Avanzado

Repaso 4

Page 69: iOS Avanzado

1.Introducción

- Notification and Rotation- Camera- Settings- Localization- Multitarea- Gestures- Animaciones- Transiciones predefinidas entre vistas

Page 70: iOS Avanzado

2.Notification and Rotation- Usaremos notificaciones para atender a ciertos eventos, como por ejemplo la rotación del dispositivo

// Obtener el objeto dispositivo UIDevice *device = [UIDevice currentDevice]; // Empezar a monitorizar notificaciones que tengan que ver con la orientación [device beginGeneratingDeviceOrientationNotifications];

//Añadir notificación y asignarle un método NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:device];

- (void)orientationChanged:(NSNotification *)note{ NSLog(@"orientationChanged: %d", [[note object] orientation]);}

- Método manejador

Texto

Page 71: iOS Avanzado

3.Camera

- Crearemos un programa que tome una foto de la biblioteca y la asigne en un UIImageView sobre la pantalla

Page 72: iOS Avanzado

3.Camera

- Implementar el protocolo UIImagePickerControllerDelegate

- Obtener foto

-(IBAction)tomarFoto:(id)sender{ //Instanciar controlador UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { [imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera]; } else { [imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; }

//Asignar la vista actual como delegate y presentarlo [imagePicker setDelegate:self]; [self presentModalViewController:imagePicker animated:YES]; [imagePicker release]; }

Page 73: iOS Avanzado

3.Camera- Obtener foto seleccionada

- (void)imagePickerController:(UIImagePickerController *)pickerdidFinishPickingMediaWithInfo:(NSDictionary *)info{ //Obtener la imagen y asignarla a un UIImageView UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; [imagen setImage:image]; [self dismissModalViewControllerAnimated:YES];}

Page 74: iOS Avanzado

4.Settings

NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];

standardUserDefaults setObject:@”Hola” forKey:@"clave"];

[standardUserDefaults synchronize]; !

NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];

NSString *cadena = [[standardUserDefaults objectForKey:@”clave”] retain];

- Guardar preferencias

- Cargar preferencias

Page 75: iOS Avanzado

5.Localization - XIBs

- Creamos nuevo proyecto Single View Application

- Añadimos algunos labels y otros elementos a la vista

- Después añadimos a la localización de la vista el idioma Español

- Desde la consola usaremos el comando ibtool para generar el fichero de cadenas a partir de nuestro xib

ibtool --generate-strings-file ~/Desktop/ViewController.strings ViewController.xib

- Ese comando genera un fichero con el siguiente contenido:

/* Class = "IBUILabel"; text = "Texto del label"; ObjectID = "8"; */"8.text" = "Texto del label";

/* Class = "IBUINavigationItem"; title = "Titulo de la vista"; ObjectID = "10"; */"10.title" = "Titulo de la vista";

Page 76: iOS Avanzado

5.Localization - XIBs

- Editamos el fichero para asignar los valores del idioma al que vamos a traducir

/* Class = "IBUILabel"; text = "Texto del label"; ObjectID = "8"; */"8.text" = "Label text";

/* Class = "IBUINavigationItem"; title = "Titulo de la vista"; ObjectID = "10"; */"10.title" = "Title of the view";

- Ahora usamos ibtool para crear un nuevo Xib basado en nuestro xib anterior

ibtool --strings-file ~/Desktop/ViewController.strings --write ../es.lproj/ViewController.xib ViewController.xib

- Esto nos modifica el XIB cuando ese idioma sea el de por defecto utilizando los valores del fichero de texto

Page 77: iOS Avanzado

5.Localization - Código

- Para localizar el código de la aplicación utilizaremos la macro NSLocalizedString

- Vamos a crear un label dinámicamente y queremos que esté localizado

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,100,20)];[self.view addSubview:label]; [label setText: NSLocalizedString(@"Label añadida",@"El texto de la label que añadimos") ];

- Desde la consola usaremos el comando

$ genstrings ViewController.m

Page 78: iOS Avanzado

5.Localization - Código

- Ese comando nos genera el fichero Localizable.strings que contiene lo siguiente

/* El texto de la label que añadimos */"Label añadida" = "Label añadida";

- Lo modificamos

/* El texto de la label que añadimos */"Label añadida" = "Label added";

- Añadimos el fichero Localizable.strings a nuestro proyecto

- Si ejecutamos la app en el idioma al que hemos traducido, saldrá localizada, ya que NSLocalizedString lee el fichero Localizable.strings

Page 79: iOS Avanzado

6.Multitarea

- Controlamos la multitarea mediante la implementación de ciertos métodos en el AppDelegate de nuestras aplicaciones

Page 80: iOS Avanzado

7.Gestures

• UITapGestureRecognizer - Toque

• UIPinchGestureRecognizer - Zoom

• UIRotationGestureRecognizer - Rotar

• UISwipeGestureRecognizer - Arrastrar izquierda o derecha

• UIPanGestureRecognizer - Mover

• UILongPressGestureRecognizer - Pulsación larga

- Podemos definir ciertos comportamiento para los gestos que registra una vista mediante Gesture Recognizers

Page 81: iOS Avanzado

7.Gestures

- La forma de trabajar con gestures es la siguiente:

1. Implementar protocolo UIGestureRecognizerDelegate

2. Crear el gesture regognizer y añadirlo a la vista

-(void)swipeRightAction:(UIPanGestureRecognizer*)gestureRecognizer{ //METER CÓDIGO}

3. Crear método que se encarga de recoger el evento cuanto se produce ese gesto sobre la vista

UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRightAction:)];

swipeRight.direction = UISwipeGestureRecognizerDirectionRight;swipeRight.delegate = self;[self.view addGestureRecognizer:swipeRight];![swipeRight release];

Page 82: iOS Avanzado

8.Animaciones

//DEFINIR PROPIEDADES INICIALES

imagen.alpha=1;

[UIView beginAnimations:nil context:NULL];[UIView setAnimationDuration:0.5];[UIView setAnimationDelegate:self];

!//DEFINIR PROPIEDADES FINALES!imagen.alpha=0;

[UIView commitAnimations];

- Crear una interpolación en el valor de las propiedades de un objeto

Page 83: iOS Avanzado

9.Transiciones predefinidas entre vistas

[primerControlador.view removeFromSuperview];[primerControlador release];primerControlador=nil;

[UIView beginAnimations:nil context:NULL];[UIView setAnimationDuration:1.0];[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.window cache:YES];! segundoControlador = [[SegundoControlador alloc] initWithNibName:@"SegundoControlador" bundle:nil];! [self.window addSubview:segundoControlador.view];[UIView commitAnimations];

- Eliminar primer controlador, instanciar segundo y indicar transición

Page 84: iOS Avanzado

9.Transiciones predefinidas entre vistas

Posibles transiciones entre vistas:

UIViewAnimationTransitionCurlUpUIViewAnimationTransitionCurlDownUIViewAnimationTransitionFlipFromLeftUIViewAnimationTransitionFlipFromRigth

Page 85: iOS Avanzado

19.Touch Events and UIResponder

Page 86: iOS Avanzado

- Elementos como UIScrollView permiten hacer eventos multitáctiles pero su comportamiento está predefinido.

- Usaremos Touch Events cuando queramos un comportamiento específico.

- En este apartado crearemos una vista sobre la que poder pintar usando las características multitouch de los dispositivos.

- Utilizaremos el gesto doble-tap para limpiar la pantalla

1.Touch Events

Page 87: iOS Avanzado

- Heredando de UIResponder podremos sobreescribir cuatro métodos para capturar los distintos eventos:

1.Touch Events

Uno o varios dedos tocan la pantalla:- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

Uno o varios dedos se mueven por la pantalla:- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;

Uno o varios dedos son retirados de la pantalla:- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;

El sistema interrumpe este movimiento antes de que termine por una llamada u otro evento:- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

Page 88: iOS Avanzado

- Podemos tocar y arrastrar sobre la pantalla y arrastrar para dibujar una linea

- Con doble-tap limpiamos la pantalla

2.Creating the TouchTracker Application

Page 89: iOS Avanzado

- Crear proyecto de tipo Single View Application

- Crear objeto Line de tipo NSObject

- Crear objeto TouchDrawView de tipo NSObject y personalizarlo para que herede de UIView

- Asociar un objeto de tipo UIView de ViewController.xib en interface builder con nuestra clase TouchDrawView

- Sobreescribir los métodos de dibujado en TouchDrawView

- Sobreescribir los métodos de toques en TouchDrawView

2.Creating the TouchTracker Application

Page 90: iOS Avanzado

Line.h

#import <Foundation/Foundation.h>

@interface Line : NSObject {CGPoint begin;CGPoint end;

}@property (nonatomic) CGPoint begin;@property (nonatomic) CGPoint end;@end

Line.m

#import “Line.h”

@implementation Line

@synthesize begin,end;

@end

2.Creating the TouchTracker Application

Page 91: iOS Avanzado

TouchDrawView.h

#import <Foundation/Foundation.h>

@interface TouchDrawView : UIView{ NSMutableArray *lines; CGPoint lineInProgressBegin; CGPoint lineInProgressEnd;}

@end

2.Creating the TouchTracker Application

Page 92: iOS Avanzado

TouchDrawView.m

#import “TouchDrawView.h”#import “Line.h”

@implementation TouchDrawView

-(id)initWithCoder:(NSCoder*)c{ self = [super initWithCoder:c]; if (self) { lines = [[NSMutableArray alloc] init]; lineInProgressBegin = CGPointMake(0,0); lineInProgressEnd = CGPointMake(0,0); [self setMultipleTouchEnabled:FALSE]; } return self;}

-(void)dealloc{ [lines release]; [super dealloc];}

2.Creating the TouchTracker Application

Page 93: iOS Avanzado

- Sobreescribir el método drawRect para dibujar lo que queramos sobre la vista.

3.Drawing with TouchDrawView

- (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetLineWidth(context, 10.0); CGContextSetLineCap(context, kCGLineCapRound); // Lineas completas en negro [[UIColor blackColor] set]; for (Line *line in lines) { CGContextMoveToPoint(context, [line begin].x, [line begin].y); CGContextAddLineToPoint(context, [line end].x, [line end].y); CGContextStrokePath(context); } // Linea en progreso en rojo [[UIColor redColor] set]; if (!CGPointEqualToPoint(lineInProgressBegin,lineInProgressEnd)){ CGContextMoveToPoint(context, lineInProgressBegin.x, lineInProgressBegin.y); CGContextAddLineToPoint(context, lineInProgressEnd.x, lineInProgressEnd.y); CGContextStrokePath(context); }}

Page 94: iOS Avanzado

- Hacer el método de limpiar

3.Drawing with TouchDrawView

- (void)clearAll { // Borrar la lineas [lines removeAllObjects]; // Redibujar [self setNeedsDisplay];}

Page 95: iOS Avanzado

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ //Capturamos el evento del primer dedo detectado UITouch *t = [touches anyObject]; if ([t tapCount] > 1) { [self clearAll]; } else { //Inicializamos el comienzo y el final de la linea lineInProgressBegin = [t locationInView:self]; lineInProgressEnd = lineInProgressBegin; // Redibujar [self setNeedsDisplay]; }}

4.Turning Touch into Lines

Page 96: iOS Avanzado

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t = [touches anyObject];

//Actualizamos el final de la linea lineInProgressEnd = [t locationInView:self];

// Redibujar [self setNeedsDisplay];}

4.Turning Touch into Lines

Page 97: iOS Avanzado

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ //Creamos un objeto de tipo linea y

//le asignamos los valores de nuestra linea en progreso Line *linea = [[Line alloc] init]; linea.begin=lineInProgressBegin; linea.end=lineInProgressEnd; //Añadimos dicho objeto a la lista de lineas [lines addObject:linea]; //Reiniciar linea en progreso lineInProgressBegin = CGPointMake(0,0); lineInProgressEnd = CGPointMake(0,0); // Redibujar [self setNeedsDisplay];}

4.Turning Touch into Lines

Page 98: iOS Avanzado

5.The Responder Chain

UIResponder no maneja el evento porque al no estar sobrescritos sus métodos entonces envía el evento al siguiente respondedor.

Mandar mensaje explícitamente al siguiente respondedor:[[self nextResponder] touchesBegan:touches withEvent:event];

Page 99: iOS Avanzado

- Crear dos botones, uno de Save y otro de Load en nuestra vista y asociarlos a dos métodos que implementaremos

6.Saving and Loading

-(IBAction)save:(id)sender{ NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:lines]; [prefs setObject:data forKey:@"Lineas"]; [prefs synchronize];}

- Para poder utilizar el método archiveDataWithRootObject es necesario que implementemos en nuestro objeto Line algunos métodos

Page 100: iOS Avanzado

6.Saving and Loading

-(IBAction)load:(id)sender{

if (lines!=nil) { [lines release]; } NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; NSData *data = [prefs objectForKey:@"Lineas"]; if (data != nil) { NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:data]; if (oldSavedArray != nil) lines = [[NSMutableArray alloc] initWithArray:oldSavedArray]; else lines = [[NSMutableArray alloc] init]; } [self setNeedsDisplay];}

Page 101: iOS Avanzado

6.Saving and Loading

Line.m

- (void)encodeWithCoder:(NSCoder *)coder;{ [coder encodeFloat:begin.x forKey:@"beginX"]; [coder encodeFloat:begin.y forKey:@"beginY"]; [coder encodeFloat:end.x forKey:@"endX"]; [coder encodeFloat:end.y forKey:@"endY"];}

- (id)initWithCoder:(NSCoder *)coder;{ self = [[Line alloc] init]; if (self != nil) { begin = CGPointMake([coder decodeFloatForKey:@"beginX"],[coder decodeFloatForKey:@"beginY"]); end = CGPointMake([coder decodeFloatForKey:@"endX"],[coder decodeFloatForKey:@"endY"]); } return self;}

Page 102: iOS Avanzado

7.Circles

- Podemos tocar y arrastrar sobre la pantalla y arrastrar para dibujar una círculo

- Con doble-tap limpiamos la pantalla

Page 103: iOS Avanzado

//Establecer contextoCGContextRef context = UIGraphicsGetCurrentContext();

//Poner Color[[UIColor blackColor] set];

//Calcular cuadrado que lo englobaCGPoint vector = CGPointMake(circle.end.x-circle.begin.x, circle.end.y-circle.begin.y);float radio = sqrt(pow(vector.x,2) + pow(vector.y,2));float diametro = radio*2;float origenX = circle.begin.x - radio;float origenY = circle.begin.y - radio;CGRect bordes = CGRectMake(origenX,origenY,diametro,diametro);

//Pintar CírculoCGContextFillEllipseInRect(context,bordes);

7.Circles

Page 104: iOS Avanzado

- UIControl es la superclase de muchos elementos de Cocoa Touch, entre ellos UIButton o UISlider.

- UIControl sobreescribe los mismos métodos de UIResponder que hemos visto en este apartado

- UIControl asocia cada posible evento con una constante. Por ejemplo UIControlEventTouchUpInside.

8.UIControl

Page 105: iOS Avanzado

8.UIControl

- Veamos como UIControl manejaría el evento UIControlEventTouchUpInside

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {// Referencia al toque que acaba de terminar UITouch *touch = [touches anyObject];

//Posición del punto en el sistema de coordenadas del control CGPoint touchLocation = [touch locationInView:self];

// ¿Esta el punto dentro de los márgenes de mi vista?if (CGRectContainsPoint([self bounds], touchLocation)) {

//Enviar mensaje a todos los targets registrados para este evento[self sendActionsForControlEvents:UIControlEventTouchUpInside];

} else {//Enviar un mensaje diferente[self sendActionsForControlEvents:UIControlEventTouchUpOutside];

}}

Page 106: iOS Avanzado

20.Core Animation Layer

Page 107: iOS Avanzado

0.Introduction

- Las animaciones son la marca característica de las interfaces de iOS y Mac OS X

- Cuando queramos usar Core Animation debemos añadir el framework de QuartzCore

- Hay dos clases con las que Core Animation funciona, estas son CALayer y CAAnimation

- CALayer es un buffer que contiene una imagen, se pinta en pantalla mediante hardware, por lo que su funcionamiento es muy rápido.

- CAAnimation causa un cambio mediante interpolación en alguna propiedad de un objeto a lo largo de un periodo de tiempo.

- En este apartado nos centraremos en CALayer

Page 108: iOS Avanzado

1.Layers and views

- Una UIView es realmente una abstracción de un objeto visible con el que poder interaccionar

- Cada UIView renderiza su contenido en una CALayer que tiene implícitamente

- Un CALayer es un buffer que se pinta directamente sobre la pantalla y que sólo tiene que ver con el renderizado y no con la interacción con el usuario

- No todas las CALayer son implícitas, ya que podemos crear CALayers explícitamente

Page 109: iOS Avanzado

2.Creating a CALayer

- Imagen de fondo

- Rectángulo rojo que resalta un área

Page 110: iOS Avanzado

2.Creating a CALayer

- Crear proyecto de tipo Single View Application

- Añadir el framework QuartzCore

- Incluir la librería <QuartzCore/QuartCore.h> donde vayamos a usar este framework

- Añadir imagen a la vista ViewController.xib

- Crear el puntero a la CALayer en ViewController.m

- Instanciar nuestra CALayer en el método ViewDidLoad de ViewController.m

Page 111: iOS Avanzado

2.Creating a CALayer

ViewController.m

//Crear el nuevo objeto de tipo CALayerboxLayer = [[CALayer alloc] init]; //Ajustar el tamaño adecuado[boxLayer setBounds:CGRectMake(0,0,85,85)]; //Ajustar la posición[boxLayer setPosition:CGPointMake(160,100)]; //Crear un UIColor, después convertirlo a CGColorRef y asociarlo al color de fondoUIColor *rojo = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.5];CGColorRef rojoCG = [rojo CGColor];[boxLayer setBackgroundColor:rojoCG]; //Hacer que la CALayer se sublayer de la layer de la vista del controlador[[self.view layer] addSublayer:boxLayer];

- Instanciaremos la CALayer en el método ViewDidLoad de ViewController.m

Page 112: iOS Avanzado

3.Layer Content

- Una layer es simplemente una imagen

- Su contenido puede ser generado a partir de una imagen o programáticamente

//Crear imagen, y obtener la CGImageUIImage *layerImage = [UIImage imageNamed:@"image.jpg"];CGImageRef image = [layerImage CGImage]; //Poner la imagen en la layer[boxLayer setContents:(id)image];

- Cada layer tiene una coordenada Z para ver cual se pinta primero

[boxLayer setZPosition:-5];

Page 113: iOS Avanzado

4.Implicity Animatable Properties

- Las CALayer tienen propiedades que pueden ser modificadas y cuando se cambian realizan una animación hasta alcanzar su nuevo valor

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

UITouch *t = [touches anyObject];CGPoint p = [t locationInView:self.view]; [boxLayer setPosition:p];

}

- Modifiquemos el ejemplo anterior para que la capa reaccione a nuestros toques en la pantalla con una animación de la CALayer desplazandose hasta la posición donde hemos tocado

Page 114: iOS Avanzado

5.Programmatically Generating Content

- Para generar el contenido podemos crear un NSObject y cambiar el padre para que herede de CALayer y implementar la rutinas de pintado:

@implementation LayerSubclass

- (void)drawInContext:(CGContextRef)ctx {! UIImage *layerImage = [UIImage imageNamed:@"image.png"];! CGRect boundingBox = CGContextGetClipBoundingBox(ctx);! CGContextDrawImage(ctx, boundingBox, [layerImage CGImage]);}

@end

- Necesario invocar al método setNeedsDisplay de CALayer para que se dibuje su contenido

Page 115: iOS Avanzado

5.Programmatically Generating Content

- También podemos generar el contenido asignando el delegate de nuestra instancia CALayer a otro objeto que implemente las rutinas de pintado

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{ UIImage *layerImage = [UIImage imageNamed:@"image.jpg"]; CGRect boundingBox = CGContextGetClipBoundingBox(ctx); CGContextDrawImage(ctx,boundingBox,[layerImage CGImage]);}

[boxLayer setDelegate:self];[boxLayer setNeedsDisplay];

- Añadir después de la instanciación

- Necesario invocar al método setNeedsDisplay de CALayer para que se dibuje su contenido

Page 116: iOS Avanzado

6.Layers, Bitmaps, and Contexts

- Una CALayer es simplemente una imagen que después se vuelca a pantalla.

- Una imagen es una posición de memoria donde se almacena una lista de componentes rojo, verde, azul y alpha para cada pixel

- Cuando ejecutamos instrucciones de QuarzCore estas realizan acciones de pintado sobre una CALayer en concreto, estas instrucciones son ejecutadas en ese contexto

Page 117: iOS Avanzado

7.Dynamic Layer Content

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{ /* 480 - 1.0 y - cantidadAlpha; */ float cantidadAlpha = p.y/480.0; UIColor *rojo = [UIColor colorWithRed:1 green:0 blue:0 alpha:cantidadAlpha]; CGColorRef rojoCG = [rojo CGColor]; [boxLayer setBackgroundColor:rojoCG];}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *t = [touches anyObject]; p = [t locationInView:self.view]; [boxLayer setPosition:p]; [boxLayer setNeedsDisplay];}

- Modificar el ejemplo para que dependiendo de la posición y de donde toquemos el rectángulo sea más o menos rojo

Page 118: iOS Avanzado

21.Controlling Animation with CAAnimation

Page 119: iOS Avanzado

1.Animation Objects

- Un objeto de animación es un conjunto de instrucciones que cambian las propiedades de una instancia de CALayer.

- Muchas de las propiedades de CALayer como opacidad, posición, tamaño, etc pueden ser animadas por los objetos de animación utilizando una función de interpolación.

- Un ejemplo sería “Muevete del punto A al punto B durante 2 segundos”

Page 120: iOS Avanzado

1.Animation Objects

- En la practica, la forma de utilizar estos objetos de animación es mediante alguna de las subclases que heredan de CAAnimation

Page 121: iOS Avanzado

2.Spinning with CABasicAnimation

- Imagen de fondo

- 3 botones, cada uno con la imagen de un jugador

- Cuando pulsemos en cada uno de esos botones la pelota se desplazará hacia el jugador

Page 122: iOS Avanzado

2.Spinning with CABasicAnimation

- Crear proyecto de tipo Single View Application

- Añadir el framework QuartzCore

- Incluir la librería <QuartzCore/QuartCore.h> donde vayamos a usar este framework

- Añadir imágenes y botones a la vista ViewController.xib

- Crear los punteros y hacer los enlaces

- Crear un método para la pulsación de cada botón

Page 123: iOS Avanzado

2.Spinning with CABasicAnimation

- Método de uno de los botones

-(IBAction)jugador1:(id)sender{ CABasicAnimation *animacion = [CABasicAnimation animationWithKeyPath:@"position"]; [animacion setDuration:1.0];

CGPoint p1 = CGPointMake(pelota.center.x, pelota.center.y); CGPoint p2 = CGPointMake(jugador1.center.x, jugador1.center.y);

[animacion setFromValue:[NSValue valueWithCGPoint:p1]]; [animacion setToValue:[NSValue valueWithCGPoint:p2]]; [[pelota layer] addAnimation:animacion forKey:@"animacion"];}

Page 124: iOS Avanzado

3.Timing functions

- En nuestro ejemplo la animación comienza de repente porque se hace una interpolación lineal

- Podemos cambiar la función de interpolación por otra diferente como por ejemplo una que comience aceleradamente y termine desacelerando

CAMediaTimingFunction *tf = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

[animacion setTimingFunction:tf];

Diferentes valores de interpolación son:

kCAMediaTimingFunctionLinearkCAMediaTimingFunctionEaseInEaseOutkCAMediaTimingFunctionEaseInkCAMediaTimingFunctionEaseOut

Page 125: iOS Avanzado

4.Animation completion

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { [pelota setCenter:puntoFinal];}

- Asociar nuestro controlador como delegate de la animación

- Cuando la animación termine se realizará una llamada al método animationDidStop que deberemos sobreescribir para realizar una acción justo después de que la animación haya terminado.

[animacion setDelegate:self];

Page 126: iOS Avanzado

5.Bouncing with a CAKeyframeAnimation

- En las animaciones con CAKeyframeAnimation especificaremos diferentes valores a lo largo del tiempo. Cada valor será un key frame

- Vamos a extender el ejemplo anterior para que la pelota cambie de tamaño a lo largo del tiempo, simulando de esta forma como si subiera en altura

Page 127: iOS Avanzado

5.Bouncing with a CAKeyframeAnimation

//Crear la animación de tipo key frames CAKeyframeAnimation *animacion3 = [CAKeyframeAnimation animationWithKeyPath:@"transform"]; //Crear los valores por los que pasará el parámetro transform CATransform3D t1 = CATransform3DMakeScale(1.0,1.0,1.0); CATransform3D t2 = CATransform3DMakeScale(2.0,2.0,1.0); CATransform3D t3 = CATransform3DMakeScale(1.0,1.0,1.0); //Asociamos los diferentes valores [animacion3 setValues:[NSArray arrayWithObjects: [NSValue valueWithCATransform3D:t1], [NSValue valueWithCATransform3D:t2], [NSValue valueWithCATransform3D:t3], nil]]; //Ajustamos el tiempo que durará la animación y configuramos su delegado [animacion3 setDuration:2.0]; [animacion3 setDelegate:self]; //Añadimos la animación [[pelota layer] addAnimation:animacion3 forKey:@"keyAnimation"];

Page 128: iOS Avanzado

6.More Animation

- Como hemos visto se pueden solapar varias animaciones en un mismo instante de tiempo

- Extender el ejemplo anterior para que la pelota:- tarde 2 segundos en desplazarse de un jugador a otro- mientras tanto su alpha cambia de 0.0 a 1.0 durante 1 segundo- además su escala cambia a lo largo de 2 segundos los valores 1,2,1- que los pelotazos empiecen con velocidad y terminen en seco

Page 129: iOS Avanzado

7.The Presentation Layer and the Model Layer

- Cuando una instancia de la clase CALayer esta siendo animada esta tiene unos parámetros como son opacity, transform, position que cambian de valor. Cuando hay una animación en marcha, ldicha clase tiene dos copias de esos parámetros.

- La capa de modelo contiene los valores finales de la animación

- La capa de presentación contiene los valores interpolados para ese instante de tiempo.

- Esto es útil por ejemplo cuando se tienen objetos con una animación en curso y el usuario realiza un toque sobre ellos. En ese caso la información correcta de la posición la tendrá la capa de presentación.

CGPoint whereIsItWhenAnimationStops = [layer position];CGPoint whereIsItNow = [[layer presentationLayer] position];

Page 130: iOS Avanzado

22.Blocks and Categories

Page 131: iOS Avanzado

1.Colorizing TouchDrawView

- Partiremos de nuestro ejemplo TouchDrawView

- Vamos a utilizar bloques para pintar con diferentes colores

Page 132: iOS Avanzado

1.Colorizing TouchDrawView

- Antes de nada vamos a modificar la clase “line” para que almacene un color

Line.h :

#import <Foundation/Foundation.h>

@interface Line : NSObject { CGPoint begin; CGPoint end; UIColor *color;}

@property(nonatomic) CGPoint begin;@property(nonatomic) CGPoint end;@property(nonatomic,retain) UIColor *color;

@end

Page 133: iOS Avanzado

1.Colorizing TouchDrawViewLine.m :

#import "Line.h"

@implementation Line

@synthesize begin,end;@synthesize color;

- (void)encodeWithCoder:(NSCoder *)coder;{ [coder encodeFloat:begin.x forKey:@"beginX"]; [coder encodeFloat:begin.y forKey:@"beginY"]; [coder encodeFloat:end.x forKey:@"endX"]; [coder encodeFloat:end.y forKey:@"endY"];}

- (id)initWithCoder:(NSCoder *)coder;{ self = [[Line alloc] init]; if (self != nil) { color = [UIColor blackColor]; begin = CGPointMake([coder decodeFloatForKey:@"beginX"],[coder decodeFloatForKey:@"beginY"]); end = CGPointMake([coder decodeFloatForKey:@"endX"],[coder decodeFloatForKey:@"endY"]); } return self;}

-(void)dealloc{ [color release];}

@end

Page 134: iOS Avanzado

1.Colorizing TouchDrawView

- En TouchDrawView.m, en el método drawRect sustituiremos:

[[UIColor blackColor] set];

- Por la propiedad el color de nuestra linea, quedando el bucle de la siguiente forma:

for (Line *line in lines) { [[line color] set]; CGContextMoveToPoint(context, [line begin].x, [line begin].y); CGContextAddLineToPoint(context, [line end].x, [line end].y); CGContextStrokePath(context); }

Page 135: iOS Avanzado

2.Blocks

- Trozos de código que se pueden guardar en variables o pasar como argumentos

- También se pueden devolver como el resultado de un mensaje y ejecutar posteriormente

- Los bloques capturan el entorno léxico, entenderemos que quiere decir esto unas transparencias más adelante.

Page 136: iOS Avanzado

3.Blocks as variables

-(void)transformLineColorsWithBlock:(UIColor* (^)(Line*))color{ for (Line *l in lines) { UIColor *c = color(l); [l setColor:c]; } [self setNeedsDisplay];}

- Vamos a crear un método que reciba como variable un bloque.

- El nombre de esa variable la llamamos color

- Dentro del método usamos el bloque color si fuera una función

Page 137: iOS Avanzado

3.Blocks as variables

- Después crearemos un botón en la interface y lo enlazaremos con un método que llamaremos colorear

-(IBAction)colorear:(id)sender{

UIColor *(^esquemaDeColor)(Line*)=^(Line *l) { float longitudX = l.end.x - l.begin.x; float longitudY = l.end.y - l.begin.y; /* Regla de tres 320 - 1.0 longitudX - r

480 - 1.0 longitudY - g */ float r=longitudX/320.0; float g=longitudY/480.0; float b=1.0; return [UIColor colorWithRed:r green:g blue:b alpha:1]; }; [self transformLineColorsWithBlock:esquemaDeColor];}

Page 138: iOS Avanzado

4.Capturing variables

- Los bloques capturan el entorno léxico

-(void)metodo{ int value = 5; void (^aBlock)() = ^(void){ NSLog(@"valor=%d",value); }; aBlock(); //Imprime "valor=5" value = 10; aBlock(); //Imprime "valor=5"}

Page 139: iOS Avanzado

5.Using blocks with other build-in methods

- Podemos usar bloques en las siguientes acciones:

- Ordenación (el bloque se proporciona como método de comparación)- Notificación (cuando ocurra un evento ejecuta el bloque)- Gestores de error (cuando ocurra un error ejecutar bloque)- Gestores de finalización (cuando termines de hacer una tarea, ejecutar bloque)- Animación de vistas- Para multitarea mediante Grand Dispatch Central

Page 140: iOS Avanzado

6.Keeping code compact with blocks

- Vamos a ver un ejemplo de uso de bloques en una notificación

- En nuestro ejemplo vamos a hacer que cuando el dispositivo rote todas las lineas se pongan rojas

- Añadimos la siguiente notificación al método initWithCoder de TouchDrawView.m

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserverForName:UIDeviceOrientationDidChangeNotification object:nil queue:nil usingBlock: ^(NSNotification *note) { [self transformLineColorsWithBlock: ^(Line *l) { return [UIColor redColor]; }]; }];

Page 141: iOS Avanzado

7.Categories

- Crear una categoría consiste en coger una clase existente y añadirle métodos que después podrán ser utilizables sin tener que hacer subclassing

- En nuestro ejemplo crearemos una clase de tipo NSObject y la llamaremos “UIColor+Inverse”

UIColor+Inverse.h :

#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>

@interface UIColor (Inverse)

-(UIColor*)colorInvertido;

@end

Page 142: iOS Avanzado

7.Categories

UIColor+Inverse.m :

#import "UIColor+Inverse.h"

@implementation UIColor (Inverse)

-(UIColor*)colorInvertido{ CGColorRef cgCLR = [self CGColor]; const CGFloat *components = CGColorGetComponents(cgCLR); float r = 1.0 - components[0]; float g = 1.0 - components[1]; float b = 1.0 - components[2]; UIColor *colorInvertido = [UIColor colorWithRed:r green:g blue:b alpha:1.0]; return colorInvertido;}

@end

Page 143: iOS Avanzado

7.Categories

- Para usarlo en nuestro ejemplo, en TouchDrawView.m importaremos la cabecera UIColor+Inverse.h

- Ya podemos usar el método colorInvertido con cualquier instancia de UIColor

- Modifiquemos el ejemplo anterior, para que el método transformLineColorsWithBlock invierta el color devuelto por el bloque colorear antes de asignarlo al color de la linea

-(void)transformLineColorsWithBlock:(UIColor* (^)(Line*))color{ for (Line *l in lines) { UIColor *c = color(l); [l setColor:[c colorInvertido]]; } [self setNeedsDisplay];}

Page 144: iOS Avanzado

8.Memory Managment and Blocks

- Los bloques en Cocoa son objetos aunque algo limitados. Se pueden enviar los siguientes mensajes a los bloques:

- copy- retain- release- autorelease

Por defecto los bloques reservan memoria automáticamente cuando se crean y son destruidos cuando la función que los crea se completa.

Page 145: iOS Avanzado

9.Pros and Cons of Callback Options

- Contras:

- Sintaxis compleja- Difíciles de usar

- Pros:

- Código más compacto- Necesario conocerlos métodos predefinidos en su uso con Grand Dispatch Central (GCD) o para pasar como criterio de comparación en una ordenación por ejemplo.- Útiles en algunas circunstancias especiales. Por ejemplo como los bloques retienen en memoria los objetos que usan durante su ejecución, si un bloque se pasa como fragmento de código a usar al terminar una determinada acción nos garantizamos de que todos los objetos que van a ser usados permanecen en memoria hasta que se complete esta acción.

Page 146: iOS Avanzado

23.Web Services and UIWebView

Page 147: iOS Avanzado

1.Web Services- Cualquier navegador de internet utiliza el protocolo HTTP para comunicarse con un servicio web.

- La interacción más simple consiste en que el cliente envía una petición sobre una URL específica al servidor y este le responde enviando el contenido de la página que se ha solicitado.

- Para interacciones más complejas el navegador puede incluir parámetros en su petición. Estos parámetros son procesados por el servidor para enviar el contenido de una página dinámicamente construida con respecto a esos parámetros.

- Usar un servicio web desde una aplicación iOS requiere los siguientes pasos:

- Formatear los datos para enviar como XML o JSON- Enviar los datos en una petición HTTP- Recibir la respuesta HTTP- Parsear y procesar el los datos recibidos como XML o JSON

Page 148: iOS Avanzado

2.Starting the Nerdfeed application

- Lector RSS

- Una lista con noticias RSS obtenidas de un servidor.

- Tocando sobre cada una de esos elementos podremos ver la noticia

Page 149: iOS Avanzado

2.Starting the Nerdfeed application

- Crear proyecto de tipo Empty Application

- Añadir una nueva clase de tipo NSObject y cambiar su padre para que herede de UITableViewController

- Añadir a este fichero métodos requeridos de data source

- En AppDelegate.m crear una instancia de ListViewController y asignarla como root view controller de un navigation controller.

- Hacer el navigation controller el root view controller de window

Page 150: iOS Avanzado

2.Starting the Nerdfeed application

- En AppDeletage.m en el método didFinishLaunchingWithOptions añadiremos

//Instanciar controllador ListViewControllerListViewController *lvc =

[[ListViewController alloc] initWithStyle:UITableViewStylePlain];[lvc autorelease]; //Instanciar controlador de navegación UINavigationController *masterNav =

[[UINavigationController alloc] initWithRootViewController:lvc];[masterNav autorelease]; //Asignar como rootViewController el control de navegación[[self window] setRootViewController:masterNav];

- Con eso tendremos nuestro UITableView en pantalla, pero está vacío

Page 151: iOS Avanzado

3.Fetching data from a URL

- 1. Crear contenedor de datos

- 2. Iniciar conexión

- 3. Recoger datos con los métodos delegados

- Para crear nuestro contenedor de datos:

ListViewController.h

NSMutableData *responseData;

ListViewController.m : Instanciarlo Antes de usarlo

responseData = [[NSMutableData data] retain];

Page 152: iOS Avanzado

4.Working with NSURLConnection

- Iniciaremos la conexión

responseData = [[NSMutableData data] retain];

NSURL *baseURL = [[NSURL URLWithString:@"http://www.atomicflavor.com/feed/"] retain];

NSURLRequest *request = [NSURLRequest requestWithURL:baseURL];[[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];

- Después debemos implementar una serie de métodos en nuestro controlador para que se vayan recibiendo los datos

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error- (void)connectionDidFinishLoading:(NSURLConnection *)connection

- Asignamos nuestro controlador como delegate de la conexión

Page 153: iOS Avanzado

4.Working with NSURLConnection

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ [responseData setLength:0];}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ [responseData appendData:data];}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ NSLog(@"No se pudo descargar el XML");}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection{! //Realizar parseo}

- Métodos a implementar

Page 154: iOS Avanzado

5.Parsing XML

- Veamos un fragmento de lo que contiene un fichero XML y lo que vamos a parsear

<item><title>Entrevista a Atomic Flavor en Baquia TV</title><link>http://www.atomicflavor.com/entrevista-a-atomic-flavor-en-baquia-tv/</link><comments>http://www.atomicflavor.com/entrevista-a-atomic-flavor-en-baquia-tv/#comments</comments><pubDate>Wed, 15 Jun 2011 09:28:48 +0000</pubDate>

- Vamos a usar NSXMLParser, que es la librería incluida por defecto en el SDK de iOS

Page 155: iOS Avanzado

5.Parsing XML

- 1. Implementar el protocolo NSXMLParserDelegate

- 2. Declarar cadenas del XML

- 3. Sobreescribir los métodos de parseo

- 4. Invocar comienzo de parseo

Page 156: iOS Avanzado

5.Parsing XML

- 1. Declarar cadenas de parseo

NSXMLParser * rssParser;

NSMutableData *responseData;NSMutableArray *items;!NSMutableDictionary * item;!NSString * currentElement;

NSMutableString * currentTitle;NSMutableString * currentDate;NSMutableString * currentSummary;NSMutableString * currentLink;

Page 157: iOS Avanzado

5.Parsing XML

- 2. Iniciar parseo

! NSString *str = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];

! NSData *data= [str dataUsingEncoding:NSUTF8StringEncoding];

items = [[NSMutableArray alloc] init];!! rssParser = [[[NSXMLParser alloc] initWithData:data] autorelease];! [rssParser setDelegate:self];! [rssParser setShouldProcessNamespaces:NO];! [rssParser setShouldReportNamespacePrefixes:NO];! [rssParser setShouldResolveExternalEntities:NO];! [rssParser parse];!

Page 158: iOS Avanzado

5.Parsing XML

- 3. Sobreescribir métodos del delegado NSXMLParserDelegate:

- 3.1 Empieza parseo de elemento

- 3.2 Encontrada cadena

- 3.4 Termina parseo de elemento

- 3.5 Terminado de parsear

Page 159: iOS Avanzado

5.Parsing XML

- 3.1 Empieza parseo de elemento

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{! currentElement = [elementName copy];!! if ([elementName isEqualToString:@"item"]) ! {! ! item = [[NSMutableDictionary alloc] init];! ! currentTitle = [[NSMutableString alloc] init];! ! currentDate = [[NSMutableString alloc] init];! ! currentSummary = [[NSMutableString alloc] init];! ! currentLink = [[NSMutableString alloc] init];! }}

Page 160: iOS Avanzado

5.Parsing XML

- 3.2 Encontrada cadena

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{! if ([currentElement isEqualToString:@"title"]) {! ! [currentTitle appendString:string];! } else if ([currentElement isEqualToString:@"link"]) {! ! [currentLink appendString:string];! } else if ([currentElement isEqualToString:@"pubDate"]) {! ! [currentDate appendString:string];! }}

Page 161: iOS Avanzado

5.Parsing XML

- 3.4 Termina parseo de elemento

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{! if ([elementName isEqualToString:@"item"]) {! ! [item setObject:currentTitle forKey:@"title"];! ! [item setObject:currentLink forKey:@"link"];! ! [item setObject:currentDate forKey:@"pubDate"];! ! [items addObject:[item copy]];! }}

Page 162: iOS Avanzado

5.Parsing XML

- 3.5 Terminado de parsear

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {! NSLog(@"No se pudo parsear el XML");}

- (void)parserDidEndDocument:(NSXMLParser *)parser {! [[self tableView] reloadData];}

Page 163: iOS Avanzado

5.Parsing XML

- Por último modificamos los métodos de nuestro ListViewController para que muestren la lista de elementos recuperados

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return [items count];}

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];

if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"] autorelease]; } NSMutableDictionary *i = [items objectAtIndex:indexPath.row]; [[cell textLabel] setText:[i objectForKey:@"title"]]; return cell;}

Page 164: iOS Avanzado

5.Parsing XML

- También crearemos un método para que podamos invocar el comienzo del parseo desde AppDelegate

-(void)comenzarConexion{ responseData = [[NSMutableData data] retain]; NSURL *baseURL = [[NSURL URLWithString:@"http://www.atomicflavor.com/feed/"] retain]; NSURLRequest *request = [NSURLRequest requestWithURL:baseURL]; [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];}

Page 165: iOS Avanzado

6.A quick tip on logging

- En este ejercicio veremos como se imprime mucha información en la consola de depuración sobre todo el contenido que recibimos y enviamos

- Lo que vamos a hacer es crear diferentes etiquetas para NSLog en nuestro fichero Nerdfeed_Prefix.pch que es donde importamos las librerías del precompilador

#define WSLog NSLog #define OSLog NSLog

- Cada vez que queramos imprimir información relaccionada con los webservices usaremos WSLog en lugar de NSLog. Para el resto de información usaremos OSLog.

- Después cuando queramos que la información de los webservices no se imprima deberemos redefinir la etiqueta en nuestra clase:

#define WSLog

Page 166: iOS Avanzado

7.UIWebView

- UIViewController.h :

#import <Foundation/Foundation.h>

@interface WebViewController : UIViewController { }

@property (nonatomic,readonly) UIWebView *webView;

@end

- Creamos una nueva clase de tipo NSObject y le cambiamos el padre para que herede de UIViewController

Page 167: iOS Avanzado

7.UIWebView

#import "WebViewController.h"

@implementation WebViewController

@synthesize webView;

- (void) loadView{ CGRect screenFrame = [[UIScreen mainScreen] applicationFrame]; UIWebView *wv = [[UIWebView alloc] initWithFrame:screenFrame]; [wv setScalesPageToFit:YES]; [self setView:wv]; [wv release];}

-(UIWebView*)webView { return (UIWebView*)[self view];}

@end

- UIViewController.m :

Page 168: iOS Avanzado

7.UIWebView

- Instanciarlo desde el AppDelegate

- Crear una propiedad en ListViewController para apuntar a WebViewController

WebViewController *webViewController;

@property(nonatomic,retain) WebViewController *webViewController;

@synthesize webViewController;

Page 169: iOS Avanzado

7.UIWebView

- Método para cuando seleccionemos alguna fila de ListViewController

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ //Desplazar la vista actual a webViewController [[self navigationController] pushViewController:webViewController animated:YES];

//Obtener el elemento NSMutableDictionary *i = [items objectAtIndex:indexPath.row]; //Limpiar cadena de parásitos finales NSString *cadenaURL = [i objectForKey:@"link"]; WSLog(@"[cadena =%@]",cadenaURL); cadenaURL = [cadenaURL substringToIndex:[cadenaURL length]-1];! cadenaURL = [cadenaURL stringByReplacingOccurrencesOfString:@" " withString:@""];! cadenaURL = [cadenaURL stringByReplacingOccurrencesOfString:@"\n" withString:@""]; WSLog(@"[cadena =%@]",cadenaURL); //Crear NSURL NSURL *url = [NSURL URLWithString:cadenaURL]; //Crear petición con esa URL NSURLRequest *req = [NSURLRequest requestWithURL:url]; //Decirle a webviewcontroller que cargue esa petición [[webViewController webView] loadRequest:req]; //Poner como título del control de navegación el título de la noticia [[webViewController navigationItem] setTitle:[i objectForKey:@"title"]];}

Page 170: iOS Avanzado

8.NSXMLParser

- En este ejercicio hemos utilizado NSXMLParser que es el parseador disponible por defecto en la api del SDK de iOS.

- En internet podremos encontrar otras librerías de terceros que también podremos usar para parsear fichero XML sustituyendo a NSXMLParser

- Algunas de las librerías de terceros que podremos encontrar disponibles son:

- libxml2- TBXML- TouchXML- KissXML- TinyXML- GDataXML

Page 171: iOS Avanzado

9.The Request Body

- Veamos como es en detalle cada una de las peticiones que realiza nuestro programa.

- Hay tres partes, la linea de petición, las cabeceras HTTP y el cuerpo HTTP

Page 172: iOS Avanzado

10.Credentials

- Cuando se accede a un servicio web, hay veces que es necesario identificarse para poder trabajar con dicho servicio.

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

// Se ha fallado 2 veces if ([challenge previousFailureCount] > 1) {

//Dar mensaje de aviso } else { // Responder NSURLCredential *cred = [[[NSURLCredential alloc]

initWithUser:@"user" password:@"pass"

persistence:NSURLCredentialPersistenceForSession] autorelease]; [[challenge sender] useCredential:cred forAuthenticationChallenge:challenge]; }}

Page 173: iOS Avanzado

11.More Data

- Vamos a crear una clase CustomCell que sea subclase de UTTableViewCell que tenga varios labels, uno para el título y otro para la fecha.

CustomCell.h:

#import <UIKit/UIKit.h>

@interface CustomCell : UITableViewCell { UILabel *titulo; UILabel *hora;}

@property(nonatomic,retain) UILabel *titulo;@property(nonatomic,retain) UILabel *hora;

@end

Page 174: iOS Avanzado

11.More Data

- Método constructor

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { //Instanciamos los labels titulo = [[UILabel alloc] initWithFrame:CGRectZero]; hora = [[UILabel alloc] initWithFrame:CGRectZero]; [titulo setBackgroundColor:[UIColor clearColor]]; [hora setBackgroundColor:[UIColor clearColor]];

//Los añadimos a el contenido de la vista [[self contentView] addSubview:titulo]; [[self contentView] addSubview:hora]; //Liberamos [titulo release]; [hora release]; } return self;}

Page 175: iOS Avanzado

11.More Data

- Colocación de SubVistas

-(void)layoutSubviews{ [super layoutSubviews]; CGRect bounds = [[self contentView] bounds];

float inset = 5.0; float w = bounds.size.width; float h = bounds.size.height; CGRect titleFrame = CGRectMake(inset,inset, w, (h/2)); CGRect hourFrame = CGRectMake(inset,inset+h/2, w, (h/2)); [titulo setFrame:titleFrame]; [hora setFrame:hourFrame];}

Page 176: iOS Avanzado

11.More Data

- Modificar cellForRowAtIndexPath de ListViewController.m

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:@"CustomCell"];

if (cell == nil) { cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CustomCell"] autorelease]; } NSMutableDictionary *i = [items objectAtIndex:indexPath.row]; [[cell titulo] setText:[i objectForKey:@"title"]]; [[cell hora] setText:[i objectForKey:@"pubDate"]]; return cell;}

Page 177: iOS Avanzado

12.More UIWebView

- Añadir UIActivityIndicatorView a nuestra clase WebViewController en el centro de la pantalla

- Hay que implementar UIWebViewDelegate en nuestro controlador

- Vamos a sobreescribir los métodos de UIWebViewDelegate:

- Cuando la noticia empiece a cargar en el centro de la pantalla deberemos ver un UIActivityIndicatorView con su animación activada

- Cuando la noticia se haya terminado la carga, el UIActivityIndicatorView desaparecerá

Page 178: iOS Avanzado

12.More UIWebView

- Cambios sobre loadView

- (void) loadView{ CGRect screenFrame = [[UIScreen mainScreen] applicationFrame]; UIWebView *wv = [[UIWebView alloc] initWithFrame:screenFrame]; [wv setScalesPageToFit:YES]; [wv setDelegate:self]; activityIndicator = [[UIActivityIndicatorView alloc] init]; [activityIndicator setHidesWhenStopped:TRUE]; [activityIndicator stopAnimating]; [activityIndicator setBackgroundColor:[UIColor clearColor]]; [activityIndicator setColor:[UIColor blackColor]]; [self setView:wv];

[self.view addSubview:activityIndicator]; [activityIndicator setCenter:CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2)];

[activityIndicator release]; [wv release];}

Page 179: iOS Avanzado

12.More UIWebView

- Métodos de UIWebViewDelegate:

- (void)webViewDidStartLoad:(UIWebView *)webView{! [activityIndicator startAnimating];}

- (void)webViewDidFinishLoad:(UIWebView *)webView{ [activityIndicator stopAnimating]; }

Page 180: iOS Avanzado

24.UISplitViewController

Page 181: iOS Avanzado

1.Splitting Up Nerdfeed

- Tanto iPhone como iPod Touch tienen un tamaño de pantalla bastante limitado, por eso la manera usual de presentar las vistas es mediante un UINavigationController.

- En cambio, el iPad tiene una pantalla mucho mayor y puede utilizar clases predefinidas como UISplitViewController que sólo están disponible para iPad.

- Para trabajar con UISplitViewController debemos tener dos controladores, el master y el detail.

- El controlador master ocupará poco en la pantalla y será una lista que nos permita elegir que contenido visualizar

- El controlador detail ocupará la mayor parte de la pantalla y mostrará en detalle el contenido seleccionado en el master

Page 182: iOS Avanzado

1.Splitting Up Nerdfeed

- Añadiremos un UISplitViewController a nuestro ejemplo Nerdfeed

Page 183: iOS Avanzado

1.Splitting Up Nerdfeed

- Partimos de el proyecto anterior

- Realizamos cambios en AppDelegate para instanciar el UISplitViewController en caso de estar ejecutando el programa en un iPad

- Configuramos los controladores para que soporten cambios a orientación horizontal si el programa se esta ejecutando en un iPad

Page 184: iOS Avanzado

1.Splitting Up Nerdfeed

- Modificamos el método didFinishLaunchingWithOptions del fichero AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; //Instanciar controllador ListViewController ListViewController *lvc = [[ListViewController alloc] initWithStyle:UITableViewStylePlain]; [lvc autorelease]; //Instanciar controlador de navegacion UINavigationController *masterNav = [[UINavigationController alloc] initWithRootViewController:lvc]; [masterNav autorelease]; WebViewController *wvc = [[[WebViewController alloc] init] autorelease]; [lvc setWebViewController:wvc];

...

Page 185: iOS Avanzado

1.Splitting Up Nerdfeed

.... if ([[UIDevice currentDevice] userInterfaceIdiom] ==UIUserInterfaceIdiomPad){ //El detalle debe ir en un navigation controller UINavigationController *detailNav = [[UINavigationController alloc] initWithRootViewController:wvc]; [detailNav autorelease]; NSArray *vcs = [NSArray arrayWithObjects:masterNav,detailNav, nil]; UISplitViewController *svc = [[[UISplitViewController alloc] init] autorelease]; [svc setDelegate:wvc]; [svc setViewControllers:vcs]; [[self window] setRootViewController:svc]; } else { //Asignar como rootViewController el control de navegación [[self window] setRootViewController:masterNav]; } [lvc comenzarConexion]; [self.window makeKeyAndVisible]; return YES;}

Page 186: iOS Avanzado

1.Splitting Up Nerdfeed

- Añadir el método shouldAutorotateToInterfaceOrientation a ListViewController.m y WebViewController.m para que la app soporte orientación horizontal

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{ if ([[UIDevice currentDevice] userInterfaceIdiom] ==UIUserInterfaceIdiomPad){ return YES; } else { return interfaceOrientation == UIInterfaceOrientationPortrait; }}

- Solo se realizará el cambio a horizontal en iPad

Page 187: iOS Avanzado

2.Master-Detail Communication

- Como desde ListViewController tenemos un puntero hacia WebViewController, podremos seguir indicando a este controlador la página que debe cargar

- Por lo general en el master siempre tendremos un puntero hacia el detalle para modificar el contenido

- En el caso de estar disponible un splitViewController tendremos quemodificar la forma de presentar el contenido cuando se seleccione un elemento de la lista. Para ello realizaremos el siguiente cambio en didSelectRowAtIndexPath:

if (![self splitViewController]) {[[self navigationController] pushViewController:webViewController animated:YES];

}

Page 188: iOS Avanzado

3.Displaying the Master View Controller in Portrait Mode

- (void)splitViewController:(UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController:(UIPopoverController*)pc{ [barButtonItem setTitle:@"Lista"]; self.navigationItem.leftBarButtonItem = barButtonItem;}

- (void)splitViewController:(UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem{ self.navigationItem.leftBarButtonItem = nil;}

- Hacemos que nuestro WebViewController implemente UISplitViewControllerDelegate y sobreescribimos los siguientes métodos

Page 189: iOS Avanzado

4.Universalizing Nerdfeed

- Ahora podemos probar con los diferentes simuladores de iPhone o iPad para ver como la aplicación se comporta de forma diferente.

Page 190: iOS Avanzado

25.Media Playback and Background Execution

Page 191: iOS Avanzado

1.Creating the MediaPlayer Application

- Muchas aplicaciones necesitan reproducir audio o video

- En este tema aprenderemos a utilizar la forma mas común de reproducir contenido de vídeo y audio utilizando las rutinas que nos ofrece la SDK de iOS

- También aprenderemos más sobre multitarea y ejecución en segundo plano

Page 192: iOS Avanzado

1.Creating the MediaPlayer Application

- Aplicación multimedia

- Reproducir fichero de audio

- Reproducir un sonido del sistema corto

- Reproducir fichero de video

Page 193: iOS Avanzado

1.Creating the MediaPlayer Application

- Crear proyecto de tipo Single View Application

- Creamos 3 botones en ViewController.xib y sus correspondientes métodos que enlazaremos a estos botones

- Programamos el método para cada uno de los botones

Page 194: iOS Avanzado

2.System Sounds

- Los llamados sonidos del sistema son sonidos cortos sin comprimir que se utilizan típicamente en las interfaces o cuando el usuario realiza alguna acción

- El framework Auditoolbox nos permite registrar una serie de sonidos en el servidor de sonidos del sistema. Estos sonidos deben ser :

- Menores a 30 segundos de duración- Estar empaquetados en formato PCM o IMA4- Ser de uno de los siguientes tipos: CAF, WAV o AIFF

afconvert -f caff -d LEI16@44100 -c 1 sound.mp3 sound.caf

- Como pasar de mp3 a caf :

Page 195: iOS Avanzado

2.System Sounds

- Importar framework de AudioToolbox

- Añadir objeto de tipo SystemSoundID

ViewController.h :

#import <UIKit/UIKit.h>#import <AudioToolbox/AudioToolbox.h>

@interface ViewController : UIViewController { SystemSoundID sonidoCorto;}

-(IBAction)reproducirAudio:(id)sender;-(IBAction)reproducirSonidoCortoSistema:(id)sender;-(IBAction)reproducirVideo:(id)sender;

@end

Page 196: iOS Avanzado

3.Registering system sounds

//Puntero a su ruta NSString *soundPath = [[NSBundle mainBundle] pathForResource:@"sound" ofType:@"caf"]; if (soundPath) { //Creamos un NSURL y lo cargamos en sonidoCorto NSURL *soundURL = [NSURL fileURLWithPath:soundPath]; AudioServicesCreateSystemSoundID((CFURLRef)soundURL, &sonidoCorto); }

- Cargamos el sonido en viewDidLoad de ViewController.m

Page 197: iOS Avanzado

4.Playing system sounds

- Para reproducir el sonido

-(IBAction)reproducirSonidoCortoSistema:(id)sender{ AudioServicesPlaySystemSound(sonidoCorto);}

- Para reproducir el sonido y que vibre el dispositivo

-(IBAction)reproducirSonidoCortoSistema:(id)sender{ AudioServicesPlaySystemSound(sonidoCorto); AudioServicesPlayAlertSound(sonidoCorto);}

- Descargar sonido

AudioServicesDisposeSystemSoundID(sonidoCorto);

Page 198: iOS Avanzado

5.Compressed Audio Files

- Para reproducir sonidos comprimidos de más de 30 segundos usaremos AVAudioPlayer

- Primero importamos el framework AVFoundation y creamos un puntero de tipo AVAudioPlayer

#import <UIKit/UIKit.h>#import <AudioToolbox/AudioToolbox.h>#import <AVFoundation/AVFoundation.h>

@interface ViewController : UIViewController { SystemSoundID sonidoCorto; AVAudioPlayer *musica;}

-(IBAction)reproducirAudio:(id)sender;-(IBAction)reproducirSonidoCortoSistema:(id)sender;-(IBAction)reproducirVideo:(id)sender;

@end

Page 199: iOS Avanzado

5.Compressed Audio Files

- Cargamos la canción en viewDidLoad de ViewController.m

NSString *musicPath = [[NSBundle mainBundle] pathForResource:@"music" ofType:@"mp3"]; if (musicPath) { //Creamos un NSURL y lo cargamos en musica NSURL *musicURL = [NSURL fileURLWithPath:musicPath]; musica = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL error:nil]; [musica setDelegate:self]; }

- Implementamos en ViewController el delegado AVAudioPlayerDelegate para poder capturar eventos

Page 200: iOS Avanzado

5.Compressed Audio Files

- Reproducimos la canción

-(IBAction)reproducirAudio:(id)sender{ if ([musica isPlaying]) { [musica stop]; [sender setTitle:@"Reproducir música" forState:UIControlStateNormal]; } else { [musica play]; [sender setTitle:@"Parar música" forState:UIControlStateNormal]; }}

Page 201: iOS Avanzado

5.Compressed Audio Files

- Si queremos hacer cosas más avanzadas con AVAudioPlayer sobreescribimos los métodos de su delegado

-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player{ [musica play];}

-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{ NSLog(@"Reproducción terminada");}

Page 202: iOS Avanzado

6.Playing Movie Files

- MPMovieplayerController es el responsable de reproducir vídeos en iOS.

- La aplicación de YouTube y de Vídeos utilizan esta misma clase para reproducir vídeos

- Solo reproduce vídeos en dos formatos:

- H264 (Baseline Profile Level 3.0)- MPEG-4 Part2 video (Simple Profile)

- Para codificar cualquier video en estos formatos podemos utilizar iTunes. Seleccionando un vídeo y desde el menú avanzado.

- También podemos utilizar programas de terceros que nos permitan realizar este tipo de conversiones

Page 203: iOS Avanzado

6.Playing Movie Files

- Importar framework de MediaPlayer

- Añadir objeto de tipo MPMoviePlayerController

ViewController.h :

#import <UIKit/UIKit.h>#import <AudioToolbox/AudioToolbox.h>#import <AVFoundation/AVFoundation.h>#import <MediaPlayer/MediaPlayer.h>

@interface ViewController : UIViewController<AVAudioPlayerDelegate> { SystemSoundID sonidoCorto; AVAudioPlayer *musica; MPMoviePlayerController *video;}

-(IBAction)reproducirAudio:(id)sender;-(IBAction)reproducirSonidoCortoSistema:(id)sender;-(IBAction)reproducirVideo:(id)sender;

@end

Page 204: iOS Avanzado

6.Playing Movie Files

- Cargamos el video en viewDidLoad de ViewController.m

NSString *moviePath = [[NSBundle mainBundle] pathForResource:@"video" ofType:@"m4v"]; if (moviePath) { //Creamos un NSURL y lo cargamos en musica NSURL *movieURL = [NSURL fileURLWithPath:moviePath]; video = [[MPMoviePlayerController alloc] initWithContentURL:movieURL]; [self.view addSubview:video.view]; float h = self.view.bounds.size.height/2; float w = self.view.bounds.size.width;

[video.view setFrame:CGRectMake(0, h, w, h)]; }

-(IBAction)reproducirVideo:(id)sender{ [video play];}

- Y lo reproducimos

Page 205: iOS Avanzado

7.MPMoviePlayerViewController

- Si queremos presentar un vídeo en pantalla completa usaremos la clase MPMoviePlayerViewController y después lanzaremos su presentación

MPMoviePlayerViewController *playerViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:movieURL];

[self presentMoviePlayerViewControllerAnimated:playerViewController];

Page 206: iOS Avanzado

8.Preloading video

- Cuando iniciamos la carga de un vídeo no está disponible de manera inmediata. Mas aún si lo cargamos en streaming desde un servidor

- Podemos añadir a nuestro objeto de vídeo una notificación apuntando a un método para que sea llamado cuando el vídeo se haya terminado de cargar completamente.

NSString *moviePath = [[NSBundle mainBundle] pathForResource:@"video" ofType:@"m4v"]; if (moviePath) { //Creamos un NSURL y lo cargamos en musica NSURL *movieURL = [NSURL fileURLWithPath:moviePath]; video = [[MPMoviePlayerController alloc] initWithContentURL:movieURL]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displayPreloadedVideo:) name:MPMoviePlayerLoadStateDidChangeNotification object:video]; }

Page 207: iOS Avanzado

8.Preloading video

- El método que añadimos para atender a esa notificación

-(void)displayPreloadedVideo:(NSNotification*)note{ [self.view addSubview:video.view]; float h = self.view.bounds.size.height/2; float w = self.view.bounds.size.width; [video.view setFrame:CGRectMake(0, h, w, h)]; }

Page 208: iOS Avanzado

9.Background Processes

- Una aplicación puede reproducir audio incluso si no es la aplicación activa.

- Para configurar este tipo de proceso de segundo plano debemos añadir a el fichero Info.plist de nuestro proyecto el correspondiente modo de background. Para el lo añadimos un campo más con el valor UIBackgroundModes. La clave que corresponde con la reproducción de audio en segundo plano es App plays audio

Page 209: iOS Avanzado

9.Background Processes

- Para que la aplicación pueda reproducir audio en segundo plano deberemos cambiar la categoría de AVAudioSession.

- La categoría que permite reproducir audio en segundo plano es AVAudioSessionCategoryPlayback

NSString *musicPath = [[NSBundle mainBundle] pathForResource:@"music" ofType:@"mp3"]; if (musicPath) { //Creamos un NSURL y lo cargamos en musica NSURL *musicURL = [NSURL fileURLWithPath:musicPath]; //Cambiamos la categoria para reproducir audio en segundo plano [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

musica = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL error:nil]; [musica setDelegate:self]; }

Page 210: iOS Avanzado

10.Guidelines for background execution

- Hay algunas reglas que Apple nos dice que debemos de seguir en el caso de las aplicaciones que tienen ejecución en segundo plano. Algunas de estas son:

- No uses OpenGL ES, el sistema operativo termina el proceso por la elevada carga

- No actualices tus vistas, el usuario no puede verlas

- Libera recursos cuando se produzca una llamada de low-memory warning

Page 211: iOS Avanzado

11.Other forms of background execution

- Además de la reproducción de audio en segundo plano hay otros dos modos de ejecución:

- VOIP (Voice over internet protocol)

- Registers for location updates

Page 212: iOS Avanzado

12.Low-level APIs

- En este tema hemos visto la forma más sencilla de reproducir vídeo y audio utilizando la API de alto nivel

- Si queremos hacer cosas más avanzadas como recodificar, aplicar filtros, efectos o lo que necesitemos deberemos utilizar las APIs de bajo nivel

- Usaremos AudioToolbox y CoreAudio para los sonidos

- Usaremos CoreVideo para el vídeo

Page 213: iOS Avanzado

13.Audio Recording

- Podemos grabar audio utilizando la clase AVAudioRecorder

- Vamos a modificar nuestro ejemplo para añadir :

- Un botón grabar que nos permita grabar

- Un botón play que reproduzca el último archivo grabado

Page 214: iOS Avanzado

13.Audio Recording

- Punteros necesarios

AVAudioPlayer *grabadoPlayer; AVAudioRecorder *grabadoRecorder; bool grabando;

-(IBAction)grabar:(id)sender;-(IBAction)reproducir:(id)sender;

Page 215: iOS Avanzado

13.Audio Recording-(IBAction)grabar:(id)sender{ if (!grabando) { //Settings de grabar NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] initWithCapacity:10]; [recordSettings setObject:[NSNumber numberWithInt: kAudioFormatLinearPCM] forKey: AVFormatIDKey]; [recordSettings setObject:[NSNumber numberWithFloat:44100.0] forKey: AVSampleRateKey]; [recordSettings setObject:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey]; [recordSettings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey]; [recordSettings setObject:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey]; [recordSettings setObject:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey]; //Ponemos la sesion en modo grabación AVAudioSession *audioSession = [AVAudioSession sharedInstance]; [audioSession setCategory:AVAudioSessionCategoryRecord error:nil]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *recDir = [[paths objectAtIndex:0] stringByAppendingFormat:@"/%@", @"fichero"]; NSURL *url = [NSURL fileURLWithPath:recDir]; if (grabadoRecorder!=nil) { [grabadoRecorder stop]; [grabadoRecorder release]; } grabadoRecorder = [[ AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:nil]; if ([grabadoRecorder prepareToRecord] == YES) { grabando=true; [sender setTitle:@"Parar grabación" forState:UIControlStateNormal]; [grabadoRecorder record]; } } else { grabando=false; [sender setTitle:@"Grabar" forState:UIControlStateNormal]; [grabadoRecorder stop]; }}

Page 216: iOS Avanzado

13.Audio Recording

-(IBAction)reproducir:(id)sender{ //Ponemos la sesion en modo reproducción AVAudioSession *audioSession = [AVAudioSession sharedInstance]; [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *recDir = [[paths objectAtIndex:0] stringByAppendingFormat:@"/%@", @"fichero"]; NSURL *url = [NSURL fileURLWithPath:recDir]; if (grabadoPlayer!=nil) { [grabadoPlayer stop]; [grabadoPlayer release]; } grabadoPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; [grabadoPlayer play];}

- Método de reproducción

Page 217: iOS Avanzado

26.Push Notifications and Networking

Page 218: iOS Avanzado

1.Push Notifications

- Push Notifications, mecanismo para enviar notificaciones a los usuarios que tengan instalada nuestra aplicación

- Podemos mostrar un mensaje corto

- Podemos reproducir un sonido

- Podemos cambiar el badge de el icono de la aplicación

Page 219: iOS Avanzado

1.Push Notifications

- Esquema de Push Notification

Page 220: iOS Avanzado

2.Anatomy of a Push Notification

- Una notificación básica{ "aps": { "alert": "Hello, world!", "sound": "default" }}

- Configurando también el botón de abrir la notificación{ "aps": { "alert": { "action-loc-key": "Open", "body": "Hello, world!" }, "badge": 2 }}

- Como lo usaremos

{"aps":{"alert":"Hello, world!","sound":"default"}}

Page 221: iOS Avanzado

3.Provisioning Profiles

- Para habilitar push notifications en tus aplicaciones, necesitan ser firmadas con un provisioning profile que esté configurado para push.

- Tu servidor necesita firmar las comunicaciones con los servidores APNS mediante un certificado SSL

- Hay dos tipos de push server certificates:- Development- Production

Page 222: iOS Avanzado

4. Generating the Certificate Signing Request (CSR)

- Abrir keychain

- Solicitar un certificado para una autoridad de certificación

Page 223: iOS Avanzado

4. Generating the Certificate Signing Request (CSR)

Page 224: iOS Avanzado

4. Generating the Certificate Signing Request (CSR)

Page 225: iOS Avanzado

4. Generating the Certificate Signing Request (CSR)

Page 226: iOS Avanzado

5. Making App ID and SSL Certificate

Page 227: iOS Avanzado

5. Making App ID and SSL Certificate

Page 228: iOS Avanzado

5. Making App ID and SSL Certificate

Page 229: iOS Avanzado

5. Making App ID and SSL Certificate

Page 230: iOS Avanzado

5. Making App ID and SSL Certificate

- Una vez descargado el certificado.cer hacer doble click y instalarlo

- Después abriremos de nuevo KeyChain, buscamos el certificado y le damos a detalle

- Veremos los dos, el certificado y la clave privada

- Seleccionar los dos y exportamos como un fichero.p12

Page 231: iOS Avanzado

5. Making App ID and SSL Certificate

Page 232: iOS Avanzado

5. Making App ID and SSL Certificate

- Convertimos el certificado en un fichero .pem

openssl pkcs12 -in Certificado.p12 -out Certificado.pem -nodes

Page 233: iOS Avanzado

6. Simple PHP Server

- Subimos los ficheros:

- ck.pem generado- simplepush.php

- Tendré acceso desde:

http://www.atomicflavor.com/PushNotification/simplepush.php

Page 234: iOS Avanzado

7. Simplepush.php<?php

// Put your device token here (without spaces):$deviceToken = '0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78';

// Put your private key's passphrase here:$passphrase = 'curso';

// Put your alert message here:$message = 'My first push notification!';

////////////////////////////////////////////////////////////////////////////////

$ctx = stream_context_create();stream_context_set_option($ctx, 'ssl', 'local_cert', 'Certificado.pem');stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);

// Open a connection to the APNS server$fp = stream_socket_client(! 'ssl://gateway.sandbox.push.apple.com:2195', $err,! $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);

if (!$fp)! exit("Failed to connect: $err $errstr" . PHP_EOL);

echo 'Connected to APNS' . PHP_EOL;

// Create the payload body$body['aps'] = array(! 'alert' => $message,! 'sound' => 'default'! );

// Encode the payload as JSON$payload = json_encode($body);

// Build the binary notification$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;

// Send it to the server$result = fwrite($fp, $msg, strlen($msg));

if (!$result)! echo 'Message not delivered' . PHP_EOL;else! echo 'Message successfully delivered' . PHP_EOL;

// Close the connection to the serverfclose($fp);

Page 235: iOS Avanzado

8. Simple iPhone Client

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{! self.window.rootViewController = self.viewController;! [self.window makeKeyAndVisible]; ! // Let the device know we want to receive push notifications! [[UIApplication sharedApplication] registerForRemoteNotificationTypes:! ! (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];  return YES;}

- Nueva single view application

- Hacemos que la app se registre para recibir push notifications

Page 236: iOS Avanzado

8. Simple iPhone Client

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{! self.window.rootViewController = self.viewController;! [self.window makeKeyAndVisible]; ! // Let the device know we want to receive push notifications! [[UIApplication sharedApplication] registerForRemoteNotificationTypes:! ! (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];  return YES;}

- Nueva single view application

- Hacemos que la app se registre para recibir push notifications

Page 237: iOS Avanzado

8. Simple iPhone Client

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{! self.window.rootViewController = self.viewController;! [self.window makeKeyAndVisible]; ! // Para que el dispositivo reciba push notifications! [[UIApplication sharedApplication] registerForRemoteNotificationTypes:! ! (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];  return YES;}

- Nueva single view application

- Hacemos que la app se registre para recibir push notifications

Page 238: iOS Avanzado

8. Simple iPhone Client

- Nos hacen falta algunos métodos más

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken{! NSLog(@"My token is: %@", deviceToken);}

- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error{! NSLog(@"Failed to get token, error: %@", error);}

Page 239: iOS Avanzado

9. Create Provisioning Profile

- Ahora vamos a crear el provisioning profile y a instalarlo en el XCode

- A continuación podremos ejecutar nuestra app en el dispositivo

- Vemos el token del dispositivo y modificamos el php del servidor para que la notificación se envíe a ese dispositivo

- Ya podemos enviar nuestra primera push notification desde el servidor y recibirla en el dispositivo

Page 240: iOS Avanzado

¿COMO CONTINUO EL CAMINO PARA CONVERTIRME EN UN

PROGRAMADOR NINJA?

Objective CCocoaQuartz

HTML5Javascript

CSS

OpenGL ES 1.5OpenGL ES 2.0

GLSL

PHPMySQLSQLite

Page 241: iOS Avanzado

LIBROS RECOMENDADOS EDITORIAL APRESS

Page 242: iOS Avanzado

Miguel José García [email protected] : @miguelgarciacorhttp://www.atomicflavorgames.comhttp://www.atomicflavor.com

CONTACTO