Mostrando entradas con la etiqueta iOS. Mostrar todas las entradas
Mostrando entradas con la etiqueta iOS. Mostrar todas las entradas

domingo, 15 de marzo de 2015

Asset Catalogs

La existencia de dispositivos iOS con diferente resolución, hace que sea necesario gestionar la resolución de las imágenes utilizadas en las Apps según el dispositivo en el que se lance para evitar que salgan pixeladas. Anteriormente se utilizaban diferentes nombres, como file@2x.png, file-568h@2x.png, etc. para una misma imagen.

Asset Catalogs nos permite simplificar la gestión de las imágenes que utiliza nuestra aplicación. Ya no es necesario utilizar diferentes nombres o comprobar mediante código en que dispositivo se está ejecutando la aplicación.

Para añadir nuevos elementos al Asset Catalog, se selecciona el xcassets y, pulsando con el botón derecho del ratón en la ventana donde aparece la lista de conjuntos de imágenes, aparecerá un menú emergente mediante el que podemos añadir un nuevo conjunto de imágenes, el icono de la aplicación, la imagen que aparece al lanzar la aplicación o un icono OS X.

Fig. 1



En la siguiente imagen se ha creado un conjunto de imágenes  llamado Clock. Al haber seleccionado en el Attributes Inspector que el tipo de dispositivo es Universal, nos permite añadir imágenes con resolución 1x, 2x y 3x.

Fig. 2

Para hacer referencia a este conjunto de imágenes, basta con utilizar su nombre, en este caso Clock, sin necesidad de extensión. El nombre de los archivos que contienen las imágenes puede ser cualquiera, no hace falta que tenga ningún tipo de sufijo del tipo @2x o @3x.

En el caso de que sea necesaria una determinada imagen según el tipo de dispositivo, es necesario seleccionar la opción Device Specific y marcar los checks de los dispositivos para los que se crea el conjunto de imágenes, tal y como se muestra en la siguiente figura.

Fig. 3

@Fin

sábado, 14 de marzo de 2015

Conceptos de Core Data

Vamos a ver algunos de los conceptos más importantes relacionados con Core Data.

 Lo primero, ¿que es Core Data? Core Data es un framework que permite trabajar con tus datos como si fueran objetos, independientemente de cómo están almacenados en disco. Se situa entre tu aplicación y un persistent store, que es el término genérico para un fichero de datos, el cual puede ser una base de datos SQLite, un fichero XML o un almacen binario. A estos ficheros se les llama persistent por que permanecen a pesar de que cierres la aplicación, reinicies el dispositivo, etc.

Core Data utiliza un Managed Object Model para configurar los datos de tu aplicación utilizando un object graph. Cada objeto del object graph se llaman entity. Una vez que tienes definidos managed objects, se puede manipular nativamente en Objective-C sin necesidad de utilizar SQL (o cualquier otro tipo de tratamiento de un persistent store).

En la siguiente imagen vemos un object graph muy simple con dos entidades Client y Purchase. Como veis, es similar a un esquema de base de datos, en donde las entities serían las tablas.

Fig. 1


Un managed object contiene una copia de datos de un persistent store. Si tu persistent store es una base de datos, un managed objet representaría una fila de una tabla de la base de datos. Un managed object es una instancia de NSManagedObject, aunque usualmente es una instancia de una subclase de NSManagedObject.

Todos los managed objects existen en un managed object context, que existe en memoria RAM. De esta manera se accede a los datos que han sido previamente leidos de disco de una manera muy rápida sin necesidad de estar continuamente accediendo a disco. El managed object necesitará llamar a save: para escribir los cambios en disco.

En la imagen Fig.2 se ilustra como encajan las piezas principales de Core Data:

Fig. 2

