Gran curva de aprendizaje
Hay que aprender a utilizar Cocoa frameworks (UIKit, etc)
También iPhone frameworks
Un nuevo IDE: XCode y nuevas herramientas Interface Builder, StoryBoard, Instruments.
Lenguajes de programación: Objective C / Swift
Diseñar iconos y gráficos específicos para la aplicación.
Objective-C
Cocoa Touch es la base inicial de cualquier proyecto, contiene el framework UIKit
Obj-C librería gráfica
Audio / Vídeo / Animaciones
Librerías para servicios básicos: red, SQL, etc
Librerías para gestión de memoria, threads
Está diseñado para no bajar de nivel en la pila si no es realmente necesario implementar un comportamiento realmente novedoso.
Acceder a la cámara
Obtener la posición
Eventos de acelerómetro y movimiento
Acceder al sonido
Guardar datos en el móvil: SQLite DB
Iniciar una llamada
Acceder a los contactos
Utilizar un WebView (HTML/JS)
Background tasks (>iOS7)
Notificaciones
Para publicar una App
Pública: 99$ - 100 dispositivos TEST y ad-hoc
Privada: 299$ - Entidad de más de 500 empleados
Universitaria: Gratis – 200 dispositivos TEST
Se utiliza iTunes Connect:
Simulador: No es necesaria licencia.
Sistema de instalación Ad-Hoc (Test):
Text
Stepstone->NextStep->Apple
int, short, long
float, double
char
BOOL = boolean (YES|NO)
Como en C
&i -> dirección en memoria de la variable i
Puntero:
int *addressofi = &i;
global variables definidas al principio del fichero
static variables con el keyword "static"
if / else if / else
for
while
break
continue
do-while
for in loop (Obj-C 2.0)
for(int i=0; i< 22; i++) {
printf(“Checking i=“@d\n”, i);
if(i+90 == i*i)
{
break;
}
}
for(Item_Type *item in Collection_of_Items) {
//do whatever with the item
Nslog(@” Looking now at %@”, item);
// %@ converts anything to string
}
return_type functionName(type v1, type v2, ...)
{
// code of function
}
//Example:
void showMeInfo(int age)
{
printf("you are %d years old", age); // or NSLog()
}
/////////////////////// Function - Pass by reference
return_type functionName(type v1, type *v2, ….)
{
//code of function
}
//Example – call above
int v1 = 2;
int v2 = 3;
functionName(v1, &v2);
#import <whatever/what.h>
int main(int argc, const char *argv[])
{
@autoreleasepool {
//your code here*******
return 0;
}
}
Automatic Reference Counting %= automatic garbage collection de java
Se acabaron los memory leaks con @autoreleasepool
printf()
printf(“Hi Lynne”); //this is actual C code
NSLog()
NSLog(@”Hi Lynne”); //this is strictly Objective-C
r
Java:
public void function(int x, int y, char z) {};
Object.function(x, y, z);
Objective C:
-(void) method:(int)x, (int)y, (char)z;
[Object function:x, y, z];
// aplicar function al objeto Object con los parámetros x,y,z
- (int) multiplyThis:(int) x ByThis:(int) y AndThis:(int) z;
Method name is multiplyThis:ByThis:AndThis
Los métodos se declaran con + o -
+ -> "class method" , como métodos estáticos de java
- -> "instance methods", métodos públicos en una instancia de clase/objeto
Para declarar setter/getter de variables
// Person.h
@interface Person : NSObject{
//don’t need to declare the variables here
// ---they are done with @property}
@property float heightInMeters; //will create setter and getter method for this var
@property float weightInKilos; //will create setter and getter method for this var
- (float) bodyMassIndex; //instance method
@end
// Person.m
#import “Person.h”
@implementation Person
@synthesize heightInMeters, weightInKilos;
- (float) bodyMassIndex
{ return weightInKilos / (heightInMeters * heightInMeters); }
@property (nonatomic) <type> <property name>
@property (strong or weak) <type which is a pointer to an object> <property name>
@property (getter=<getter name>) ...
@property (readonly) ... & @property (readwrite) ...
@synthesize <prop name> = _<prop name> (only if you implement both setter and getter)
ClassName *object = [[ClassName alloc] init];
ClassName *object = [[ClassName alloc] initWith* ];
NSString* myString = [[NSString alloc] init];
alloc -> reserva memoria e instancia el objeto.
init -> inicializa valores en el nuevo objeto.
Algunas clases crean un método específico de inicialización.
ClassName *object = [ClassName method_to_create];
NSString* myString = [NSString string];
A partir de otros objetos
NSString’s - (NSString *)stringByAppendingString:(NSString *)otherString;
NSString’s & NSArray’s - (id)mutableCopy;
NSArray’s - (NSString *)componentsJoinedByString:(NSString *)separator;
Using class methods to create objects
NSString’s + (id)stringWithFormat:(NSString *)format, ...
UIButton’s + (id)buttonWithType:(UIButtonType)buttonType;
NSMutableArray’s + (id)arrayWithCapacity:(int)count;
NSArray’s + (id)arrayWithObject:(id)anObject;
Destroying a class object = nil; // null
self == this
//some code inside a method of a class
//method class to getter for the variable heightInMeters
float h = [self heightInMeters];
//another example a method in a class that adds itself (object) to an array that is //passed
-(void) addYourselfToArrayLNSMutableArray *) theArray
{ [theArray addObject:self]; }
Cada referencia a un objeto incrementa el contador en memoria, y no se libera mientras exista una referencia.
Todo objeto que hereda de NSObject:
isKindOfClass: herencia
isMemberOfClass: tipo de clase de objeto, no herencia
Obtener una clase es un método de clase: if ([obj isKindOfClass:[NSString class]]) {}
respondsToSelector: devuelve si un objeto responde a un método.
Directiva @selector() (SEL)
if ([obj respondsToSelector:@selector(shoot)]) { [obj shoot]; }
//Init the mutable array with an initial capacity of 5 elements
NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity:5];
//Init a UIView element
UIView* myView = [[UIView alloc] init];
//Add the new view to the array
[array addObject:myView]; // myView has received here a "retain" message
//Release the object (no automatic deallocation because array keeps the ownership)
[myView release];
@[@“a”,@“b”] == [[NSArray alloc] initWithObjects:@“a”,@“b”,nil].
myArray[index] == [myArray objectAtIndex:index].
NSInteger number;
NSUInteger unsignedNumber
Usado en iOS en vez de char* type
NSString *theMessage = @"hola";
NSUInteger charCount= [theMessage length];
if([string_1 isEqual: string_2]) { // equal strings }
@”Hello World”;
NSString *myString = @”Hello World”;
int len = [myString length];
int len = [@”Hello World” length];
NSString *myString = [[NSString alloc] initWithString:@”Hello World”];
int len = [myString length];
// Formatting output with NSLog
int a = 1;
float b = 33.22;
char c = ‘A’;
NSLog(@”Integer %d Float: %f Char: %c”, a, b, c);
NSString es inmutable. Normalmente las funciones envían un mensaje a un NSString que devuelve un NSString nuevo.
self.display.text = [self.display.text stringByAppendingString:digit];
NSMutableString
Versión de NSString modificable para realizar modificaciones sin crear un nuevo objeto
NSArray *thearray = [NSArray arrayWithObjects:o1,o2,o3,o4, nil];
//get element
[thearray objectAtIndex:0]; //element at index 0
//Example
NSDate *now = [NSDate date];
NSDate *tomorrow = [now dateByAddingTImeInterval:24.0*60.0*60.0]; //add a day
NSDate *yesterday = [now dateByAddingTimeInterval:-24.0*60.0*60.0]; //minus a day
//array of Dates
NSArray *dateList = [NSArray arrayWithObjects:now, tomorrow, yesterday];
//get elements in array
NSDate *first = [dateList objectAtIndex:0];
NSUInteger dateCount = [dateList count];
for(int i=0; i<dateCount; i++)
{ NSDAte *d = [dateList objectAtIndex:i];
NSLog(@” Date is %@”, d);
}
for(NSDate *d in dateList)
{ NSDAte *d = [dateList objectAtIndex:i];
NSLog(@” Date is %@”, d);
}
NSMutableArray *thearray = [NSArray arrayWithObjects:o1,o2,o3,o4, nil];
//get element
[thearray objectAtIndex:0]; //element at index 0
+ (id)dictionaryWithObjects:(NSArray *)values forKeys:(NSArray *)keys;
+ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...;
//Creation example:
NSDictionary *base = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:2], @“binary”,
[NSNumber numberWithInt:16], @“hexadecimal”, nil];
Can create with this syntax: @{ key1 : value1, key2 : value2, key3 : value3 }
NSDictionary *colors = @{ @“green” : [UIColor greenColor],
@“blue” : [UIColor blueColor],
@“red” : [UIColor redColor] };
Colección de colecciones
Cualquiera de las clases NSArray, NSDictionary, NSNumber, NSString, NSDate, NSData
Se utiliza para ficheros de propiedades:
[plist writeToFile:(NSString *)path atomically:(BOOL)] //plist is NSArray or NSDictionary
Almacenamiento de property lists en un NSDictionary, persistente entre ejecuciones de la aplicación.
[[NSUserDefaults standardUserDefaults] setArray:rvArray forKey:@“RecentlyViewed”];
// Write after batch of changes
[[NSUserDefaults standardUserDefaults] synchronize];
UIKit is the framework with all graphical elements for native iphone apps
UIViewController es el controlador para Navegación y Tabs
UIButton
UILabel
UIFont
UIColor
UITextView
Objeto que representa un color.
Se puede inicializar con RGB, HSB, etc
Pueden tener un alpha:
UIColor *color = [otherColor colorWithAlphaComponent:0.3]).
Hay una lista de colores estándar:
[UIColor greenColor];
[UIColor lightTextColor].
UIFont *font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
UIFontDescriptor: + (UIFont *)fontWithDescriptor:(UIFontDescriptor *)descriptor size:(CGFloat)size;
Bold from UIFontTextStyleBody preferred font
UIFont *bodyFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
UIFontDescriptor *existingDescriptor = [bodyFont fontDescriptor];
UIFontDescriptorSymbolicTraits traits = existingDescriptor.symbolicTraits;
traits |= UIFontDescriptorTraitBold;
UIFontDescriptor *newDescriptor = [existingDescriptor fontDescriptorWithSymbolicTraits:traits];
UIFont *boldBodyFont = [UIFont fontWithFontDescriptor:newDescriptor size:0];
Controla las características de una fuente en pantalla (color, outline, stroke, underline, etc).
NSAttributedString no es un NSString : - (NSString *)string;
NSMutableAttributedString
- (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range;
- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range;
- (void)removeAttribute:(NSString *)attributeName range:(NSRange)range;
Ejemplos:
UIButton’s - (void)setAttributedTitle:(NSAttributedString *)title forState:...;
UILabel’s @property (nonatomic, strong) NSAttributedString *attributedText; UITextView’s @property (nonatomic, readonly) NSTextStorage *textStorage;
Se puede utilizar a través de la propiedad text.
pero también:
@property (nonatomic, strong) NSAttributedString *attributedText;
Este atributo no es mutable, por lo que para modificarlo hay que utilizar una copia.
NSMutableAttributedString *labelText = [myLabel.attributedText mutableCopy]; [labelText setAttributes:...];
myLabel.attributedText = labelText;
UILabel multi-línea, editable, scroll, etc. Texto y attributos utilizando NSMutableAttributedString
@property (nonatomic, readonly) NSTextStorage *textStorage;
NSTextStorage hereda de NSMutableAttributedString.
Foundation
UIKit
Otros: Mapkit, Message
Media
Core Services
Core OS
Hay 6 patrones de diseño importantes
Los patrones no son más que maneras de hacer las cosas. Hay que adaptarse a ellos.
Alternativa a hacer subclases (Objective-C NO es multiherencia)
En UIKit Framework se hace un uso intensivo
Básicamente, una clase es asignada como 'delegada' para realizar algunas funciones de otra clase (Patrón de Decoración)
Cada aplicación tiene un delegado que cumple este protocolo, para dotar de comportamiento a la aplicación
Métodos importantes:
•applicationDidFinishLaunching
•applicationWillTerminate
•applicationDidReceiveMemoryWarning
Una acción (método) de un objeto (target) es invocada al realizar algún tipo de interacción (event)
En UIKit Framework la clase UIControl se encarga de encapsular estos tres elementos
En iOS una clase o subclase de UIControl puede realizar diferentes acciones en diferentes objetos en respuesta al mismo evento
UIApplication
Gestiona el bucle de eventos y coordina otros aspectos del comportamiento de la aplicación. Notifica al objeto delegado cuando ocurren eventos en la aplicación
Objeto delegado
Inicializa la aplicación y presenta la ventana de la interfaz. Además se encarga de tratar los eventos de la aplicación, aviso de memoria, la aplicación va a terminar...
View Controllers objects (UIViewController)
Se encargan de la presentación de cada vista, y de gestionar la interacción del usuario con la interfaz, así como modificar el modelo de datos de la aplicación.
UIWindow
Presentar las vistas y mandar los eventos de la interfaz al controlador.
Vistas, controles y capas
Dibujan la representación de los datos, controles y el patrón target-action.
Clases e información de la aplicación.
Modificación sintáctica: id <MyProtocol> obj, como el interfaz en Java
@protocol Foo <xyz>
- (void)someMethod;
@optional
- (void)methodWithArgument:(BOOL)argument;
@required
// getter (only) is part of this protocol
@property (readonly) int readonlyProperty;
// getter and setter are both in the protocol
@property NSString *readwriteProperty;
- (int)methodThatReturnsSomething;
@end
// importing the header file that declares the Foo @protocol
#import “Foo.h”
@interface MyClass : NSObject <Foo>
// MyClass is saying it implements the Foo @protocol
// (do not have to declare Foo’s methods again here,
// it’s implicit that you implement it)
@end
- (void)giveMeFooObject:(id <Foo>)anObjectImplementingFoo;
@property (nonatomic, weak) id <Foo> myFooProperty; // properties too!
Comienza con el carácter ^
Es un conjunto de código ejecutable y referenciable como objeto
[aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
NSLog(@“value for key %@ is %@”, key, value);
if ([@“ENOUGH” isEqualToString:key]) {
*stop = YES; }
}];
Función:
Puede contener:
awakeFromNib -> Instanciado desde el storyboard
viewDidLoad -> IBOutlets creados
cuando se posicionan las vistas
viewWillLayoutSubviews: viewDidLayoutSubviews:
cuando aparece y desaparece el controlador
viewWillAppear: viewDidAppear:
cuando cambia la posición (rotación)
viewWillLayoutSubviews: viewDidLayoutSubviews:
si es por rotación, también se reciben
[will/did]Rotate[To/From]InterfaceOrientation
viewWillDisappear: viewDidDisappear:
en caso de falta de memoria -> didReceiveMemoryWarning
Acción que se puede ejecutar en respuesta a un evento.
- (IBAction)exampleAction:(id)sender;
Se enlaza con una acción directamente en el interfaz
Referencia a un elemento gráfico.
@property (weak, nonatomic) IBOutlet UITextField *textField;
Gestiona la barra de navegación (UINavigationBar)
Todos los controladores tienen una variable UINavigationItem
Personaliza la barra superior cuando el controlador está en el top de la pila (visible)
UIBarButtonItem *lftBoton = [UIBarButtonItem alloc];
[lftBoton initWithTitle:@”Dale!”
style: UIBarButtonItemStyleBordered
target:self action:@selector(metodoBoton:)];
self.navigationItem.leftBarButtonItem =lftBoton;
[lftBoton release];
Estructura:
NSString *title; UIBarButtonItem *leftBarButtonItem, *rightBarButtonItem; UIView *titleView; NSString *backButtonTitle;
Gestiona la barra de pestañas (UITabBar)
Todos los controladores tienen una variable UINavigationItem
Personaliza la barra superior cuando el controlador está en el top de la pila (visible)
UITabBarItem *item = [[UITabBarItem alloc]
initWithTabBarSystemItem:UITabBarSystemItemBookmarks tag:0];
self.tabBarItem = item;
[item release];
Estructura:
NSString *title; UIImage *image; NSString *badgeValue
Las escenas se conectan con segues, que represetan una transición entre dos view controllers.
Show/Push: se añade nuevo contenido a la pila
Show detail: añade nuevo contenido o reemplaza el existente.
Present modally: muestra un controlador modal como "hijo" del actual, que tiene la responsabilidad sobre el mismo
Popover presentation: se presenta un popover sobre una vista del view controller.
Custom: una transición realizando sobre UIStoryboardSegue.
Unwind: reverse navigation.
Se implementa en código utilizando:
- (void)performSegueWithIdentifier:(NSString *)segueId sender:(id)sender;
sender -> es el iniciador del evento
- (IBAction)rentEquipment
{
if (self.snowTraversingTalent == Skiing) {
[self performSegueWithIdentifier:@“AskAboutSkis” sender:self];
} else {
[self performSegueWithIdentifier:@“AskAboutSnowboard” sender:self];
} }
Se transmite la información que necesite al siguiente controlador, y se cede el control.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@“DoSomething”]) {
if ([segue.destinationViewController isKindOfClass:[DoSomethingVC class]]) {
DoSomethingVC *doVC = (DoSomethingVC *)segue.destinationViewController;
doVC.neededInfo = ...; }
} }
Si se responde NO a este método, el segue no continuaría. Por ejemplo, para validación de datos.
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([segue.identifier isEqualToString:@“DoAParticularThing”]) {
return [self canDoAParticularThing] ? YES : NO;
}
}
// Target viewcontroller
- (IBAction)myUnwindAction:(UIStoryboardSegue*)unwindSegue
Permiten mostrar una nueva pantalla para coger información, presentar de forma distinta un contenido, etc.
Mostrar un controlador de forma modal
[self presentModalViewController:viewController animated:YES];
Ocultar un controlador
[self dismissModalViewControllerAnimated:YES];
- (void)add:(id)sender {
// Create the root view controller for the navigation controller
// The new view controller configures a Cancel and Done button for the
// navigation bar.
RecipeAddViewController *addController = [[RecipeAddViewController alloc] init];
addController.modalPresentationStyle = UIModalPresentationFullScreen;
addController.transitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:addController animated:YES completion: nil];
}
UITableView
UITableViewController
Protocolos
Celdas (UITableViewCell)
Delegado de UITableViewDataSource y UITableViewDelegate, separando modelo y vista
UITableViewDelegate
–willDisplayCell:forRowAtIndexPath;
–willSelectRowAtIndexPath;
–didSelectRowAtIndexPath;
–accessoryTypeForRowWithIndexPath
– accessoryButtonTappedForRowWithIndexPath
UITableViewDataSource
–numberOfSectionsInTableView;
–numberOfRowsInSection:(NSInteger)section; *
–cellForRowAtIndexPath:(NSIndexPath *)indexPath;
–InsertSections, deleteSections, reloadSections
–insertRowsAtIndexPaths...
UITableView Protocols
delegate controla cómo se muestra la tabla
dataSource proporciona los datos de la table
UITableViewController
es automáticamente un UITableView delegate y dataSource
@property (nonatomic, strong) UITableView *tableView;
( == self.view en UITableViewController!)
- (UITableViewCell *)tableView:(UITableView *)sender
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
}
typedef enum {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
} UITableViewCellStyle;
Hay que definir un UITableViewCell
Salvo que trabajes en estático y no te importe el por defecto.
- (UITableViewCell *)tableView:(UITableView *)sender
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
cell = [self.tableView
dequeueReusableCellWithIdentifier:@“Flickr Photo Cell”
forIndexPath:indexPath];
cell.textLabel.text = [self
getMyTitleForRow:indexPath.row
inSection:indexPath.section];
return cell;
}
- (void)tableView:(UITableView *)sender didSelectRowAtIndexPath:(NSIndexPath *)path ! {!
// go do something based on information about my Model!
// corresponding to indexPath.row in indexPath.section
}
- (void)tableView:(UITableView *)sender
accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
// Do something related to the row at indexPath,
// but not the primary action associated with touching the row
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
// prepare segue.destinationController to display based on information
// about my Model corresponding to indexPath.row in indexPath.section
}
El sender es un UITableViewCell
Cada aplicación tiene su propio conjunto de directorios
<Application Home>
MyApp.app
MyApp
MainWindow.nib
SomeImage.png
Documents
Library
Caches
Preferences
Sólo pueden escribir en su directorio Home
NSPathUtilities.h → Conjunto de categorías para trabajar con rutas del sistema de archivos de una aplicación
Buscando el directorio Documents:
NSString *documentDirectory;
NSArray *paths = NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
documentsDirectory = [paths objectAtIndex:0];
Accediendo al bundle de la aplicación
NSBundle *bundle = [NSBundle mainBundle];
Obteniendo la ruta de un recurso en el bundle
NSBundle *bundle = [NSBundle mainBundle];
[bundle pathForResource:@”image” ofType:@”jpg”];
Es una manera de representar jerarquías simples de datos
Sólo soporta arrays, diccionarios, strings, fechas, enteros, dobles y booleanos
Una property list es una clase (array o diccionario) que engloba uno o varios objetos soportados
Representada con XML o con un binario (más compacto)
Se usa para una cantidad menor de unos pocos cientos de KBs
Métodos para escribir
- (BOOL)writeToFile:(NSString *)aPath atomically:(BOOL)flag;
- (BOOL)writeToURL:(NSURL *)aURL atomically:(BOOL)flag;
Métodos para leer
- (id)initWithContentsOfFile:(NSString *)aPath;
- (id)initWithContentsOfURL:(NSURL *)aURL;
Ejemplo de lectura (en el caso de NSArray):
NSString *path = [[NSBundle mainBundle] pathForResource:@"places" ofType:@"plist"];
NSArray *placesArray = [NSArray initWithContentsOfFile:path];
Integradas en la aplicación o en la aplicación Settings
Normalmente, si tienen poco uso, se utilizarían las settings.
Clase NSUserDefaults
–Método de clase → +(id)standardUserDefaults
–Es una Property List
Ejemplo de uso (insertar valor y recuperarlo)
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@”Pedro” forKey: @”name”];
NSString *nombre = [defaults stringForKey:@”name”];
Settings.bundle en tu aplicación sirve para añadir las preferencias a la aplicación Settings de iPhone/iPod/iPad
Muestra una pantalla con controles
Cada control tiene (al menos)
*Tipo (Type)
*Titulo (Title)
*Clave (Key)
NSXMLParser - clase para parsear XML dirigido por eventos (como JAXP)
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse]; //empieza a parsear
Iniciar un objeto NSXMLParser y asignarle un delegado
didStartElement → Guardar el tag y reservar memoria estructura
didEndElement → Guardar valores en la estructura
foundCharacters → Guardar valor del tag
NSJSONSerialization - clase para parsear JSON
NSMutableDictionary *returnedDict =
[NSJSONSerialization JSONObjectWithData:data
options:kNilOptions error:&error];
O utilizando una librería como JSONKit
NSData* jsonData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
NSDictionary *resultsDictionary = [jsonData objectFromJSONData];
Core Data no es un RDBMS, aunque utiliza SQLite por debajo.
Utiliza un NSManagedObjectContext, a partir de un UIManagerdDocument o el AppDelegate.
El modelo se crea como un Data Model en el XCode.
Insert/Delete un objeto:
NSManagedObjectContext *context = aDocument.managedObjectContext;
NSManagedObject *photo =
[NSEntityDescription insertNewObjectForEntityForName:@“Photo”
inManagedObjectContext:context];
[context deleteObject:photo];
Atributos:
- (id)valueForKey:(NSString *)key; !
- (void)setValue:(id)value forKey:(NSString *)key; !
El tipo es nil por defecto.
NSNumber para números, boolean; NSData para datos binarios, NSDate para fechas, NSSet para relaciones
Queries: NSFetchRequest
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@“Photo”];
request.fetchBatchSize = 20;
request.fetchLimit = 100;
request.sortDescriptors = @[sortDescriptor];
request.predicate = ...;
NSArray *photographers = [context executeFetchRequest:request error:&error];
NSPredicate:
NSString *serverName = @“flickr-5”;
NSPredicate *predicate =
[NSPredicate predicateWithFormat:@“thumbnailURL contains %@”, serverName];
// unique a photo in the database
@“uniqueId = %@”, [flickrInfo objectForKey:@“id”]
// matches name case insensitively!
@“name contains[c] %@”, (NSString *)
// viewed is a Date attribute in the data mapping!
@“viewed > %@”, (NSDate *)
// Photo search (by photographer’s name)!
@“whoTook.name = %@”, (NSString *)
// Photographer search (not a Photo search)
@“any photos.title contains %@”, (NSString *)
NSFetchedResultsController enlaza un NSFetchRequest con UITableViewController. Normalmente se utiliza una @property para referenciar el NSFetchedResultsController
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@“Photo”];
request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@“title” ...]];
request.predicate = [NSPredicate predicateWithFormat:@“whoTook.name = %@”, photogName];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc]
initWithFetchRequest:(NSFetchRequest *)request
managedObjectContext:(NSManagedObjectContext *)context
sectionNameKeyPath:(NSString *)keyThatSaysWhichSectionEachManagedObjectIsIn
cacheName:@“MyPhotoCache”]; // careful!
//UITableView
- (NSUInteger)numberOfSectionsInTableView:(UITableView *)sender
{
return [[self.fetchedResultsController sections] count];
}
- (NSUInteger)tableView:(UITableView *)sender
numberOfRowsInSection:(NSUInteger)section
{
return [[[self.fetchedResultsController sections]
objectAtIndex:section] numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)sender
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = ...;
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
Acceso a una Sqlite a bajo nivel.
#import <sqlite3.h>
sqlite3_open, sqlite3_prepare_v2, sqlite3_step
sqlite3_column_count, sqlite3_column_text
sqlite3_column_name, sqlite3_changes, sqlite3_last_insert_rowid, sqlite3_errmsg, sqlite3_finalize, sqlite3_close
Una vista es una clase que hereda de UIView
Es parte de una jerarquía:
Una vista sólo tiene un padre - (UIView *)superview
Pero puede tener muchos hijos - (NSArray *)subviews
El orden importa (front-back)
UIWindow
UIView raíz de la jerarquía
En iOS sólo tenemos (normalmente) un UIWindow, no se trata de ventanas.
Se construye gráficamente en Xcode, aunque se puede hacer por código.
- (void)addSubview:(UIView *)aView; // sent to aView’s (soon
- (void)removeFromSuperview; // sent to the view that is being removed
La raíz de nuestro modelo es: @property view
UIViewCo@property (strong, nonatomic) UIView *view
Esta vista es el padre de nuestras vistas, la que sufre la rotación, se le añaden vistas hijo, etc.
CGFloat
CGPoint: struct con 2 CGFloat (x,y)
CGPoint p = CGPointMake(34.5, 22.0);
CGSize: struct con 2 CGFloat (ancho, alto)
CGSize s = CGSizeMake(100.0, 200.0);
CGRect: struct con CGPoint origen y size
CGRect aRect = CGRectMake(45.0, 75.5, 300, 500);
Pixels por punto en la pantalla
@property CGFloat contentScaleFactor
Espacio ocupado
@property CGRect bounds;
Centro
@property CGPoint center;
Límites de la vista padre:
@property CGRect frame;
Normalmente se crean en el XCode, arrastrando una vista y modificándola en el Identity Inspector
También se puede crear por código:
CGRect labelRect = CGRectMake(20, 20, 50, 30);
UILabel *label = [[UILabel alloc] initWithFrame:labelRect];
label.text = @”Hello!”;
[self.view addSubview:label];
Para crear nuestras propias vistas o manejar los eventos de una forma diferente.
Extender UIView y sobreescribir el método:
- (void)drawRect:(CGRect)aRect;
Se dibuja utilizando Core Graphics.
Actualizar mediante los métodos
- (void)setNeedsDisplay;
- (void)setNeedsDisplayInRect:(CGRect)aRect;
UIColor tiene la propiedad de alpha, que se puede modificar en el UIView backgrounnColor
@property BOOL opaque; debe de ser NO
@property CGFloat alpha en UIView modifica la transparencia de toda la vista.
@property (nonatomic) BOOL hidden; puede esconder completamente una vista
Subview para dibujar text en una vista.
UILabel *scoreLabel = [ [UILabel alloc ]
initWithFrame:CGRectMake((self.bounds.size.width / 2), 0.0, 150.0, 43.0) ];
scoreLabel.textAlignment = UITextAlignmentCenter;
scoreLabel.textColor = [UIColor whiteColor];
scoreLabel.backgroundColor = [UIColor blackColor];
scoreLabel.font = [UIFont fontWithName:@"Arial Rounded MT Bold" size:(36.0)];
[self addSubview:scoreLabel];
scoreLabel.text = [NSString stringWithFormat: @"%d", score];
//How much space will a piece of text will take up when drawn?
CGSize textSize = [text size];
Crear una imagen a partir de un fichero de los recursos
UIImage *image = [UIImage imageNamed:@“foo.jpg”];
De un fichero o de datos raw
UIImage *image = [[UIImage alloc] initWithContentsOfFile:(NSString *)fullPath];
UIImage *image = [[UIImage alloc] initWithData:(NSData *)imageData];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"image Name"]] ;
@property (nonatomic) UIViewContentMode contentMode;
Controla el refresco de la pantalla.
UIViewContentMode {Left,Right,Top,Right,BottomLeft,BottomRight,TopLeft,TopRight}
UIViewContentModeScale{ToFill,AspectFill,AspectFit} UIViewContentModeRedraw -> repintar con cada cambio de límites
Default: UIViewContentModeScaleToFill
Animaciones de propiedades de un UIView
frame
transform (translation, rotation, escale)
alpha (opacity)
Animación de apariencia (UI)
Animación dinámica (gravedad, fuerzas, alineamiento, etc)
Fade en 3 segundos sólo si se completa la animacion.
Animation class method in UIView
+ (void)animateWithDuration:(NSTimeInterval) duration
delay:(NSTimeInterval)delay
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
[UIView animateWithDuration:3.0
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{ myView.alpha = 0.0; }
completion:^(BOOL fin) { if (fin) [myView removeFromSuperview]; }];
UIViewAnimationOptions
BeginFromCurrentState
AllowUserInteraction
LayoutSubviews
Repeat
Autoreverse
OverrideInheritedDuration
OverrideInheritedCurve
AllowAnimatedContent
CurveEaseInEaseOut
CurveEaseIn
CurveLinear
Reemplazar una vista
+ (void)transitionFromView:(UIView *)fromView
toView:(UIView *)toView
duration:(NSTimeInterval)duration
options:(UIViewAnimationOptions)options
completion:(void (^)(BOOL finished))completion;
Pasos:
Crear un UIDynamicAnimator
Añadir UIDynamicBehaviors (gravedad, colisiones, etc)
Añadir UIDynamicItems (UIViews)
//Create a UIDynamicAnimator
UIDynamicAnimator *animator = [[UIDynamicAnimator alloc]
initWithReferenceView:aView];
//If animating views, all views must be in a view hierarchy
// with reference view at the top.
//Create and add UIDynamicBehaviors
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] init];
[animator addBehavior:gravity];
ICollisionBehavior *collider = [[UICollisionBehavior alloc] init];
[animator addBehavior:collider];
Añadir items a behaviors
id <UIDynamicItem> item1 = ...; id <UIDynamicItem> item2 = ...; [gravity addItem:item1]; [collider addItem:item1]; [gravity addItem:item2]; <UIDynamicItem> @protocol UIDynamicItem @property (readonly) CGRect bounds; @property (readwrite) CGPoint center; @property (readwrite) CGAffineTransform transform; @end
UIGravityBehavior: ángulo y magnitud
UICollisionBehavior: colisiones y límites
UIPushBehavior: aceleración en 2 modos [continuous, instantaneous]
UIAttachmentBehavior: alineamiento a un punto u otra vista.
UISnapBehavior: animación de estar pegado a un punto.
UIDynamicBehavior superclase para crear una composición
- (void)addChildBehavior:(UIDynamicBehavior *)behavior;
Definir las UIViews con reglas en vez de números. Muchas restricciones pueden afectar a las vistas:
- Rotación
- Tamaño de la pantalla
- Composición de controles
Normalmente se utiliza el XCode para configurarlo
UIGestureRecognizer es la clase que reconoce los gestos. Es una clase abstracta, normalmente la implementación está contenida en el UIViewController o en el mismo UIView.
- (void)setPannableView:(UIView *)pannableView
{
_pannableView = pannableView;
UIPanGestureRecognizer *pangr =
[[UIPanGestureRecognizer alloc]
initWithTarget:pannableView //La propia vista como target
action:@selector(pan:)]; //Método a ejecutar en el target
[pannableView addGestureRecognizer:pangr]; //Activar el gesto en la vista
}
UIPanGestureRecognizer tiene 3 métodos:
- (CGPoint)translationInView:(UIView *)aView;
- (CGPoint)velocityInView:(UIView *)aView;
- (void)setTranslation:(CGPoint)translation inView:(UIView *)aView;
Estados:
@property: @property (readonly) UIGestureRecognizerState state;
{Possible, Recognized, Began, Failed,
Changed, Ended, Continuous, Cancelled}
- (void)pan:(UIPanGestureRecognizer *)recognizer
{
if ((recognizer.state == UIGestureRecognizerStateChanged) ||
(recognizer.state == UIGestureRecognizerStateEnded)) {
CGPoint translation = [recognizer translationInView:self];
// move something in myself (I’m a UIView) by translation.x and translation.y
// for example, if I were a graph and my origin was set by an @property called origin
self.origin = CGPointMake(self.origin.x+translation.x, self.origin.y+translation.y);
[recognizer setTranslation:CGPointZero inView:self]; // Reset cumulative distance
 }
}
UIPinchGestureRecognizer
@property CGFloat scale;
@property (readonly) CGFloat velocity;
UIRotationGestureRecognizer
@property CGFloat rotation;
@property (readonly) CGFloat velocity;
UISwipeGestureRecognizer
@property UISwipeGestureRecognizerDirection direction; @property NSUInteger numberOfTouchesRequired;
UITapGestureRecognizer
@property NSUInteger numberOfTapsRequired;
@property NSUInteger numberOfTouchesRequired;
Dentro del XCode: un UIView se puede embeber en un Scroll View.
Objeto aplicación, diferente del Application Delegate que contiene información global.
UIApplication *myApplication =
[UIApplication sharedApplication];
Propiedad de UIApplication.
@property (nonatomic, getter=is...) networkActivityIndicatorVisible;
Cuando esta propiedad se pone a YES, aparece un spinner en la status bar indicando cuándo hay actividad en la red.
Se superpone a la pantalla actual, rompiendo la navegación -> Ventana modal.
Transiciones:
@property UIModalTransitionStyle modalTransitionStyle; UIModalTransitionStyleCoverVertical, UIModalTransitionStyleFlipHorizontal. UIModalTransitionStyleCrossDissolve, UIModalTransitionStylePartialCurl
Pantalla en iPad
@property UIModalPresentationStyle modalPresentationStyle;
UIModalPresentationFullScreen, UIModalPresentationPageSheet, UIModalPresentationFormSheet, UIModalPresentationCurrentContext
UILabel pero editable. Interacción clave con el teclado.
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
if (textField == SSN) { // SSN is an outlet
NSString *regEx = @"[0-9]{3}-[0-9]{2}-[0-9]{4}";
NSRange r = [textField.text rangeOfString:regEx options:NSRegularExpressionSearch];
if (r.location == NSNotFound) {
UIAlertView *av = [[[UIAlertView alloc] initWithTitle:@"Entry Error"
message:@"Enter social security number in 'NNN-NN-NNNN' format"
delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease];
[av show];
return NO;
}
}
return YES;
}
@property(nonatomic) UIKeyboardType keyboardType
myTextField.keyboardType = UIKeyboardTypeASCIICapable;
UIKeyboardTypeDefault, UIKeyboardTypeASCIICapable, UIKeyboardTypeNumbersAndPunctuation, UIKeyboardTypeURL, UIKeyboardTypeNumberPad, UIKeyboardTypePhonePad, UIKeyboardTypeNamePhonePad, UIKeyboardTypeEmailAddress, UIKeyboardTypeDecimalPad, UIKeyboardTypeTwitter, UIKeyboardTypeWebSearch, UIKeyboardTypeAlphabet = UIKeyboardTypeASCIICapable
Menú de acciones de estilo iPhone.
Menú de acciones de estilo iPhone.
-(id)initWithTitle:(NSString *)title
delegate:(id <UIActionSheetDelegate>)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
destructiveButtonTitle:(NSString *)destructiveButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ...;
- (void)addButtonWithTitle:(NSString *)buttonTitle;
//Displaying the Action Sheet!
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:...];
[actionSheet showInView:(UIView *)]; // centers the view on iPad
[actionSheet showFromRect:(CGRect) inView:(UIView *) animated:(BOOL)];
[actionSheet showFromBarButtonItem:(UIBarButtonItem *) animated:(BOOL)];
-(id)initWithTitle:(NSString *)title
message:(NSString *)message
delegate:(id <UIActionSheetDelegate>)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ...;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:...];
[alert show];
alert.alertViewStyle = UIAlertViewStyle{SecureText,PlainText,LoginAndPassword}Input;
UIAlertView *alert = [[[UIAlertView alloc]
initWithTitle:@"Username:"
message:@"Please enter your username:"
delegate:self cancelButtonTitle:@"Cancel"
otherButtonTitles:nil] autorelease];
alert.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
alert.tag = 12;
[alert addButtonWithTitle:@"Go"];
[alert show];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 12) {
if (buttonIndex == 1) {
UITextField *textfield = [alertView textFieldAtIndex:0];
NSLog(@"username: %@", textfield.text);
}
}
}
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:@"Title"
message:@"Message"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* yesButton = [UIAlertAction
actionWithTitle:@"Yes, please"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
//Handel your yes please button action here
[alert dismissViewControllerAnimated:YES completion:nil];
}];
UIAlertAction* noButton = [UIAlertAction
actionWithTitle:@"No, thanks"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:yesButton];
[alert addAction:noButton];
[self presentViewController:alert animated:YES completion:nil];
UIAlertView is deprecated. Use UIAlertController with a preferredStyle of UIAlertControllerStyleAlert instead
UIPopoverController no es un UIViewController, contiene una referencia al controller.
@property (nonatomic, strong) UIViewController *contentViewController;
- (IBAction)presentPopover:(UIBarButtonItem *)item
{
if (!self.popover) {
self.popover = [[UIPopoverController alloc] initWithViewController:vc];
[self.popover presentPopoverFromBarButtonItem:item ...];
}
}
- (void)dismissPopoverAnimated:(BOOL)animated;
UIModalPresentationPopover
avc.modalPresentationStyle = UIModalPresentationPopover;
avc.popoverPresentationController.sourceView = theButton;
[self presentViewController:avc animated:YES completion:nil];
Es una única aplicación que corre tanto en iPhone como en iPad.
Se crea un nuevo storyboard para iPad/iPhone
BOOL iPad = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad); !
Top-level controller para iPad. Con 2 view controller, master y detail.
Model Framework for Cocoa and Cocoa Touch
Permite componer una capa model de abstracción sencilla.
Evitando tener que trabajar con
-hash
-isEqual:
NSCopying
NSCoding
Example: Book Model
@interface TGRBook : MTLModel
@property (copy, nonatomic, readonly) NSString *author;
@property (copy, nonatomic, readonly) NSString *overview;
@property (copy, nonatomic, readonly) NSArray *genres;
@property (copy, nonatomic, readonly) NSDate *releaseDate;
@property (copy, nonatomic, readonly) NSNumber *identifier;
@property (copy, nonatomic, readonly) NSString *title;
@property (copy, nonatomic, readonly) NSURL *coverURL;
@end
NSError *error = nil;
TGRBook *book = [TGRBook modelWithDictionary:@{
@"title" : @"The Sandman",
@"author" : @"Neil Gaiman",
@"genres" : @[@"Graphic Novels", @"Fantasy"], ...
} error:&error];
TGRBook *anotherBook = [book copy];
[NSKeyedArchiver archiveRootObject:book toFile:@"my_file"];
TGRBook *b = [NSKeyedUnarchiver unarchiveObjectWithFile:@"my_file"];
Simplemente heredando de MTLModel
Our Book Model could have a JSON representation
{
...
"artistName": "Neil Gaiman, Sam Keith & Mike Dringenberg",
"description": "<p>NEW YORK TIMES bestselling author...",
"genres": ["Graphic Novels", "Books", "Comics & Graphic Novels"],
"releaseDate": "2012-08-21T07:00:00Z",
"trackId": 554016043,
"trackName": "The Sandman, Vol. 1: Preludes & Nocturnes (New Edition)", "artworkUrl100": "http://a4.mzstatic.com/us/r30/Publication/...",
...
}
This is how iTunes represents media (including books)
Simplemente heredando de MTLModel
@interface TGRBook : MTLModel <MTLJSONSerializing>
Hay que especificar:
Cómo mapear las propiedades con el path JSON
Como convertir el valor de JSON a property key.
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{
@"author" : @"artistName",
@"overview" : @"description",
@"identifier" : @"trackId",
@"title" : @"trackName",
@"coverURL" : @"artworkUrl100" };
}
+<key>JSONTransformer methods
Predefinidos:
MTLURLValueTransformerName MTLBooleanValueTransformerName
+ (NSValueTransformer *)releaseDateJSONTransformer {
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString *str) {
return [self.dateFormatter dateFromString:str];
} reverseBlock:^(NSDate *date) {
return [self.dateFormatter stringFromDate:date];
}];
}
+ (NSValueTransformer *)coverURLJSONTransformer {
return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
}
@interface TGRUser : MTLModel <MTLJSONSerializing>
@property (copy, nonatomic, readonly) NSArray *purchasedBooks;
@property (copy, nonatomic, readonly) TGRBook *nowReading;
@end
+ (NSValueTransformer *)purchasedBooksJSONTransformer {
return [NSValueTransformer mtl_JSONArrayTransformerWithModelClass:TGRBook.class];
}
+ (NSValueTransformer *)nowReadingJSONTransformer {
return [NSValueTransformer mtl_JSONDictionaryTransformerWithModelClass:TGRBook.class];
}
TGRBook *book =
[MTLJSONAdapter modelOfClass:TGRBook.class
fromJSONDictionary:JSONDictionary
error:&error];
NSDictionary *dictionary =
[MTLJSONAdapter JSONDictionaryFromModel:book];
Transformer reversible.
Puede convertir cualquier modelo que implemente <MTLJSONSerializing> en un NSDictionary y viceversa
// MTLManagedObjectSerializing protocol
// Entity name
+ (NSString *)managedObjectEntityName {
return @"Book";
}
// Map
+ (NSDictionary *)managedObjectKeysByPropertyKey {
return @{
@"coverURL" : @"coverLink"
};
}
// Transformer
+ (NSValueTransformer *)coverURLEntityAttributeTransformer {
return [[NSValueTransformer valueTransformerForName:MTLURLValueTransformerName]
}
//MTLManagedObjectAdapter
NSManagedObject *managedBook =
[MTLManagedObjectAdapter managedObjectFromModel:book
insertingIntoContext:moc];
TGRBook *book = [MTLManagedObjectAdapter modelOfClass:TGRBook.class
fromManagedObject:object error:&error];
Easy Networking Framework
NSURL *url = [NSURL URLWithString:@"https://itunes.apple.com"];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:url]; !
[client getPath:@"search" parameters:@{
@"term" : @"the sandman",
@"entity" : @"ebook"
} success:^(AFHTTPRequestOperation *operation, id JSONResponse) {
NSLog(@"Search results: %@", JSONResponse);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
...
}];
{
"resultCount": 50, "results": [{
"artistId": 3603584,
"artistName": "Neil Gaiman, Sam Kieth & Mike Dringenberg",
"kind": "ebook",
"price": 1.99,
"description": "<p>The first issue of the first volume...",
"currency": "USD",
"genres": ["Graphic Novels", "Books", "Comics & Graphic Novels"],
"genreIds": ["10015", "38", "9026"],
"releaseDate": "2013-05-01T07:00:00Z",
"trackId": 642469670,
"trackName": "Sandman #1",
...
NSURL *url = [NSURL URLWithString:@"https://itunes.apple.com"];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:url]; !
[client getPath:@"search"
parameters:@{
@"term" : @"Neil Gaiman",
@"entity" : @"ebook"
} success:^(AFHTTPRequestOperation *operation, NSDictionary *JSONResponse) {
NSArray *results = JSONResponse[@"results"];
NSValueTransformer *transformer;
transformer = [NSValueTransformer
mtl_JSONArrayTransformerWithModelClass:TGRBook.class];
NSArray *books = [transformer transformedValue:results];
NSLog(@"Books: %@", books);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
...
}];
El mapping puede ser costoso, debería hacerse fuera del thread principal de la UI
Notificaciones de eventos del MVC
[NSNotificationCenter defaultCenter]
Registrarse a eventos:
- (void)addObserver:(id)observer selector:(SEL)methodToInvokeIfSomethingHappens
name:(NSString *)name // name of station (a constant somewhere)
object:(id)sender; // whose changes you’re interested in (nil is anyone’s)
Notificación del evento:
- (void)methodToInvokeIfSomethingHappens:(NSNotification *)notification {
notification.name // the name passed above
notification.object // the object sending you the notification
notification.userInfo // notification-specific information about what happened }
// System/Library/Frameworks/UIKit.framework/Headers/UIApplication.h
UIKIT_EXTERN NSString *const UIApplicationDidEnterBackgroundNotification NS_AVAILABLE_IOS(4_0);
UIKIT_EXTERN NSString *const UIApplicationWillEnterForegroundNotification NS_AVAILABLE_IOS(4_0);
UIKIT_EXTERN NSString *const UIApplicationDidFinishLaunchingNotification;
UIKIT_EXTERN NSString *const UIApplicationDidBecomeActiveNotification;
UIKIT_EXTERN NSString *const UIApplicationWillResignActiveNotification;
{
"aps":
{
"alert":
{
"action-loc-key": "Open",
"body": "Hello, world!"
},
"badge": 2
}
}
"Asynchronous design approach"
Mecanismo basado en colas, donde se acumulan los bloques a ejecutar, que luego se ejecutan en el thread asociado
La cola principal es la que maneja la UI. Toda actividad en la UI debe realizarse en la cola principal. Actividad no relacionada con la UI que consuma mucho tiempo debería realizarse sin bloquear la cola principal.
Obtener la referencia a la cola principal
NSOperationQueue *mainQ = [NSOperationQueue mainQueue];
Ejecutar una acción en la cola principal.
NSObject method
- (void)performSelectorOnMainThread:(SEL)aMethod
withObject:(id)obj
waitUntilDone:(BOOL)waitUntilDone;
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL urlWithString:@“http://...”]];
NSURLConfiguration *configuration = ...;
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
delegate:nil
delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDownloadTask *task;
task = [session downloadTaskWithRequest:request
completionHandler:^(NSURL *localfile,
NSURLResponse *response, NSError *error) {
/* UI tasks on mainQueue delegate */ }];
[task resume];
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL urlWithString:@“http://...”]];
NSURLConfiguration *configuration = ...;
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
// No delegateQueue
NSURLSessionDownloadTask *task;
task = [session downloadTaskWithRequest:request
completionHandler:^(NSURL *localfile,
NSURLResponse *response, NSError *error) {
/* Non UI tasks */
/* Now UI tasks on main queue */
[self performSelectorOnMainThread:@selector(doUIthings)
withObject:nil waitUntilDone:NO];
}];
[task resume];
Biblioteca para la ejecución de código concurrente, on iOS y OSX multicore.
Proporciona colas thread-safe:
serial queues -> colas de ejecución de una tarea cada vez (FIFO)
concurrent queues -> comienzan FIFO, pero acaban sin garantía
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
NSURL *url = [NSURL URLWithString:myURL];
myData = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
[self processTheData:myData];
});
});
// UI Update
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
NSURL *url = [NSURL URLWithString:myURL];
myData = [NSData dataWithContentsOfURL:url];
[self processTheData:myData];
dispatch_async(dispatch_get_main_queue(), ^{
// code that updates UI goes here
});
});
@property (nonatomic, strong) NSOperationQueue *queue;
- (void)viewDidLoad
{
[super viewDidLoad];
self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 4;
}
[self.queue addOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:myURL];
myData = [NSData dataWithContentsOfURL:url];
[self processTheData:myData];
[[NSOperationQueue mainQueue] addOperationWithBlock:{
// code that updates UI goes here
}];
}];
- (void)showOrHideNavPrompt
{
NSUInteger count = [[PhotoManager sharedManager] photos].count;
double delayInSeconds = 1.0;
dispatch_time_t popTime =
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // 2
if (!count) {
[self.navigationItem setPrompt:@"Add photos with faces to Googlyify them!"];
} else {
[self.navigationItem setPrompt:nil];
}
});
}
+ (instancetype)sharedManager
{
static PhotoManager *sharedPhotoManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPhotoManager = [[PhotoManager alloc] init];
sharedPhotoManager->_photosArray = [NSMutableArray array];
});
return sharedPhotoManager;
}
Permite hacer un singleton thread-safe
NSURL* url = [NSURL URLWithString:@"http://example.com"]; NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url]; [urlRequest addValue:@"application/json" forHTTPHeaderField:@"Accept"]; NSOperationQueue* queue = [[NSOperationQueue alloc] init]; [NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse* response, NSData* data, NSError* error) { if (data) { NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; // check status code and possibly MIME type (which shall start with "application/json"): NSRange range = [response.MIMEType rangeOfString:@"application/json"]; if (httpResponse.statusCode == 200 /* OK */ && range.length != 0) { NSError* error; id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; if (jsonObject) { dispatch_async(dispatch_get_main_queue(), ^{ // self.model = jsonObject; NSLog(@"jsonObject: %@", jsonObject); }); } else { dispatch_async(dispatch_get_main_queue(), ^{ //[self handleError:error]; NSLog(@"ERROR: %@", error); }); } } else { // status code indicates error, or didn't receive type of data requested NSString* desc = [[NSString alloc] initWithFormat:@"HTTP Request failed with status code: %d (%@)", (int)(httpResponse.statusCode), [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]]; NSError* error = [NSError errorWithDomain:@"HTTP Request" code:-1000 userInfo:@{NSLocalizedDescriptionKey: desc}]; dispatch_async(dispatch_get_main_queue(), ^{ //[self handleError:error]; // execute on main thread! NSLog(@"ERROR: %@", error); }); } } else { // request failed - error contains info about the failure dispatch_async(dispatch_get_main_queue(), ^{ //[self handleError:error]; // execute on main thread! NSLog(@"ERROR: %@", error); }); } }];
API para acceder a los sensores del dispositivo
Mediante la clase CMMotionManager. Acceso a un recurso global, por lo que es recomendable obtener sólo una instancia por aplicación para mantener el rendimiento.
Primero hay que obtener la disponibilidad del hardware
@property (readonly) BOOL {accelerometer, gyro, magnetometer, deviceMotion}Available
Programar los sensores para obtener los datos
-(void)start {Accelerometer,Gyro,Magnetometer,DeviceMotion}Updates;
@property (readonly) BOOL {accelerometer,gyro,magnetometer,deviceMotion}Active;
- (void)stop{Accelerometer,Gyro,Magnetometer,DeviceMotion}Updates;
Registrarse para recibir datos de acelerómetro:
- (void)startAccelerometerUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMAccelerometerHandler)handler;
queue == [[NSOperationQueue alloc] init]
Recibir datos de giróscopo:
- (void)startGyroUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMGyroHandler)handler;
Registrarse al campo magnético
- (void)startMagnetometerUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMMagnetometerHandler)handler;
typedef void (^CMMagnetometerHandler)(CMMagnetometerData *data, NSError *error)
Registrar los idiomas. Los storyboards extraerán ficheros .strings para cada idioma.
Para strings en el código, utilizar genstrings *-m
NSString *NSLocalizedStringWithDefaultValue(NSString *key, NSString *table,
NSString *bundle, NSString *defaultValue,
NSString *comment);
Ejemplo: @“hello” -> NSLocalizedString(@“hello”, @“Greeting at start of application.”)
Los recursos están contenidos en un bundle para cada idioma (es.lproj).
Dentro de estos directorios estarán los .string, imágenes, sonidos. Primero se busca el idioma top-level (default) y luego el locale específico.
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@“speedlimit”
ofType:@“jpg”];
NSLocale es diferente para cada idioma. Indica el idioma seleccionado por el usuario en los ajustes.
De esta manera, se pueden ajustar fechas y números según el idioma.
+ (NSString *)localizedStringFromNumber:(NSNumber *)number
numberStyle:(NSNumberFormatterStyle)style
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *parsedNumber = [formatter numberFromString:userInputtedString];
Getter
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"CacheDataAggressively"]) {
// Do something
}
Setter
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:YES forKey:@"CacheDataAggressively"];
[defaults setObject:[NSDate dateWithTimeIntervalSinceNow:(3600 * 24 * 7)]
forKey:@"CacheExpirationDate"]; // Set a 1-week expiration
Es un framework para manejar localización y direcciones, no tiene UI
CLLocation
@propertys: coordinate, altitude, horizontal/verticalAccuracy, timestamp, speed, course
@property (readonly) CLLocationCoordinate2D coordinate;
typedef {
CLLocationDegrees latitude; // a double
CLLocationDegreeslongitude; //adouble } CLLocationCoordinate2D;
}
@property (readonly) CLLocationDistance altitude; // meters A negative value means “below sea level.”
@property (readonly) CLLocationAccuracy horizontalAccuracy; // in meters
@property (readonly) CLLocationAccuracy verticalAccuracy; // in meters
kCLLocationAccuracyBestForNavigation
kCLLocationAccuracyBest
kCLLocationAccuracyNearestTenMeters
kCLLocationAccuracyHundredMeters
kCLLocationAccuracyKilometer
kCLLocationAccuracyThreeKilometers
@property (readonly) CLLocationDirection course;
@property (readonly) NSDate *timestamp;
// in degrees, 0 is north, clockwise
@property (readonly) CLLocationSpeed speed; // in meters/second
Distance between CLLocations
- (CLLocationDistance)distanceFromLocation:(CLLocation *)otherLocation; // in meters
+ (CLAuthorizationStatus)authorizationStatus; // Authorized, Denied or Restricted (parental, enterprise)
+ (BOOL)locationServicesEnabled; // user has enabled (or not) location services for your application
+ (BOOL)significantLocationChangeMonitoringAvailable;
+ (BOOL)isMonitoringAvailableForClass:(Class)regionClass; //[CLBeacon/CLCircularRegionclass]
+ (BOOL)isRangingAvailable; // device can tell how far it is from beacons!
// Error reporting
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error;
kCLErrorLocationUnknown
kCLErrorDenied
kCLErrorHeadingFailure
@property CLLocationAccuracy desiredAccuracy;
@property CLLocationDistance distanceFilter; !
- (void)startUpdatingLocation;
- (void)stopUpdatingLocation;
// Recomendable asegurarse de pararlo si no se van a procesar.
//Notificación
(void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations; // of CLLocation!
- (void)startMonitoringSignificantLocationChanges;
- (void)stopMonitoringSignificantLocationChanges;
CLLocationManager
- (void)startMonitoringForRegion:(CLRegion *)region;
// CLCircularRegion/CLBeaconRegion
- (void)stopMonitoringForRegion:(CLRegion *)region;
Detectando cuándo se entra en una región o cerca de otro dispositivo
CLLocationManager’s delegate
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region;
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region;
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region
withError:(NSError *)error;
git checkout splash
git checkout splash
git checkout alumno
git checkout editar_alumno
Vista model para obtener referencias a las fotos y vídeos de la cámara y la galería.
Primero hay que probar qué capacidades hay disponibles:
+ (BOOL)isSourceTypeAvailable:(UIImagePickerControllerSourceType) sourceType;
UIImagePickerControllerSourceTypePhotoLibrary/Camera/SavedPhotosAlbum
+ (BOOL)isCameraDeviceAvailable:(UIImagePickerControllerCameraDevice) cameraDevice;
UIImagePickerControllerCameraDeviceFront| UIImagePickerControllerCameraDeviceRear
- (IBAction)takePhoto:(UIButton *)sender {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// extract image/movie data/metadata here, more on the next slide
UIImage *chosenImage = info[UIImagePickerControllerEditedImage];
[self dismissViewControllerAnimated:YES completion:...]; // or popover dismissal
}
// Also dismiss it when cancel happens!
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[self dismissViewControllerAnimated:YES completion:...]; // or popover dismissal
}
git checkout editar_alumno
git checkout expediente
git checkout ayuda
git checkout settings
git checkout avisos
git checkout login_network
git checkout login_network