Come molti programmatori usano fare, anch’io mi costruisco le mie librerie di funzioni pronte per essere riutilizzate in più progetti e in più contesti. Lasciatemi passare il titolo di questo post, forse un po’ azzardato ma, tuttavia, come vedremo, non lontano dalla realtà.
In Objective-C è possibile scrivere e chiamare codice C/C++, compreso l’assembly se è per questo. Questa sua caratteristica lo rende un linguaggio davvero versatile e, per certi aspetti, fenomenale. Da un lato, infatti, è possibile utilizzare e apprezzare la sintassi prettamente Objective-C, dall’altro è possibile eseguire velocemente porting di codice scritto in ANSI C (magari per Digital Unix o Sun) e utilizzarlo comodamente nelle nostre applicazioni iPhone o iPad; per non parlare di tutto il Kernel BSD già disponibile su Mac OS X!
Continua...
Come anticipato in iPhone FirstApp: indovina il numero – parte 1 vediamo come realizzare un’applicazione per Apple iPhone senza usare Interface Builder! Anzi, elimineremo fisicamente i file di Interface Builder creati dal wizard di Xcode. Alla fine di questo post, quindi, avremo un’applicazione identica, in tutto e per tutto, a quella realizzata nella parte prima, con la differenza che realizzeremo tutti i nostri componenti visivi, compresa la Window principale, completamente da codice.
L’applicazione già fatta, se volete solo scaricarla, è disponibile sul mio Google Code repository:
Vorrei far notare da subito come lo ZIP di questo esempio pesi meno di quello della scorsa volta!
Creiamo il progetto
Iniziamo creando il nostro progetto SecondApp (per distinguerlo da FirstApp), anche se questa volta sceglieremo Window-based Application:

Adesso andiamo ad eliminare tutto ciò che riguarda Interface Builder. Eliminiamo il file MainWindow.xib, situato nella cartella Resources: eliminatelo anche dal file system, quindi selezionate Also Move to Trash. Selezionate, poi, il file SecondoApp-info.plist ed eliminate il riferimento alla MainWindow nella casella Main nib file base name:

A questo punto non abbiamo più nessuna Window, almeno tramite Interface Builder. Quindi apriamo il file main.m, situato in Other Sources, e modifichiamo la funzione main() in questo modo:
1 2 3 4 5 6 7 8 9 10 11
| int main (int argc, char *argv []) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc ] init ];
// avendo eliminato il file .xib abbiamo perso il puntamento
// all'app delegate, quindi lo passiamo a "mano"
int retVal = UIApplicationMain (argc, argv, nil, @"SecondAppAppDelegate");
[pool release ];
return retVal;
} |
Apriamo quindi SecondAppAppDelegate.m e creiamo la Window principale via codice:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| - (void)applicationDidFinishLaunching:(UIApplication *)application {
// ottiene le dimensioni delle schermo (320, 480)
CGRect windowRect = [[UIScreen mainScreen] applicationFrame];
// creiamo una finestra - visto che abbiamo fatto bye bye a Interface Builder
UIWindow *mainWindow = [[UIWindow alloc] initWithFrame:windowRect];
// impostiamo il background della Window a giallo, per differenziarlo
// dalla precedente applicazione FirstApp
[mainWindow setBackgroundColor:[UIColor yellowColor]];
[self setWindow:mainWindow];
[window makeKeyAndVisible];
[mainWindow release];
} |
Potete già provare l’applicazione; se appare una window gialla avete fatto tutto bene!
Nel file SecondAppAppDelegate.h possiamo eliminare IBOutlet, necessario solo se si usa Interface Builder. Inoltre aggiungiamo qui le nostre variabili globali che, nella scorsa volta, avevamo inserito nel Controller. Modifichiamo quindi il file SecondAppAppDelegate.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #import <UIKit/UIKit.h>
@interface SecondAppAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UITextField *numero;
UIButton *bottone;
int numeroACaso;
}
@property (nonatomic, retain ) UIWindow *window;
- (void)controllaNumero;
@end |
Anche in questo caso abbiamo preparato la definizione del metodo controllaNumero, come la scorsa volta, ma abbiamo eliminato l’indicazione IBAction, in quanto non stiamo usando Interface Builder.
Costruiamo l’interfaccia da codice
E’ giunto il momento di creare tramite codice tutti i componenti della nostra interfaccia. Torniamo nel file SecondAppAppDelegate.m, posizioniamoci prima del [mainWindow release] ed inseriamo il seguente codice:
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
| // crea la barra del titolo
UINavigationBar *myNavigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)];
myNavigationBar.barStyle = UIBarStyleDefault;
UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Indovina un numero"];
[myNavigationBar pushNavigationItem:navigationItem animated:NO];
[window addSubview:myNavigationBar];
// crea la label
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 50, 300, 80)];
myLabel.backgroundColor = [UIColor clearColor];
myLabel.numberOfLines = 2;
myLabel.text = @"iPhone ha pensato un numero da 1 a 10, prova ad indovinarlo?";
[window addSubview:myLabel];
// crea il text input
numero = [[UITextField alloc] initWithFrame:CGRectMake(10, 120, 300, 30)];
numero.borderStyle = UITextBorderStyleRoundedRect;
numero.textAlignment = UITextAlignmentCenter;
numero.keyboardType = UIKeyboardTypeNumberPad;
numero.placeholder = @"Inserisci il numero";
[window addSubview:numero];
// crea il bottone
bottone = [UIButton buttonWithType:UIButtonTypeRoundedRect];
bottone.frame = CGRectMake(10, 180, 300, 30);
[bottone setTitle:@"Premi qui" forState:UIControlStateNormal];
[bottone addTarget:self action:@selector(controllaNumero) forControlEvents:UIControlEventTouchUpInside];
[window addSubview:bottone];
[myLabel release];
[navigationItem release];
[myNavigationBar release]; |
Dato che l’applicationDidFinishLaunching corrisponde alla viewDidLoad della scorsa volta, subito dopo il [mainWindow release]; inseriamo:
1
| numeroACaso = 1 + arc4random() % 10; |
Ora non ci rimane che implementare il metodo controllaNumero, che sarà identico (a parte il prototipo) a quello usato la scorsa volta:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| - (void) controllaNumero {
NSLog (@"Premuto bottone di controlla numero");
int numeroInserito = [numero.text integerValue ];
NSString *message;
NSLog (@"Il numero inserito è %d", numeroInserito );
if( numeroInserito <numeroACaso ) {
message = @"Troppo basso...";
} else if (numeroInserito> numeroACaso ) {
message = @"Troppo alto...";
} else if (numeroInserito == numeroACaso ) {
message = @"Bravo hai indovinato";
numeroACaso = 1 + arc4random () % 10;
NSLog (@"Numero pensato %d", numeroACaso );
}
UIAlertView *alertMessaggio = [[UIAlertView alloc ]
initWithTitle :@"Responso"
message : message
delegate :nil
cancelButtonTitle :@"OK"
otherButtonTitles :nil];
[alertMessaggio show ];
[alertMessaggio release ];
numero.text = @"";
} |
Abbiamo finito!
Conclusioni e considerazioni
Questo esempio non fa uso diretto di una UIView o di un UIViewController, proprio perchè volevo lasciarlo il più semplice e snello possibile e, anche, per dimostrare che non sono elementi sempre necessari. Tuttavia inserire gli oggetti direttamente nella window può avere un qualche senso in questo esempio ed in altri sporadici contesti. L’uso delle UIView e dei UIViewController portano comunque benefici in tantissimi altri casi, ein alcuni sono praticamente indispensabili; come avremo modo di vedere in futuro.
Continua...
Il primo sorgente che mi è passato tra le mani era scritto in Basic e consisteva in poche righe di codice, per me illuminanti. Era un semplice giochino che generava un numero casuale da 1 a 10 e, tramite l’input da tastiera, verificava che il numero inserito era maggione, minore o uguale al numero casuale. Nonostante la sua rozza semplicità rimane, per me, uno dei migliori esempi – semplici, divertenti e pratici – per spiegare a chi non sa nulla di programmazione cosa s’intenda effettivamente per “programma per computer”. Così ho pensato di riproporlo per Apple iPhone, magari aiuterà qualcuno…
Continua...
La classe UIAletrView permette di mostrare una finestra modale (animata) per informare e/o chiedere qualcosa all’utente. Le UIAlterView (viste alert) sono molto comode e semplici da usare. Anche la gestione della risposta utente viene risolta intercettando l’indice del bottone premuto. Per visualizzare un semplice UIAlterView basta scrivere:
1 2 3 4
| // semplice Alert con un bottone, senza callback
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:@"Titolo" message:@"Messaggio" cancelButtonTitle:@"Annulla" otherButtonTitles: nil];
[myAlert show];
[myAlert release]; |
E’ possibile aggiungere ulteriori bottoni in otherButtonTitles:
1 2 3 4
| // semplice Alert con due bottoni, senza callback
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:@"Titolo" message:@"Messaggio" cancelButtonTitle:@"Annulla" otherButtonTitles:@"Ok", nil];
[myAlert show];
[myAlert release]; |
Per determinare quale bottone è stato premuto, basta aggiungere la delegate:self nell’inizializzazione dell’oggetto UIAlterView:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // semplice Alert con due bottoni e callback
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:@"Titolo" message:@"Messaggio" delegate:self cancelButtonTitle:@"Annulla" otherButtonTitles:@"Ok", nil];
[myAlert show];
[myAlert release];
// la callback ha questa sintassi
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
switch ( buttonIndex ) {
case 1:
// codice
break;
}
} |
La callback alertView deve avere necessariamente quel nome; per maggiori informazioni vedi UIAlertViewDelegate
Avendo a disposizione solo la callbck alertView:clickedButtonAtIndex, più UIAletrView punteranno sempre a quella. Utilizzando il campo tag dell’oggetto UIAletrView, possiamo determinare quale sia il “chiamante”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| // semplice Alert con due bottoni e callback
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:@"Titolo" message:@"Messaggio" delegate:self cancelButtonTitle:@"Annulla" otherButtonTitles:@"Ok", nil];
myAlertA.tag = 1;
[myAlertA show];
[myAlertA release];
//
UIAlertView *myAlertB = [[UIAlertView alloc] initWithTitle:@"Titolo" message:@"Messaggio" delegate:self cancelButtonTitle:@"Annulla" otherButtonTitles:@"Ok", nil];
myAlertA.tag = 2;
[myAlertB show];
[myAlertB release];
// gestione multipla UIAlertView
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(@"Alert Tag: %i", alertView.tag);
NSLog(@"buttonIndex: %i", buttonIndex);
} |
Continua...
La sintassi con le parentesi quadre in Objective-C è uno dei motivi di maggior frustazioni per chi proviene da altri linguaggi (ad oggetti). Tuttavia, fatta l’abitudine, ci si rende conto di quanto il codice diventi ugualmente leggibile con questa il mix della sintassi classica e quella più spiccatamente Smalltalk. Ad esempio vediamo come la stessa procedura può essere scritta in entrambe le sintassi. Prendiamo l’inizializzazione di un UIAlertView:
1 2 3 4 5 6 7 8
| UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:@"Titolo"
message:@"Messaggio"
delegate:self
cancelButtonTitle:@"Annulla"
otherButtonTitles:@"Ok",nil];
myAlert.tag = 1;
[myAlert show];
[myAlert release]; |
L’impostazione del tag potavamo farla anche così:
1 2 3 4 5 6 7 8
| UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:@"Titolo"
message:@"Messaggio"
delegate:self
cancelButtonTitle:@"Annulla"
otherButtonTitles:@"Ok",nil];
[myAlert setTag:1];
[myAlert show];
[myAlert release]; |
Stessa cosa in lettura durante l’evento:
1 2 3 4 5
| - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if( alertView.tag == 1 ) { // ...
// oppure
if ([alertView tag] == 1) { // ...
} |
Nota bene: non tutte le proprietà o metodi hanno questa duplice sintassi. Nel caso dell’UIAlertView abbiamo questa doppia funzionalità. Per altri oggetti o in altri casi, invece, potrebbero funzionare una sola delle due… ma non chiedetevi perchè (dipende da come gli sviluppatori hanno definito l’interfaccia).
Continua...
Tutti gli oggetti che derivano dalla classe <a target="_blank" href="http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/cl/UIView">UIView</a>, ereditano l’utilissima proprietà <a target="_blank" href="http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/doc/uid/TP40006816-CH3-SW25">tag</a>. Questa proprietà è un vero e proprio user-data (un “posto” che lo sviluppatore usa per usi generici) di tipo <a target="_blank" href="http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_DataTypes/Reference/reference.html#//apple_ref/doc/c_ref/NSInteger">NSInteger</a>, dove quindi possiamo memorizzare esclusivamente numeri.
L’uso che se ne può fare dipende ovviamente dalle circostanze tuttavia risulta utilissimo per individuare un determinato oggetto allo scattare di un evento comune. Ad esempio immaginiamo di avere due <a target="_blank" href="http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIAlertView_Class/UIAlertView/UIAlertView.html">UIAlertView</a> che rispondono allo stesso evento:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| // primo alert
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Primo" message:@"Primo Alert" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil,nil];
[alert show];
[alert release];
// ...
// secondo alert
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Secondo" message:@"Secondo Alert" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil,nil];
[alert show];
[alert release];
// Evento
// Cliccandi sul bottone OK degli Alter verrà chiamata questa funzione
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
// todo
} |
Nota: negli esempi di codice troverete l’indicazione C++. Questo non è del tutto corretto in quanto, come saprete, il linguaggio usato è Objective-C. Questo a causa del fatto che il Plugin che uso per visualizzare il codice sorgente non supporta Objective-C, e il C++ è quello che meglio gli si avvicina.
Come distinguere i due Alert? Utilizzando appunto la proprietà tag. Dopo la creazione dell’oggetto alert basta inserire:
1 2 3 4 5 6 7 8 9 10 11 12 13
| // primo alert
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Primo" message:@"Primo Alert" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil,nil];
[alert setTag:1]; // imposto il tag di questo Alert ad 1
[alert show];
[alert release];
// ...
// secondo alert
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Secondo" message:@"Secondo Alert" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil,nil];
[alert setTag:2]; // imposto il tag di questo Alert ad 2
[alert show];
[alert release]; |
Ora modifichiamo l’evento in modo da capire quale Alert è stato chiuso:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // Evento
// Cliccandi sul bottone OK degli Alter verrà chiamata questa funzione
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
switch (alertView.tag) {
case 1:
// Primo Alert
break;
case 2:
// Secondo Alert
break;
default:
break;
}
} |
Stessa identica tecnica può essere utilizzata se abbiamo una serie di <a target="_blank" href="http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIButton_Class/UIButton/UIButton.html">UIButton</a> creati runtime. Ad esempio:
1 2 3 4 5 6 7 8 9 10
| for( unsigned int i=0; i < 10; i++) {
UIButton *mybutton = [[UIButton buttonWithType:UIButtonTypeCustom] initWithFrame:CGRectMake(i*20, i*20, 20, 20)];
[mybutton setTitle:@"But" forState:UIControlStateNormal];
[mybutton setTag:i]; // imposto il tag
// stesso evento per tutti
[mybutton addTarget:self action:@selector(onTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:mybutton];
} |
Nell’evento onTouchUpInside recuperiamo dal sender (dove eseguiamo un casting <a target="_blank" href="http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/cl/UIView">UIView</a>) la proprietà <a target="_blank" href="http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/doc/uid/TP40006816-CH3-SW25">tag</a>:
1 2 3 4
| -(void)onTouchUpInside:(id)sender {
unsigned int button_tag = ((UIView *)sender).tag;
// todo
} |
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...