En Fig.2, el Persistent Store Coordinator se muestra conteniendo un persistent store con filas de una tabla. Cuando se configura un persistent store coordinator, normalmente se elige una base de datos SQLite como persistent store. Un persistent store coordinator puede tener varios persistent store. Por ejemplo, cuando Core Data se integra con iCloud, poniendo datos que no pertenecen a iCloud en un persistent store y los que si pertenecen en otro, se optimiza el ancho de banda y el espacio de almacenamiento usado en iCloud. Se utilizan instancias de NSPersistentStore y NSPersistentStoreCoordinator para crear persistent stores y persistent store coordinators respectivamente.

En el medio de Fig.2 se representa en el medio el Managed Object Model. Es la representación gráfica de la estructura de datos. Las entities no contienen datos, solo definen las propiedades que tienen los managed objects basados en ellas. Un managed object model se crea mediante una instancia de NSManagedObjectModel.

Finalmente, en la derecha de la Fig. 2 se muestra el managed object context que se encarga de gestionar el ciclo de vida de los managed objects incluidos en él y proporciona características para hacer fetch de los datos, validaciones y seguimiento de los cambios. También se pueden definir varios managed object context, lo cual se utiliza sobre todo para realizar procesamiento en background, como salvar o importar datos. Para crearlo se instancia NSManagedObjectContext.

@Fin

sábado, 16 de noviembre de 2013

Sonidos del sistema en iOS

Para añadir sonidos del sistema a nuestra App, lo primero que hay que hacer es añadir el framework AudioToolbox.


En aquellos View Controllers en los que se vaya a utilizar, se deberá importar:

#import <AudioToolbox/AudioToolbox.h>

Para generar el sonido utilizaremos el método AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID). Podéis encontrar una lista de todos los system sounds en iOS aquí.

Lo más recomendable es definir los IDs que se vayan a utilizar con un nombre significativo, por ejemplo algo así:

#define systemSoundKeyPressed   1104
#define systemSoundUnlock       1101
#define systemSoundFailedUnlock 1102


Y hacerlos sonar así:

AudioServicesPlaySystemSound(systemSoundKeyPressed);


Hay que tener en cuenta que en el simulador no se oyen, deberéis probarlo en un dispositivo iOS.

@Fin

sábado, 31 de agosto de 2013

Animación de imágenes en iOS

En iOS se pueden programar animaciones de imágenes de manera sencilla. Vamos a ver un ejemplo en el que dos UIImageView intercambian sus posiciones, deslizándose de una a otra, cuando el usuario toca una de ellas. Además veremos las diferencias de usar el sistema de springs y struts o Autolayout.

Primero vamos a crear los dos UIImageView en el storyboard, image1 e image2 y cablearemso hacía el código para crear las propiedades.



De esta forma en el interface tendremos algo así:


@interface AnimacionViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *image1;
@property (weak, nonatomic) IBOutlet UIImageView *image2;
@end

Desde el inspector de atributos a una la pondremos como Background amarillo y a la otra rojo para poder diferenciarlas. Hay que tener también en cuenta que se debe permitir la interacción del usuario, por lo que el check deberá estar marcado tal y como se ve en la siguiente figura.




Ahora añadiremos el reconocimiento de gestos. Como lo que queremos es que la animación se produzca al tocar las imágenes, necesitamos el UITapGestureRecognizer. Lo crearemos en el viewDidLoad y se lo añadimos a las dos imágenes.



- (void)viewDidLoad
{
    [super viewDidLoad];
    
UITapGestureRecognizer *tapGesture1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];

UITapGestureRecognizer *tapGesture2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];
    
    [self.image1 addGestureRecognizer:tapGesture1];
    [self.image2 addGestureRecognizer:tapGesture2];
}

Como veis, la acción que se va a ejecutar es tap, que todavía no hemos definido. En ella lo que vamos a hacer es ejecutar el mensaje interchangImageView:imageView1 withImageView:imageView2, que también tenemos que definir.


- (void)tap
{
    [self interchangImageView:self.image1
                withImageView:self.image2];
    
}


