In questo ultimo periodo ho lavorato su tre applicazioni per Apple iPhone, praticamente in contemporanea. Due sono state già approvate da Apple e si trovano su App Store: TheFirst e iMakeLove.

In entrambe le applicazioni ho adottato l’uso di un oggetto UIWebView per visualizzare le istruzioni.
L’oggetto UIWebView si è ilevato davvero comodo in questo caso. Mi ha permesso di visualizzare in modo rapido e gradevole una testo, corredandolo in caso di immagini e qualsiasi altra cosa sia visualizzabile su una pagina Web (Flash escluso su iPhone).

L’oggetto UIWebView è davvero versatile e necessita di pochissime impostazioni per essere utilizzato. Unico difetto riscontrato è la presenza di una misteriosa ombra che appare quando si effettua uno scroll fuori dall’area del controllo, sia in basso che in alto:

Dopo varie ricerche ho capito che questo comportamento dipende dalle SDK, in particolare dagli ultimi aggiornamenti. Inoltre Apple non ha rilasciato nessuna dichiarazione ufficiale su come rimuoverlo, al contrario ha bollato come codice non permesso una serie di hack che fanno uso di funzioni riservate o non documentante. Alla fine avevo lasciato perdere, sia per questioni di tempo, sia perchè le soluzioni sembravano tutte fuori dagli schemi Apple.
Fortunatamente mi è arrivata la soluzione, spero abbastanza regolare, che sembra proprio risolvere il problema (grazie ad Adolfo):
1 2 3 4 5 6
| // webView è il controllo UIWebView
id scroller = [webView.subviews objectAtIndex:0];
for (UIView *subView in [scroller subviews])
if ([[[subView class] description] isEqualToString:@"UIImageView"])
subView.hidden = YES; |
Come sottolineato da Adolfo questo è comunque un hack che potrebbe smettere di funzionare con futuri rilasci delle SDK.
Continua...
Dopo aver illustrato come localizzare le nostre stringhe in Xcode, vediamo adesso quanto risulta semplice – applicando la medesima tecnica – localizzare immagini e viste/interfacce realizzate con Interface Builder.
Localizzare risorse grafiche
Il processo, come accennato, è il medesimo; se abbiamo un’immagine già inserita nelle nostre risorse, o ne inseriamo una nuova, e vogliamo “localizzarla” – cioè gestire due o più immagini in base alle lingue supportate – basterà cliccare con il tasto destro sull’immagine (Adium.png in questo esempio) e selezionare Get Info:

Clicchiamo Make File Localizable in basso a sinistra.

Clicchiamo su Add Localization e inseriamo Italian:

In modo da ottenere:

La nostra immagine viene spostata (fisicamente, una delle rare volte in cui quello che accade nell’alberatura Xcode si riflette sul filesystem) sotto le cartelle virtuali English.lproj e Italian.lproj esattamente come accadeva con il testo:

A questo punto ognuna delle cartelle English.lproj e Italian.lproj contiene una stessa versione dell’immagine. Questa immagine è manipolabile all’interno di Interface Builder, dove ne vedremo – per default – la versione inglese.
Arrivati a questo punto, basta sovrascrivere uno (o entrambi i file Adium.png) per ottenere una localizzazione delle immagini “lampo”.
Localizzare i file XIB
Anche le interfacce costruite con Interface Builder possono essere localizzate nella loro interezza, quando lo si ritiene necessario. Il procedimento è identico a quello svolto con le risorse grafiche: aggiungiamo un ViewController (ad esempio infoViewController) con interfaccia XIB, lo selezioniamo, scegliamo Get Info dal menu contestuale, rendiamo il file localizzabile, aggiungiamo la localizzazione in italiano:

Cliccando su English o su Italian si aprirà Interface Builder! Questa volta, sul filesystem, noteremo che sono state create due cartelle (English.lproj e Italian.lproj) all’interno della classica cartella Classes. Entrambe avranno il loro file infoViewController.xib. La comodità, in questo, risolta evidente nel codice; quando andremo ad istanziare il nostro controller si avrà un codice “pulito” di questo tipo:
1 2
| InfoViewController *info = [InfoViewController alloc];
[self.view addSubview:info.view]; |
Come vedete non c’è traccia di nessuna dichiarazione relativa alla localizzazione, totalmente gestita da sistema. Le due interfacce, ovviamente, possono essere completamente differenti, in quanto risultano a tutti gli effetti come due file XIB separati.
Continua...
Eseguire un metodo dopo n secondi
Tutta la famiglia performSelector è davvero interessante e può essere utile in una moltitudine di casi. La sua applicazione più semplice e comune è la seguente:
1 2 3 4 5
| [self performSelector:@selector(myMethod) withObject:nil afterDelay:3];
//
- (void)myMethod {
NSLog(@"Hello World!");
} |
Tuttavia considerate che il “timer” non è preciso. Questa procedura, quindi, va usata quando non è richiesta una “notevole” precisione temporale.
Recuperare la versione dell’applicazione
1 2
| NSString *version = [[[NSBundle mainBundle ] infoDictionary ] objectForKey :@"CFBundleVersion"];
NSLog (@"versione = %@", version ); |
YES, true o TRUE?
Andando a spulciare nel Kernel Apple iPhone è possibile renderci conto che YES, true e TRUE sono in pratica la stessa edentica cosa:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| // definizione di YES
#define YES (BOOL)1
#define NO (BOOL)0
// definizione di true
#define true 1
#define false 0
// definizione di TRUE
#if !defined(TRUE)
#define TRUE 1
#endif
#if !defined(FALSE)
#define FALSE 0
#endif |
Almeno per adesso…
Vibrazione
1 2 3
| #import <AudioToolbox/AudioToolbox.h>
//
AudioServicesPlaySystemSound (kSystemSoundID_Vibrate); |
Puntatore CGImageRef a partire da un UIImage
1 2 3 4
| UIImage *heart = [UIImage imageNamed:@"LittleHeart.png"];
CGImageRef image = [heart CGImage];
// L'immagine adesso può essere "rasterizzata" su un CGContextRef
CGContextDrawImage(c, (CGRect){0, 0, 100, 100}, image); |
Animazioni
1 2 3 4 5
| [UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
// ...
[UIView commitAnimations]; |
NSLog
1 2 3
| NSLog(@"NSString object %@ ", myString);
NSLog(@"Float: %f ", myFloat);
NSLog(@"Integer: %i ", myInt); |
Convertitore da RGB a UIColor
1
| #define RGBA(r,g,b,a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a] |
Passare parametri ad un NSTimer
Sfruttando il parametro userInfo è possibile inviare un puntatore ad un nostro oggetto al metodo richiamato da timer.
1 2 3 4 5 6 7 8 9 10 11 12 13
| [NSTimer scheduledTimerWithTimeInterval :1 target :self selector :@selector(timerMethod ) userInfo :objectPointer repeats :YES];
// ...
-(void)timerMethod :(NSTimer*)timer {
// Recupero il puntatore al mio oggetto
objectPointer = [timer userInfo ];
// oppure
[[timer userInfo ] myMethod ];
int a = [[timer userInfo ] myProperty ];
// che è lo stesso
int a = [objectPointer myProperty ];
} |
Tempo di esecuzione
Ecco un semplice modo per calcolare tempi brevi utili per verificare la velocità di esecuzione del codice:
1 2 3 4
| CFAbsoluteTime initialTime = CFAbsoluteTimeGetCurrent();
// ... code
CFAbsoluteTime finalTime = CFAbsoluteTimeGetCurrent();
NSLog(@"Tempo trascorso %f", finalTime-initialTime); |
Continua...
Gli stati UIControlStateSelected o UIControlStateHighlighted non funzionano quando un UIButton è impostato in modalità UIButtonTypeCustom! O meglio, non funzionano come dovrebbero (perchè riservati agli altri tipi di bottone), ad esempio per creare un bottone a due stati: toggle appunto. Se abbiamo creato due immagini (stato1.png e stato2.png) per il nostro bottone, possiamo procedere in questo modo:
1 2 3
| // Nell'header file creiamo una variabile globale che usaremo per
// controllare il toggle state
BOOL toggleFlag; |
Ora creiamo il nostro bottone:
1 2 3 4 5 6 7 8 9
| // Creaiamo un bottone e lo poniamo inizialmente nello stato "stato1.png"
// Modificate initWithFrame:(CGRect){100,100,50,50} con la posizione e
// dimensioni della vostra immmagine
toggleFlag = YES;
UIButton *toggleButton = [[UIButton buttonWithType:UIButtonTypeCustom] initWithFrame:(CGRect){100,100,50,50}];
[toggleButton setTitle:@"" forState:UIControlStateNormal];
[toggleButton setBackgroundImage:[UIImage imageNamed:@"stato1.png"] forState:UIControlStateNormal];
[toggleButton addTarget:self action:@selector(onToggle:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:toggleButton]; |
Quando si clicca sul bottone verrà inviato un messaggio gestito da onToggle:
1 2 3 4 5 6 7
| - (void)onToggle:(id)sender {
// Recupero puntatore al UIButton
UIButton *buttonClicked = (UIButton *)sender;
// Eseguo il toogle
toggleFlag = !toggleFlag;
[buttonClicked setBackgroundImage:[UIImage imageNamed:(toggleFlag) ? @"stato1.png" : @"stato2.png"] forState:UIControlStateNormal];
} |
Continua...
Un’alternativa davvero semplice per eseguire uno streaming di un file mp3 su Apple iPhone potrebbe essere:
Continua...
1. Stringhe su più righe
In Xcode è possibile “spezzare” un stringa su più righe inserendo a alla fine un backslash “\”. Questa caratteristica può risultare utilissima quando, ad esempio, vogliamo inserire del testo HTML in un controllo UIWebView:
Continua...
Il controllo UIDatePicker è forse il più bel controllo grafico presente su Apple iPhone. Oltre alla bellezza risulta anche semplice da utilizzare, versatile ed estremamente utilizzato in moltissime situazioni.
Continua...
La sintassi NSLog(@"%@", ... ); funziona ed è utilizzata per ottenere informazioni sugli oggetti, ma non funziona su tipi dato C come struct CGRect o CGPoint, ad esempio. Per poter sfruttare NSLog(@"%@", ... ); anche su struct di tipo C possiamo appoggiarci a funzioni di conversione come NSStringFromCGRect() o NSStringFromCGPoint:
1 2 3 4 5
| CGrect mioRect = (CGRect){10,20,30,40};
CGPoint mioPoint = (CGPoint){32,64};
//
NSLog( @"Info rettangolo: %@", NSStringFromCGRect(mioRect) );
NSLog( @"Info point: %@", NSStringFromCGPoint(mioPoint) ); |
Nello specifico è possibile perfezionare tale procedure scrivendosi delle piccole macro utili come:
1
| #define NSLogRect(rect) NSLog(@"%s: (%0.0f, %0.0f) %0.0f x %0.0f", #rect, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height) |
Oppure:
1 2 3 4
| #define NSLogCGPoint(point) NSLog(@"%s: (%0.0f, %0.0f)", #point point.x, point.y)
CGPoint mioPoint = (CGPoint){32,64};
NSLogCGPoint(mioPoint); |
Che darà come output:
Continua...
Icona applicazione
L’icona 57×57 pixel che andrà a rappresentare la nostra applicazione viene “alterata” automaticamente dall’Apple iPhone: viene aggiunto un bordo arrotondato, un effetto luminoso e 3D. Questa impostazione può essere cambiata selezionando il file [nome applicazione]-Info.plist e aggiungendo la property “Icon already includes gloss and bevel effects”:
Continua...
Se avete bisogno di generare numeri casuali in una applicazione Apple iPhone dovete mettere da parte Objective-C, in quanto non propone nessuna classe allo scopo. La soluzione viene dal C che propone: rand(), srand(), random(), srandom() e arc4random().
Continua...
Ultimi Commenti
Marco: Ti ringrazio moltissimo, mi hai illuminato
ho risolto impostando [cc_objc] //OptionViewController.m -...
Giovambattista Fazioli: @Marco: Ti consiglio un approccio credo più corretto. Se hai eseguito il subclass del tab...
Marco: Scusa lo spam.. ho notato che c’è un errore.. ecco la correzione [cc_objc] /** PrimaClasse.h **/ #import...
Marco: dimenticato.. in [cci]OptionViewController[/cci ] il [cci]@syntetize[/cci] del delegato l’ho messo
luigi: molto chiaro e semplice devo ammettere che anche scrivendo da un pà difficilmente uso delegati creati da...