UIWindow ed UIView
L'elemento base della user interface e' l'oggetto UIWindow. Esso fornisce il supporto per mostrare informazioni ed oggetti all'interno delle nostre applicazioni. UIWindow agisce come una cornice in cui e' possibile inserire contenuti. Sebbene sia l'oggetto principale su cui basarsi per la costruzione di un'interfaccia grafica, non contiene caratteristiche visuali: e' semplicemente un contenitore trasparente che fornisce il punto di contatto a piu' basso livello tra la nostra applicazione e lo schermo iPhone. Tipicamente verra' creato solo un oggetto UIWindow all'interno delle nostre applicazioni; al suo interno potranno essere aggiunti oggetti - derivanti, principalmente, dalla classe UIView - necessari alla corretta visualizzazione della nostra applicazione a schermo.

La classe UIView e' una classe base pensata per realizzare la visualizzazione di oggetti all'interno della window. Se la UIWindow e' stata definita come una cornice, allora la classe UIView potrebbe essere pensata come una tela su cui disegnare. Esistono numerose classi derivate da UIView, utili a creare oggetti visuali, che renderizzano a schermo testo, etichette, immagini e tabelle. La classe base contiene un livello generico di funzionalita' che fornisce logiche di controllo base, metodi di disegno a schermo e risponditori ad eventi. E' possibile utilizzare la classe UIView per creare i propri oggetti e/o per aggiungere al suo interno altri componenti del framework UIKit.



Nella figura sopra e' rappresentata la decomposizione per oggetti dell'applicazione Clock. Window e' l'oggetto base su cui vengono aggiunti gli ulteriori oggetti, necessari al completamento dell'applicazione: UINavigationBar, UITabBar, UIView.

Idealmente, potremmo sintetizzare quanto disegnato sopra con del codice:

[myContainerView addSubview: myNavigationBar];
[myContainerView addSubview: myTabBar];
[myContainerView addSubview: myCustomView];
[myWindow addSubview: myContainerView];



Ovviamente, le cose non sono proprio cosi' semplici, ma l'esempio sopra aiuta a capire come annidare gli oggetti tra loro.






Crezione di una finestra ed una vista
Prima di potere mostrare qualcosa sullo schermo del nostro iPhone, dobbiamo creare una window che ospitera' tutti i contenuti. Per costruire una finestra e' necessario un frame. Un frame e' un'area rettangolare dello schermo dove mostreremo i nostri contenuti. La struttura che contiene queste informazioni si chiama CGRect. Una struttura CGRect contiene due elementi: le coordinate per l'angolo piu' in alto a sinistra della window (origin) e la larghezza ed altezza del frame (size).

struct CGRect {
CGPoint origin;
CGSize size;
};
typedef struct CGRect CGRect;



Ogni oggetto che puo' essere mostrato a schermo ha un frame che definisce la sua area visibile. Quest'ultima, puo' essere impostata automaticamente per alcuni oggetti di alto livello ma, tipicamente, useremo il metodo di inizializzazione denominato initWithFrame. Quando creiamo la finestra principale, creiamo un frame le cui coordinate sono riferite allo schermo stesso. Tutti gli oggetti seguenti, invece, saranno riferiti al proprio oggetto padre.

Ad esempio, il frame di una view aggiunta alla window principale, sara' riferito alle coordinate della window e non allo schermo. Gli oggetti aggiunti alla view, saranno riferiti alle coordinate della view e cosi' via.

Tipicamente, un'applicazione utilizza tutto lo schermo disponibile. Per assegnare alla nostra window principale il frame corretto esistono due metodi.

Il metodo bounds, ritorna gli estremi dello schermo, incluso lo spazio usato dalla status bar:

CGRect screenBounds = [ [ UIScreen mainScreen ] bounds ];



Il metodo applicationFrame, ritorna la porzione di schermo visibile, escluso lo spazio usato dalla status bar:

CGRect screenBounds = [ [ UIScreen mainScreen ] applicationFrame ];



In entrambi i casi, la struttura ritornata viene utilizzata per inizializzare correttamente il nostro oggetto UIWindow:

window = [ [ UIWindow alloc ] initWithFrame: screenBounds ];



Abbiamo appena creato la nostra window principale. Essa, pero', non contiene nulla di utile ai fini di visualizzazione a schermo: e' semplicemente un oggetto invisibile. E' necessario creare un oggetto che possa mostrare contenuti su quanto sopra creato. Abbiamo bisogno, quindi, di un oggetto basato sulla classe UIView.