Y es en interchangImageView:imageView1 withImageView:imageView2 donde veremos la diferencia de usar Autolayout o no.

Sin Autolayout


- (void)interchangImageView:(UIImageView *)imageView1 withImageView:(UIImageView *)imageView2
{
    CGPoint image1CenterPoint = imageView1.center;
    CGPoint image2CenterPoint = imageView2.center;
    
    [UIView animateWithDuration:1.0 animations:^{
        imageView1.center = image2CenterPoint;
        imageView2.center = image1CenterPoint;
    }];
}

Vemos que simplemente obtenemos el centro de cada imagen y luego los intercambiamos utilizando el class method animateWithDuration:animations de UIView. La duración de la animación será un segundo y en en bloque animations es donde intercambiamos los centros de las imágenes.

Con Autolayout
El método anterior no funciona si autolayout está activado. Si se moverían las imágenes pero, debido a las constraint definidas, volverían a su posición original. Por eso la animación debe basarse en las constraints. 

Añadiremos las constraints necesarias a ambas imágenes. En este caso son Top Space, Leading Space, Width y Height.


Cablearemos las dos Vertical Space para crear las dos propiedades NSLayoutConstraint, con lo que el interface ahora deberá quedar así:


@interface AnimacionViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *image1;
@property (weak, nonatomic) IBOutlet UIImageView *image2;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *verticalConstraintImage1;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *verticalConstraintImage2;
@end


Y el interchangImageView:imageView1 withImageView:imageView2 en este caso lo crearemos así:


- (void)interchangImageView:(UIImageView *)imageView1 withImageView:(UIImageView *)imageView2
{
    CGFloat img1ConstraintConstant = self.verticalConstraintImage1.constant;
    CGFloat img2ConstraintConstant = self.verticalConstraintImage2.constant;
    
    self.verticalConstraintImage1.constant = img2ConstraintConstant;
    self.verticalConstraintImage2.constant = img1ConstraintConstant;
    
    [imageView1 setNeedsUpdateConstraints];
    [imageView2 setNeedsUpdateConstraints];
    
    [UIView animateWithDuration:1.0 animations:^{
        [imageView1 layoutIfNeeded];
        [imageView2 layoutIfNeeded];
    }];
}

Lo que conseguimos con esto es intercambiar la distancia de ambas al top layout, por eso una subirá y la otra bajará. Al haber cambiado las constraints de las imágenes, es necesario llamar al mensaje setNeedsUpdateConstraints. Y, en el animateWithDuration, llamamos a layoutIfNeeded.

@Fin


martes, 13 de agosto de 2013

Utilización de diferentes versiones del Storyboard

Con el nuevo aspecto del interface en el iOS 7, probablemente os habréis encontrado con que vuestras aplicaciones diseñadas para versiones anteriores no quedan bien.

Para evitar tener que rediseñar toda la aplicación para que tenga la misma apariencia en todas las versiones de iOS, se pueden crear dos (o los necesarios) storyboards, uno para la versión 7 y posteriores y otro para las anteriores. De esta forma se consigue que los usuarios que migren a la nueva versión del iOS tengan un interfaz acorde con el aspecto del iOS 7 pero, los que no migren, mantengan la apariencia de las versiones anteriores con el que ya están familiarizados.

Lo primero que tenemos que hacer es asegurarnos que, dentro de la pestaña General,  el Main interface está vacío. De esta forma no asignamos ningún storyboard por defecto.



Después haremos una copia del storyboard actual a la que vamos a llamar, por ejemplo iPhoneiOS7.storyboard. A la versión actual la renombraremos a iPhoneiOS6.storyboard.



Es conveniente también que en el File inspector, se seleccionen las siguientes opciones para cada storyboard.

iPhoneiOS6.storyboard

iPhoneiOS7.storyboard

En el AppDelegate, añadiremos el siguiente código dentro del application:willFinishLaunchingWithOptions:


UIStoryboard *mainStoryboard = nil;

if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0" options:NSNumericSearch] == NSOrderedAscending)
{
    // Versiones anteriores a iOS 7
    // Instaciamos un objeto storyboard usando el archivo iPhoneiOS6.storyboard
    mainStoryboard = [UIStoryboard storyboardWithName:@"iPhoneiOS6" bundle:nil];
    
}
else
{
    // Versiones iguales o posteriores a iOS 7
    // Instaciamos un objeto storyboard usando el archivo iPhoneiOS7.storyboard
    mainStoryboard = [UIStoryboard storyboardWithName:@"iPhoneiOS7" bundle:nil];
    
}

// Instanciamos un objeto UIWindow y lo inicializamos con el tamaño de pantalla del dispositivo
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

// Hacemos que el initialViewController sea el rootviewcontroller
self.window.rootViewController = [mainStoryboard instantiateInitialViewController];

Además, en el application:didFinishLaunchingWithOptions: añadiremos la siguiente línea de código justo antes del return:

[self.window makeKeyAndVisible];


Con esto, lo que faltaría sería modificar el iPhoneiOS7.storyboard para adaptarlo al interfaz de la nueva versión del iOS. Una vez modificado se ejecutaran diferentes storyboards según la versión de iOS en la que se ejecute la App.

@Fin

domingo, 24 de marzo de 2013

Localización de imagenes en iOS

En esta ocasión os voy a contar como podemos hacer la localización para diferentes idiomas de imagenes que incluyen texto sin utilizar ni una sola línea de código.

Basta con añadir la imagen a nuestro proyecto Xcode y, seleccionándola ..


... accedemos al menú Utilities, sección Localization y pulsamos Localize.


Aparecerá una ventana en la que debemos indicar el idioma de la imagen:


De esta forma, indicaremos el idioma de la imagen seleccionada y, automáticamente, en la sección Localization aparecerán todos los idiomas de nuestra App.


Deberemos seleccionar las localizaciones deseadas y copiar el archivo correspondiente en su directorio lproj. Por ejemplo, si vamos a añadir la imagen en español, marcaremos el check Spanish y copiaremos el archivo con la imagen en el directorio es.lproj.

Y eso es todo.

@Fin

viernes, 1 de febrero de 2013

Asignar el Anchor de una PopOver a una UITableViewCell

Vamos a ver una forma de colocar el Anchor de un Segue de tipo Popover apuntando a una celda de una tabla dinámica. En el storyboard lo que tenemos es un prototipo de la celda, por lo que que si intentamos cablear el segue hacia ella, nos va a dar un error.

Así que os voy a contar como podemos solucionarlo. Se trata de crear un botón oculto que va a ser al que va a apuntar la ventana popover. Y moveremos su posición en función de la celda seleccionada.

Lo primero que haremos es crear el popover segue desde el ViewControler. Le asignaremos un Identifier, por ejemplo cellSegue.


Después añadiriremos a nuestra View un UIButon. Lo podemos colocar en cualquier sitio por qué va a tener su propiedad Hidden marcada, luego va a estar oculto. En el texto del botón voy a poner Anchor, aunque esto nos daría igual.


Y seleccionaremos el botón creado como Anchor del segue.


Ahora crearemos un outlet para el botón, que voy a llamar popOverAnchor:


@property (weak, nonatomic) IBOutlet UIButton *popOverAnchor;

En el @implementation colocaremos su synthesize:

@synthesize popOverAnchor = _popOverAnchor;


Deberemos implementar el método tableView:didSelectRowAtIndexPath: del UITableViewDelegate de esta forma:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{


//Recuperamos la celda que se ha seleccionado
        UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
        
//Creamos el CGRect al que va a apuntar el popover en función de la posición de la celda seleccionada
        CGRect rect = CGRectMake(tableViewCell.frame.origin.x + tableViewCell.frame.size.width/2, tableViewCell.center.y + tableView.frame.origin.y - tableView.contentOffset.y, 1, 1);
        
//Movemos el botón a esa posición
        self.popOverAnchor.frame = rect;

//Y para terminar llamamos al segue        
        [self performSegueWithIdentifier:@"cellSegue" sender:tableViewCell];

}

