may 09

Al pasar de la versión 3 a la 4 de Xcode una de las cosas más molestas que nos hemos encontrado fue la forma de mostrar la documentación. Antes, al abrir la documentación de una clase, en el lado izquierdo de la ventana podíamos ver una especie de índice que nos permitía visualizar directamente la lista de métodos de clase y de instancia que tenía esa clase concreta y podíamos saltar directamente a ver la info de un método determinado, etc.

Con la salida del Xcode 4 esa tabla de contenidos desapareció (con el tiempo que nos ahorraba).

Resumiendo, para volver a mostrar esa tabla de contenidos en la documentación hay que ejecutar el siguiente comando en la terminal (recomiendo hacer un backup de los docsets por si algo sale mal):

sudo egrep -lRZ "Prototype.Browser.Xcode4OrNewer=Prototype.Browser.XcodeVersion<1000;" /Library/Developer/Documentation/DocSets | xargs -0 -L % sudo sed -i '' -e 's/Prototype\.Browser\.Xcode4OrNewer\=Prototype\.Browser\.XcodeVersion\<1000\;/Prototype\.Browser\.Xcode4OrNewer\=false\;/g'

Esta información se ha obtenido de este hilo de stackoverflow.

VN:F [1.9.8_1114]
Rating: 4.0/5 (4 votes cast)
VN:F [1.9.8_1114]
Rating: 0 (from 0 votes)
Tagged with:
nov 11

Hoy vengo con artículo muy corto y simple. En este artículo vamos a ver la forma más fácil de crear un color transparente.

He visto en el código de mucha gente en el mío cuando emepzaba usar lo siguiente para obtener un color transaparente:
[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.0]

Es decir, ponemos cualquier cosa en los componentes rojo, verde y azul pero en el alpa ponemos cero para conseguir un color transparente.

Pero los de Apple ya han pensado en que ese color es muy útil y se usa bastante y han creado un método específico para eso:

[UIColor clearColor]

Eso es todo por hoy.

VN:F [1.9.8_1114]
Rating: 4.8/5 (5 votes cast)
VN:F [1.9.8_1114]
Rating: +4 (from 4 votes)
Tagged with:
oct 07

Hace unos minutos descubri un error en una de mis aplicaciones y pensé que estaría bien comentarlo con el resto de la gente para que no cometan el mismo error.

Cuando intentamos enviar emails desde nuestras aplicaciones usando el MFMailComposeViewController podemos tener un problema si los usuarios no tienen configurada ninguna cuenta.

mail = [[MFMailComposeViewController alloc] init];
[mail setMessageBody:[NSString stringWithFormat:@"Texto del cuerpo"]  isHTML:YES];
mail.mailComposeDelegate = self;
[self presentModalViewController:mail animated:YES];
[mail release];

El problema con el código de arriba consiste en que si el usuario no tienen ninguna cuenta configurada por lo que el init del MFMailComposeViewController devolverá nil y al intentar mostrar la vista de mail ([self presentModalViewController:mail animated:YES]) la aplicación se cerrará.

Para evitar eso tenemos que comprobar que el mail no es nil antes de hacer nada. El código buenos sería este:

mail = [[MFMailComposeViewController alloc] init];
[mail setMessageBody:[NSString stringWithFormat:@"Texto del cuerpo"]  isHTML:YES];
mail.mailComposeDelegate = self;
if (mail !=nil)
    [self presentModalViewController:mail animated:YES];
else
   //Informar al usuario o lanzar la aplicación mail.
[mail release];
VN:F [1.9.8_1114]
Rating: 5.0/5 (4 votes cast)
VN:F [1.9.8_1114]
Rating: +6 (from 6 votes)
Tagged with:
ago 31

Según la aplicación que estamos desarrollando, puede ser interesante o incluso necesario evitar que el iPhone se autobloquee mientras nuestra aplicación esta en marcha. Por suerte tenemos una manera muy sencilla de conseguirlo y con sólo una instrucción.

[UIApplication sharedApplication].idleTimerDisabled = YES;

Eso es todo lo que necesitamos para que el iPhone permanezca todo el tiempo encendido, un buen lugar para ponerlo es en el método applicationDidFinishLaunch: en nuestro UIApplication delegate.

Tened cuidado porque si el usuario sale corriendo y deja su iPhone solo, durante mucho tiempo, al dejar la pantalla encendida todo el rato, lo dejaremos sin batería.

VN:F [1.9.8_1114]
Rating: 5.0/5 (1 vote cast)
VN:F [1.9.8_1114]
Rating: +2 (from 2 votes)
Tagged with:
jul 25