Come detto sopra, il posizionamento dell'oggetto window e' relativo allo schermo, mentre quello del nostro oggetto view, sara' relativo alla window. Se la window inizia sotto la status bar (0, 20), la view dovra' iniziare dalla posizione (0,0).





Per ottenere questo risultato operiamo una trasposizione di coordinate, rispetto a quelle ottenute dal metodo applicationFrame:

CGRect viewBounds = [ [ UIScreen mainScreen ] applicationFrame ];
viewBounds.origin.y = 0.0;



Useremo queste nuove coordinate per renderizzare a schermo la vista:

UIView *myView = [ [ UIView alloc ] initWithFrame: viewBounds ];



Adesso che la view e la window sono state create correttamente, bisogna occuparsi della visualizzazione a schermo. Per fare questo, aggiungiamo la view alla window utilizzando il metodo di addSubview:.

[ window addSubview: myView ];



L'ultimo passo da effettuare e' quello di mettere in primo piano e visualizzare la window. Il metodo da usare in questo caso e' makeKeyAndVisibile.

[ window makeKeyAndVisibile ];





Un esempio completo
Prima di passare all'uso del modello ViewController, proveremo ad usare quanto appreso per creare un semplice applicativo che illustra le caratteristiche e le funzionalita' sopra esposte. Per prima cosa, apriremo Xcode creando un nuovo progetto:




Scegliamo il template Window-based Application e diamo un nome appropriato al nostro progetto. Ricordiamoci che il nome del progetto sara' anche la prima parte del nome delle nostre classi autogenerate. Dare un nome significativo, quindi, e' quanto mai importante.




Sulla parte di destra, troviamo tutte le risorse ed i files necessari alla corretta compilazione del nostro progetto. Il template, in automatico, genera per noi anche un file di risorse con estensione .xib, contenente tutte le informazioni per la generazione di un interfaccia grafica modificabile attraverso Interface Builder. Visto che stiamo lavorando programmaticamente, non sara' necessario fare uso di questo file. Selezionamo il file e con il tasto destro scegliamo l'opzione Delete.




Dobbiamo effettuare un'ultima importante modifica al template autogenerato. Selezioniamo il file main.m dalla voce Other Sources.




Il file main.m contiene alcune inizializzazioni fondamentali per la nostra applicazione.




Viene creato un NSAutoreleasePool, responsabile di controllare l'autorilascio di tutti gli oggetti allocati in memoria che fanno uso del metodo autorelease.

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
/*
...

Tutti gli oggetti che verranno creati in questo range,
saranno gestiti dall'oggetto NSAutorelase.

...
*/
[pool release];



Il ciclo main, inoltre, invoca la funzione UIApplicationMain. Questa funzione e' responsabile della creazione dell'oggetto applicazione - in seguito vedremo come riferirci a questo particolare oggetto attraverso le keywords UIApplication e sharedApplication - e della configurazione del suo delegato principale.





int UIApplicationMain (
int argc,
char *argv[],
NSString *principalClassName,
NSString *delegateClassName
);



Nella figura sopra e' evidenziata la sintassi della funzione UIApplicationMain; l'argomento di nostro interesse e' il quarto. delegateClassName indica la classe designata come Application Delegate per la nostra applicazione. Per il template autogenerato questo parametro e' impostato a nil perche' quest'assegnazione e' fatta in fase di runtime dal file di risorse .xib. Ricordiamo, pero', che nel passo precedente abbiamo eliminato ogni riferimento ad interface builder. Dovremo, quindi, impostare in maniera programmatica questo delegato.




Impostando l'ultimo parametro della funzione ad una stringa statica, indicante il nome della classe delegata, stiamo legando il nostro ciclo di runtime principale alla classe evidenziata nella figura sotto.




Per creare un delegato per la nostra applicazione, dobbiamo essere sicuri che la classe indicata - ViewBasedAppDelegate - adotti il protocollo richiesto: UIApplicationDelegate.




La sintassi:

@interface ViewBasedAppDelegate: NSObject <UIApplicationDelegate> {
...
...
}



indica che la classe specificata, adottera' il protocollo UIApplicationDelegate. Adottare un protocollo significa implementare tutti, o parte, dei metodi richiesti nella specifica dello stesso. Nel nostro esempio, adottando il protocollo UIApplicationDelegate, implementeremo un solo metodo opzionale: applicationDidFinishLaunching:.




