iOS Avanzado

Preview:

Citation preview

Repaso 1

QUE NECESITO PARA HACER UNA APP IOS

Hardware Software Conocimiento Ideas Tiempo

HARDWARE NECESARIO

Mínimo

Recomendado

SOFTWARE NECESARIO

Mínimo

Recomendado

SDK PIXELMATOR SMULTRON

SDK PHOTOSHOP DREAMWEAVER SMARTSVN NAVICAT

CONOCIMIENTO NECESARIO PARA HACER APPS

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

¿QUE VAMOS A APRENDER EN ESTE CURSO?

Objective CCocoaQuartz

HTML5Javascript

CSS

OpenGL ES 1.5OpenGL ES 2.0

GLSL

PHPMySQLSQLite

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

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.

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

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

4.Gestión de vistas

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

5.Ciclo de vida

Delegate Controlador Vista

Inicio

Fin

Botón {Preferencias()}

Inicio {AbrirTitulo()}

AbrirPreferencias() Preferencias()

AbrirTitulo()

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

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.

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];

8.Mensajes

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

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

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

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];

11.Heredar de una clase

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

- Una clase hereda el comportamiento de su superclase

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.

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.

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.

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;

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];

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.

15.Gestión de memoria con ARC

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];

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;

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.

Repaso 2

1.Introducción

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

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];! !

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];

...

4.Label

Cambiar texto de un UILabel:

[label setText:@”Texto deseado”];

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;

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];}

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;! }}

8.AlertView

Mostramos un AlertView

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

[alert show];[alert release];

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

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;}

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

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

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];

12.UIScrollView

- Devolver vista sobre la que se realizará zoom

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

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];}

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;! }! ! !}

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;

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

Repaso 3

1.Introducción

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

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

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"];

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;}

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]; }}

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];}

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]; }}

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]);}

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;}

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];

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

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

4.UIPopoverController

- Método del delegate

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

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

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; }

}

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..

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

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

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:@"correo@enviar.com"]; ! ! !! ! ! [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];!!! ! } ! }

Repaso 4

1.Introducción

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

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

3.Camera

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

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]; }

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];}

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

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";

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

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

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

6.Multitarea

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

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

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];

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

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

9.Transiciones predefinidas entre vistas

Posibles transiciones entre vistas:

UIViewAnimationTransitionCurlUpUIViewAnimationTransitionCurlDownUIViewAnimationTransitionFlipFromLeftUIViewAnimationTransitionFlipFromRigth

19.Touch Events and UIResponder

- 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

- 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;

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

- Con doble-tap limpiamos la pantalla

2.Creating the TouchTracker Application

- 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

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

TouchDrawView.h

#import <Foundation/Foundation.h>

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

@end

2.Creating the TouchTracker Application

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

- 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); }}

- Hacer el método de limpiar

3.Drawing with TouchDrawView

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

- (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

- (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

- (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

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];

- 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

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];}

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;}

7.Circles

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

- Con doble-tap limpiamos la pantalla

//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

- 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

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];

}}

20.Core Animation Layer

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

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

2.Creating a CALayer

- Imagen de fondo

- Rectángulo rojo que resalta un área

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

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

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];

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

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

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

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

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

21.Controlling Animation with CAAnimation

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”

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

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

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

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"];}

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

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];

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

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"];

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

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];

22.Blocks and Categories

1.Colorizing TouchDrawView

- Partiremos de nuestro ejemplo TouchDrawView

- Vamos a utilizar bloques para pintar con diferentes colores

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

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

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); }

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.

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

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];}

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"}

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

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]; }]; }];

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

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

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];}

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.

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.

23.Web Services and UIWebView

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

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

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

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

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];

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

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

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

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

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;

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];!

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

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];! }}

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];! }}

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]];! }}

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];}

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;}

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];}

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

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

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 :

7.UIWebView

- Instanciarlo desde el AppDelegate

- Crear una propiedad en ListViewController para apuntar a WebViewController

WebViewController *webViewController;

@property(nonatomic,retain) WebViewController *webViewController;

@synthesize webViewController;

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"]];}

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

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

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]; }}

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

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;}

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];}

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;}

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á

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];}

12.More UIWebView

- Métodos de UIWebViewDelegate:

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

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

24.UISplitViewController

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

1.Splitting Up Nerdfeed

- Añadiremos un UISplitViewController a nuestro ejemplo Nerdfeed

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

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];

...

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;}

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

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];

}

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

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.

25.Media Playback and Background Execution

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

1.Creating the MediaPlayer Application

- Aplicación multimedia

- Reproducir fichero de audio

- Reproducir un sonido del sistema corto

- Reproducir fichero de video

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

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 :

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

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

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);

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

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

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]; }}

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");}

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

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

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

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];

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]; }

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)]; }

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

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]; }

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

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

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

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

13.Audio Recording

- Punteros necesarios

AVAudioPlayer *grabadoPlayer; AVAudioRecorder *grabadoRecorder; bool grabando;

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

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]; }}

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

26.Push Notifications and Networking

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

1.Push Notifications

- Esquema de Push Notification

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"}}

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

4. Generating the Certificate Signing Request (CSR)

- Abrir keychain

- Solicitar un certificado para una autoridad de certificación

4. Generating the Certificate Signing Request (CSR)

4. Generating the Certificate Signing Request (CSR)

4. Generating the Certificate Signing Request (CSR)

5. Making App ID and SSL Certificate

5. Making App ID and SSL Certificate

5. Making App ID and SSL Certificate

5. Making App ID and SSL Certificate

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

5. Making App ID and SSL Certificate

5. Making App ID and SSL Certificate

- Convertimos el certificado en un fichero .pem

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

6. Simple PHP Server

- Subimos los ficheros:

- ck.pem generado- simplepush.php

- Tendré acceso desde:

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

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);

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

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

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

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);}

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

¿COMO CONTINUO EL CAMINO PARA CONVERTIRME EN UN

PROGRAMADOR NINJA?

Objective CCocoaQuartz

HTML5Javascript

CSS

OpenGL ES 1.5OpenGL ES 2.0

GLSL

PHPMySQLSQLite

LIBROS RECOMENDADOS EDITORIAL APRESS

Miguel José García Corcheromiguel@atomicflavor.comtwitter : @miguelgarciacorhttp://www.atomicflavorgames.comhttp://www.atomicflavor.com

CONTACTO