NSString *str = @"Hola Mundo"; NSString *newStr = [str substringWithRange:NSMakeRange(0,4)];newStr sería igual a "Hola".
Hola a todos! Este blog surge a partir de que no encontraba muchos recursos en español para aprender Objective-C. Espero que les sea útil! ;)
viernes, 30 de diciembre de 2011
Obtener una subcadena de un objeto String
miércoles, 28 de diciembre de 2011
Evitar que el teclado quede sobre los textfields
Uno de los problemas más comunes cuando programamos para iphone es que el teclado puede ocultar los textfields. La solución más rápida es correr los textfields de manera que no queden ocultos pero en mi aplicación yo tenía varios textfields y no me quedaba lugar para correrlos.
Encontré este tutorial http://joshhighland.com/blog/2010/04/20/iphone-keyboard-covers-text-field/ que me pareció que es el que tiene el código más simple y me funciono perfecto.
La idea es la siguiente, realizar dos IBAction en este caso slideFrameUp y slideFrameDown que seran llamados por los textfields cuando ocurran los enventos Editing Did Begin y Editing Did End respectivamente (como se muestra en la figura).
El código de los IBAction y del método que llaman los mismos es el siguiente:
Encontré este tutorial http://joshhighland.com/blog/2010/04/20/iphone-keyboard-covers-text-field/ que me pareció que es el que tiene el código más simple y me funciono perfecto.
La idea es la siguiente, realizar dos IBAction en este caso slideFrameUp y slideFrameDown que seran llamados por los textfields cuando ocurran los enventos Editing Did Begin y Editing Did End respectivamente (como se muestra en la figura).
El código de los IBAction y del método que llaman los mismos es el siguiente:
-(IBAction) slideFrameUp; { [self slideFrame:YES]; } -(IBAction) slideFrameDown; { [self slideFrame:NO]; } -(void) slideFrame:(BOOL) up { const int movementDistance = 50; // lo que sea necesario, en mi caso yo use 80 const float movementDuration = 0.3f; // lo que sea necesario int movement = (up ? -movementDistance : movementDistance); [UIView beginAnimations: @"anim" context: nil]; [UIView setAnimationBeginsFromCurrentState: YES]; [UIView setAnimationDuration: movementDuration]; self.view.frame = CGRectOffset(self.view.frame, 0, movement); [UIView commitAnimations]; }
miércoles, 14 de diciembre de 2011
Mostrar una imagen desde una url
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: @"http://myurl/mypic.jpg"]]; cell.image = [UIImage imageWithData: imageData]; [imageData release];
lunes, 5 de diciembre de 2011
¿Cómo convierto un int a un NSString?
Una de las formas es mediante un método de NSString
[NSString stringWithFormat:@”%i” , elentero]
¿Y de NSString a float o a int?
La clase NSString tiene métodos para esto uno es floatValue y el otro intValue.
NSString *aNumberString = @"35"; int i = [aNumberString intValue];
Imágenes con bordes redondeados
Cada objeto basado en una vista tiene asociado una capa y cada capa tiene un corner al que se le puede setear su radio. Por lo tanto lo que podemos hacer es lo siguiente:
No hay que olvidar que debemos importar el siguiente framework
imageView.layer.cornerRadius = 5.0; imageView.layer.masksToBounds = YES; //para agregar un borde: imageView.layer.borderColor = [UIColor lightGrayColor].CGColor; imageView.layer.borderWidth = 1.0;
Etiquetas:
borde,
imagen,
redondeado,
UIImage,
UIImageView
lunes, 7 de noviembre de 2011
iOS 5 - ¿Cómo instalar otros simuladores en XCode 4.2?
Desde el menú de Xcode nos vamos a XCode > Preferences > Downloads y desde ahí podemos seleccionar el simulador iOS 4.3, iOS 4.0 o iOS 3.0.
domingo, 6 de noviembre de 2011
miércoles, 2 de noviembre de 2011
Como crear una pantalla Splash
La imagen splash es aquella que se muestra al iniciar la aplicación. Simplemente hay que crear una imagen de 320x480 px y llamarla default.png. Luego hay que incluirla en el proyecto, en la raiz o en la carpeta Resources (recordemos que las carpetas creadas en el proyecto son virtuales) y XCode la tomará como la imagen splash que va aparecer antes de que comience la aplicación.
miércoles, 12 de octubre de 2011
Tips Xcode 4
Cambiar entre proyectos abiertos
cmd + `
Agregar un Framework
Para intercambiar entre el .h y el .m
opt + cmd + flecha hacia arriba
Obtener número del celular
cmd + `
Agregar un Framework
- En el navegador de proyecto (“project navigator”), selecciona tu proyecto.
- Selecciona el objetivo (“target”)
- Selecciona la pestaña “Build Phases”.
- Abre la sección “Link binaries with libraries”
- Haz clic en el botón “+”
- Selecciona tu framework
- Arrastra la framework que acaba de añadirse a tu proyecto al grupo de “Frameworks” (opcional).
Para intercambiar entre el .h y el .m
opt + cmd + flecha hacia arriba
Obtener número del celular
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/.GlobalPreferences.plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(@"%@", [dict objectForKey:@"SBFormattedPhoneNumber"]);
jueves, 6 de octubre de 2011
Adaptar el teclado para que solo se ingresen decimales
myTextField.keyboardType=UIKey boardTypeDecimalPad;
¿Cómo evitar un valor null que obtenemos de json?
[aTweet objectForKey:@"starsHotel"] != [NSNull null]
domingo, 4 de septiembre de 2011
Parte 2: Interface Builder y Xcode. Traducción de la lección 2
De vuelta otra vez!! Les dejo el link al pdf traducido de la lección 2.
pdf en español
pdf en ingles
pdf en español
pdf en ingles
Etiquetas:
lecciones,
traduccion
jueves, 1 de septiembre de 2011
jueves, 4 de agosto de 2011
Ejemplo de como obtener información de un .plist
En este caso necesitaba obtener información de un .plist para verla en un textField al iniciar la aplicación para eso en el método viewDidLoad de mi viewController agregue este código:
- (void)viewDidLoad { NSString *path = [[NSBundle mainBundle] bundlePath]; NSString *finalPath = [path stringByAppendingPathComponent:@"Info.plist"]; NSDictionary *plistData = [[NSDictionary dictionaryWithContentsOfFile:finalPath] retain]; NSString *asa = [plistData objectForKey:@"AsaPapel"]; //asaE es el textField asaE.text = asa; }
martes, 2 de agosto de 2011
NSLog - Especificadores de formato
%@ Objecto %d, %i signed int %u unsigned int %f float/double %x, %X hexadecimal int %o octal int %zu size_t %p puntero %e float/double (en notación científica) %g float/double ( %f o %e, dependiendo del valor) %s C string (bytes) %S C string (unichar) %.*s Pascal string (requiere dos argumentos, pstr[0] el primero, pstr+1 el segundo) %c character %C unichar %lld long long %llu unsigned long long %Lf long double
Etiquetas:
cast,
Casting,
conversion de datos,
NSLog,
Type Conversion
domingo, 29 de mayo de 2011
Objective-C - Punteros
La memoria en la computadora puede ser imaginada como una serie de casillas de correo, cada una con el menor tamaño que usa una computadora (un byte).
Estas casillas de correo se enumeran secuncialmente, entonces para obtener la dirección siguiente agregamos un 1 a la dirección actual.
Por ejemplo, tomemos la siguiente declaración:
Las direcciones de memoria pueden contener distintos tipos de valores, un int, un float o un puntero que es una dirección de memoria.
Ahora veamos esta declaración:
El * le dice al compilador que este tipo es un puntero a un entero en vez de un entero. La referencia & le dice al compilador que quiere que anIntPointer se inicialice con la dirección de anInteger, que es la variable que declaramos antes.
En otras palabras, anIntPointer tendra la dirección de memoria de anInteger, por lo tanto anIntPointer tendra guardado el valor 32.
Para ir desde el puntero anIntPointer que contiene la dirección de anInteger al valor actual de anInteger utilizamos el operador (*).
por ejemplo, el resultado del siguiente código
anInteger = 42
*anIntPointer = 42
Estas casillas de correo se enumeran secuncialmente, entonces para obtener la dirección siguiente agregamos un 1 a la dirección actual.
Por ejemplo, tomemos la siguiente declaración:
int anInteger = 42;Asumimos que anInteger (con el valor 42) es alojado en la dirección de memoria 32, en otras palabras la dirección de memoria 32 que se llama anInteger contiene el valor 42.
Las direcciones de memoria pueden contener distintos tipos de valores, un int, un float o un puntero que es una dirección de memoria.
Ahora veamos esta declaración:
int *anIntPointer = &anIntegerLa primera parte de la declaración declara una variable llamada anIntPointer.
El * le dice al compilador que este tipo es un puntero a un entero en vez de un entero. La referencia & le dice al compilador que quiere que anIntPointer se inicialice con la dirección de anInteger, que es la variable que declaramos antes.
En otras palabras, anIntPointer tendra la dirección de memoria de anInteger, por lo tanto anIntPointer tendra guardado el valor 32.
Para ir desde el puntero anIntPointer que contiene la dirección de anInteger al valor actual de anInteger utilizamos el operador (*).
por ejemplo, el resultado del siguiente código
int anInteger = 42; int *anIntPointer = &anInteger; NSLog (@”anInteger = %i”, anInteger); NSLog (@”*anIntPointer = %i”, *anIntPointer);es
anInteger = 42
*anIntPointer = 42
jueves, 5 de mayo de 2011
¿Cómo programar C con XCode?
Seleccionamos File > New > New Project, luego elegimos "Command Line Utility" y seleccionamos C.
En el proyecto vamos a ver que tenemos creado un archivo llamado main.c para escribir nuestro código.
Eso es todo!
En el proyecto vamos a ver que tenemos creado un archivo llamado main.c para escribir nuestro código.
Eso es todo!
viernes, 29 de abril de 2011
Objective-C - Clase NSNumber
Los números se pueden trabajar con NSNumber.
NSNumber tiene métodos de clase para crear NSNumbers de distintos tipos, por ejemplo:
Una vez creado el número podemos acceder a el de manera de que nos devuelva diferentes tipos.
También hay métodos para comparar:
– isEqualToNumber:
En que caso uso NSNumber y en cuales NSInteger?
No hay regla, es depende de lo que quieres hacer.
A diferencia de NSNumber, NSInteger no es un objeto, es un macro.
Se debe usar NSNumber si o si (de lo contrario la aplicación nos dara error) cuando se quiere insertar objetos a un array.
NSNumber tiene métodos de clase para crear NSNumbers de distintos tipos, por ejemplo:
+ numberWithBool:
Una vez creado el número podemos acceder a el de manera de que nos devuelva diferentes tipos.
También hay métodos para comparar:
– compare:
– isEqualToNumber:
En que caso uso NSNumber y en cuales NSInteger?
No hay regla, es depende de lo que quieres hacer.
A diferencia de NSNumber, NSInteger no es un objeto, es un macro.
Se debe usar NSNumber si o si (de lo contrario la aplicación nos dara error) cuando se quiere insertar objetos a un array.
sábado, 5 de marzo de 2011
Objective-C - Introspección
Introspección significa determinar en tiempo de ejecución la estructura de un objeto.
En Objective-C podemos realizar la introspección mediante 3 métodos:
isKindOfClass: si un objeto es ese tipo de clase (incluye herencia)
isMemberOfClass: si un objeto es ese tipo de clase (sin herencia)
respondsToSelector: si un objetore responde a un método determinado
En Objective-C podemos realizar la introspección mediante 3 métodos:
isKindOfClass: si un objeto es ese tipo de clase (incluye herencia)
isMemberOfClass: si un objeto es ese tipo de clase (sin herencia)
respondsToSelector: si un objetore responde a un método determinado
martes, 1 de marzo de 2011
viernes, 18 de febrero de 2011
Conceptos Fundamentales - Categorías y Extensiones
Categorías
Una categoría permite añadir nuevos métodos a clases ya existentes sin crear subclases. Lo que no se puede es agregar variables de instancia para esa clase.
Para una categoría tenemos dos archivos que son los mismos que para una clase, la interfaz que consiste en un archivo .h y la implementación que consiste en el archivo .m .
La diferencia que existe con una clase es que, las categorías:
1 - Tienen un nombre de una clase ya existente como por ej NSObject
2 - En la interfaz no indicamos de quien heredamos
3 - En la intefaz como en la implementación el nombre de la categoría va entre paréntesis
No se puede usar @sintetize
El nombre del archivo de una categoría es el de la clase seguido de la categoría.
Las categorías son muy útiles cuando se quiere agregar funcionalidad a una clase, pero no queremos reescribirla o no contamos con el código fuente el código fuente de la clase como cuando esta se encuentra en una biblioteca.
Por ejemplo:
Supongamos que tenemos la clase Perro y agregamos la categoría PerroTrucos
// Perro.h
@interface Perro : NSObject{
}
-(void) ladra;
@end
// Perro.m
#import "Perro.h"
@implementation Perro
-(void) ladra {
//Guau guau
}
@end
// PerroTrucos.h
@interface Perro (Trucos) {
}
-(void) traeLaVarita;
@end
// Perro.m
#import "PerroTrucos.h"
@implementation Perro (Trucos)
-(void) traeLaVarita {
//va por la varita
}
@end
Para crear una categoría, se debe declarar con @interface NombreDeClaseExistente (NombreDeCategoriaNueva)
Una vez declarada la categoría se define con @implementation NombreDeClaseExistente (NombreDeCategoriaNueva)
Extensiones
La extensión es un caso especial de categoría a la cual no se le da un nombre entre parentesis, es anonima.
Sus métodos se implementan en la implementación principal de nuestra clase para que puedan definir metodos privados.
Una categoría permite añadir nuevos métodos a clases ya existentes sin crear subclases. Lo que no se puede es agregar variables de instancia para esa clase.
Para una categoría tenemos dos archivos que son los mismos que para una clase, la interfaz que consiste en un archivo .h y la implementación que consiste en el archivo .m .
La diferencia que existe con una clase es que, las categorías:
1 - Tienen un nombre de una clase ya existente como por ej NSObject
2 - En la interfaz no indicamos de quien heredamos
3 - En la intefaz como en la implementación el nombre de la categoría va entre paréntesis
No se puede usar @sintetize
El nombre del archivo de una categoría es el de la clase seguido de la categoría.
Las categorías son muy útiles cuando se quiere agregar funcionalidad a una clase, pero no queremos reescribirla o no contamos con el código fuente el código fuente de la clase como cuando esta se encuentra en una biblioteca.
Por ejemplo:
Supongamos que tenemos la clase Perro y agregamos la categoría PerroTrucos
// Perro.h
@interface Perro : NSObject{
}
-(void) ladra;
@end
// Perro.m
#import "Perro.h"
@implementation Perro
-(void) ladra {
//Guau guau
}
@end
// PerroTrucos.h
@interface Perro (Trucos) {
}
-(void) traeLaVarita;
@end
// Perro.m
#import "PerroTrucos.h"
@implementation Perro (Trucos)
-(void) traeLaVarita {
//va por la varita
}
@end
Para crear una categoría, se debe declarar con @interface NombreDeClaseExistente (NombreDeCategoriaNueva)
Una vez declarada la categoría se define con @implementation NombreDeClaseExistente (NombreDeCategoriaNueva)
Extensiones
La extensión es un caso especial de categoría a la cual no se le da un nombre entre parentesis, es anonima.
Sus métodos se implementan en la implementación principal de nuestra clase para que puedan definir metodos privados.
jueves, 17 de febrero de 2011
Parte 2 - Tarea 1. Ejemplo aplicación iphone (Calculadora)
Objetivos
1) Bajar e instalar el iOS4 SDK. (esto pueden hacerlo desde http://developer.apple.com/devcenter/ios/index.action )
2) Crear un nuevo proyecto con Xcode.
3) Definir un modelo, vista y controlador y conectarlos.
4) Usar Interface Builder para crear la interfaz gráfica
Parte 1
Crear un nuevo proyecto en Xcode
1) Bajar e instalar el iOS4 SDK. (esto pueden hacerlo desde http://developer.apple.com/devcenter/ios/index.action )
2) Crear un nuevo proyecto con Xcode.
3) Definir un modelo, vista y controlador y conectarlos.
4) Usar Interface Builder para crear la interfaz gráfica
Parte 1
Crear un nuevo proyecto en Xcode
1. Abrimos /Developer/Applications/Xcode
2. En la pantalla que aparece seleccionamos create a new Xcode project
6. Volvemos a Xcode y podemos ver que hay un arbol de carpetas llamados Grupos y Archivos (Groups & Files). En este lugar es donde serán administrados todos los archivos.
En la sección Classes se crean automaticamente los archivos .h y .m para las dos clases CalculatorAppDelegate y CalculatorViewController. Por ahora no hay que preocuparse por CalculatorAppDelegate.
Parte 2
Crear una nueva clase para nuestro modelo
7. Para crear una nueva clase hacemos click en Groups & Files y seleccionamos New File ... desde el menú.
Parte 3
Definir las conexiones desde/hasta el controlador
Ahora que ya existen las clases de nuestro modelo hay que implementar nuestro controlador.
Comencemos definiendolo.
10. Hacer click en CalculatorViewController.h.
Vamos a ver una pantalla como la siguiente:
Notar que Xcode ha puesto el #import de la UIKit que necesitamos y ha hecho que
CalculatorViewController sea una subclase de UIViewController. Los Controladores son siempre directa o indirectamente subclases de UIViewController.
Nuestro archivo de cabecera todavía necesita que definamos lo siguiente:
a. outlets (variables de instancia en nuestro controlador que apuntan a objetos en nuestra vista)
b. actions (métodos en nuestro controlador que van a ser enviados desde la vista)
c. Una variable de instancia en nuestro controlador que apunta a nuestro modelo
11. Vamos a agregar el outlet que permite que nuestra CalculatorViewController hable con UILabel que representa el display en la vista. LLamaremos display a ese outlet.
Notar que la palabra clave IBOutlet no hace nada excepto identificar los oulets.
12. Ahora agregaremos una variable de instancia llamada brain que apunta desde nuestro Controlador hasta nuestro modelo CalculatorBrain. Necesitamos agregar un #import a CalculatorViewController.h para encontrar la declaración de CalculatorBrain.
Parte 4
Crear la Vista con el Interface Builder
No necesitamos escribir código sino que solo utilizaremos la herramienta Interface Builder. Cuando creamos un proyecto View-base Xcode automaticamente se crea un template . Este template se llama CalculatorViewController.xib (también denominado archivo nib, algunos lo llaman archivo zib ).
15. Abrir CalculatorViewController.xib.
16. Interface Builder tiene tres ventanas principales que contienen objetos o grupos de objetos. Es recomendable seleccionar Hide Others para ver que sucede en el Interface Builder.
La pantalla principal en el Interface Builder muestra todos los objetos de tu archivo .xib:
38. Y finalmente un método que nos permita realizar una operación.
39. Copiar la declaración de los dos métodos en CalculatorBrain.h, luego cambiar a CalculatorBrain.m y pegarlo entre @implementation y @end.
40. La implementación de setOperand: es facil. Lo que hacemos es darle el valor que viene como argumento a la variable de instancia.
41. La implementación de performOperation: también es simlple si el operando lo es, como por ej sqrt.
Veamos ahora un truco de debugging. Hay dos técnicas principales de debuging, una es utilizar el debugger que viene con el programa y la otra es usar printf, Objective-C provee la funcción NSLog() para esto. Vamos a utilizar NSLog() en operationPressed: y después vamos a ejecutar nuestra calculadora y ver en la consola el resultado.
El primer argumento es un NSString ( no una constante char *, por eso no olvidar @), y el resto de los argumentos son los valores para cualquier campo % en el primer argumento. Un nuevo tipo del campo % ha sido agregado, %@, que significa que el argumento correspondiente es un objeto.
47. Veamos este ejemplo con el método operationPressed:
Si hacemos click en un botón que realiza una operación nos aparecerá “The answer
to life, the universe and everything is 42.” ¿pero donde lo vemos?
48. Luego vamos al menú Run en Xcode y elegimos Console. Nos aparecerá una ventana. Ahí será donde veremos la salida. También se puede hacer click en Build and Run (o Build and Debug) en esa ventana para ejecutar el programa desde ahí. Hacemos click en una operación y deberíamos ver algo así:
50. Lo que necesitamos ahora es setear la variable Brain, para eso vamos a crear un método que cree y que retorne nuestra variable. Vamos a ponerlo justo antes de @implementation.
Basicamente queremos crear una variable brain, por eso hacemos la creación de la misma si esta no existe. Creamos la variable brain y la inicializamos.
51. Ahora que tenemos en el método CalculatorViewController.m que retorna a CalculatorBrain (nuestro Modelo) vamos a usarlo.
52. Tenemos el resultado de nuestra operación, solo necesitamos mostrarla en el display, para esto enviamos el mensaje setText: a nuestro display outlet (recuerden que esta ligado al UILabel en nuestra Vista View). El argumento que vamos a pasar es un NSString creado utilizando stringWithFormat:. Es como printf() o NSLog() pero para objetos NSString. Notar que estamos enviando un mensaje directamente a una clasee NSString (ni es una instancia de la clase sino la clase en si misma). Así es como creamos los objetos.
53. Volvemos a CalculatorViewController.h y agregamos la variable de instancia userIsInTheMiddleOfTypingANumber. Este tipo de dato es BOOL y es una versión del tipo de dato booleano en Objective-C’s . Puede tomar dos valores, YES or NO.
54. Ahora volvamos a CalculatorViewController.m y agregamos código a operationPressed: que lo que va a hacer es chequear si estamos tipeando un número y si es así actualizará el operando de CalculatorBrain para que sea el que ingreso el usuario (luego no sucedera más esto de estar en el medio del tipeo de un número).
Pero cuando se setea userIsInTheMiddleOfTypingANumber ? Bueno, se setea cuando el usuario empieza a ingresar dígitos. De todas maneras necesitamos implementar DigitPressed.
Pensemos en la lógica de este método. Hay dos situaciones diferentes cuando hacemos click sobre un dígito. El usuario puede estar ingresando un dígito, en este caso agregamos el dígito a lo que ya venia digitando o no y en este caso es cuando queremos mostrar en el display ese digito y hacer notar que estamos ingresando un número.
55. Agreguemos nuestra primera linea de código para digitPressed:. Nos va a retornar el dígito que fue presionado desde el titleLabel del UIButton que envio digitPressed:mensaje (el que envio).
56. Ahora que tenemos el dígito, lo agregamos a lo que ha sido tipeado (usando otro método NSString llamado stringByAppendingString:) o lo seteamos para que sea el nuevo número que estamos tipeando y hacer notar que comenzamos a tipear.
Quizas se pregunten si userIsInTheMiddleOfTypingANumber comienza con un NO.
Sí, lo hace porque los objetos que heredan de NSObject obtienen todas las instancias seteadas a cero. El cero para un valor del tipo BOOL es igual a NO. Hay que tener cuidado si el mensaje retorna un C struct (en este caso el resultado es indefinido).
Eso es todo, ahora deberíamos ejecutar nuestra aplicación y ver si no tenemos ningún error de sintaxis.
2. En la pantalla que aparece seleccionamos create a new Xcode project
También se puede crear un nuevo proyecto seleccionando New Proyect desde el menú.
3. En el dialogo que aparece hacemos click sobre View based Application
4. En la pantalla que se presenta hacemos lo siguiente:
- Navegamos hasta el lugar donde queremos mantener los proyectos para este curso
- En el campo Save as le ponemos el nombre Calculator
5. hacemos click en Save.
Ahora podemos ejecutar la aplicación aunque aparecerá la pantalla del simulador del iphone en blanco. Si todo esto funciona bien entonces el SDK se ha instalado correctamente :).
6. Volvemos a Xcode y podemos ver que hay un arbol de carpetas llamados Grupos y Archivos (Groups & Files). En este lugar es donde serán administrados todos los archivos.
En la sección Classes se crean automaticamente los archivos .h y .m para las dos clases CalculatorAppDelegate y CalculatorViewController. Por ahora no hay que preocuparse por CalculatorAppDelegate.
Parte 2
Crear una nueva clase para nuestro modelo
7. Para crear una nueva clase hacemos click en Groups & Files y seleccionamos New File ... desde el menú.
8. Hacemos click sobre Objective class (Subclase de NSObject) y después en el botón siguiente. Todos los objectos en Objective-C son subclases de NSObject.
9. Xcode preguntará por el nombre de la clase. Escribimos CalculatorBrain.m y dejamos chequeado la opción “CalculatorBrain.h” (Also create “CalculatorBrain.h” ) porque queremos ambos archivos, el de cabecera (.h) y el de implementación (.m) para nuestra clase CalculatorBrain class.
Nuestro Modelo ya ha sido creado. Vamos a dejar de lado el modelo y volvemos al controlador para escribir algunas declaraciones para las conexiones que necesitamos hacer entre la vista y el controlador.
Definir las conexiones desde/hasta el controlador
Ahora que ya existen las clases de nuestro modelo hay que implementar nuestro controlador.
Comencemos definiendolo.
10. Hacer click en CalculatorViewController.h.
Vamos a ver una pantalla como la siguiente:
Notar que Xcode ha puesto el #import de la UIKit que necesitamos y ha hecho que
CalculatorViewController sea una subclase de UIViewController. Los Controladores son siempre directa o indirectamente subclases de UIViewController.
Nuestro archivo de cabecera todavía necesita que definamos lo siguiente:
a. outlets (variables de instancia en nuestro controlador que apuntan a objetos en nuestra vista)
b. actions (métodos en nuestro controlador que van a ser enviados desde la vista)
c. Una variable de instancia en nuestro controlador que apunta a nuestro modelo
11. Vamos a agregar el outlet que permite que nuestra CalculatorViewController hable con UILabel que representa el display en la vista. LLamaremos display a ese outlet.
@interface CalculatorViewController : UIViewController { IBOutlet UILabel *display; } @end
Notar que la palabra clave IBOutlet no hace nada excepto identificar los oulets.
12. Ahora agregaremos una variable de instancia llamada brain que apunta desde nuestro Controlador hasta nuestro modelo CalculatorBrain. Necesitamos agregar un #import a CalculatorViewController.h para encontrar la declaración de CalculatorBrain.
#import <UIKit/UIKit.h> #import "CalculatorBrain.h" @interface CalculatorViewController : UIViewController { IBOutlet UILabel *display; CalculatorBrain *brain; } @end13. Y finalmente vamos a agregar dos acciones que la vista va a enviar cuando se presionen los botones.
@interface CalculatorViewController : UIViewController { IBOutlet UILabel *display; CalculatorBrain *brain; } - (IBAction)digitPressed:(UIButton *)sender; - (IBAction)operationPressed:(UIButton *)sender; @end
El único argumento de cada método es un objeto UIButton (el objeto le envia el mensaje a nuestro controlador cuando hace click sobre el mismo).
14. Compilamos y ejecutamos nuevamente la aplicación para saber si hay algún error. Seguramente hay advertencias porque los métodos no están implementados pero no hay problema con esto ya que son solo advertencias. Si aparece algún error se vera el circulo rojo como se ve en la imagen siguiente:
haciendo click sobre el error se muestra más información sobre el mismo.Ahora utilizaremos la herramienta gráfica para agregar un display algunos botones.
Parte 4
Crear la Vista con el Interface Builder
No necesitamos escribir código sino que solo utilizaremos la herramienta Interface Builder. Cuando creamos un proyecto View-base Xcode automaticamente se crea un template . Este template se llama CalculatorViewController.xib (también denominado archivo nib, algunos lo llaman archivo zib ).
15. Abrir CalculatorViewController.xib.
16. Interface Builder tiene tres ventanas principales que contienen objetos o grupos de objetos. Es recomendable seleccionar Hide Others para ver que sucede en el Interface Builder.
La pantalla principal en el Interface Builder muestra todos los objetos de tu archivo .xib:
El controlador CalculatorViewController es el File’s Owner . Por ahora ignoremos el objeto First Responder. El objeto View es la vista de más alto nivel, es la supervista de todas las vistas que tendremos en la interface. Todos los objetos UIView tienen una jerarquía en la cual cada uno tiene una superview y subviews. Otra de las ventanas es la librería que contiene todos los elementos necesarios para construir la vista. Por ahora vamos a usar dos UIButton and UILabel.
Otra de las ventanas es el inspector. El contenido de esta ventana cambia dependiendo que objeto este seleccionado.
17. Si hacemos click en File’s Owner en la pantalla principal, se debería ver esta pantalla:
La clase de tu File’s Owner debe ser CalculatorViewController. Lo próximo a realizar es poner algo en la pantalla de esta manera:
18. Comencemos con el dígito 7, localizamos en nuestra librería un botón Round Rect y simplemente lo arrastramos a la vista.
19. Modificamos el botón para que tenga 64 pixels de ancho.
20. La parte más importante es unir el botón con el CalculatorViewController (el File’s Owner en la pantalla principal) que es quien va a enviar el mensaje digitPressed: cuando hagan click sobre el botón. Para esto hay que hacer click en Ctrl y arrastrar una linea desde el botón hasta el Files Owner.
A medida que te acercas al File’s Owner, debe rodearlo una caja azul, cuando soltas el mouse debe aparecer una ventanita, selecciono en ella el metodo digitPressed:
22. Y ahora que ya tenemos hecha la conexión copiamos y pegamos los botones, todos ellos enviaran digitPressed.
23. Haciendo doble click sobre cada botón se le puede cambiar el nombre.
Ahora vamos a hacer las operaciones de los botones:
24. Arrastramos a la pantalla un botón.
25. Mantenga presionado Ctrl y arrastro una lines desde este nuevo boton hasta File’s Owner. Nuevamente va a aparecer la ventanita negra pero en este caso la operación va a ser operationPressed:.
26. Copiamos esto 5 veces (necesitaremos * / + - = y sqrt)
La interfaz debería verse de esta manera:
27. Arrastramos una etiqueta (UILabel) desde la librería, hacemos doble click y escribimos 0.
28. Seleccionamos la etiqueta.
29. Podes modificar las propiedades de los objetos como quieras como se ve en la siguiente imagen:
El CalculatorViewController necesita enviar mensajes al outlet para actualizarlo.
31. Arrastramos desde el File’s Owner to the UILabel.
32. Cuando soltamos el mouse debe aparecer la siguiente ventana. Selecciona display.
33. Guardamos el archivo de la Interface Builder y luego vuelvemos al Xcode.
34. Ejecutamos la aplicación.
Parte 5
Implementar el Modelo
El próximo paso es implementar el modelo CalculatorBrain.
35. Hacemos click en CalculatorBrain.h en la sección Groups & Files.
36. Primero nuestro modelo necesita un operando. Será un puntero flotante, entonces hagamos una variable de instancia que sea double.
double operand;
37. Ahora agreguemos un método que nos permita setear un operando.
- (void)setOperand:(double)aDouble;
38. Y finalmente un método que nos permita realizar una operación.
@interface CalculatorBrain : NSObject { double operand; } - (void)setOperand:(double)aDouble; - (double)performOperation:(NSString *)operation; @end
39. Copiar la declaración de los dos métodos en CalculatorBrain.h, luego cambiar a CalculatorBrain.m y pegarlo entre @implementation y @end.
// // CalculatorBrain.m // Calculator // // Copyright Stanford CS193p. All rights reserved. #import "CalculatorBrain.h" @implementation CalculatorBrain - (void)setOperand:(double)aDouble; - (double)performOperation:(NSString *)operation; @end
@implementation CalculatorBrain - (void)setOperand:(double)aDouble {} - (double)performOperation:(NSString *)operation {} @end
40. La implementación de setOperand: es facil. Lo que hacemos es darle el valor que viene como argumento a la variable de instancia.
- (void)setOperand:(double)aDouble { operand = aDouble; }
41. La implementación de performOperation: también es simlple si el operando lo es, como por ej sqrt.
- (double)performOperation:(NSString *)operation { if ([operation isEqual:@"sqrt"]) { operand = sqrt(operand); } return operand; }
El envío de mensajes a los objetos se hace entre corchetes. En este caso el mensaje es isEqual:.
Pensemos en operaciones con 2 operandos. Esto es un poco más dificil.
Imaginemos un usuario que interactua con la calculadora. El usuario ingresa un número,
luego una operacion, después otro número y despues presiona otra operación (o el igual) que es cuando espera que aparezca el resultado.
Si hace 12 + 4 sqrt = el resultado que se espera sera 14 y no 4. Las operaciones de 2 operandos deben ser retrasadas hasta la próxima operación o hasta que se presione el igual.
42. Volvemos a CalculatorBrain.h y agregamos 2 variables de instancias que necesitaremos para las operaciones de 2 operandos: una variable para la operación que esta esperando ser ejecutada hasta que se presione el otro operando y la otra para la siguiente operación.
43. Volvemos a CalculatorBrain.m. Esta es una implementación que soporta 2 operandos performOperation:
Basicamente si se le pide a CalculatorBrain que realice una operación que no es simple (código invocado por el else) entonces CalculatorBrain llama al método performWaitingOperation (que todavía no hemos escrito) para realizar waitingOperation
Todavía necesitamos implementar performWaitingOperation.
Cuando el mensaje es enviado a self significa que el mensaje se envia a si mismo. Otros lenguajes lo llaman this. performWaitingOperation es un método privado por lo tanto no lo pondremos en CalculatorBrain.h si no que ira en CalculatorBrain.m.
44. Esta es la implementación de performWaitingOperation. Es importante que este código lo pongas en tu archivo CalculatorBrain.m en alguna parte antes de la implementación de performOperation:.
Esto es porque performWaitingOperation es un método privado. No se encuentra en la API pública. Debe ser declarado o definido antes de ser usado en un archivo. El mejor lugar es quizás entre la implementación de setOperand: y la de performOperation:.
Usamos la sentencia if {} else para que waitingOperation pueda utilizarce con cualquier operación despues realizamos la operación usando el operando actual y el que estaba esperando (waitingOperand).
Nuestro modelo esta implementado. Lo único que nos falta es hacer el controlador.
Parte 6:
Implementar el controlador
Lo que nos falta codificar es lo que sucede cuando se presiona un dígito (digitPressed:) o una operación (operationPressed:). Este código ira en nuestro CalculatorViewController. Ya hemos declarado estos métodos en la cabecera archivo (.h) , pero ahora tendremos que hacer la implementación en el archivo .m.
45. Abrimos CalculatorViewController.m y seleccionamos y borramos todo el código de ayuda (“helpful” code) que se encuentra entre @implementation y @end.
46. Ahora volvemos a to CalculatorViewController.h y copiamos las dos declaraciones
de los métodos en CalculatorViewController.m entre @implementation y @end. Borramos los punto y coma y lo reemplazamos por { } (llaves vacías). Debería verse así:
Imaginemos un usuario que interactua con la calculadora. El usuario ingresa un número,
luego una operacion, después otro número y despues presiona otra operación (o el igual) que es cuando espera que aparezca el resultado.
Si hace 12 + 4 sqrt = el resultado que se espera sera 14 y no 4. Las operaciones de 2 operandos deben ser retrasadas hasta la próxima operación o hasta que se presione el igual.
42. Volvemos a CalculatorBrain.h y agregamos 2 variables de instancias que necesitaremos para las operaciones de 2 operandos: una variable para la operación que esta esperando ser ejecutada hasta que se presione el otro operando y la otra para la siguiente operación.
@interface CalculatorBrain : NSObject { double operand; NSString *waitingOperation; double waitingOperand; } - (void)setOperand:(double)aDouble; - (double)performOperation:(NSString *)operation;
43. Volvemos a CalculatorBrain.m. Esta es una implementación que soporta 2 operandos performOperation:
- (double)performOperation:(NSString *)operation { if ([operation isEqual:@"sqrt"]) { operand = sqrt(operand); } else { [self performWaitingOperation]; waitingOperation = operation; waitingOperand = operand; } return operand; }
Basicamente si se le pide a CalculatorBrain que realice una operación que no es simple (código invocado por el else) entonces CalculatorBrain llama al método performWaitingOperation (que todavía no hemos escrito) para realizar waitingOperation
- (double)performOperation:(NSString *)operation { if ([operation isEqual:@"sqrt"]) { operand = sqrt(operand); } else if ([@"+/-" isEqual:operation]) { operand = - operand; } else { [self performWaitingOperation]; waitingOperation = operation; waitingOperand = operand; } return operand; }
Todavía necesitamos implementar performWaitingOperation.
Cuando el mensaje es enviado a self significa que el mensaje se envia a si mismo. Otros lenguajes lo llaman this. performWaitingOperation es un método privado por lo tanto no lo pondremos en CalculatorBrain.h si no que ira en CalculatorBrain.m.
44. Esta es la implementación de performWaitingOperation. Es importante que este código lo pongas en tu archivo CalculatorBrain.m en alguna parte antes de la implementación de performOperation:.
Esto es porque performWaitingOperation es un método privado. No se encuentra en la API pública. Debe ser declarado o definido antes de ser usado en un archivo. El mejor lugar es quizás entre la implementación de setOperand: y la de performOperation:.
- (void)performWaitingOperation { if ([@"+" isEqual:waitingOperation]) { operand = waitingOperand + operand; } else if ([@"*" isEqual:waitingOperation]) { operand = waitingOperand * operand; } else if ([@"-" isEqual:waitingOperation]) { operand = waitingOperand - operand; } else if ([@"/" isEqual:waitingOperation]) { if (operand) { operand = waitingOperand / operand; } } }
Usamos la sentencia if {} else para que waitingOperation pueda utilizarce con cualquier operación despues realizamos la operación usando el operando actual y el que estaba esperando (waitingOperand).
Nuestro modelo esta implementado. Lo único que nos falta es hacer el controlador.
Parte 6:
Implementar el controlador
Lo que nos falta codificar es lo que sucede cuando se presiona un dígito (digitPressed:) o una operación (operationPressed:). Este código ira en nuestro CalculatorViewController. Ya hemos declarado estos métodos en la cabecera archivo (.h) , pero ahora tendremos que hacer la implementación en el archivo .m.
45. Abrimos CalculatorViewController.m y seleccionamos y borramos todo el código de ayuda (“helpful” code) que se encuentra entre @implementation y @end.
46. Ahora volvemos a to CalculatorViewController.h y copiamos las dos declaraciones
de los métodos en CalculatorViewController.m entre @implementation y @end. Borramos los punto y coma y lo reemplazamos por { } (llaves vacías). Debería verse así:
//CalculatorViewController.m //Calculator //Copyright Stanford CS193p. All rights reserved. #import "CalculatorViewController.h" @implementation CalculatorViewController - (IBAction)digitPressed:(UIButton *)sender { } - (IBAction)operationPressed:(UIButton *)sender { } @end
El primer argumento es un NSString ( no una constante char *, por eso no olvidar @), y el resto de los argumentos son los valores para cualquier campo % en el primer argumento. Un nuevo tipo del campo % ha sido agregado, %@, que significa que el argumento correspondiente es un objeto.
47. Veamos este ejemplo con el método operationPressed:
- (IBAction)operationPressed:(UIButton *)sender { NSLog(@"The answer to %@, the universe and everything is %d.", @"life", 42); }
to life, the universe and everything is 42.” ¿pero donde lo vemos?
48. Luego vamos al menú Run en Xcode y elegimos Console. Nos aparecerá una ventana. Ahí será donde veremos la salida. También se puede hacer click en Build and Run (o Build and Debug) en esa ventana para ejecutar el programa desde ahí. Hacemos click en una operación y deberíamos ver algo así:
49. Reemplacemos la implementación de NSLog(). Notar que el argumento para operationPressed: es el botón que nos esta enviando el mensaje. Le preguntaremos a quien nos envia el nombre del botón (titleLabel, los objetos UIButton usan objetos UILabel ) ,luego preguntamos el UILabel retornado para saber el texto. El resultado sera un NSString con un + ó * ó / ó - ó = ó sqrt.
- (IBAction)operationPressed:(UIButton *)sender { NSString *operation = [[sender titleLabel] text]; }
50. Lo que necesitamos ahora es setear la variable Brain, para eso vamos a crear un método que cree y que retorne nuestra variable. Vamos a ponerlo justo antes de @implementation.
- (CalculatorBrain *)brain { if (!brain) brain = [[CalculatorBrain alloc] init]; return brain; }
Basicamente queremos crear una variable brain, por eso hacemos la creación de la misma si esta no existe. Creamos la variable brain y la inicializamos.
51. Ahora que tenemos en el método CalculatorViewController.m que retorna a CalculatorBrain (nuestro Modelo) vamos a usarlo.
- (IBAction)operationPressed:(UIButton *)sender { NSString *operation = [[sender titleLabel] text]; double result = [[self brain] performOperation:operation]; }
52. Tenemos el resultado de nuestra operación, solo necesitamos mostrarla en el display, para esto enviamos el mensaje setText: a nuestro display outlet (recuerden que esta ligado al UILabel en nuestra Vista View). El argumento que vamos a pasar es un NSString creado utilizando stringWithFormat:. Es como printf() o NSLog() pero para objetos NSString. Notar que estamos enviando un mensaje directamente a una clasee NSString (ni es una instancia de la clase sino la clase en si misma). Así es como creamos los objetos.
- (IBAction)operationPressed:(UIButton *)sender { NSString *operation = [[sender titleLabel] text]; double result = [[self brain] performOperation:operation]; }
@interface CalculatorViewController : UIViewController { IBOutlet UILabel *display; CalculatorBrain *brain; BOOL userIsInTheMiddleOfTypingANumber; }
54. Ahora volvamos a CalculatorViewController.m y agregamos código a operationPressed: que lo que va a hacer es chequear si estamos tipeando un número y si es así actualizará el operando de CalculatorBrain para que sea el que ingreso el usuario (luego no sucedera más esto de estar en el medio del tipeo de un número).
- (IBAction)operationPressed:(UIButton *)sender { if (userIsInTheMiddleOfTypingANumber) { [[self brain] setOperand:[[display text] doubleValue]]; userIsInTheMiddleOfTypingANumber = NO; } NSString *operation = [[sender titleLabel] text]; double result = [[self brain] performOperation:operation]; [display setText:[NSString stringWithFormat:@"%g", result]]; }
Pero cuando se setea userIsInTheMiddleOfTypingANumber ? Bueno, se setea cuando el usuario empieza a ingresar dígitos. De todas maneras necesitamos implementar DigitPressed.
Pensemos en la lógica de este método. Hay dos situaciones diferentes cuando hacemos click sobre un dígito. El usuario puede estar ingresando un dígito, en este caso agregamos el dígito a lo que ya venia digitando o no y en este caso es cuando queremos mostrar en el display ese digito y hacer notar que estamos ingresando un número.
55. Agreguemos nuestra primera linea de código para digitPressed:. Nos va a retornar el dígito que fue presionado desde el titleLabel del UIButton que envio digitPressed:mensaje (el que envio).
- (IBAction)digitPressed:(UIButton *)sender { NSString *digit = [[sender titleLabel] text]; }
56. Ahora que tenemos el dígito, lo agregamos a lo que ha sido tipeado (usando otro método NSString llamado stringByAppendingString:) o lo seteamos para que sea el nuevo número que estamos tipeando y hacer notar que comenzamos a tipear.
- (IBAction)digitPressed:(UIButton *)sender { NSString *digit = [[sender titleLabel] text]; if (userIsInTheMiddleOfTypingANumber) { [display setText:[[display text] stringByAppendingString:digit]]; } else { [display setText:digit]; userIsInTheMiddleOfTypingANumber = YES; } }
Quizas se pregunten si userIsInTheMiddleOfTypingANumber comienza con un NO.
Sí, lo hace porque los objetos que heredan de NSObject obtienen todas las instancias seteadas a cero. El cero para un valor del tipo BOOL es igual a NO. Hay que tener cuidado si el mensaje retorna un C struct (en este caso el resultado es indefinido).
Eso es todo, ahora deberíamos ejecutar nuestra aplicación y ver si no tenemos ningún error de sintaxis.
Fuente: http://www.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2010-fall
Pdf en ingles: Assignment 1
Pdf en ingles: Assignment 1
miércoles, 26 de enero de 2011
Parte 1 - MVC para comenzar a programar Cocoa/Objective-C
MVC - Modelo Vista Controlador
La idea con el patrón MVC es dividir la aplicación en estas tres capas.
La capa de Modelo: en el modelo debemos definir de que se trata la aplicación y no como se muestra la aplicación. Si tomamos como ejemplo un juego de naves espaciales, el modelo se refiere a que armas hay, que daño hacen esas armas o en que naves están esas armas.
La capa controlador: Se encarga de como el modelo es presentado al usuario. Es decir que para un iphone el controlador sera diferente que para un ipad porque cada una de las pantallas es diferente.
La capa vista: es quien se encarga de servir al controlador. Es lo que utiliza el controlador para hacer su tarea. Es responsable de como se muestra la información en la pantalla.
¿Puede la vista enviar mensajes al controlador? no exactamente. Los objetos de la vista son genéricos como botones o labels y no queremos que estos importen el .h de nuestro controlador, para esto existen tres mecanismos que hacen posible esta comunicación:
1. Target Action: el controlador maneja un target y una acción en la vista, cuando por ejemplo se toca el botón se manda la acción al target.
2. Delegación: a veces la vista necesita sincronizarse con el controlador o saber que se supone que debería hacer en un caso determinado para esto hay instancias en la vista llamadas delegates que preguntan al controlador que deberían realizar porque necesitan más información para realizar la acción.
3. Data Source: protocolo que permite que la vista disponga de los datos.
La vista no es dueña de los datos que muestra, por eso, si necesita de ellos debe obtenerlos a través del controlador. La función de los controladores es interpretar la información que viene del modelo para poder pasársela a la vista.
El controlador siempre puede comunicarse directamente con el modelo y con la vista, pero la vista con el modelo no pueden hacerlo directamente.
Pdf en ingles: Lecture 1
Suscribirse a:
Entradas (Atom)