Implementando il metodo applicationDidFinishLaunching: cattureremo il messaggio che il ciclo main dell'applicazione, lancera' alla nostra classe delegata quando tutte le operazioni di inizializzazione saranno complete.

Adottare un protocollo, quindi, significa implementare le azioni/risposte ai messaggi di nostro interesse. In questo caso, adottando il protocollo UIApplicationDelegate ed implementandone il messaggio applicationDidFinishLaunching:, creeremo un'azione di risposta al messaggio lanciato dal ciclo main dell'applicazione, dopo l'inizializzazione della stessa.




Il codice di creazione della window e delle view sara' inserito all'interno di questo metodo, come risposta al messaggio applicationDidFinishLaunching:, lanciato dal ciclo main dell'applicazione.



Inizializzazione e creazione degli elementi base
Come detto, per questo primo esempio, utilizzeremo direttamente il messaggio applicationDidFinishLaunching: per creare e renderizzare a schermo il nostro esempio. Gli oggetti che useremo nell'applicativo vanno dichiarati nell'interfaccia della classe.

Selezioniamo il file di interfaccia - MVC1AppDelegate.h ed inseriamo le dichiarazioni necessarie:






Cominciamo con l'inserire il seguente codice all'interno del metodo designato:




una volta inizializzate la finestra - window - e la view - myView - procediamo con la colorazione del background di quest'ultima.

Vogliamo aggiungere alla nostra applicazione una barra di stato che rifletta gli eventi che si susseguiranno durante l'uso. Per fare questo, aggiungiamo alla view principale un'oggetto UILabel opportunamente configurato.



Come si puo' notare nelle ultime tre righe, dopo il rilascio continuiamo a referenziare la statusBar. Questo e' possibile grazie al concetto di retain/release. Il nostro oggetto, infatti, prima di essere rilasciato e' stato aggiunto alla view principale - addSubiew: -, incrementandone di fatto il contatore di uso. Effettuare un release, quindi, significa decrementare il contatore di uso e demandare la responsabilita' della distruzione dell'oggetto alla view che lo contiene. Quando distruggeremo la view principale, il contatore dell'oggetto statusBar verra' decrementato, raggiungendo il valore di zero ed un conseguente rilascio della memoria impiegata.

La tecnica sopra descritta risultera' utile quando ci troveremo a dialogare con molti oggetti all'interno di una gerarchia di viste; in questo modo, potremo dispensarci dalla deallocazione manuale dei singoli oggetti, demandando tutto all'oggetto padre che li contiene.

Proviamo ad aggiungere due nuovi oggetti alla nostra view. UIButton fornisce i metodi e le caratteristiche base per la creazione e gestione di un bottone all'interno di una view.




button1 viene istanziato attraverso l'uso del metodo di classe buttonWithType:. Questo metodo, prende un tipo predefinito per il nostro bottone ed inizializza l'oggetto. Anche UIButton eredita dalla classe UIView. In particolare, l'oggetto UIButton specializza la classe UIControl che, a sua volta, specializza la classe UIView




Questa particolare struttura, ci permettera' di individuare dei comportamenti (e metodi) comuni a moltissimi oggetti che ereditano dalla classe UIControl.

Prima di tutto, inizializziamo un frame adatto a contenere il nostro bottone. L'uso del metodo setFrame:, abbinato alla funzione CGRectMake(), fornisce il punto di partenza per creare uno spazio a schermo per il nostro oggetto.

Visto che si tratta di un bottone che dovra' rispondere ad eventi di pressione, utilizzeremo un metodo comune a tutti gli oggetti della classe UIControl.

addTarget:(id)target action:(selector)anAction forControlEvents:(UIControlEventsType)type aggiunge, al bottone appena creato, un metodo risponditore - anAction - per l'evento type. Il parametro target, specifica in quale classe dovra' essere cercato il metodo specificato nel parametro action.

Cosi', la riga:

[button1 addTarget:self action:@selector(leftPressed)
              forControlEvents:UIControlEventTouchUpInside];



Aggiunge il supporto per l'evento di pressione ( UIControlEventTouchUpInside ), demandando il controllo ad un metodo chiamato leftPressed, facente parte della classe attuale ( self ).




L'esempio completo e' scaricabile dal link in alto a destra in questa pagina.



Riferimenti

- Mac OS X Reference Library: iPhone Application Programming Guide
- Mac OS X Reference Library: Window and Views
- iPhone SDK Application Development - Capitolo 2
- iPhone SDK Application Development - Capitolo 3



    lap1

downloads