Programar para Iphone desde tres. NSMutableArray. Nivel Luis Aragonés.
29 June 2008
Hoy juega España la final de la Eurocopa y publicar el nivel Luis Aragonés es nuestra forma de conmemorar este evento,
Hemos aprendido ya muchas cosas. En el ejemplo anterior aprendimos a adquirir datos, que el usuario insertaba en un formulario. Hoy vamos a aprender a guardar esos datos de manera permanente, es decir, creando array de datos y guardarlos en ficheros.
Empezamos:
Como este es el Artículo “Programar para Iphone desde Tres” me basaré en los anteriores, “desde Cero”, “desde Uno” y "desde Dos", y doy por sabidas las cosas expuestas en ellos.
Explicación del ejemplo:
Pretendo realizar un programa que sea como una agenda de contactos, que guarde el nombre, la dirección y el teléfono de mis amigos. Evidentemente pretendo poder añadir nuevos contactos y también eliminarlos. Por lo tanto, crearé en mi ventana, tres UITextField que me servirán tando para adquirir datos como para mostrar información. Y además pondré 4 botones, uno de añadir contacto, otro de borrar contacto, y otros dos de siguiente y anterior.
Creación del proyecto:
1.- Abrimos xcode.app y creamos un proyecto de tipo window.
Definición de Objetos y Acciones:
Como ya tenemos una cierta base ire al grano.
2.- En el archivo.h de nuestra app delegate, crearemos todos los Outlet y todas las Actions. Además necesitaré un NSMutableArray, que será la variable que guarde todos mis datos.
Antes de nada hay que hablar un poco del NSMutableArray. Se trata de un Array en el que en cada posición de su tabla interna de datos, puedes guardar cualquier tipo de objeto, es decir, en cada posición podría guardar un entero, o una cadena. En nuestro caso, en cada posición de nuestro NSMutableArray tendremos a otro NSMutableArray. Con esto logramos crear una especie de estructura. Este segundo Array tendrá tres cadenas, una para el nombre, otra para la dirección y otra la el teléfono.
[cpp]#import
@class DATOSViewController;
@interface DATOSAppDelegate : NSObject
{
IBOutlet UIWindow *window;
IBOutlet id BOTON_SIGUIENTE;
IBOutlet id BOTON_ANTERIOR;
IBOutlet id BOTON_ANADIR;
IBOutlet id BOTON_BORRAR;
IBOutlet id FORM_NOMBRE;
IBOutlet id FORM_DIRECCION;
IBOutlet id FORM_TELEFONO;
NSMutableArray *DATOS;
NSString *NOMBRE;
NSString *DIRECCION;
NSString *TELEFONO;
NSUInteger INDICE;
NSUInteger NUMERO_ELEMENTOS;
}
@property (nonatomic, retain) UIWindow *window;
- (IBAction) PULSAR_BOTON_SIGUIENTE: (id) sender;
- (IBAction) PULSAR_BOTON_ANTERIOR: (id) sender;
- (IBAction) PULSAR_BOTON_ANADIR: (id) sender;
- (IBAction) PULSAR_BOTON_BORRAR: (id) sender;
- (IBAction) CARGAR_DATOS: (NSUInteger) indice;
@end
[/cpp]
Creación de interface y asignación de elementos:
3.- Ahora hacemos doble click sobre el archivo.xib para abrir y editar con el interface builder nuestra pantalla. Simplemente pongo la captura, ya que las vinculaciones ya las deberiais saber hacer.
Implementación de funciones:
Como ya lo tenemos todo vinculado, es hora de implementar nuestras funciones.
4.- Cerramos interface builder y nos vamos al Archivo.m. Allí escribiremos las funciones.
Como vamos a trabajar con ficheros, lo primero que tiene que hacer nuestra aplicación es cargar los datos del fichero para luego gestionarlos. El fichero se llamará: "datos.txt".
[cpp]
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
//Cargamos los datos del fichero.
DATOS = [[NSMutableArray alloc] initWithContentsOfFile:@"datos.txt"];
// DATOS = [[NSMutableArray alloc] init]; Asi se inicializaria normal.
[window makeKeyAndVisible];
[application setStatusBarHidden:YES animated:NO];//Oculta la status Bar
NUMERO_ELEMENTOS = [DATOS count]; // inicializamos esta variable al numero de objetos en DATOS
INDICE = 0;
// ejecutamos el metodo CARGAR_DATOS
[self CARGAR_DATOS:INDICE];
}
[/cpp]
Si compilaramos ahora mismo, aparecerían todos los elementos, e incluso podríamos rellenar datos en los UITextField, asi que ahora escribiremos el Método de Añadir (PULSAR_BOTON_ANADIR).
[cpp]
- (IBAction) PULSAR_BOTON_ANADIR: (id) sender
{
NOMBRE =[FORM_NOMBRE text];
DIRECCION =[FORM_DIRECCION text];
TELEFONO =[FORM_TELEFONO text];
NSMutableArray *ESTRUC = [[NSMutableArray alloc] initWithObjects:NOMBRE,DIRECCION,TELEFONO,nil];
[DATOS addObject:ESTRUC];
[DATOS writeToFile:@"datos.txt" atomically:YES];
NUMERO_ELEMENTOS = NUMERO_ELEMENTOS + 1;
[FORM_TELEFONO resignFirstResponder]; //oculta teclado
[FORM_DIRECCION resignFirstResponder];
[FORM_NOMBRE resignFirstResponder];
}
[/cpp]
Tengo creadas tres variable globales, son tres cadenas, en las que introduciré los datos que haya en el momento de pulsar el botón en los UITextField (este paso no es necesario para hace al código más entendible). Seguidamente me creo un NSMutableArray local, que lo inicializo con las tres cadenas de antes. Es importante respetar el mismo orden de para la carga de datos. Primero nombre, después dirección y por último teléfono. Añado este NSMutableArray local, a nuestro NSMutableArray general, y además lo escribo en el fichero, sobreescribiendo. Y Actualizo la variable NUMERO_ELEMENTOS. Cada vez que pulse el botón se añadirá un elemento, aunque no haya cambiado los datos.
El método CARGAR_DATOS nos servirá para cambiar el texto de los UITextField e introducirles los datos que haya en la posición del NSMutableArray que indique INDICE. Hay que tener en cuenta que este método será de la siguiente manera:
[cpp]
- (IBAction) CARGAR_DATOS: (NSUInteger) indice;
{
NSMutableArray *ESTRUC = [[NSMutableArray alloc] init];
ESTRUC = [DATOS objectAtIndex:indice]; //devuelve el objeto de esa posición.
[FORM_NOMBRE setText:[ESTRUC objectAtIndex:0]]; //respetamos el orden
[FORM_DIRECCION setText:[ESTRUC objectAtIndex:1]];
[FORM_TELEFONO setText:[ESTRUC objectAtIndex:2]];
}
[/cpp]
El resto del código no tiene ninguna complicación así que lo pondré tal cual.
[cpp]
- (IBAction) PULSAR_BOTON_SIGUIENTE: (id) sender
{
if (INDICE == (NUMERO_ELEMENTOS - 1))
{
[self CARGAR_DATOS: INDICE];
}
else
{
INDICE = INDICE + 1;
[self CARGAR_DATOS: INDICE];
}
}
- (IBAction) PULSAR_BOTON_ANTERIOR: (id) sender
{
if (INDICE == 0)
{
[self CARGAR_DATOS: INDICE];
}
else
{
INDICE = INDICE - 1;
[self CARGAR_DATOS: INDICE];
}
}
- (IBAction) PULSAR_BOTON_BORRAR: (id) sender
{
[DATOS removeObjectAtIndex:INDICE];
NUMERO_ELEMENTOS = NUMERO_ELEMENTOS - 1;
INDICE = INDICE - 1;
[DATOS writeToFile:@"datos.txt" atomically:YES];
[self CARGAR_DATOS:INDICE];
}[/cpp]
5.- Compilamos y comprobamos el funcionamiento.
Ya se que las últimas tres funciones no las tengo explicadas, pero no deberiais tener problemas en entenderlas por vosotros mismo, ya que solo hay que relacionar conceptos ya adquiridos para saber que es lo que hacen.
Como siempre es conveniente que investigueis por vuestra cuenta y mejoreis el programa a vuestro estilo.
Otros Artículos de la serie:
Programar para Iphone desde cero. UIButton. Nivel Tarzán.
Programa para Iphone desde uno. UIView. Nivel Piolín.
Programar para Iphone desde dos. UITextField. Nivel Barragán.
Programar para Iphone desde cuatro. Controllers. Nivel Cantinflas.