Y ya lo tenemos, como podéis ver, el botón se mueve al centro de la celda seleccionada y al ser el anchor de la ventana popover, conseguimos nuestro proposito.

@Fin

sábado, 3 de noviembre de 2012

Hacer Segue desde un UILabel o UIView

Los Segues son una especie de vínculos que nos permiten movernos entre los view controllers. Hacer segue desde botones es la opción más común, pero no la única. Os voy a mostrar como se puede hacer tambien desde UIView o UILabel.

Vamos a suponer que tenemos un View Controller tan simple como el de la siguiente figura, con solamente una etiqueta azul. Queremos conseguir que, al tocar sobre la etiqueta, nos lleve a otro View Controller.


Dentro del Attributes Inspector de la etiqueta es importante marcar el check User interaction Enabled.


A continuación arrastraremos un Tap Gesture Recognizer y lo soltamos sobre la etiqueta. 


Nos creamos otro View Controller simple hacia el que vamos a hacer segue al tocar sobre la etiqueta y, pinchando sobre el Tap Gesture Recognizer con el botón derecho arrastramos hacia el nuevo View Controller.



Y ya está, así de sencillo.

Lo mismo se podría hacer sobre una vista.

@Fin

sábado, 20 de octubre de 2012

Utilización de UIScrollView con UIPageControl

Vamos a ver como utilizar un UIScrollView con un UIPageControl, lo que nos va a permitir crear una aplicación con varias páginas.
La clase UIPageControl son los puntos que aparecen en la parte inferior la ventana de muchas aplicaciones y que indican la ventana en la que estáis cuando hay varias.
Lo primero que haremos será añadir un UIPageControl al UIVIewController en el StoryBoard:



Lo voy a hacer solo con 2 páginas, así que pongo la propiedad Pages a 2. Seleccionando el UIPagecontrol con el botón derecho, lo "cableo" hacía el código del UIViewController para crear la propiedad:

@property (weak, nonatomic) IBOutlet UIPageControl *pageControl; 

Vamos con el UIScrollView. En esta ocasión lo voy a hacer por código, aunque también se podría añadir en el StoryBoard.
Lo primero que haremos es añadir el delegate de UIScrollView al interface ViewController:

@interface myViewController : UIViewController <UIScrollViewDelegate>


A continuación definimos un método que va crear dos vistas con dos etiquetas y las va a añadir al ScrollView:

-(void)createTwoViewsWithTwoLabels
{
    // ScrollView
    UIScrollView *scrollView=[[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 60)];
    
    // View 1
    UIView *view1=[[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 40)];
    view1.backgroundColor=[UIColor colorWithRed:0.5f green:1.0f blue:0.5f alpha:1.0f];
    
    // Label 1
    UILabel *label1 = [[UILabel alloc]initWithFrame:CGRectMake(22, 20, 275, 38)];
    [label1 setBackgroundColor:[UIColor clearColor]];
    [label1 setText:@"Vista 1"];
    label1.textColor=[UIColor blueColor];
    label1.adjustsFontSizeToFitWidth=YES;
    label1.textAlignment = NSTextAlignmentRight;
    label1.font = [UIFont fontWithName:@"Chalkduster" size:50];
    label1.numberOfLines = 1;
    [view1 addSubview:label1];
    
    //Se añade la primera vista al scrollview
    [scrollView addSubview:view1];
    
    //View 2
    UIView *view2=[[UIView alloc]initWithFrame:CGRectMake(self.view.frame.size.width, 0, self.view.frame.size.width, self.view.frame.size.height - 40)];
    view2.backgroundColor=[UIColor colorWithRed:1.0f green:1.0f blue:0.7f alpha:1.0f];
    
    //Label 2
    UILabel *label2 = [[UILabel alloc]initWithFrame:CGRectMake(22, 20, 275, 38)];
    [label2 setBackgroundColor:[UIColor clearColor]];
    [label2 setText:@"Vista 2"];
    label2.textColor=[UIColor blueColor];
    label2.adjustsFontSizeToFitWidth=YES;
    label2.textAlignment = NSTextAlignmentRight;
    label2.font = [UIFont fontWithName:@"Chalkduster" size:50];
    label2.numberOfLines = 1;
    [view2 addSubview:label2];
    
     // Se añade la segunda vista al scrollview
    [scrollView addSubview:view2];
    
    // Propiedades necesarias del scrollview
    scrollView.pagingEnabled = YES;
    scrollView.delegate = self;
    scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * 2, scrollView.frame.size.height);
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.showsVerticalScrollIndicator = NO;
    scrollView.bounces = NO;
    
    //Se añade el scroll view a la vista del ViewController
    [self.view addSubview:scrollView];
    
}


Este método se llamará en el viewDidLoad:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self createTwoViewsWithTwoLabels]; 
}


Por último voy a implementar la función del UIScrollViewDelegate que detecta el cambio de página en el scroll:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    
    CGFloat pageWidth = scrollView.frame.size.width;
    int pagina = floor((scrollView.contentOffset.x- pageWidth / 2) / pageWidth) + 1;
    self.pageControl.currentPage=pagina;
    
}

Y con esto ya tendríamos un sencillo scroll con page control:



@Fin



martes, 21 de agosto de 2012

Ejecución de un proyecto con una localización específica en iOS

Cuando tengáis un proyecto con varias localizaciones, al ejecutarlo en el simulador, se verá la localización definida por defecto.
Necesitaréis comprobar como se comporta la App en los distintos idiomas. La mejor solución es utilizar un argumento que se pasan al ejecutable. Se trata del -AppleLanguages. Se le pueden pasar una lista de idiomas, separados por comas, en el orden preferido. En nuestro caso como lo que necesitamos es probar las diferentes localizaciones de nuestro proyecto, le indicaremos el idioma que queramos verificar en cada momento. Para ello, desde Xcode se accede al menú Product > Edit Scheme...  



A continuación, en la configuración del Run, acceder a la pestaña Arguments, pulsar '+' y añadir el argumento -AppleLanguages "(<localización>)". En el ejemplo de la siguiente imagen, la localización a probar es la española.


Otra forma de probar las diferentes localizaciones es desde un dispositivo iOS. Se cambia el idioma del dispositivo desde las preferencias y se ejecuta el proyecto. Aunque esta otra solución yo la veo más para las últimas fases de los tests.

@Fin



sábado, 2 de junio de 2012

Indicador de actividad

Una de las cosas que se debe evitar por todos los medios cuando se está desarrollando una App es que la ventana se quede bloqueada y no se muestre algún indicador que informe que está trabajando. Por ejemplo cuando se está cargando una página web. 
Una solución para evitar esta situación tan frustrante para el usuario es utilizar la propiedad setNetworkActivityIndicatorVisible de la clase UIApplication. Con esto se consigue añadir el circulo con rayitas que van cambiado de tono en la parte superior de la aplicación:


Otra posible forma sería utilizar la clase UIActivityIndicatorView. Vamos a ver un sencillo en el que mostraremos estos dos tipos de indicadores mientras se carga una página web.

Crearemos un Story Board con un View Controller al que añadiremos un Web View. Además cablearemos este UIWebView hacía el controller para especificarlo como su delegate. A continuación lo metemos en un Navigation Controller.


Network Activity Indicator


El interface del View Controller será así:

#import <UIKit/UIKit.h>