Con este artículo vamos a comenzar una serie de artículos sobre cocos2d.

Cocos2d es un framework pensado para el desarrollo de juegos 2d en el iPhone/iPad. El framework esta construido sobre OpenGL ES por lo que se aprovecha de todo el poderio gráfico del iPhone. Según nuestra experiencia este framework es una maravilla.

Contiene toneladas de APIs que nos facilitan muchísimo la creación de juegos de calidad y vistosos.

En resumen: si piensas crear un juego 2d para el iPhone o el iPad a día de hoy no hay nada mejor que cocos2d.

En este primer artículo vamos a explicar como instalar la librería (es muy fácil) y como empezar nuestros proyectos con cocos2d (aquí tenéis la documentación de instalación en inglés) sin entrar en detalles y de la manera más simple y rápida.

Primero de todo tenemos que descargar cocos2d y descomprimirlo en una carpeta.

Al descomprimir el cocos2d tendremos el siguiente contenido:

Una vez descomprimido el zip podemos installar las plantillas de cocos2d que nos ayudan empezar nuevos proyectos basados en cocos2d sin tener que configurar nada.

Para eso abrimos la terminal y nos vamos a la carpeta donde hemos descomprimido el cocos2d.

En esa carpeta ejecutamos el siguiente comando:

./install-templates.sh

Con esto ya tendremos las nuevas plantillas disponibles en el XCode.

Creando un proyecto cocos2d nuevo

Una vez instalado las plantillas crear un proyecto cocos2d es trivial.

  1. Abrimos el XCode
  2. File > New Project
  3. Elegimos una de las tres plantillas cocos2d disponibles (ver la imagen de abajo).

Verá que tenemos 3 plantillas a elegir. El primero nos da un esqueleto de una aplicación cocos2d estándar. El segundo a parte de lo que hace el primero también configura el motor de físicas Box2d y el tercero hace lo mismo pero en vez del motor de Box2d usa el motor Chipmunk.

En principio si en sus juegos no necesitais tener que modelar física sobre los objetos de su juego tenéis que elegir la primera plantilla.

Ejemplos de cocos2d

Para empezar a jugar con cocos2d es muy recomendable echar un vistazo sobre los ejemplos de cocos2d que vienen en el mismo zip. Si os fijáis, en la carpeta descomprimida hay un fichero de proyecto XCode (cocos2d-iphone.xcodeproj). Es un megaproyecto que contiene un montón de ejemplos de cocos2d.

Para ejecutar cada una de los ejemplos tenemos que hacer coincidir el “Active Target” y el “Active Executable”.

Por ejemplo si queremos ejecutar el AtlasTest tenemos que elegir como Active Target el AtlasTest y como “Active Executable” también el AtlasTest (ver imágenes de abajo).

Con todo esto ya tenemos mucho material con que jugar.

En los siguientes artículos hablaremos más sobre cocos2d.

Si tenéis cualquier duda sobre la instalación no dudéis en preguntarnoslo vía comentarios (los leemos todos). También se aceptan sugerencias :-)

VN:F [1.9.8_1114]
Rating: 3.6/5 (5 votes cast)
VN:F [1.9.8_1114]
Rating: +4 (from 4 votes)
Tagged with:
jul 15

Hace algún tiempo os comenté como detectar cambios en propiedades mediante el KVO (Key-Value Observing) hoy veremos como hacer lo mismo con notificaciones.

La idea del ejemplo es la siguiente: Tenemos un objeto A de una clase X, del que queremos ser avisados cuando una propiedad sea modificada, para ello avisaremos a otro objeto B, para que éste último realize alguna acción. Para ello usaremos notificaciones, el objeto A enviará una notificación cuando cambie su propiedad y el objeto B estará pendiente de dichas notificaciones.

Veamos como hacer esto desde el código, primero nos creamos una función que será la encargada de enviar las notificaciones. Usamos el centro de notificaciones por defecto de nuestra aplicación y el método postNotificationName:object: para enviar la notificación. Los parámetros son un string para el nombre de la notificación y el objeto que envía la notificación.

#pragma mark -
#pragma mark Notification methods
 
- (void)sendNumberPadPressedNotification {
     [[NSNotificationCenter defaultCenter] postNotificationName:@"number pad pressed" 
                                                         object:self];
}

Después sólo tenemos que llamar a esta función cada vez que queramos notificar del evento o cambio producido, por ejemplo si pulsamos un botón.

