Archivi categoria: Objective C

I miei esperimenti e appunti su Objective C , mac osx e iOS programming

NSXmlParser il default XmlParser di iOS

Per chi lavora da molto con la programmazione, sia Java che .Net, avere a disposizione un Dom per la lettura dei files xml tramite un documento a grafo e magari espressioni xpath, è oramai legge.

Eppure per lavorare su IPhone tramite iOS e in generale su Mac OSX, di default troviamo solo ed esclusivamente un SaxParser, l’NSXmlParser..

Superato l’impatto delle Sax (da sempre ostiche ma sicuramente molto performanti), facciamo un velocissimo esempio di come usarle in una nostra classe, per leggere velocemente dei dati da un file xml.

Innanzitutto aggiungiamo l’interfaccia  NSXMLParserDelegate alla classe che riceverà gli eventi Sax, perche le Sax lavorano completamente ad eventi. Quando verrà lanciato il parser, scatteranno eventi ogni volta che si verificherà una condizione (aperto nodo, presenza testo, chiuso nodo, etc etc etc )

1
@interface MiaClasse : NSObject <NSXMLParserDelegate>

 

in questa classe (che può essere una qualsiasi classe del nostro progetto, dichiariamo l’xml parser nell’header , l’elemento che conterrà l’array di dati che andiamo ad estrarre, i puntatori per i singoli valori (on the fly come variabili secche, ma in genere meglio definire un oggetto Item che esponga tutti gli attributi), il puntatore all’elemento xml corrente che il parser sta analizzando:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@interface MiaClasse : NSObject <NSXMLParserDelegate>

//parser

NSXMLParser *xmlParser;

//array che conterrà i dati

NSMutableArray *elencoItems;

//variabile temporanea per ogni elemento

NSMutableDictionary *item;// nome del nodo corrente che il sax parser sta analizzando

NSString *currentElement;

//elementi che leggeremo e poi memorizzeremo

NSMutableString *nome, *cognome, *indirizzo;

ora che abbiamo definito l’header, passiamo all’implementazione.

Inizializziamo il parser e l’array di dati nel metodo in cui lo andiamo ad usare, nel costruttore o dove vogliamo.

In questo caso andiamo a leggere il file xml da risorsa locale, ma possiamo accedere tranquillamente ad un file remoto .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)applicationDidFinishLaunching:(UIApplication *)application {

elencoItems= [[NSMutableArray alloc] init];

xmlParser = [[NSXMLParser alloc] initWithData:[NSData dataWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"test.xml"]  ]];

//definiamo il delegato che riceverà gli eventi (in questo caso la classe stessa

[xmlParser setDelegate:self];

//saltiamo i problemi di namespace

[xmlParser setShouldProcessNamespaces:NO];

[xmlParser setShouldReportNamespacePrefixes:NO];

[xmlParser setShouldResolveExternalEntities:NO];

// avviamo il parsing XML

[xmlParser parse];

}

 

ora definiamo i metodi essenziali per soddisfare l’interfaccia NSXMLParserDelegate:

didStartElement , che viene eseguito ad ogni inizio di elemento xml trovato

didEndElement, che viene eseguito quando viene analizzata la chiusura di un tag xml

foundCharacters, che viene eseguito quando si analizza il contenuto di tipo “text” di un elemento

parserDidEndDocument, che scatta quando si raggiunge la fine del documento xml.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{

currentElement = [elementName copy];

//qui mettiamo tutte le regole di match del nostro xml, ad esempio cosa fare quando

// si incontra un nodo denominato "persona"

if ([elementName isEqualToString:@"persona"]) {

// inizializza tutti gli elementi che conterranno i dati. memorizzeremo ogni item come tabella per praticità

item = [[NSMutableDictionary alloc] init];

nome = [[NSMutableString alloc] init];

cognome = [[NSMutableString alloc] init];

indirizzo = [[NSMutableString alloc] init];

}

}

 

Nel metodo didEndElement andremo fisicamente a prendere i dati letti e a memorizzarli in una struttura (il nostro array)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{

if ([elementName isEqualToString:@"persona"]) {

[item setObject:nome forKey:@"nome"];

[item setObject:cognome forKey:@"cognome"];

[item setObject:indirizzo forKey:@"indirizzo"];

[elencoItems addObject:[item copy]];

}

}

 

Ed ora il metodo che fisicamente legge i dati dai nodi:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {

if ([currentElement isEqualToString:@"nome"]){

[nome appendString:string];

} else if ([currentElement isEqualToString:@"cognome"]) {

[cognome appendString:string];

} else if ([currentElement isEqualToString:@"indirizzo"]) {

[indirizzo appendString:string];

} }

 

Infine se a termine del traversing del grafo xml dobbiamo effettuare delle operazioni, come prendere il risultato e passarlo ad altri oggetti, utilizziamo il parserDidEndDocument:

1
2
3
4
5
6
7
- (void)parserDidEndDocument:(NSXMLParser *)parser {

[mioOggetti setData:elencoItems];

[elencoItems release];

}

In linea di massima e’ molto semplice utilizzare XmlParser.

Come tutti i framework e metodologie su iOS si fa un grande uso dei delegati.

Spero di esservi stato di aiuto.

 

Salvare un file jpg da un NSImage

Sembrerà stupido, ma a volte ci si perde nell’infinita documentazione di Apple..

La necessità di oggi è come ricavare una jpg e salvarla su file partendo da un oggetto NSImage, nella nostra User Interface..

 