@interface WebViewController : UIViewController <UIWebViewDelegate>
@property (nonatomic, weak) NSString *webPage;
@property (weak, nonatomic) IBOutlet UIWebView *webView;

@end


Como veis el modelo va a ser un string con la URL. Además hemos añadido el protocolo UIWebViewDelegate.

El UIWebView lo voy a definir como local:

@interface WebViewController ()
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@end


La implementación de viewDidLoad será así:

- (void)viewDidLoad
{
    [super viewDidLoad];

    //Cargamos la página web en el web view
    NSURL *url = [NSURL URLWithString:self.webPage];
    NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:requestObj];
}

Y se necesita añadir dos métodos del UIWebViewDelegate:
  •  webViewDidStartLoad: indica cuando comienza a cargarse la página web. Es en ese momento cuando se debe poner la propiedad NetworkActivityIndicator a YES.
  • webViewDidFinishLoad: indica cuando termina de cargarse la página web. Se pondrá NetworkActivityIndicator a NO.

- (void)webViewDidStartLoad:(UIWebView *)webView {
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}


Activity Indicator View

Añadiremos una propiedad local para el  UIActivityIndicatorView:

@interface  WebViewController ()
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@property (strong, nonatomic) UIActivityIndicatorView *activityIndicator;
@end

Y voy a definir su método set así:

- (UIActivityIndicatorView *)activityIndicator
{
    if (_activityIndicator == nil) 
    {
        _activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
    }
    return _activityIndicator;
}

En este caso el viewDidLoad será: 

- (void)viewDidLoad
{
    [super viewDidLoad];
//  Con esto añadimos un botón al navigation bar con el activity indicator   
    UIBarButtonItem * barButton = [[UIBarButtonItem alloc] initWithCustomView:self.activityIndicator];
   [self navigationItem].rightBarButtonItem = barButton;
    
    NSURL *url = [NSURL URLWithString:self.webPage];
    NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];

    [self.webView loadRequest:requestObj];
}

Para terminar en los métodos webViewDidStartLoad webViewDidStartLoad  empezaremos y terminaremos la animación:

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

//Called whenever the view finished loading something

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

El resultado de esta segunda opción será este:


En este caso el indicador de actividad se muestra en la barra del Navigation Controller.

@Fin



domingo, 27 de mayo de 2012

UIActionSheet & UIImagePickerController

Como usuarios de dispositivos iOS os habréis encontrado muchas veces con un Action Sheet. Están formadas por un título opcional y uno o varios botones, cada uno de los cuales se corresponde con una acción.
La clase UIActionSheet se  utiliza para proporcionar al usuario una serie de alternativas acerca de como proceder. También se puede utilizar para informar sobre como actuar ante una situación potencialmente peligrosa.
Vamos a ver un ejemplo muy útil de utilización de UIActionSheet, el que nos da opción de realizar una foto o elegirla de nuestro carrete, por ejemplo para asignarla a un contacto. Es decir, algo así:



Como podéis ver tenemos tres botones:
  1. Hacer Foto: nos abrirá la aplicación de la cámara para realizar una nueva fotografía.
  2. Elegir Foto: permitirá acceder al carrete para escoger una foto previamente realizada.
  3. Cancelar: no realizar ninguna acción y cerrar el action sheet.
En esta ocasión no vamos a poner ningún título.

Lo primero que tenemos que añadir cuando vamos a utilizar esta clase, es el protocolo UIActionSheetDelegate. Deberemos implementar el mensaje actionSheet:clickedButtonAtIndex: , que nos indicará que botón se ha pulsado. El índice va de 0 a n.


- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex

{
    switch (buttonIndex) {
        case 0: //cámara
            [self takePhoto:UIImagePickerControllerSourceTypeCamera];
            break;
        case 1:
            [self takePhoto:UIImagePickerControllerSourceTypeSavedPhotosAlbum];
            break;            
        default:
            break;
    }
}