#pragma mark -
#pragma mark Notification methods
- (IBAction)ceroButtonPushed:(id)sender {
     [input setString:[numberFormater stringFromNumber:[NSNumber numberWithFloat:0.00]]];
     coma = NO;
     decimal = 1;
     [self sendNumberPadPressedNotification];
}

En la última línea llamamos a nuestro método y la notificación es enviada, en este ejemplo se usa para avisar de que input fue cambiado.

Esto es todo lo que necesita nuestro objeto A (el que avisa del cambio o evento). Veamos ahora el código para el objeto B (el que quiere ser avisado).

Lo primero que tenemos que hacer es registrar el objeto B en el centro de notificaciones para poder recibirlas, obviamente nos tenemos que registrar en el mismo centro de notificaciones que el objeto A, en este caso el centro por defecto.

Usamos para ello el método addObserver:selector:name:object: los parámetros son: el objeto a registrar, un selector del método que queremos ejecutar al recibir la notificación, el nombre de la notificación para la que queremos ser avisados y el objeto sobre el que queremos ser avisado, éste último puede ser nil o el objeto en concreto, para poder diferenciar quien manda la notificación en caso de tener más de un objeto enviando la misma notificación.

 // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
 - (void)viewDidLoad {
     [super viewDidLoad];
     numberPadController = [[GenericNumberPad alloc] initWithNibName:@"GenericNumberPad"
     	  						      bundle:[NSBundle mainBundle]
							       frame:CGRectMake(20, 190, 280, 216)];
     [[NSNotificationCenter defaultCenter] addObserver:self 
                                              selector:@selector(numberPadPressed)
						  name:@"number pad pressed" object:nil];
     [[self view] addSubview:[numberPadController view]];
 }

Cómo veis en el código, el nombre de la notificación es el mismo que se usó en el objeto A. Ya sólo queda que realiceis vuestras acciones en el método que indicasteis a la hora de registrar el objeto B.

#pragma mark -
#pragma mark Notification methods
 
- (void)numberPadPressed {
     [importLabel setText:[numberPadController input]];
}

En este ejemplo se ha usado para actualizar una etiqueta con el valor de la variable input, cada vez que ésta era cambiado.

VN:F [1.9.8_1114]
Rating: 5.0/5 (1 vote cast)
VN:F [1.9.8_1114]
Rating: 0 (from 0 votes)
Tagged with:
jul 05

Con este post mostraré como podemos copiar un archivo, que previamente hemos añadido a nuestro proyecto, a la carpeta documentos de nuestra aplicación. La idea es la de poder inicializar de una manera rápida y cómoda la base de datos, algún archivo de configuración que tengamos o cosas por el estilo que tenemos que guardar en la carpeta documentos de la aplicación porque durante ejecución sufrirán cambios realizados por el usuario, evitando así tener que crearlos desde el código de la aplicación.

Primero, lo que tenemos que hacer es añadir ese archivo al proyecto para tener acceso a él desde el código cuando se ejecuta la aplicación. Añadid el archivo a la carpeta “Resources” del proyecto.

Una vez tenemos el archivo en el proyecto, podemos añadir el código para copiar el archivo a la carpeta documentos, añadid el siguiente código en vuestro AppDelegate, podéis crearos una función nueva para inicializar los archivos que necesiteis, o añadirlo directamente al init del AppDelegate.

// Copy init.sqlite from resources to the Documents folder in bundle
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"data.sqlite"];
if(![[NSFileManager defaultManager] fileExistsAtPath:storePath])
{
	NSString *filePath = [[NSBundle mainBundle] pathForResource:@"init" ofType:@"sqlite"];
	NSData *myData = [NSData dataWithContentsOfFile:filePath];
	if (![myData writeToFile:storePath atomically:YES]) {
		NSLog(@"ERROR writting the starting database");
	}
}

En el ejemplo anterior usamos un archivo, init.sqlite, como base de datos inicial para nuestra aplicación y guardamos el mismo archivo bajo el nombre de data.sqlite en el directorio documentos de nuestra aplicación. Daos cuenta también de que usamos un if para comprobar si el archivo existe o no, de forma que sólo se ejecutará la primera vez que ejecutemos la aplicación, ésto es así, para que cuando nuestro usuario vaya añadiendo datos no se los borremos y carguemos siempre la base de datos inicial. Si durante las pruebas queréis volver a obtener el archivo original, eliminad la aplicación del iPhone o del iPhone Simulator.

El método applicationDocumentsDirectory es la siguiente función que nos devuelve la ruta a la carpeta.