Ipotizzando di avere un controllo in grado di restituirci una NSImage (ad esempio un NSImageCell o cose simili

Per poter estrarre una jpg e salvarla dobbiamo innanzitutto rappresentare l’immagine come TIFF, importante per fare in modo che venga generata l’imageData sottostante.

Poi creiamo una rappresentazione bitmap della nostra imageData.

Poi instanziamo un dictionary per specificare le properties che vogliamo dare alla trasformazione (ad esempio la percentuale di qualità della compressione).

Infine otteniamo una rappresentazione JPEG dell’image data e la salviamo su disco.

1
2
3
4
5
6
7
8
9
10
11
NSImage *image = [mImageCell objectValue];

NSData *imageData = [image  TIFFRepresentation];

NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];

NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.75] forKey:NSImageCompressionFactor];

imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];

[imageData writeToFile:@"/tmp/test.jpg" atomically:YES];

 

ed il gioco e’ fatto..

Tutorial: Splash Screen per Iphone Apps

Generalmente basta utilizzare un immagine png predefinita per mostrare uno splash screen durante la fase di caricamento dell’applicazione.
Per far questo basta mettere un’immagine chiamata Default.png e magicamente quando clicchiamo sull’icona dell’applicazione sul nostro iphone, il dispositivo va a cercare l’immagine e a mostrarla per la durata dell’inizializzazione dell’applicazione.

Secondo le guidelines e le best practises non andrebbe mostrato nessuno splash screen perche l’applicazione dovrebbe caricarsi nel minor tempo possibile, e non è corretto ritardare lo startup per mostrare questo tipo di immagini.

In realtà questo non avviene quasi mai, e comunque abbiamo bisogno quasi sempre di mostrare il titolo, e qualche credit in fase di startup della nostra iphone App e una progress bar, e magari lasciare per qualche secondo visibile questo splash screen indipendentemente da quanto ci mette l’app a caricarsi (per mostrare il logo di chi l’ha realizzata etc etc)..

In questo tutorial andiamo a realizzare un semplice approccio per la definizione di un protocol che descriva il comportamento generico di uno splashscreen e implementarlo nel nostro Application Delegate.

Innanzitutto creiamo un nuovo progetto iOS Application da XCode, selezionando il modello Navigation-based Application

Ora andiamo nel progetto appena creato, e aggiungiamo nelle Classes un oggetto di tipo Objective-C Protocol e lo chiamiamo splash.

Nel codice generato, aggiungiamo i due metodi che descrivono l’interfaccia standard di un oggetto che utilizza lo splashScreen.

1
2
3
4
5
6
7
8
#import <UIKit/UIKit.h>
@protocol Splash

- (void)showSplash;

- (void)hideSplash;

@end

 

Ora che abbiamo definito il protocol per lo splash, andiamo a modificare il nostro application Delegate, per definire nell’interfaccia , che utilizza il protocol creato, e aggiungere il riferimento alla view che l’applicazione utilizzerà per mostrare lo splash.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#import <UIKit/UIKit.h>

#include "splash.h"

@interface splashTutorialAppDelegate : NSObject <UIApplicationDelegate,Splash> {

UIWindow *window;

UINavigationController *navigationController;

IBOutlet UIView *splashView;

}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;

@end

Ora andiamo nell’implementazione del App delegate, e aggiungiamo l’implementazione dei due metodi definiti nel protocol Splash, e mettiamo il codice di startup nello splash nel metodo di fine caricamento applicazione.

Per mostrare lo splash creiamo a runtime un modalViewController  e scheduliamo un selector che punti al metodo hideSplash, temporizzandolo, ad esempio ad un secondo (cosi attendiamo il tempo di caricamento effettivo dell’applicazione, piu un secondo)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#import "splashTutorialAppDelegate.h"

#import "RootViewController.h"

@implementation splashTutorialAppDelegate

@synthesize window;

@synthesize navigationController;

#pragma mark -

#pragma mark Application lifecycle

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Override point for customization after application launch.

// Add the navigation controller's view to the window and display.

[self.window addSubview:navigationController.view];

[self.window makeKeyAndVisible];

[self showSplash];

return YES;

}

-(void)showSplash

{

NSLog(@"show Splash");

UIViewController *modalViewController = [[UIViewController alloc] init];

modalViewController.view = splashView;

[navigationController presentModalViewController:modalViewController animated:NO];

[self performSelector:@selector(hideSplash) withObject:nil afterDelay:1.0];

[modalViewController release];

}

//hide splash screen

- (void)hideSplash{

NSLog(@"hide Splash");

[[navigationController modalViewController] dismissModalViewControllerAnimated:YES];

}



- (void)applicationWillResignActive:(UIApplication *)application {

/*

Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.

Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.

*/


}



- (void)applicationDidEnterBackground:(UIApplication *)application {

/*

Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.

If your application supports background execution, called instead of applicationWillTerminate: when the user quits.

*/


}



- (void)applicationWillEnterForeground:(UIApplication *)application {

/*

Called as part of  transition from the background to the inactive state: here you can undo many of the changes made on entering the background.

*/


}



- (void)applicationDidBecomeActive:(UIApplication *)application {

/*

Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

*/


}



- (void)applicationWillTerminate:(UIApplication *)application {

/*

Called when the application is about to terminate.

See also applicationDidEnterBackground:.

*/


}



#pragma mark -

#pragma mark Memory management



- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {

/*

Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.

*/


}



- (void)dealloc {

[navigationController release];

[window release];

[super dealloc];

}



@end

 

Ora che il codice è pronto, andiamo a modificare la View nell’interface Builder.

Apriamo quindi la MainWindow.xib e aggiungiamo una UiView e agganciamola con l’outlet splashView richiesta dal nostro ApplicationDelegate .

Quando verrà eseguito lo showSplash, verrà resa modale questa view per il tempo prefissato.

Ora torniamo su XCode e eseguiamo nel simulatore l’applicazione e vedremo comparire lo splash per un secondo circa e poi la view principale .

Scarica i Sorgenti Tutorial SplashScreen