En el código anterior, en función del índice, estamos enviando un mensaje pasando como parámetro UIImagePickerControllerSourceTypeCameraUIImagePickerControllerSourceTypeSavedPhotosAlbum. Estas dos variables son el tipo de fuente de una clase de la que no hemos hablado hasta ahora y que vamos a necesitar para tomar las fotos: UIImagePickerController. Esta clase se utiliza para gestionar el interface que nos proporciona el sistema para realizar fotografías o vídeos, bien sea desde la cámara del dispositivo o desde la librería de fotos y/o vídeos.
El tipo de fuente puede ser:
  • UIImagePickerControllerSourceTypeCamera: nos proporciona un interface de usuario para realizar fotos o vídeos. Es el que hemos utilizado en la opción 0 del action sheet.
  • UIImagePickerControllerSourceTypePhotoLibrary o UIImagePickerControllerSourceTypeSavedPhotosAlbum: nos proporciona un interface de usuario para elegir entre fotos o vídeos guardados. 
Añadiremos el protocolo UIImagePickerControllerDelegate. Además, para usar un image picker controller, hay que seguir estos pasos:
  • Verificar que es capaz de utilizar el tipo de fuente. Para ello se utiliza el class method isSourceTypeAvailable, que tiene como parámetro una constante de  la enumeración UIImagePickerControllerSourceType:.
  • Comprobar que tipos de medios hay disponibles para el tipo de fuente utilizado. Esto se hace con el class method availableMediaTypesForSourceType:. De esta forma podemos distinguir entre una cámara que se puede usar para vídeos y una que solo se puede usar para fotos.
  • Asignar la propiedad mediaTypes para ajustar el interfaz de usuario en función del tipo de medio.
  • Mostrar el interfaz de usuario llamando al método presentModalViewController:.
  • Cerrar el image picker cuando el usuario ha hecho la acción deseada.
Dicho esto, tenemos que ver como se ha implementado el método takePhoto.

- (void)takePhoto:(UIImagePickerControllerSourceType)sourceType
{
    if ([UIImagePickerController isSourceTypeAvailable:sourceType]) //Verificamos si el dispositivo permite el tipo de fuente
    {
        NSArray *mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:sourceType]; //Comprobamos que tipos de medios hay disponibles para el tipo de fuente utilizado
        if ([mediaTypes containsObject:(NSString *)kUTTypeImage]) //Identificador de tipos abstractos para imágenes
        {
            UIImagePickerController *imagePicherController = [[UIImagePickerController alloc] init];
            imagePicherController.delegate = self;
            imagePicherController.sourceType = sourceType;
            imagePicherController.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeImage];
            imagePicherController.allowsEditing = YES;
            [self presentModalViewController:imagePicherController animated:YES];
        }
    }
    else //Si el tipo de fuente es UIImagePickerControllerSourceTypeCamera y el dispositivo no tiene, se muestra una alerta
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo", "Pop Up window headerr")
                                                        message:NSLocalizedString(@"Su dispositivo no permite realizar fotos",@"Pop up window error message")
                                                       delegate:nil
                                              cancelButtonTitle:@"OK"
                                              otherButtonTitles:nil];
        [alert show]; 
    }
}

Por último, es necesario implementar dos métodos del protocolo UIImagePickerControllerDelegate: 
  • imagePickerController:didFinishPickingMediaWithInfo: para recuperar la foto
  • imagePickerControllerDidCancel: para cerrar el picker controller.


- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    UIImage *photo = [info objectForKey:UIImagePickerControllerEditedImage];
    if (!photo) photo = [info objectForKey:UIImagePickerControllerOriginalImage];

/*....Aquí haremos con la foto lo que necesitemos, guardarla en los defaults del usuario, crear un thumbnail, etc....*/

    [self dismisImagePicker];
}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [self dismisImagePicker];
}

Nada más por hoy.
@Fin