Comentarios recientes
These are actually impressive ideas in concerning blogging. You have touched some fastidious points here. Any way keep up wrinting.
Wow, that's what I was looking for, what a data! existing here at this website, thanks admin of this web site.
When I initially commented I seem to have clicked the -Notify me when new comments are added- checkbox and from now on each time a comment is added I receive four emails with the exact same comment. There has to be an easy method you can remove me from that service? Appreciate it!
Hola a todos! os recordamos que tenéis a vuestra disposición un foro donde podéis plantear las dudas que tengáis: http://www.iphonesoftware.es/foro/ Gracias!
la verdad es que yo tambien tengo problemas con el path, no se como hacer para que en el terminal no se borre, alguna recomendacion?
Hola. Soy nuevo en esto. Estoy intentando probar este ejemplo pero no hay manera de que funcione. Cuando pulso añadir el programa se peta. Alguien lo ha exo funcionar? me podriais pasar el codigo para ver que hago mal.
Puedes indicarme el path para que no se borrre ¿?
muy buenas.. estoy un poco atascado... no me crea el fichero datos.txt ... pero haciendo el debug paso a paso... resulta que en el paso anterior al que se escribe el archivo [DATOS addObject:ESTRUC]; los datos que hay en ESTRUC (que según el debug, tengo 3 objetos (nombre,dirección y teléfono) no se pasan al array DATOS, por lo que imagino que si no hay ningún dato en el array no crea el archivo. ¿alguna idea? no me da ningún error y creo que tengo todo bien relacionado.
En el simulador no debería borrarse. Si no recuerdo mal se guarda en la raiz de tu Disco Duro o de tu usuario. En el terminal, (iphone o ipod touch) es normal que se borre, porque se guardará como temporal. En el foro hay información para conseguir que no se borre. Es cambiando el path de donde se guarda. Saludos.
Una duda donde se supone que crea el archivo datos.txt??? al ejecutarlo con el emulador una vez q se cierra...se vuelve a borrar???esq me da un fallo el programa y necesito saber eso xa encontrarlo
Problema Solucionado! Hay un pequeño error desde mi punto de vista que me gustaria corregir. En el ACTION de BORRAR, si borras cuando el INDICE es 0 peta! - (IBAction) PULSAR_BOTON_BORRAR: (id) sender { [DATOS removeObjectAtIndex:INDICE]; NUMERO_ELEMENTOS = NUMERO_ELEMENTOS - 1; if(INDICE == 0) {//no hacer nada } else { INDICE = INDICE - 1; } [DATOS writeToFile:@"datos.txt" atomically:YES]; [self CARGAR_DATOS:INDICE]; }
Hola, Tengo un problema a la hora de ejecutar el programa. He seguido todos los pasos, he hecho todas las asociaciones etc.... Me crea el archivo datos.txt: pepe gran via 655555555 El problema esta cuando le doy a anterior o a siguiente, "NO HACE NADA" :( y el problema GORDO esta cuando le doy a borrar, "PETA!" Alguien sabe que puede estar pasando....?? Gracias de antemano!
Este programa no me corre, me pone la pantalla en negro, no sale ni nada, alguien puede ayudarme con esto..... gracias
Hola, me sucede algo muy extraño coloque un NSLog para ver el valor de INDICE cada vez que avanzamos o retrocedemos y lo que puedo ver es que el valor en vez de añadir o restar 1 lo hace con 4, es decir empezamos con INDICE = 0, al presionar siguiente se ejecuta esta linea INDICE = INDICE+1 lo cual se sobre entiende que el nuevo valor de INDICE debería ser 1 pero por alguna extraña razón el nuevo valor es 4. Alguien tiene idea de que esta sucediendo ahí? Muchas gracias de antemano por la ayuda.
Se me ocurre crear un método por cada textfield para que cuando suceda el evento de darle al "done" (no recuerdo el nombre) se llame al método y éste tenga la orden resignfirstresponder dentro, pero estoy seguro de que tiene que haber una forma más sencilla..
Otra preguntita, dónde habría que poner lo de "resignFirstResponder" para que al terminar de introducir cada campo, por ejemplo el nombre, si el usuario apreta "done", se escondiera el teclado? Ahora sólo se me esconde al darle a añadir, pero una vez he empezado a escribir en un textfield no desaparece en ningun momento hasta darle al boton de añadir
Arreglado, me faltaba asociar la AppDelegate al File's owner... es una conexion que no entiendo pq hay que hacer, pero bueno, ha funcionado :P
A mi me pasa igual que a pepersview... no crea el archivo ni graba absolutamente nada al darle a añadir... y creo que he seguido bien todos los pasos. ¿Alguien sabe que puede estar fallando?
por que truena la aplicacion cuando borras el primer datos o rwgstro de datos?
a CARGAR_DATOS, no hay que hacerle ninguna asociacion ya que no es un IBAction. Leete los comentarios que hay en el post, quizas sea lo de las comillas.
Lo siento x la pregunta pero es q soy muy novato. Hay q hacerle alguna asociación a CARGAR_DATOS?? No le he hecho ninguna asociación y se me para la aplicación en cuanto pulso anterior o siguiente.
Ok, ya lo he solucionado, muchas gracias por interesarte:) un saludo!!
Tengo tb las asociaciones hechas:)
Si, eso lo tengo en el metodo Añadir, pero no me creo el archivo, ese es mi problema, el código esta como el que hay aqui, por eso no se que puede pasar. Gracias!
[DATOS writeToFile:@"datos.txt" atomically:YES]; con esto es con lo que se crea el archivo. saludos.
hola, tengo una duda, por que a mi no me crea el archivo? He probado con DATOS = [[NSMutableArray alloc] initWithContentsOfFile:@"./datos.txt"]; pero tampoco:)
Acabo de descubrir que, datos.txt, escribe en formato XML !! Esto amplia las posibilidades de cargar y compartir información ! Esto se esta poniendo calientito !! Gracias
Gracias por estos magníficos tutoriales !!! Me ha ido perfecto ! Muchas gracias !
Si, exactamente. Es un fallo. Gracias por corregirlo. Saludos.
perdón por el comentario pero he puesto símbolos no admitidos... esta es la pregunta: "Os habeis dejado la clase a importar o no sale en el código. ¿tendría que ser la clase "UIKit/UIKit.h"????" Gracias
Hola una pregunta, en el codigo de ejemplo: #import @class DATOSViewController; @interface DATOSAppDelegate : NSObject { IBOutlet UIWindow *window; IBOutlet id BOTON_SIGUIENTE; IBOutlet id BOTON_ANTERIOR; IBOutlet id BOTON_ANADIR; IBOutlet id BOTON_BORRAR; IBOutlet id FORM_NOMBRE; IBOutlet id FORM_DIRECCION; IBOutlet id FORM_TELEFONO; NSMutableArray *DATOS; NSString *NOMBRE; NSString *DIRECCION; NSString *TELEFONO; NSUInteger INDICE; NSUInteger NUMERO_ELEMENTOS; } @property (nonatomic, retain) UIWindow *window; - (IBAction) PULSAR_BOTON_SIGUIENTE: (id) sender; - (IBAction) PULSAR_BOTON_ANTERIOR: (id) sender; - (IBAction) PULSAR_BOTON_ANADIR: (id) sender; - (IBAction) PULSAR_BOTON_BORRAR: (id) sender; - (IBAction) CARGAR_DATOS: (NSUInteger) indice; @end Os habeis dejado de poner la clase a importar... ¿¿¿la clase tiene que ser, #import ????
Se crea si no existe. Saludos.
El fichero datos.txt se tiene que crear o ya lo crea automaticamente si no existe!?
Lo primero agradecer tu esfuerzo por ayudarnos en el desarrollo sobre iPhone, pero al menos desde mi experiencia con tu ejemplo hay una serie de mejoras (o fallos): - (void)applicationDidFinishLaunching:(UIApplication *)application { //Cargamos los datos del fichero. DATOS = [[NSMutableArray alloc] initWithContentsOfFile:@"datos.txt"]; if (DATOS == nil) { DATOS = [[NSMutableArray alloc] init]; // Asi se inicializaria normal - No existe el fichero } [window makeKeyAndVisible]; [application setStatusBarHidden:YES animated:NO];//Oculta la status Bar NUMERO_ELEMENTOS = [DATOS count]; // inicializamos esta variable al numero de objetos en DATOS INDICE = 0; // ejecutamos el metodo CARGAR_DATOS [self CARGAR_DATOS:INDICE]; } - (IBAction) CARGAR_DATOS: (NSUInteger) indice; { if (indice < [DATOS count]) { NSMutableArray *ESTRUC = [DATOS objectAtIndex:indice]; //devuelve el objeto de esa posición. [FORM_NOMBRE setText:[ESTRUC objectAtIndex:0]]; //respetamos el orden [FORM_DIRECCION setText:[ESTRUC objectAtIndex:1]]; [FORM_TELEFONO setText:[ESTRUC objectAtIndex:2]]; } }
Deja un comentario