/**
 Returns the path to the application's Documents directory.
 */
- (NSString *)applicationDocumentsDirectory {
	return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
VN:F [1.9.8_1114]
Rating: 5.0/5 (1 vote cast)
VN:F [1.9.8_1114]
Rating: +1 (from 1 vote)
Tagged with:
jun 19

En este artículo vamos explicar los métodos de inicialización de objetos en Objective-C. Veremos cómo sobrecargarlos correctamente y como crear nuestros propios de la forma más correcta.

Antes que nada vamos a ver cómo solemos sobrecargar el método init en nuestras clases que heredan del NSObject y entender el porque lo hacemos así.

-(id)init{
 
    if (self = [super init]){
 
      // Aquí incializamos nuestros variables
 
    }
    return self;
}

Con el [super init] inicializamos las variables de la clase padre. Esta inicialización está en un if para no inicializar nuestro código y devolver un objeto corrupto con variables a medio inicializar ya que si el [super init] devuelve un nil ya no entraremos dentro del if. De esta forma o retornamos un objeto completo o nil. Dentro del if al inicializar nuestras variables si se produce algún error tenemos que retornar nil.

Regla: antes de incializar nuestras variables SIEMPRE tenemos que incializar la clase padre. Siempre vamos de arriba hacía abajo en la jerarquía.

Vamos a suponer que tenemos una clase Alumno definido de la siguiente manera:

@interface Alumno : NSObject {
	NSString *nombre;
	NSString *apellido;
}
 
@property (nonatomic, retain)NSString *nombre;
@property (nonatomic, retain)NSString *apellido;
 
@end

Y su implementación es la siguiente:

@implementation Alumno
@synthesize nombre, apellido;
 
@end

Entonces, usaríamos esta clase de la siguiente manera:

Alumno *a = [[Alumno alloc] init];
a.nombre = @"Pepe";
a.apellido = @"Garcia";
NSLog(@"Nombre de alumno: %@ y  apellido: %@", a.nombre, a.apellido);

Fijaros que para inicializar llamamos el init que no está definido en nuestra clase pero viene heredada de la clase padre (NSObject) por lo que al hacer esta llamada las variables nombre y apellido no se inicializan pero si se inicializan las variables de la clase padre.
Es natural querer que nuestra clase Alumno tenga un inicializador tipo “initConNombre:(NSString *)n yApellido:(NSString *)a” que nos permita poner directamente el nombre y el apellido del alumno.

También puede que queramos un inicializador que sólo acepte el nombre del alumno tipo “initConNombre:(NSString *)n”.

Entonces, para organizar bien nuestros inicializadores, no duplicar código y asegurarnos que todas las variables se inicializan correctamente independientemente del incializador usado se usan los inicializadores designados (designed initializators).

Inicializadores designados

Cada clase ha de tener un inicializador designado y será este el único inicializador que llamara a [super init] y se encargará de inicializar todas las variables usados por nuestra clase. El resto de los inicializadores tendrán que llamar a este inicializador.

Volviendo a nuestra clase alumno. ¿Cuál es su inicializador designado?
Lo tenemos que decidir nosotros. Casi siempre es el inicializador con más parámetros. En nuestro caso el inicializador por defecto sera este: -(id)initConNombre:(NSString *)n yApellido:(NSString *)a
A continuación vamos a ver como implementaríamos los inicializadores de nuestra clase.

#import "Alumno.h"
 
#define NOMRE_POR_DEFECTO @""
#define APELLIDO_POR_DEFECTO @""
 
@implementation Alumno
@synthesize nombre, apellido;
 
-(id)initConNombre:(NSString *)n yApellido:(NSString *)a
{
    if (self = [super init])
    {
 
    }
    return self;
}
 
-(id)initConNombre:(NSString *)n{
	return [self initConNombre:n yApellido:APELLIDO_POR_DEFECTO];
}
 
-(id)init{
	return [self initConNombre:NOMRE_POR_DEFECTO];
}
 
@end

Fijaros como desde el init llamamos al siguiente inicializador pasándole el valor por defecto, a su vez el siguiente inicializador llama al inicializador por defecto quien es el único en hacer el [super init] e incializar las variables del Alumno. Esto en la jerga de Objective-C se llama “initialization chain”. Usar cadenas de inicialización reduce la probabilidad de que cometamos errores y facilita el mantenimiento del código.

También es muy importante darse cuenta del porque definimos nuestro propio init sobreescribiendo el init (que es el init designado) del NSObject. Si no lo hubieramos hecho al hacer [[Alumno alloc] init] inicializaríamos las variables del NSObject pero no las de Alumno. Al sobreescribirlo conseguimos inicializar tanto nuestras variables como las de la clase padre. Siempre, al heredar de una clase tenemos que sobreescribir el inicializador designado de esa clase.

Resumen de las tres reglas (recomendaciones)

Regla 1: antes de incializar nuestras variables SIEMPRE tenemos que incializar la clase padre. Siempre vamos de arriba hacía abajo en la jerarquía.

Regla 2: los inicializadores de nuestras clases siempre tienen que acabar llamando al inicializador designado de nuestra clase.

Regla 3: siempre tenemos que sobreescribir el inicializador designado de la clase de la que heredamos.

Podéis descargar el código con el ejemplo que hemos visto en este artículo.
Inicializadores

VN:F [1.9.8_1114]
Rating: 4.8/5 (5 votes cast)
VN:F [1.9.8_1114]
Rating: +3 (from 3 votes)
Tagged with:
jun 13

Cuando trabajamos con tablas (UITableView), podemos crear celdas personalizadas a través de Interface Builder o crearlas directamente desde el código, el problem aparece cuando cambiamos el color de fondo por otro que no sea blanco y sólo creamos la celda cuando vamos a mostrar datos, pero no hacemos nada para las celdas vacías.

Nos encontramos con algo como esto

Esto nos pasa porque sólo creamos la celda con fondo cuando tenemos datos para rellenar, tenemos dos opciones para solucionarlo:

  • Programar nuestro código de forma que cuando rellenamos la tabla se consideren los casos en que no hay datos, es decir cargar una celda pero sin las etiquetas o imágenes.
  • Poner a todas las celdas el mismo color de fondo antes de mirar nada.

Con esta segunda opción, es la más rápida, podemos darle el mismo color a todas las celdas, la única manera de hacerlo sin crearnos una celda personalizada, sin nada más que el fondo para celdas vacías desde Interface Builder, es hacerlo dentro del código:

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"Cell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
 
    // Configure the cell.
    [[cell contentView] setBackgroundColor:[UIColor colorWithRed:0 green:128/255.f blue:0 alpha:1]];    
    return cell;
}

