domingo, 25 de marzo de 2012

Mostrar un UIPickerView con un botón para cerrarlo desde un UITextField

Hoy vamos a ver como crear un campo UITextField que, al tocarlo, nos muestre un UIPickerView donde seleccionar el dato que queremos introducir. Además, los UIPickerView no disponen de ningún botón para cerrarlos, lo cual puede ser un problema. Vamos a ver una forma de añadir uno.

El resultado final va a ser algo así:



Lo primero que hay que hacer es crear el UITextField y establecer el ViewController en el que está como su delegate. Esto lo podemos hacer "cableando" en el Storyboard o en el Interface Builder. Pulsamos con el botón derecho el UITextField y arrastramos hasta el ViewController. En el menú que se despliega seleccionamos Delegate y listo.

Ahora debemos añadir el protocolo UITextFieldDelegate. Todos los métodos de este protocolo son opcionales. En este caso vamos a implementar el siguiente:

 - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
    [self showPickerWithDoneButton:textField];
    return YES;
}

Con esto, cuando toquemos el campo texto para introducir datos, llamaremos al showPickerWithDoneButton:(UITextField *)textField, que es donde vamos a abrir el UIPickerView. Para manejar UIPickerView debemos añadir sus protocolos UIPickerViewDataSource y UIPickerViewDelegate. Su implementanción en este caso es muy sencilla, así que os lo dejo como deberes.

- (void)showPickerWithDoneButton:(UITextField *)sender
{
    UITextField *textField = sender;
    
    // Creamos UIPickerView como una vista personalizada de un keyboard View
    UIPickerView *pickerView = [[UIPickerView alloc] init];
    [pickerView sizeToFit];
    pickerView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
    pickerView.delegate = self;  
    pickerView.dataSource = self;
    pickerView.showsSelectionIndicator = YES;
    
    self.pickerView = pickerView;  //UIPickerView
    
    //Asignamos el pickerview al inputView de nuestro texfield
    textField.inputView = self.pickerView;
    
    // Preparamos el botón 
    UIToolbar* keyboardDoneButtonView = [[UIToolbar alloc] init];
    keyboardDoneButtonView.barStyle = UIBarStyleBlack;
    keyboardDoneButtonView.translucent = YES;
    keyboardDoneButtonView.tintColor = nil;
    [keyboardDoneButtonView sizeToFit];
    
    UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:         NSLocalizedString(@"Done", @"Button")                                                                                    style:UIBarButtonItemStyleBordered target:self                                                                 action:@selector(pickerHechoClicked:)];

    doneButton.tintColor = [UIColor redColor];
    
    //Para ponerlo a la derecha del todo voy a crear un botón de tipo Fixed Space
    UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace                                                                                    target:nil action:nil];        
                                                                    
    fixedSpace.width = keyboardDoneButtonView.frame.size.width - 70;    
    [keyboardDoneButtonView setItems:[NSArray arrayWithObjects:fixedSpace, doneButton, nil]];
    
    // Finalmente colocamos la keyboardDoneButtonView  en el text field...
    textField.inputAccessoryView = keyboardDoneButtonView;     
}

Solo nos queda implementar el pickerHechoClicked, que es es el método que se va a ejecutar cuando se pulse el botón.  Lo único que tendremos que hacer es mandar el mensaje resignFirstResponder al textfield que ha mostrado el picker View para cerrarlo.


domingo, 18 de marzo de 2012

Personalizar UINavigationBar y UIBarButtonItem con una imagen de fondo

A partir de iOS5, es posible modificar el aspecto de los UINavigationBar o UIButton de una manera sencilla.

Vamos a ver un ejemplo de cómo cambiar el color de fondo de un UINavigationBar y sus botones.

Primero tenemos que crearnos la imagen que vamos a usar como background. Tenemos que tener en cuenta que la altura en el iPhone cuando está en horizontal es 44 pixels y en modo vertical es 32. Para el iPad es 44 en cualquier orientación. La de los botones es 37 pixels.

Necesitamos crear dos imágenes diferentes para el background. En el iPhone 3 y 3GS y en el iPad 1 y 2 la cuadrícula de 320x480 pixel. Sin embargo iPhone 4, 4S e iPad3 , con su retina display,  utilizan 640x960 pixels en una pantalla del mismo tamaño. Esto significa que 1 punto = 2 pixels, con lo que la imagen se verá más nítida.

Para detectar la Retina Display en el dispositivo iOS comprobamos si la propiedad [UIScreen mainSceeen].scale es igual a 2.0:

if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && 
    ([UIScreen mainScreen].scale == 2.0)) 
    {
        //Retina Display
        /
    } else 
    {
        //No Retina Display
    }
Las guías de desarrollo iOS aconsejan que las imágenes tengan el mismo nombre y a la destinada para dispositivos con retina display añadirla el sufijo @2x

Mediante photoshop he creado un fondo como el de la siguiente imagen para la NavigationBar.








Los he llamado:

  • NavBG.png (320x44px) y
  •  NavBG@2x.png (640x88px)
Necesitamos crear una clase que herede de UINavigationBar. Vamos a llamarla CustomNavBar. Su fichero de interface será así:

@interface CustomNavBar : UINavigationBar
@end

Y en la implementación simplemente sobreescribiremos su método drawRect:

#import "CustomNavBar.h"
@implementation CustomNavBar
- (void)drawRect:(CGRect)rect 
{
    UIImage *image;
    
    if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0)) 
    {
        //Retina Display
        image = [UIImage imageNamed:@"NavBG@2x.png"];
    } else 
    {
        //No Retina Display
        image = [UIImage imageNamed:@"NavBG.png"];
    }
    [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
@end

Una vez que tengamos la clase, nos situaremos en el storyboard y accederemos a la Navigation Bar del Navigation Controller.



Y en el Identity Inspector cambiaremos la clase UINavigationBar por la nuestra, CustomNavBar, tal y como se ve en la figura:




Este será el resultado si ejecutamos nuestro código: 



Vamos a personalizar los botones Edit y Add con la siguiente imagen:

Button.png
En el viewDidLoad del MasterViewController añadiremos nuestra imagen. 

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.navigationItem.leftBarButtonItem = self.editButtonItem;
    
    UIImage *editButton = [[UIImage imageNamed:@"Button"resizableImageWithCapInsets:UIEdgeInsetsMake(12, 12, 12, 12)];
    [self.editButtonItem setBackgroundImage:editButton forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
    [addButton setBackgroundImage:editButton forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    self.navigationItem.rightBarButtonItem = addButton;
    self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
}

Obteniendo este resultado:




domingo, 11 de marzo de 2012

Creación de un Provisioning Distribution Profile

Y por fin llegó el esperado momento en el que habéis terminado vuestra aplicación y estáis ansiosos por enviarla a la Apple Store para que la revisen. 

Pero antes tenemos que codificarla con un certificado de distribución. Si es nuestra primera aplicación o no queremos utilizar alguna de las que ya tenemos, tenemos que conseguir una. Para ello tenemos que acceder al iOS Provisioning Center dentro de nuestro iOS Dev Center (obviamente debemos tener una developer account de Apple). 



Una buena costumbre es crear un App ID para cada una de nuestras aplicaciones. Técnicamente no es necesario, pero si dependiendo de la aplicación se necesita del Game Center, de iCloud, de las notificaciones Push, etc, nos viene bien tener diferentes App IDs. Para crear uno nuevo accederemos al menú situado a la izquierda App IDs y pulsaremos el botón New App ID. 


Introduciremos una descripción y el nombre del Apple ID que queramos, que deberá ser único y con un nombre representativo. Podéis incluir el nombre de la App, por ejemplo. Una vez pulsado el botón Submit, ya tendremos nuestro ID.


Ahora vamos al menú situado a la izquierda Provisioning y dentro de este a la pestaña Distribution, en donde pulsamos sobre New Profile



El método de distribución que marcaremos es Apple Store, damos un nombre al distribution profile (que sea significativo) y seleccionamos el App ID que acabamos de crear en el paso anterior. 


Pinchamos en Submit y veremos que el Provisioning Distribution Profile se queda en estado Pending. Para verlo activo basta con refrescar el navegador de internet. A continuación os lo descargáis y arrastráis el archivo al icono del Xcode en el dock.
Con esto ya podéis crear vuestro archivo de la App, validarlo y enviarlo a Apple. 

domingo, 4 de marzo de 2012

Localización de nib files en iOS


Seguramente, para buscar un mayor éxito de vuestra aplicación, llegaré el momento en que necesitéis traducirla a otros idiomas. Este proceso es lo que se denomina localización.

Para realizar la localización de los ficheros nib, en  los que se incluyen los storyboards, si utilizas Xcode 4.2 o posterior, Apple nos proporciona el comando de línea ibtool. 

Supongamos que queremos crear un fichero strings de localización para nuestro storyboard. Por defecto estará en la carpeta en.lproj, supuestamente en inglés. Desde el terminal nos situaremos en el directorio de en.lproj de la aplicación y ejecutaremos el comando:

ibtool --generate-strings-file storyboard.strings MainStoryboard.storyboard

De esta forma nos va a generar un archivo strings con nombre storyboard a partir de nuestro storyboard llamado MainStoryBoard con el siguiente aspecto:

/* Class = "IBUITableViewController"; title = "Master"; ObjectID = "12"; */
"12.title" = "Hello";
/* Class = "IBUINavigationItem"; title = "Vehicles"; ObjectID = "36"; */
"36.title" = "Car";
/* Class = "IBUILabel"; text = "Distance"; ObjectID = "0mA-rX-5ud"; */
"0mA-rX-5ud.text" = "Distance";

La parte izquierda del igual es la clave identificativa del texto y la derecha es el texto que aparece en el interfaz. 
Este fichero será el que habrá que traducir al resto de idiomas que tengamos en nuestra app y se dejará en su directorio correspondiente. Por ejemplo en español sería algo así:

/* Class = "IBUITableViewController"; title = "Master"; ObjectID = "12"; */
"12.title" = "Hola";
/* Class = "IBUINavigationItem"; title = "Vehicles"; ObjectID = "36"; */
"36.title" = "Coche";
/* Class = "IBUILabel"; text = "Distance"; ObjectID = "0mA-rX-5ud"; */
"0mA-rX-5ud.text" = "Distancia";

Y se dejará en el directorio es.lproj.

A partir del fichero generado para crear el storyboard en español se utiliza el siguiente comando:

ibtool --strings-file es.lproj/storyboard.strings --write es.lproj/MainStoryboard.storyboard en.lproj/MainStoryboard.storyboard

De esta forma, basándose en los strings traducidos que teníamos en es.lproj/storyboard.strings se ha generado un es.lproj/MainStoryboard.storyboard en español, a partir del que teníamos en ingles en en.lproj/MainStoryboard.storyboard.

Por último debéis añadir el nuevo storyboard a vuestro proyecto.

Hay que tener en cuenta que la labor de localización no acaba aquí. La longitud de los strings habrá variado, con lo que puede que haya que hacer ajustes en las vistas, botones, etc.

Además, los formatos de fechas, números, etc. no son los mismos en los diferentes idiomas. Para realizar la localización de ese tipo de cosas, tenemos la clase NSLocale, pero eso lo veremos en otra ocasión.