La linea que nos interesa es la que llama al método setBackgroundColor:, con esta linea es la que le damos el color que queremos. Daros cuanta de que usamos [cell contentView], tenemos que cambiar el color de la contentView, esta es la clave de que no se pueda hacer desde Interface Builder directamente cuando añadimos una tabla, desde la tabla no podemos acceder al contenido de la celda sin añadir explicitamente las celdas.

Con esa linea obtendremos algo cómo esto

VN:F [1.9.8_1114]
Rating: 5.0/5 (2 votes cast)
VN:F [1.9.8_1114]
Rating: +1 (from 1 vote)
Tagged with:
jun 09

Como todos sabréis las aplicaciones iPhone viven en su propia parcela y tiene un acceso muy restringido al resto del sistema.

Aún y así Apple nos ofrece un mecanismo para comunicarnos con otros programas. Al decir comunicar, quiero decir que podemos lanzar otras aplicaciones y opcionalmente pasarles parámetros (si lo soportan).

Para conseguirlo se usan URLs especiales. Cada aplicación puede definir su propia URL para que otros le puedan llamar.

Por ejemplo para abrir la aplicación SMS desde nuestro código escribiríamos el siguiente código:

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms:1111111"]];

Como vemos, estamos llamado al método openURL del objeto UIApplication pasándole una URL.
Y la URL en si indica primero el protocolo (sms) y luego como parámetro un número de teléfono.
Al ejecutar esta línea de código se cerrará nuestra app y se abrirá el SMS y con el campo número automáticamente rellanado con el número que le hemos pasado como parámetro (en este caso 1111111).

Los principales programas de iPhone (mail, sms, youtube, maps, itunes) soportan estos URLs. Pueden consultar el formato de los URLs de todas las apps en este link.

Y no sólo las apps oficial de Apple soportan URLs sino nosotros también podemos crear URLs especiales para nuestras apps pero eso lo explciaremos en otro artículo.

En está wiki podéis encontrar una lista bastante grande de aplicaciones (tanto los de Apple como de terceros) que soportan URLs y podréis ver como usarlos.

VN:F [1.9.8_1114]
Rating: 5.0/5 (1 vote cast)
VN:F [1.9.8_1114]
Rating: 0 (from 0 votes)
Tagged with:
preload preload preload