Page 1


SOMMARIO

Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix CAPITOLO 1

HELLO iPHONE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Un’introduzione a iOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Primi passi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Creazione del Workspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Creazione del progetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Diamo un’occhiata in giro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Esecuzione dell’applicazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Esame dei file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Un tour del progetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Modifica del template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Modifica della Main View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Aggiunta di un outlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 Aggiunta del Text Field . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Raffinare l’interfaccia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Riepilogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

CAPITOLO 2

OBJECTIVE-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 Il linguaggio Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Nove elementi fondamentali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Tipi di dati del C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Strutture dati C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 Enumerazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Operatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Funzioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Oggetti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Metodi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Protocolli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Categorie e estensioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Gestione della memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Oggetti e conteggi retain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Introduzione a ARC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Design pattern importanti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Model-View-Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Delegate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Notifiche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Codifica chiave-valore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

SOMMARIO

VII


Key-Value Observing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 Blocchi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 Riepilogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

VIII

SOMMARIO

CAPITOLO 3

ARCHITETTURA DI UNA APPLICAZIONE DI PRODUTTIVITÀ . . . . 92 Comprendere le app di produttività . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Creazione del progetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 Pulizie inziali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Impostazione di ulteriori warning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Aggiunta di immagini . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 Configurazione della tab bar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 Realizzazione del model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 La classe WeightEntry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 La classe WeightHistory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Collegare il model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 Riepilogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

CAPITOLO 4

SVILUPPO DI VIEW E DI VIEW CONTROLLER . . . . . . . . . . . . . . . . . 132 Inserimento dei dati del peso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 Impostazione dell’autorotazione e dell’autodimensionamento . . . . . 135 Aggiunta di outlet e di action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 Creazione del pulsante delle unità. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 Modifica delle unità di peso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 Definizione del delegate della view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 Implementazione del controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 Scambio dei dati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 Arrotondamento degli angoli con Core Animation . . . . . . . . . . . . . . . . . . 159 Mostrare l’history del peso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 Rispondere alle modifiche nel model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 Modifica della history view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 Mostrare le view di dettaglio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 Progettazione della table view statica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 Riepilogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183

CAPITOLO 5

DISEGNARE VIEW PERSONALIZZATE . . . . . . . . . . . . . . . . . . . . . . . . . 184 Realizzazione di GraphStats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 Realizzazione di una view personalizzata . . . . . . . . . . . . . . . . . . . . . . . . . . 190 Esecuzione del disegno personalizzato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 Disegno di una linea di andamento con una singola registrazione . 202 Disegno della linea di andamento completa . . . . . . . . . . . . . . . . . . . . . . . . 206


Disegno delle linee di riferimento e delle etichette . . . . . . . . . . . . . . . . . . 208 Calcolo delle coordinate di una registrazione del peso . . . . . . . . . . . . . . 210 Terminare il controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 Riepilogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 CAPITOLO 6

CARICAMENTO E SALVATAGGIO DEI DATI . . . . . . . . . . . . . . . . . . . 216 Il file system di iOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 Generazione di percorsi di directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 Utilizzo dei path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 Gestione delle preferenze utente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 Salvataggio su iCloud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 iCloud Document Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 iCloud Key-Value Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 Salvataggio dello stato di Health Beat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 Preparazione dell’applicazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 Creazione di una sottoclasse UIDocument . . . . . . . . . . . . . . . . . . . . . . . . . . . 234 Caricamento di documenti iCloud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 Altre modifiche allo stato del documento . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 Salvataggio dei default dell’utente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 Implementazione di storage iCloud chiave-valore . . . . . . . . . . . . . . . . . . . 281 Aggiunta del supporto alle impostazioni di sistema . . . . . . . . . . . . . . . . . 283 Abilitazione del supporto undo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286 Riepilogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289

CAPITOLO 7

CORE DATA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290 Introduzione a Core Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292 Panoramica dell’architettura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292 Un modello di oggetti gestiti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292 Contesto degli oggetti managed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300 Persistent Store Coordinator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308 Supporto iCloud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 Prestazioni di Core Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 Conversione di Health Beat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 Creazione di UIManagedDocument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 Creazione del Managed Object Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 Aggiornamento dei view controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329 Riepilogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345

CAPITOLO 8

PROGETTAZIONE DI CONTROLLI PERSONALIZZATI . . . . . . . . . . . 346 Introduzione a GravityScribbler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348 Personalizzazione dell’aspetto dell’interfaccia . . . . . . . . . . . . . . . . . . . . . 349

SOMMARIO

IX


Separazione delle view dinamiche e statiche . . . . . . . . . . . . . . . . . . . . . . . 349 Creazione di un container UIViewController . . . . . . . . . . . . . . . . . . . . . . . . 357 Personalizzazione dei controlli UIKit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372 Rispondere all’input dell’utente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378 Riconoscitori di gesture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 Core Motion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388 Esportazione delle immagini . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 Salvataggio nella Photo Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396 Invio di messaggi MMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405 Invio di allegati email . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405 Invio di messaggi utilizzando la API per Twitter . . . . . . . . . . . . . . . . . . . 409 Riepilogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413 CAPITOLO 9

L’ULTIMO MIGLIO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414 Ritocchi finali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 Elementi grafici dell’applicazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 Funzionalità richieste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418 Target del deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419 Localizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421 Accessibilità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422 File Sharing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422 Generazione per la distribuzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424 Invio all’App Store . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424 Riepilogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425

APPENDICE

DA IPHONE A IPAD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426 Introduzione all’iPad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428 Estensioni iPad all’SDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429 Opzioni per il supporto di più dispositivi . . . . . . . . . . . . . . . . . . . . . . . . . . . 432 Health Beat universale. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435 Impostazione dello storyboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436 Collegamento dei dati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 Correzione dei bug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443 Riepilogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444

Indice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445

X

SOMMARIO


1 HELLO iPHONE


Prima di poter padroneggiaree qualsiasi qu ualsiassi abilità, prima bisogna acquisire acq quisire familiarità con gli strumenti. In n questo capitolo, apprenderemo comee ut utilizzare tilizzare Xcode, il principale strumento to di d sviluppo per applicazioni iOS. Partiremo o utilizzando u Xcode per creare un nuovo progetto o a partire dal modello di progetto Utilityy Application. A pplication.. Poi esamineremo i file prodotti otti da questo modello, studiando come si integrano i vari tasselli di una tipica applicazione iOS. Una volta acquisita familiarità con il modello, lo espanderemo, utilizzando l’editor per l’interfaccia grafica di Xcode per aggiungere controlli e per stabilire connessioni tra questi controlli e le relative azioni. Questo esercizio fornirà una solida base per lavorare sui progetti più complessi dei successivi capitoli.

3


UN’INTRODUZIONE A iOS Se è la prima volta che vi avvicinate allo sviluppo per iOS, benvenuti in un entusiasmante nuovo mondo. La linea di prodotti iOS prevede diversi ottimi dispositivi da esplorare. iPhone, in particolare, riunisce un ampio ventaglio di entusiasmanti tecnologie, compreso Internet sempre attivo, geolocazione, sensori di movimento e una fotocamera. In un passato non così distante, gli sviluppatori dovevano o costruire hardware personalizzato o pagare decine di migliaia di dollari solo per sperimentare con queste tecnologie. Ora potete mettervi l’intero pacchetto in tasca e potarvelo dietro dovunque andiate. L’hardware di iPhone spalanca opportunità precedentemente inimmaginabili. Rivoluzionarie nuove applicazioni di social networking guidano la carica, e applicazioni di geoposizionamento sono diventate una parte crescente del nostro stile di vita digitale. Anche la realtà aumentata, un tempo poco più di una trovata, sta maturando in uno strumento utile (o almeno in una divertente fonte di intrattenimento). Soprattutto, non c’è alcun segno che questa ventata di innovazione rallenterà in un prossimo futuro. Abbiamo appena iniziato a scalfire la superficie di ciò che questi dispositivi possono fare; le migliori idee devono ancora venire. Nel frattempo, Apple ha creato un ricco e vibrante mercato per le nostre applicazioni. Alla World Wide Developer’s Conference del 2011, Steve Jobs annunciò che Apple aveva venduto oltre 200 milioni di dispositivi iOS, con oltre 225 milioni di clienti iTunes registrati, ciascuno dei quali a un solo clic di distanza. Questi clienti hanno scaricato oltre 14 miliardi di app finora, il che ha come risultato oltre $2.5 miliardi pagati agli sviluppatori iOS negli ultimi tre anni. Ciò fornisce entusiasmanti opportunità per grossi e anche per piccoli team di sviluppo. Per le grandi aziende, apre un canale specializzato e specifico per interagire con i propri clienti. Un’applicazione ben costruita, fortemente orientata, non solo migliora le esperienze dei propri clienti, ma diventa un potente strumento di relazioni pubbliche. La vostra app aiuterà a generare una fidelizzazione del brand con i clienti esistenti, pur migliorando la consapevolezza del brand tra i clienti potenziali. Dall’altra parte dello spettro, App Store rende molto più facile ai team di sviluppo di una o due persone proprorre i propri prodotti a milioni di potenziali clienti. Non è necessario realizzare e manutenere il proprio negozio online. Non è necessario gestire i pagamenti o processare le transazioni delle carte di credito. Apple gestisce tutti questi dettagli per voi. Potete concentrarvi sulla parte che amate del vostro lavoro: realizzare ottime applicazioni. Anche se App Store è come avere un gorilla da 400 chili nella stanza, non lasciate che vi distragga. Non è solo una questione di produzione di software spot commerciale. Un crescente numero di sviluppatori utilizza i dispositivi iOS come piattaforme per diversi progetti personali o didattici. Potete trovare esperimenti basati su iOS di ogni sorta, da quelli di scienze per la scuola media a quelli per i laboratori di robotica. Infine, Apple ci ha fornito un insieme di strumenti di sviluppo di alta qualità. Xcode 4 rappresenta un’importante miglioramento rispetto alle versioni precedenti. In un ambiente di sviluppo integrato completo, con un ampio ventaglio di utility per il testing, l’analisi e il debug del proprio codice. In particolare, l’utility Instruments può monitorare e analizzare l’utilizzo della CPU, l’allocazione della memoria, l’accesso alla rete e ai file, e molto altro ancora. Apple fornisce anche un linguaggio di programmazione di prima qualità con un eccellente insieme di framework. Lo so, lo so… molti nuovi programmatori iOS si rifiutano di apprendere Objective-C, e

4

CAPITOLO 1

HELLO iPHONE


ammetto che la curva di apprendimento può sembrare piuttosto ripida, specialmente quando si fa fatica a muovere i primi passi. Tuttavia, una volta che iniziate a trovarvi a vostro agio con il linguaggio, inizierete rapidamente ad amarlo. A parte il fatto che fa sempre bene apprendere un nuovo linguaggio di programmazione. Ciò vi renderà uno sviluppatore migliore, anche se non lo utilizzate mai per scrivere il codice in produzione. Objective-C è un linguaggio di programmazione dinamico, incredibilmente flessibile. Fornisce diverse caratteristiche che (se opportunamente utilizzate) vi aiuteranno a superare molte difficili problematiche di programmazione. Così pure, il Software Development Kit (SDK) di iOS fornisce un ampio ventaglio di eccellenti framework per aiutarci a costruire le nostre applicazioni. I framework sono uno dei componenti del software più difficili da progettare. In teoria, dovrebbero rendere semplice agli sviluppatori eseguire compiti comuni pur dandoci comunque sufficiente libertà di addentrarci in territori inesplorati. In base a queste metriche, i framework Cocoa Touch sono fra i migliori con cui abbia mai lavorato. In effetti, se vi sembra che dobbiate scrivere molto codice solo per svolgere una comune operazione, quasi certamente state facendo qualcosa di sbagliato. Spero che questo libro fornirà una graduale introduzione al mondo dello sviluppo iOS. Benchè non sia possibile trattare ogni aspetto o esplorare ogni framework, questo libro vi fornirà una solida base su cui basarvi, e gli strumenti e le competenze per proseguire in autonomia. Inoltre, anche se gran parte del libro sembra concentrarsi sullo sviluppo per iPhone, i concetti e le tecniche generalmente si applicano a qualsiasi dispositivo iOS: iPod touch, iPad e iQualunqueSarà il prossimo a venire. Le differenze specifiche verranno osservate man mano che le incontriamo. iPad, in particolare, ha alcuni elementi specifici per l’interfaccia utente, e i problemi di sviluppo specifici a iPad verranno trattati in modo approfondito nell’Appendice . Per ora, saltiamo direttamente al nostro primo progetto. Andiamo a cominciare realizzando una applicazione utility per iPhone che visualizzerà un semplice messaggio di una riga. Non preoccupatevi se non comprendete il codice a prima vista. Tratteremo il linguaggio di programmazione Objective-C in maggior dettaglio nel Capitolo 2, “Objective-C”. Per ora, utilizzate questo esercizio come opportunità per familiarizzare con l’ambiente di sviluppo.

PRIMI PASSI Chiedete a qualsiasi artigiano: se volete far bene un lavoro, dovete avere gli strumenti giusti. Per lo sviluppo iOS, ciò significa disporre di un Macintosh con OS X 10.7 o versione successiva e una copia di Xcode 4.2. Se volete eseguire i programmi su un reale dispositivo iOS, avrete anche necessità di un dispositivo compatibile (iPhone, iPod touch o iPad) e degli opportuni profili sviluppatore/fornitore. Parleremo di più dei profili nel Capitolo 3, “Architettura delle applicazioni di produttività”. Per ora, iniziamo a scaricare l’ultima versione di Xcode dall’App Store per Mac. Si tratta di un download gratuito per chiunque utilizzi OS X Lion ed è di gran lunga il modo più semplice per mantenere aggiornati tutti i propri strumenti di sviluppo. Infine dovrete iscrivervi allo iOS Developer Program per testare la vostra applicazione sui dispositivi iOS o per sottoporla all’iTunes App Store. Per ora, possiamo utilizzare Xcode per costruire e testare l’applicazioni nel simulatore. È un ottimo modo per muovere i primi passi.

PRIMI PASSI

5


Xcode è un ambiente di sviluppo integrato (IDE) concepito in modo specifico per la programmazione sia di Mac OS X sia di iOS. Come suggerisce il nome, un IDE è ben più di semplice editor di testo. È una suite interconnessa di strumenti che vi aiutano a organizzare, modificare, sottoporre a debug e comunque gestire tutte le risorse che faranno parte del proprio programma finale. Attraverso Xcode si può impostare in modo visivo l’interfaccia utente, eseguire il test del programma in un simulatore iOS, eseguire passo per passo il codice un comando per volta, analizzare le prestazioni dell’applicazione, e altro ancora. NOTA: I progetti nel libro sono stati sviluppati utilizzando Xcode 4.2 e iOS SDK 5.0. Le versioni successive varieranno indubbiamente un po’ da quanto mostrato. Le opzioni di menu e i template di progetto possono cambiare. Più raramente, gli aggiornamenti all’SDK possono influenzare come vengono compilati e eseguiti i progetti. Se state utilizzando una versione più recente di Xcode o dell’SDK, tutto funzionerà ancora, ma preparatevi a fare un po’ di indagini esplorative in autonomia.

Una volta scaricato e installato Xcode (sono quasi 4 GB, ciò richiederà un po’ di pazienza), lanciatelo e andiamo a cominciare.

CREAZIONE DEL WORKSPACE Partiremo dalla creazione di un nuovo workspace (ossia uno spazio di lavoro). In Xcode 4, un workspace è un contenitore virtuale per l’organizzazione di progetti correlati. Il workspace contiene gli schemi per la realizzazione e il lancio di questi progetti, unitamente a altri dati pertinenti. Xcode vi permetterà di effettuare ricerche in tutti i file del workspace, e supporterà l’indicizzazione di questi file a livello di workspace. Ciò permette funzionalità come il completamento del codice, saltare a una definizione (Jump to Definition), e il refactoring per lavorare senza difficoltà nell’intero workspace. I workspace ci permettono semplicemente di organizzare i progetti. Non definiscono dove o come i singoli progetti vengono memorizzati. Ciascun workspace può avere un numero qualsiasi di progetti, e ciascun progetto può esistere in qualsiasi numero di workspace. Ciò fornisce agli sviluppatori molta flessibilità nell’organizzazione del proprio lavoro. Si possono anche creare workspace specializzati che si concentrano su un particolare compito. Ad esempio, si può creare un workspace per gli unit test, un altro per il debug, e un altro ancora per il test delle prestazioni. Non è necessario creare un workspace; si può semplicemente partire con un progetto autonomo. Nondimeno, farlo è un buon modo per mantenere tutto ordinato. Andiamo a creare un workspace che conterrà tutti i progetti di questo libro. In Xcode, selezionate File > New > New Workspace. Denominate il workspace iOS Development, scegliete un percorso e cliccate Save. Xcode aprirà una finestra che mostra un workspace vuoto: nessun file, nessun editor e nessuno schema (Figura 1.1).

CREAZIONE DEL PROGETTO Con il workspace aperto, create un nuovo progetto. In Xcode, selezionate File > New > New Project. Ciò farà aprire il foglio dei modelli di progetto (Figura 1.2). Xcode fornisce un ampio ventaglio di template per i nuovi progetti. Nella colonna a sinistra, selezionate iOS > Application. Poi, selezionate l’icona Utility Application e cliccate Next.

6

CAPITOLO 1

HELLO iPHONE


FIGURA 1.1 Il nostro spazio di lavoro vuoto

FIGURA 1.2 Selezione del modello iOS Utility Application

FIGURA 1.3 Selezione delle opzioni del progetto

Ora siamo pronti per scegliere le opzioni del progetto (Figura 1.3). Digitate Hello World nel campo Product Name. L’identificatore dell’azienda (company identifier) dovrà essere una stringa univoca che identifica la vostra azienda. Il più delle volte, utilizzeremo il nome del dominio dell’azienda, con i livelli invertiti. Si parte dal dominio di massimo livello e si procede verso i domini di secondo e terzo livello. Ad esempio, potrei utilizzare il nome di dominio invertito del mio blog: com. freelancemadscience. A seguire, abbiamo il prefisso della classe. Questo prefisso viene aggiunto automaticamente a tutti i nomi di classe generati dal template. Si può utilizzare qualsiasi prefisso si desideri. Basta provare a renderlo univoco (ad es., evitate NS o UI poiché vengono già utilizzati dai framework di Apple). Tipicamente, scelgo un’abbreviazione in base al nome del progetto. In questo caso, inserite HW. Assicuratevi che nel menu pop-up Device Family sia selezionato iPhone e che sia selezionata la casella di selezione Use Storyboard. La casella di selezione Use Core Data dovrà essere deselezionata.

PRIMI PASSI

7


NOTA: Se state scrivendo un framework o una libreria, dovreste utilizzare un prefisso per tutte le vostre classi. Ciò permette a altri sviluppatori di utilizzare il vostro codice senza preoccuparsi dei possibili conflitti di denominazione. E rende anche più facile identificare da dove proviene una classe. Ad esempio, tutte le classi che iniziano con “RGS” provengono dal mio framework RobaGraficaSuperba. Se però state semplicemente scrivendo un’applicazione, i prefissi non sono necessari, e ritengo che spesso rendano il codice più difficile da leggere. Tuttavia, spesso lascio che Xcode prefigga i file autogenerati, giusto per renderli facili da distinguere dalle classi scritte a mano.

Successivamente, assicuratevi che sia selezionata l’opzione Use Automatic Reference Counting. Discuteremo di Automatic Reference Counting nel paragrafo “Introduzione a ARC” nel Capitolo 2. Per ora, basta capire che rende più facile scrivere il codice. Andremo quasi sempre a utilizzare l’Automatic Reference Counting nei nuovi progetti. Infine, tipicamente dovremo includere gli unit test nel progetto, per ora basta lasciare deselezionata anche la casella di selezione Include Unit Tests. Cliccate il pulsante Next. Dobbiamo selezionare un percorso per l’app (Figura 1.4). Selezionate qualsiasi percorso vi torni più comodo. Di solito lo salvo nella stessa cartella del workspace principale. Per default, Xcode creerà una nuova cartella utilizzando il nome del progetto. Potete anche creare un repository git per questo progetto. Come gli unit test, disporre di un sistema di controllo del sorgente è un’ottima idea e dovrebbe essere incluso in tutti i propri progetti. Inizieremo a utilizzare il controllo del sorgente nel Capitolo 3. Tuttavia, per ora, lasciate deselezionata la casella di selezione “Create local git repository for this project”. È tutto. Cliccate Create e Xcode imposterà il progetto.

DIAMO UN’OCCHIATA IN GIRO Le applicazioni utility dovranno fornire un facile accesso a una singola schermata di informazioni, con una vista posteriore per modificare le preferenze. L’app Weather di iPhone è un esempio rappresentativo. La schermata principale mostra semplici previsioni meteo per la settimana successiva (tuttavia si possono sfogliare più città). La vista flipside permette di modificare l’elenco delle città, e anche di passare da Fahrenheit a Celsius. Il modello Utility Application crea lo scheletro di base per questo tipo di applicazione. Il template, di per sé, è una app totalmente funzionante ed è compilabile e eseguibile. Naturalmente, non farà nulla di interessante, ma rimedieremo a questo aspetto tra un attimo. Per ora, andiamo a esaminare ciò che otteniamo gratuitamente.

ESECUZIONE DELL’APPLICAZIONE Per prima cosa, dobbiamo indicare a Xcode di utilizzare il simulatore. Cliccate il pulsante Scheme nella toolbar di Xcode e selezionate iPhone 5.0 Simulator (Figura 1.5). Poi cliccate il pulsante Run. Xcode compilerà l’applicazione e la lancerà nel simulatore. Come si può vedere, l’applicazione ha una main view (una “vista principale”) in grigio con un pulsante info nell’angolo inferiore destro (Figura 1.6). Se toccate il pulsante info, lo schermo si ribalta per mostrare la vista flipside con una barra del titolo e un pulsante blu Done (Figura 1.7). Toccate il pulsante Done e tornerete alla main view.

8

CAPITOLO 1

HELLO iPHONE


FIGURA 1.4 Selezione del percorso del progetto

FIGURA 1.5 Generazione per il simulatore iPhone FIGURA 1.6 La main view FIGURA 1.7 La flipside view

ESAME DEI FILE Andiamo ora a osservare i file che il template ha creato automaticamente. L’area Navigator dovrebbe essere visibile per default. Se non lo è, assicuratevi che il pulsante View del Navigator sia premuto (Figura 1.8). L’area Navigator apparirà sul lato sinistro della finestra principale di Xcode. È limitata in alto dalla barra Navigator selector, e lungo il fondo dalla barra Filter (Figura 1.9). DIAMO UN’OCCHIATA IN GIRO

9


Barra Navigator selector

FIGURA 1.8 Abilitazione dell’area Navigator

Area Navigator

FIGURA 1.9 L’area Navigator

Barra Filter

Utilizzeremo il navigatore per esaminare e organizzare un’ampia gamma di informazioni, compreso i file, le classi, gli errori di compilazione, le informazioni di debug, i punti di interruzione e il log. Si può modificare il tipo di navigatore selezionando le icone dalla barra Navigator Selector. Il Project Navigator dovrebbe essere selezionato per default. Se non lo è, selezionate l’icona che assomiglia a una cartella (l’icona più a sinistra nella barra Navigator Selector). Il Project navigator mostrerà diversi gruppi di risorse. Il gruppo Hello World contiene i file header (.h) e i file di implementazione (.m) di tutte le classi Objective-C. È qui che svolgeremo gran parte dell’effettivo lavoro di codifica. Il gruppo contiene anche il file risorsa MainStoryBoard.storyboard. Questo file definisce le differenti scene dell’applicazione e i segue tra loro. Il sottogruppo Supporting Files contiene diversi file secondari. Inizialmente, questo gruppo contiene i file Hello World-Info.plist, InfoPlist.strings, main.m e Prefix.pch dell’applicazione. Il file Hello World-Info.plist contiene diverse coppie chiave-valore utilizzate per configurare l’applicazione. Il file InfoPlist.strings contiene le versioni localizzate delle chiavi del file Hello World-Info.plist. Ciò può essere utile per la localizzazione dell’applicazione, però il file inizialmente è vuoto. Il file main.m contiene la funzione main(), la funzione iniziale che viene eseguita quando viene lanciata l’applicazione. Il file Prefix.pch è l’header precompilato dell’applicazione. Per aiutare i grossi progetti a compilarsi più velocemente, Xcode ci permette di creare un file header precompilato Prefix contenente diverse direttive #import, #include e #define comuni a gran parte del codice. Ad esempio, in un tipico progetto iOS, gran parte delle classi ha necessità di accedere ai framework UIKit e Foundation. Inserendo le dichiarazioni #import per questi framework nel file header Prefix, Xcode sa che deve preprocessare questi file e include i risultati in tutti i file sorgenti. Ciò evita al compilatore di processare i file comuni più volte durante ciascuna compilazione. Tutto ciò al momento può non aver molto senso, e questo non è un problema. Non andremo a modificare il file Prefix.pch in questo progetto. Per ulteriori informazioni, controllate la documentazione di Apple: Xcode Build System Guidelines; Reducing Build Times e Using a Precompiled Prefix Header.

10

CAPITOLO 1

HELLO iPHONE


Infine, il gruppo Frameworks contiene i framework iOS utilizzati da questo progetto, mentre il gruppo Products contiene i risultati finali: in questo caso, l’applicazione compilata. Di nuovo, non andremo a modificare nessuno di questi file in questo progetto. NOTA: In modo molto simile ai workspace di Xcode, i gruppi del navigatore rappresentano un’organizzazione virtuale: esistono solo in Xcode. Questi gruppi non necessariamente hanno una relazione con come o dove vengono memorizzate le effettive risorse, o anche con che tipo di file contengono. Siete liberi di collocare qualsiasi risorsa in qualsiasi gruppo e di aggiungere o rimuovere gruppi o sottogruppi secondo necessità.

UN TOUR R DEL PROGETTO Quando ho iniziato a scrivere applicazioni Cocoa, non capivo realmente come si integrano tutti i tasselli delle applicazioni. Certo, comprendevo le singole parti. Avevo una view e un view controller (il “controller della view”) che cooperavano per gestire l’interfaccia utente. Tuttavia, molti di questi oggetti sembravano apparire magicamente in fase di esecuzione. Non avevo alcuna idea da dove saltassero fuori. Permettetemi di rassicurarvi: non accade nulla di sovrannaturale. Ho controllato. Comunque, vale la pena concederci alcuni minuti per percorrere tutte le connessioni, giusto per vedere cosa sta avvenendo. Non preoccupatevi di memorizzare tutti i dettagli. Invece, concentratevi su come si esegue il tracciamento dei collegamenti da un oggetto all’altro. In questo modo, sarete in grado di eseguire il trace delle applicazioni in seguito. Partiamo esattamente da dove parte l’applicazione. Nel navigatore cliccate main.m (potrebbe essere necessario espandere il gruppo Supporting Files se il file non è già visibile). Questo file è in effetti molto breve. Importa UIKit e il delegate della nostra applicazione, e poi definisce la funzione main(). #import <UIKit/UIKit.h> #import “HWAppDelegate.h” int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([HWAppDelegate class])); } }

Sostanzialmente, ciò crea un blocco autorelease. All’interno del blocco, lanciamo l’applicazione Objective-C. Discuteremo dei blocchi autorelease nel paragrafo “Gestione della memoria” del Capitolo 2. Il lavoro reale avviene nella funzione UIApplicationMain(). Questa funzione accetta quattro argomenti. I primi due gestiscono gli argomenti della riga di comando dell’applicazione: argc fornisce il numero degli argomenti, mentre argv contiene gli argomenti effettivi DIAMO UN’OCCHIATA IN GIRO

11


sotto forma di array di stringhe in stile C. Questi argomenti sono in gran parte un retaggio delle applicazioni Unix a riga di comando. In iOS, le nostre applicazioni tipicamente hanno un solo unico argomento, il nome del file eseguibile dell’applicazione. Non utilizziamo quasi mai questi argomenti direttamente. I successivi due argomenti definiscono l’applicazione e il delegate dell’applicazione, rispettivamente. Ciascuno accetta una stringa che corrisponde al nome della classe desiderata. Se il terzo argomento è nil, utilizzeremo la classe UIApplication di default. Se il quarto argomento è nil, caricheremo UIApplicationDelegate dal file nib principale. Tuttavia, a partire da iOS 5.0, le applicazioni generalmente utilizzeranno gli storyboard invece dei nib. Ciò significa che dobbiamo impostare manualmente il nome del delegate dell’applicazione. Vogliamo utilizzare la nostra classe HWAppDelegate personalizzata. Sfortunatamente, non possiamo utilizzarla direttamente. Invece, invochiamo il metodo di classe HWAppDelegate per ricavare l’oggetto della classe delegate. Poi passiamo questo oggetto alla funzione NSStringFromClass(). Questa produce la stringa corretta della classe. Gran parte delle applicazioni seguiranno questo pattern. Invece del subclassing da UIApplication, tipicamente utilizziamo un oggetto UIApplication standard ma forniamo un delegate personalizzato dell’applicazione. NOTA: Raramente modificherete qualcosa in questo file. Infatti, a meno che non stiate facendo qualcosa di molto insolito, non dovrete mai utilizzarlo. Anche se siete convinti che realmente dovete apportare giusto un paio di modifiche, fate un passo indietro e riflettete a fondo ancora una volta. C’è quasi sempre una soluzione migliore.

ESAMINARE LO STORYBOARD

Finora tutto bene. La funzione main() invoca UIApplicationMain(). Questa funzione istanzia l’applicazione e la nostra classe personalizzata HWAppDelegate. Poi imposta il ciclo principale degli eventi e inizia a processare gli eventi. Se il file info.plist dell’applicazione definisce uno storyboard principale, allora carica il view controller iniziale e la view dallo storyboard. Ma, cos’è uno storyboard? Gli storyboard ci permettono di progettare graficamente le scene e disegnare i segue che le connettono. Si tratta dell’evoluzione più recente con Xcode, delle tecnologie di sviluppo dell’interfaccia. Le versioni precedenti di Xcode utilizzavano Interface Builder per progettare graficamente l’interfaccia utente. Interface Builder salvava i disegni dell’interfaccia come file nib. Xcode può quindi includere i nib in un’applicazione, caricandoli a runtime. In Xcode 4, Interface Builder è stato incorporato in Xcode stesso. Ciò ci permette di modificare l’interfaccia e il relativo codice fianco a fianco. Con iOS 5, gli storyboard ci permettono di incapsulare diversi nib, il che ci permette non solo di definire una miriade di informazioni della singola scena, ma anche di disegnare le transizioni tra scene. Come vedrete nel resto del capitolo, gli storyboard contengono molto più di semplici view e controlli. Contengono anche oggetti controller, e anche le connessioni tra i vari controlli, view e controller. Faremo qualche esperienza di disegno di queste connessioni man mano che espandiamo l’app Hello World nel paragrafo “Modifica del template”, in seguito nel capitolo. Inoltre, discuteremo dei nib, la tecnologia sottostante alla base degli storyboard, in maggior dettaglio nel riquadro “La vita segreta dei nib” del Capitolo 3”. 12

CAPITOLO 1

HELLO iPHONE


NOTA: In origine, Interface Builder salvava gli archivi nib come file binari, con un’estensione .nib. A partire da Xcode 3.0, Interface Builder permetteva la memorizzazione dei file nib in un formato XML intermedio con estensione .xib. Ciò permetteva una maggiore compatibilità con il controllo di versione del sorgente e con altri tool di sviluppo. I file .xib vengono poi compilati in file binari .nib quando l’applicazione viene generata. Per semplicità, entrambe le versioni vengono tipicamente chiamate “file nib”. In Xcode 4.2, gli storyboard vengono anche memorizzati come file XML con un’estensione .storyboard. Questi file vengono automaticamente compilati in uno o più file nib. Il sistema poi carica questi nib secondo necessità a runtime.

Come fa UIApplicationMain() a sapere quale file storyboard aprire? Come abbiamo visto prima, il nome dello storyboard principale è memorizzato nel file di configurazione. Cliccate Hello World-Info.plist nel gruppo Supporting Files. Dovreste vedere un elenco di coppie chiave-valore. Cercate la chiave Main storyboard file base name. Dovrebbe essere impostata a MainStoryboard. Ciò significa che UIApplicationMain() caricherà automaticamente il file MainStoryboard.storyboard. Andiamo ad aprire questo file. Cliccate MainStoryboard.storyboard nel navigatore Project. Questa azione aprirà il file storyboard nell’area Editor di Xcode. Probabilmente vorrete chiudere l’area Navigator per dare all’editor più spazio possibile. E vorrete anche assicurarvi che l’area Utilities sia aperta lungo il lato destro dello schermo. Assicuratevi che sia premuto il pulsante View dell’area Utilities (Figura 1.10).

FIGURA 1.10 Abilitazione dell’area Utilities

L’editor dello storyboard è costituito da due regioni separate. Sulla sinistra, abbiamo l’elenco delle scene. Questo elenco mostra tutte le scene dello storyboard, e anche ogni oggetto di ciascuna scena. Sulla destra, abbiamo l’Interface Builder, che ci permette di visualizzare e progettare l’effettiva interfaccia utente delle scene, e anche il flusso di controllo tra le scene (Figura 1.11). Il nostro progetto inizialmente ha due scene. Se si osserva l’elenco delle scene, si vedrà che ciascuna scena ha almeno due oggetti di livello massimo: il primo responder e il view controller della scena. Partiamo dal primo responder. Si tratta, per molti versi, di un oggetto molto atipico. O piuttosto, non è affatto un oggetto: è un proxy. Il sistema non crea mai un’istanza del primo responder. Invece, imposta il primo responder a runtime. Il primo responder rappresenta il primo oggetto della catena dei responder dell’applicazione. Questo può comportarsi da destinazione per qualsiasi azione che deve essere inviata alla catena dei responder. Ad esempio, un’azione di copia dovrebbe interessare il testo correntemente selezionato, indipendentemente dal controllo in cui questo testo può trovarsi. Non intendiamo legare l’azione a un singolo controllo: vogliamo invece instradare l’azione al controllo attualmente attivo. Rivolgendoci al primo responder, il nostro messaggio viene automaticamente passato al controllo corretto a runtime.

DIAMO UN’OCCHIATA IN GIRO

13


Interface Builder Scena Main view controller

Freccia in ingresso

Dock della scena

Segue

Elenco scene

FIGURA 1.11 Lo Storyboard editor

Scena Flipside view controller

Ciò detto, in iOS raramente abbiamo necessità di interagire con il primo responder direttamente. Successivamente, esaminate il view controller. Ciascuna scena ha il proprio view controller: un’istanza di UIViewController o (più probabilmente) una delle sue sottoclassi. Parleremo dei controller in modo più approfondito quando discuteremo del pattern Model-View-Controller nel Capitolo 2. Per ora, basta considerare il controller come l’anello di collegamento tra la scena e il resto del nostro codice. Ciascun view controller contiene una view. Questa view, a sua volta, può contenere qualsiasi numero di sottoview e di controlli (che possono contenere le proprie sottoview e i relativi controlli). Spesso ci riferiremo all’intero grafo di viste e sottoviste come gerarchia delle view. Xcode ci permette di esaminare e manipolare facilmente sia la gerarchia delle view sia l’aspetto effettivo della view. L’elenco delle scene ci mostra la gerarchia, mentre Interface Builder ci mostra l’organizzazione visuale. Inoltre, le scene possono avere altri oggetti di livello massimo. Ad esempio, la scena del main view controller ha un segue: la transizione tra la main view e la flipside view. Le scene possono anche comprendere i riconoscitori di gesture (gesture recognizer), le view o anche oggetti di dati come oggetti di livello top. Tutti questi oggetti di livello top (eccetto i segue) appaiono anche nel dock della scena nell’area Interface Builder. Ciò rende più facile tracciare connessioni tra gli elementi dell’interfaccia della scena e questi oggetti di livello top, specialmente quando l’elenco delle scene inizia a crescere. I segue, invece, appaiono tra le view che collegano. Come il dock, ciò aiuta a mantenerli attigui. Aspetto ancor più importante, è che visualizza graficamente il flusso di controllo nell’applicazione, fornendoci una sensazione su come il tutto è connesso. Ciascuno storyboard designa un’unica scena come scena iniziale. Il view controller di questa scena viene restituito quando si istanzia la scena iniziale dallo storyboard. Nel nostro caso, è la scena che carica UIApplicationMain() quando l’applicazione viene avviata. 14

CAPITOLO 1

HELLO iPHONE


Oggetto selezionato

Identity inspector

Classe dell’oggetto

FIGURA 1.12 Ispezione della classe del main view controller

La scena iniziale appare nell’area dell’Interface Builder con una freccia in ingresso che la punta. A differenza dei segue e di altre relazioni, questa freccia non ha una scena all’altra estremità. Per default, la freccia in ingresso parte dall’estremità sinistra di Interface Builder. La scena iniziale è la prima scena, con ulteriori scene che si diramano man mano che ci spostiamo verso destra. Possiamo cambiare la scena iniziale selezionando un altro view controller e selezionando il relativo attributo “is Initial View Controller”. Un solo view controller per volta può avere questo attributo abilitato. Se lo si abilita in un nuovo view controller, Interface Builder automaticamente lo disabiliterà per il vecchio controller. Nel nostro caso, la scena del main view controller è la scena iniziale. È esattamente ciò che vogliamo. Andiamo a vedere un ingrandimento della scena principale. Con un doppio clic sullo sfondo andremo a impostare/rimuovere lo zoom tra una panoramica dell’intero storyboard e la view a dimensione effettiva. Iniziamo col cliccare ciascun oggetto nella scena principale e esaminiamo la classe. Nella barra selettore dell’inspector, assicuratevi che sia selezionato Identity inspector (terza icona da sinistra in alto nell’area Utilities). Poi clicchiamo sul main view controller. Non sorprendentemente, è un’istanza della classe HWMainViewController (Figura 1.12). Man mano che esaminiamo il resto degli oggetti, dovreste vedere che la nostra view è una generica UIView. All’interno, abbiamo un UIButton (il pulsante info nell’angolo inferiore destro). Infine, il segue non visualizza alcuna informazioni nell’Identity inspector. Spostandoci alla scenda del flipside view controller, abbiamo un HWFlipsideViewController. All’interno di questo, abbiamo una ulteriore UIView; tuttavia, in alto a questa view abbiamo una barra UINavigation, con un UINavigationItem. All’interno, abbiamo il nostro pulsante Done: un UIBarButtonItem. DIAMO UN’OCCHIATA IN GIRO

15


Passiamo ora al Connection inspector (l’icona all’estrema destra in alto all’area Utilities). Ciò ci permette di visualizzare e modificare i collegamenti tra gli oggetti. Benché possa sembrare piuttosto complesso, questo inspector mostra solo cinque tipi di connessioni: outlet, action, eventi, segue e relazioni. I primi tre vengono utilizzati per definire le connessioni in una scena. Gli outlet sono variabili che possono memorizzare un puntatore a un oggetto. Definiscono le relazioni tra gli oggetti del grafo. Le action, invece, sono metodi che possiamo collegare agli eventi. Quando si verifica un evento, il sistema invoca automaticamente l’action corrispondente. I controlli definiscono un insieme di eventi comuni che si verificano quando l’utente interagisce con il controllo. A seguire, i segue e le relazioni definiscono i collegamenti tra scene. I segue si comportano come transizioni. Quando l’applicazione innesca un segue, questo passa alla scena successiva utilizzando l’animazione specificata. Le relazioni sono molto simili ai segue, ma vengono utilizzate per mostrare la relazione di possesso. I controller container (come i tab view controller) utilizzeranno le relazioni per connettersi alle view che gestiscono. Se esaminiamo il main view controller, possiamo vedere che ha un outlet view che punta all’oggetto view contenuto. Questo significa che possiamo referenziare questo oggetto view nel codice del nostro controller. Abbiamo anche un outlet di referenziamento dal flipside view controller. Questo significa semplicemente che il flipside view controller ha un outlet che punta all’indietro al main view controller: lo vedremo tra un attimo (Figura 1.13). In questa scena c’è solo un’altra connessione. Cliccate il pulsante info; come si può vedere, ha un modal storyboard segue che collega il metodo performSegueWithIdentifier:sender: al flipside view controller. Il sistema invocherà questo metodo automaticamente ogni qualvolta viene premuto il pulsante info, attivando il segue alla flipside view. Andiamo ora a dare un’occhiata al flipside view controller (Figura 1.14). Come abbiamo visto prima, abbiamo un outlet delegate che punta all’indietro al main view controller. Ciò permette al flipside view controller di invocare metodi del main view controller. Precisamente, siccome il main view controller è il delegate del flipside view controller, la flipside view si aspetterà che la main view implementi metodi specifici per modificare o controllare il comportamento della flipside view. Il flipside view controller ha anche un secondo outlet che lo connette alla sua view. Come probabilmente avrete intuito, si tratta di un pattern generale. Tutti i view controller hanno un outlet che li connette alla relativa view. Il flipside view controller ha anche due connessioni in ingresso. Una è il segue del pulsante info che abbiamo visto prima. Un’altra rappresenta un’action in ingresso dal pulsante Done. La vedremo tra un attimo. In questa scena ci sono solo due altre connessioni. Il navigator item referenzia il pulsante Done nel relativo outlet leftBarButtonItem. Questo posiziona il pulsante della barra correttamente nella barra di navigazione. Inoltre, vediamo la connessione action dal pulsante Done al metodo done: del flipside view controller. Questo metodo verrà invocato quando viene premuto il pulsante Done. Infine, selezionate l’inspector Attributes (la terza icona da destra), e selezionate il segue. Ciò mostra i dettagli del segue (Figura 1.15). Il campo Identifier ci permette di identificare questo segue nel codice. Il menu popup Style ci permette di definire come verrà visualizzata la nuova view, mentre il menu popup Transition ci permette di definire la sequenza di animazione utilizzata per spostarsi da una view alla successiva. Nel nostro caso, il segue è denominato showAlternate. Presenteremo la nuova scena come view modale, ribaltando la vecchia view orizzontalmente per rivelare la nuova view alle sue spalle. 16

CAPITOLO 1

HELLO iPHONE


FIGURA 1.13 Le connessioni del main view controller FIGURA 1.14 Le connessioni del flipside view controller FIGURA 1.15 Ispezione degli attributi del segue

In gran parte delle interfacce utente grafiche, una view modale è una view che deve essere chiusa (di solito cliccando un pulsante OK o Cancel) prima che l’utente possa eseguire qualsiasi altra azione nell’applicazione. Ciò costringe l’utente a concentrarsi su qualsiasi cosa ci sia all’interno della view modale: l’utente deve completare l’operazione e chiuderla prima di poter proseguire. Nelle applicazioni desktop, generalmente utilizziamo una view modale per visualizzare, ad esempio, dialoghi come Salva o Stampa. Sull’iPhone, le view modali ricoprono l’intero schermo (però iPad ha alcune opzioni ulteriori). Pertanto, che significa tutto ciò? Bene, stiamo eseguendo il trace su come vengono creati gli oggetti dell’applicazione. Il metodo UIApplicationMain() crea un’istanza della classe UIApplication e della classe HWAppDelegate. Successivamente, UIApplicationMain() ricerca il main storyboard nel file info.plist dell’applicazione. Poi carica la scena iniziale dello storyboard, visualizzando la view del main view controller nella finestra dell’applicazione. L’applicazione è quindi pronta per l’input dell’utente. Se l’utente preme il pulsante info, viene lanciato il segue. Questo carica il flipside view controller e lo visualizza come view modale. Quando l’utente preme il pulsante Done, questa invoca l’action done: del flipside view controller. Sappiamo che questa action chiude la flipside view, ma non sappiamo ancora come funziona. Per vederlo, dobbiamo esaminare il codice dell’applicazione. ESAMINARE IL CODICE

Mentre lo storyboard ci fornisce una buona panoramica dell’applicazione, per comprenderla realmente dovremo indagare nel codice. Non preoccupatevi se non tutto ciò ha ancora un senso, esamineremo Objective-C in maggior dettaglio nel Capitolo 2. Per ora, vogliamo solo farci un’idea su dove si trovano le varie cose e cosa fanno. Partiamo da HWAppDelegate.h. Questo file dichiara l’interfaccia della classe HWAppDelegate. Qui non troverete nulla di terribilmente sorprendente. HWAppDelegate implementa il protocollo UIApplicationDelegate, che gli permette di comportarsi da delegate dell’applicazione. Un delegate è un oggetto che agisce per conto di, o in coordinamento con, un ulteriore oggetto. Il delegate tipicamente si comporta da assistente dell’oggetto delegante principale. L’oggetto principale invocherà i metodi predefiniti del delegate in risposta agli eventi specificati. Il delegate poi utilizza questi metodi per monitorare e controllare l’oggetto delegante. Ciò ci permette di alterare il comportamento dell’oggetto delegante senza modificare l’oggetto stesso. Dichiariamo anche la proprietà window. Mentre l’applicazione carica lo storyboard, questa istanzia un oggetto window e lo assegna a questa proprietà. Di solito, le applicazioni iOS hanno una singola finestra che riempie l’intero schermo. Questa finestra si comporta come radice della gerarchia delle view. Questa finestra crea uno spazio in cui altre view possono essere visualizzate e distribuisce gli eventi alle opportune view o sottoview. DIAMO UN’OCCHIATA IN GIRO

17


#import <UIKit/UIKit.h> @interface HWAppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @end

Il protocollo UIApplicationDelegate definisce un ampio ventaglio di metodi opzionali che possiamo implementare per modificare il comportamento dell’applicazione. Questi comprendono metodi che rispondono alle modifiche dello stato dell’applicazione, e anche a notifiche locali, remote e di Sistema. Nella documentazione per sviluppatori è disponibile un elenco completo di questi metodi. Cliccando la parola UIApplicationDelegate nell’editor viene mostrata una breve descrizione del protocollo nel Quick Help inspector (la metà superiore dell’area Utilities). Questa tecnica ci permette di ottenere una rapida descrizione delle classi, dei metodi o delle funzioni. Inoltre, il Quick Help inspector contiene gli hyperlink a ulteriori risorse. Se si clicca la versione hyperlink del nome del protocollo, Xcode aprirà una descrizione completa dalla documentazione per sviluppatori. Per vedere un elenco completo dei metodi basta scorrere verso il basso alla sezione Tasks. Se si apre il file HWAppDelegate.m, si può vedere che abbiamo definito diversi di questi metodi, ma nessuno fa qualcosa. Adesso, sono semplicemente stub di metodi: occupano spazio, in attesa che inseriamo i dettagli. #import “HWAppDelegate.h” @implementation HWAppDelegate @synthesize window = _window; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Punto di ridefinizione per la personalizzazione // dopo il lancio dell’applicazione. return YES; }

- (void)applicationWillResignActive:(UIApplication *)application { /* Inviato quando l’applicazione sta per passare dallo stato attivo allo stato inattivo. Ciò può verificarsi per certi tipi di interruzioni temporanee (ad esempio una telefonata in arrivo o un messaggio SMS) o quando l’utente termina l’applicazione e inizia la transizione allo stato di background.

18

CAPITOLO 1

HELLO iPHONE


Utilizzare questo metodo per mettere in pausa le attività in corso, disabilitare i timer, e rallentare i frame rate di OpenGL ES. I giochi dovranno utilizzare questo metodo per mettere in pausa il gioco. */ } - (void)applicationDidEnterBackground:(UIApplication *)application { /* Utilizzare questo metodo per rilasciare le risorse condivise, salvare i dati utente, invalidare i timer, e memorizzare sufficienti informazioni sullo stato dell’applicazione per ripristinare l’applicazione allo stato corrente nel caso venga terminata in seguito. Se l’applicazione supporta l’esecuzione in background, questo metodo viene invocato invece di applicationWillTerminate: quando utente termina. */ } - (void)applicationWillEnterForeground:(UIApplication *)application { /* Invocato come parte della transizione da background allo stato inattivo; qui si può eseguire l’undo di molte delle modifiche effettuate entrando nell’esecuzione in background. */ } - (void)applicationDidBecomeActive:(UIApplication *)application { /* Riavvia i task sospesi (o non ancora avviati) mentre l’applicazione era inattiva. Se l’applicazione è stata precedentemente in background, aggiorna facoltativamente l’interfaccia utente. */

DIAMO UN’OCCHIATA IN GIRO

19


} - (void)applicationWillTerminate:(UIApplication *)application { /* Invocato quando l’applicazione sta per terminare. Salva i dati se opportuno. Vedi anche applicationDidEnterBackground:. */ } @end

Questi metodi rispondono alle modifiche dello stato dell’applicazione. Vengono invocati al primo caricamento dell’applicazione, quando diventa attiva o inattiva, quando va in o esce dal background, o quando si prepara a terminare. Discuteremo di questi (e altri) metodi in maggior dettaglio nel riquadro “Attività che ogni applicazione dovrà eseguire” del Capitolo 3. Per ora, leggete i commenti negli stub di questi metodi, che dovrebbero fornire una buona panoramica dell’utilizzo previsto. Successivamente, diamo un’occhiata a HWMainViewController.h. Questo file header è ancora più semplice della dichiarazione di interfaccia del delegate dell’app. Definiamo la nostra classe come sottoclasse di UIViewController. E adotta inoltre un protocollo personalizzato, HWFlipsideViewControllerDelegate. Eccolo. #import “HWFlipsideViewController.h” @interface HWMainViewController : UIViewController <HWFlipsideViewControllerDelegate> @end

Diamo un’occhiata all’implementazione selezionando HWMainViewController.m. In modo molto simile al delegate dell’app, la classe UIViewController ha un certo numero di metodi che possiamo ridefinire per monitorare la view. In particolare, ciò riguarda qualsiasi metodo con will o did nel nome. I metodi will vengono invocati subito prima che venga generato l’evento. I metodi did vengono invocati subito dopo. Consultate UIViewController nella documentazione per sviluppatori per vedere l’elenco completo (cliccate per aprire il Quick Help inspector, e poi cliccate il link del nome per aprire la descrizione completa). Se esaminate questo file, vedrete che ne comprende diversi come stub di metodi. Molti hanno implementazioni di default che non fanno nulla; invocano semplicemente l’implementazione della superclasse. Ci sono solo tre metodi che contengono codice funzionale shouldAutorotateToInterfaceOrientation:, flipsideViewControllerDidFinish: e prepareForSegue:sender:. - (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation

20

CAPITOLO 1

HELLO iPHONE


{ // Restituisce YES per orientazioni supportate. return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); } #pragma mark - Flipside View - (void)flipsideViewControllerDidFinish: (HWFlipsideViewController *)controller { [self dismissModalViewControllerAnimated:YES]; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@”showAlternate”]) { [[segue destinationViewController] setDelegate:self]; } }

Andiamo a considerarli per ordine. Il metodo shouldAutorotateToInterfaceOrientation: viene invocato ogni qualvolta l’utente ruota il dispositivo in una nuova orientazione. Se il metodo restituisce YES, la view ruoterà automaticamente per adattarsi a quella orientazione. Altrimenti, la view rimarrà nell’orientazione corrente. L’implementazione corrente permette alla main view di ruotare in qualsiasi orientazione eccetto l’orientazione portrait ribaltata (a testa in giù); (landscape sinistra, landscape destra e portrait sono tutte OK). Il metodo flipsideViewControllerDidFinish: è un metodo delegate del flipside view controller. Come si evince dal nome, il flipside view controller invocherà questo metodo quando ha terminato. La nostra implementazione corrente chiude semplicemente la view modale corrente. Ricordate che quando abbiamo esaminato il segue, abbiamo visto che ha visualizzato il flipside view controller come view modale. Perciò, questo metodo rimuove semplicemente la flipside view, utilizzando la stessa transizione (ribaltamento orizzontale) per ritornare alla main view. Infine, prepareForSegue:sender: viene invocato ogni qualvolta un segue viene attivato dalla scena corrente. Benché l’implementazione sembri un po’ complessa, verifica semplicemente che l’id del segue corrisponda al valore previsto, showAlternate. Se corrisponde, assegniamo il main view controller come delegate della destinazione. È sempre una buona idea controllare l’identificatore del segue prima di fare qualsiasi cosa nel metodo prepareForSegue:sender:. Adesso non è strettamente necessario. Abbiamo un solo segue, pertanto sappiamo che l’identificatore corrisponderà sempre. Tuttavia, verificare l’identificatore aiuta a testare il codice. Non vogliamo ritrovarci improvvisamente con strani bug solo perché abbiamo aggiunto un nuovo segue. DIAMO UN’OCCHIATA IN GIRO

21


Inoltre, se ricordate, il segue showAlternate collega la scena del main view controller con la scena del flipside view controller. Questo significa che il destinationViewController sarà HWFlipsideViewController. Pertanto, l’utente preme il pulsante info, attivando il segue. Il sistema istanzia una copia di HWFlipsideViewController, poi invoca prepareForSegue:sender: di HWMainViewController. Qui, HWMainViewController si assegna come delegate di HWFlipsideViewController. Si verifica la transizione, e la view si ribalta orizzontalmente, rivelando la flipside view. Quando la flipside view è terminata (lo vedremo tra un attimo), il flipside view controller invoca flipsideViewControllerDidFinish: del delegate, HWMainViewController. HWMainViewController poi chiude il flipside view controller e le view si ribalta nuovamente, rivelando nuovamente la main view. È importante rendersi conto che questo pattern dimostra la tecnica preferita per il passaggio di dati da scena a scena attraverso un segue. Nel passaggio di dati dal view controller di provenienza al view controller di destinazione, ridefiniamo il metodo prepareForSegue:sender: del view controller di provenienza e unitamente passiamo i dati. Per passare i dati all’indietro, rendiamo la view di provenienza un delegate della view di destinazione e poi invochiamo gli opportuni metodi delegate. Naturalmente, questi metodi devono essere dichiarati nel protocollo del delegate. A seguire andiamo a vedere il protocollo di HWFlipsideViewControllerDelegate. Aprite HWFlipsideViewController.h. Qui, partiamo col dichiarare il protocollo HWFlipsideViewControllerDelegate. Questo ha un unico metodo, che abbiamo già visto, flipsideViewControllerDidFinish:. A seguire, dichiariamo la classe HWFlipsideViewController stessa. Anche questa è semplice, e contiene solo due elementi: la proprietà outlet delegate e l’action done:. Li abbiamo visti entrambi prima. Abbiamo appena discusso la proprietà delegate in relazione a HWMainViewController, e abbiamo già visto l’action done: quando stavamo esplorando lo storyboard. È connessa al pulsante Done della barra di navigazione. La pressione di questo pulsante avvierà questo metodo. #import <UIKit/UIKit.h> @class HWFlipsideViewController; @protocol HWFlipsideViewControllerDelegate - (void)flipsideViewControllerDidFinish: (HWFlipsideViewController *)controller; @end @interface HWFlipsideViewController : UIViewController @property (weak, nonatomic) IBOutlet id <HWFlipsideViewControllerDelegate> delegate; - (IBAction)done:(id)sender; @end

Ora aprite HWFlipsideViewController.m. In modo molto simile a HWMainViewController.m, in gran parte contiene stub di metodi. Tuttavia, in basso abbiamo l’implementazione della nostra action done:. - (IBAction)done:(id)sender

22

CAPITOLO 1

HELLO iPHONE


NOTA: I lettori attenti ricorderanno che nello storyboard l’outlet delegate era anche connesso a HWMainViewController. Ciò significa che è stato connesso due volte: una volta nel codice e una volta in Interface Builder. Ne risulta che la connessione in Interface Builder non fa effettivamente niente. Eliminarla non influenzerà l’app: non vi sarà neanche permesso di disegnarla nuovamente. Tuttavia, commentando il metodo prepareForSegue:sender: si interrompe il collegamento, e la proprietà delegate di HWFlipsideViewController non viene mai impostata. Quando viene premuto il pulsante info si passerà ancora alla flipside view, ma la pressione del pulsante Done non ci riporta indietro.

{ [self.delegate flipsideViewControllerDidFinish:self]; }

Questo invoca semplicemente il metodo flipsideViewControllerDidFinish: del delegate. Il che chiude il cerchio. Quando l’utente preme il pulsante Done, l’evento scatena l’action done:. Questa poi invoca flipsideViewControllerDidFinish: di HWMainViewController, che chiude la scena del flipside view controller. Tutto è ora connesso.

FIGURA 1.16 Il grafo completo degli oggetti

DIAMO UN’OCCHIATA IN GIRO

23


FIGURA 1.17 Impostazione dell’attributo Background FIGURA 1.18 Impostazione dell’attributo Type FIGURA 1.19 Selezione della Object library

Di nuovo, non sta avvenendo nulla di magico. Se mai vi sentiste persi in un nuovo progetto, partite dal file dello storyboard principale (definito nel file info.plist del progetto) e procedete nel file. Dovreste essere in grado di tracciare una serie di connessioni dall’oggetto UIApplication a qualsiasi oggetto del progetto. Alcune possono essere definite nel codice e alcune nello storyboard, ma con un po’ di pazienza, sarete in grado di individuare ogni cosa.

MODIFICA DEL TEMPLATE Ok, basta chiacchiere. Iniziamo a realizzare qualcosa di nuovo. Andremo ad aggiungere una label alla main view che visualizzerà un breve messaggio. Aggiungeremo anche una textbox alla flipside view, permettendo all’utente di modificare questo messaggio. Pertanto, andiamo a incominciare.

MODIFICA DELLA MAIN VIEW Aprite nuovamente MainStoryboard.storyboard, e ingrandite la scena del main view controller. Lo sfondo è un po’ troppo scuro per i miei gusti. Selezionate l’oggetto View, e poi passate all’inspector Attributes. Modificate l’attributo Background a Light Gray Color (Figura 1.17). Successivamente, selezionate il pulsante info e modificate l’impostazione di Type a Info Dark (Figura 1.18). Ora dobbiamo aggiungere una label. Xcode rende facile aggiungere oggetti dell’interfaccia utente. Basta trascinare l’oggetto desiderato dal pannello Library (la metà inferiore dell’area Utilities) e trascinarlo sulla scena. Assicuratevi sia visualizzata la Object library (la seconda icona a cubo da destra nella barra del selettore della libreria) (Figura 1.19), e scorrete in modo che l’oggetto Label sia visibile. Cliccate l’oggetto Label e trascinatelo dal pannello Library alla view della scena del main view controller. Xcode vi aiuterà a posizionare correttamente il controllo. Mentre trascinate la label nell’area Editor, questa si evidenzia e va ad etichettare la view corrente. E aggiunge anche delle guide blu per aiutarvi a posizionare opportunamente gli oggetti. Trascinate la label finché non è centrata verticalmente ed è allineata al margine sinistro della view, come è mostrato nella Figura 1.20.

24

CAPITOLO 1

HELLO iPHONE


FIGURA 1.20 Allineamento della label con il margine sinistro della view FIGURA 1.21 Impostazione del font della label FIGURA 1.22 Centraggio della label

Quando rilasciate la label, apparirà circondata da otto punti blu. Potete cliccare e trascinare questi punti ancora per ridimensionare la label. Selezionate il lato destro e trascinatelo finché non raggiunge la guida del margine destro. Poi, rendetela alta circa la metà della view che la contiene. Ora, con la label ancora selezionata, andiamo a modificarne l’aspetto. Aprite l’inspector Attributes. Nell’attributo Alignment, cliccate il pulsante del testo centrato. Poi cliccate l’icona all’estrema destra del campo text dell’attributo Font. Ciò farà aprire una finestra pop-up. Modificate l’impostazione Font a System Bold e l’impostazione di Size a 24 (Figura 1.21). Ora andiamo a modificare il testo. Potete modificare l’attributo Text della label, o effettuate un doppio clic sulla label e modificatelo direttamente. Sostituite “Label” con Hello World. Infine, cliccate e trascinate la label finché non è centrata orizzontalmente e verticalmente (se non potete spostare la label, provate a cliccare la view per azzerare la selezione, poi cliccate e trascinate nuovamente la label). Se avete fatto tutto correttamente, dovranno apparire anche le guide sinistra e destra del margine (Figura 1.22). Eseguite l’applicazione. Le modifiche dovranno apparire nella main view.

AGGIUNTA DI UN OUTLET Per modificare il testo della label a runtime, abbiamo necessità di un modo per accedere via codice. Il modo più semplice è aggiungere un outlet al view controller. Prima di Xcode 4.0, ciò significava saltare avanti e indietro tra Xcode e Interface Builder dovendo modificare il codice a mano. Ora c’è un modo molto più semplice. Per prima cosa, dovremo probabilmente collassare la lista delle scene. Cliccate il pulsante rotondo grigio di chiusura nell’angolo inferiore sinistro dell’area Editor. Poi, cliccate il pulsante Assistant Editor (Figura 1.23). Ciò visualizzerà due aree Editor adiacenti. L’area più a sinistra contiene il file selezionato, mentre quella più a destra contiene un file strettamente correlato. Ad esempio, se selezionate il file header HWMainViewController.h, l’editor di destra dovrà visualizzare il file di MODIFICA DEL TEMPLATE

25


FIGURA 1.23 Il pulsante Assistant Editor FIGURA 1.24 Modifica fianco a fianco dello storyboard e del file header

implementazione HWMainViewController.m. Nel nostro caso, l’editor di sinistra mostra lo storyboard, mentre l’editor di destra mostra il file header del controller (Figura 1.24). Control-clic sulla label e trascinatela nel file header. Vedrete una riga blu che si estende dalla label al cursore. Posizionate il cursore subito sopra la direttiva @end, come è mostrato nella Figura 1.25, e poi rilasciate il mouse. Un dialogo pop-up vi permetterà ora di configurare l’outlet. Assicuratevi che l’impostazione Connection sia impostata a Outlet. Nel campo Name, inserite label. Type dovrà essere UILabel, e Storage dovrà essere Strong (Figura 1.26). Cliccate Connect per continuare. NOTA: Benché Xcode generalmente svolga un ottimo lavoro di autoselezione del corretto file associato, possono esserci delle volte in cui non opera opportunamente, o se invece si vuole selezionare un file differente. Si può fare ciò nella jump bar. La selezione dell’elemento foglia vi permetterà di scegliere un file alternativo in base alle stesse regole generali di selezione. La selezione dell’elemento radice vi permetterà di scegliere differenti regole di selezione. Xcode fornisce diverse regole di selezione differenti specifiche al contesto. Si può anche scegliere di selezionare il file associato, se necessario.

Xcode effettuerà diverse modifiche a H W M a i n V i e w C o n t r o l l e r . h , H W M a i n V i e w Controller.m, e MainStoryboard.storyboard. Nel file header, dichiara la proprietà della label. #import “HWFlipsideViewController.h” @interface HWMainViewController : UIViewController <HWFlipsideViewControllerDelegate> @property (strong, nonatomic) IBOutlet UILabel *label; @end

26

CAPITOLO 1

HELLO iPHONE


FIGURA 1.25 Creazione di un outlet FIGURA 1.26 Configurazione dell’outlet

Nel file di implementazione, Xcode sintetizza la proprietà e la imposta a nil quando la view viene scaricata. Ciò aiuta a liberare la memoria non necessaria. #import “HWMainViewController.h” @implementation HWMainViewController @synthesize label; .... - (void)viewDidUnload { [self setLabel:nil]; [super viewDidUnload]; // Rilascia le subview “retained” della main view. // Ad esempio, self.myOutlet = nil;. }

Nel file storyboard, Xcode connette la label e l’outlet appena creato. Si può visualizzare ciò utilizzando il Connection inspector. In alternativa, Control-clic sull’icona Main View Controller per fa apparire un dialogo Connections (Figura 1.27).

FIGURA 1.27 Connessioni di Main View Controller MODIFICA DEL TEMPLATE

27


AGGIUNTA DEL TEXT FIELD I primi passi ripetono quasi esattamente ciò che abbiamo fatto per la main view, solo che utilizziamo un controllo Text Field, non un oggetto Label. Assicuratevi che MainStoryboard.storyboard sia aperto e ingrandite sul flipside view controller. Ora, trascinate un controllo Text Field dal pannello Library. Ridimensionatelo in modo che riempia la view da margine a margine. Stavolta non dobbiamo modificare l’altezza o gli attributi. Basta creare un outlet per il text field trascinandolo premendo Control in un punto subito sopra la proprietà delegata. Denominate l’outlet labelText. Infine, posizionatelo nella metà superiore della flipside view; altrimenti, può essere coperto dalla tastiera. Ogni volta che vogliamo modificare il testo della label, dobbiamo semplicemente ricavare il valore del testo dall’outlet labelText e copiarlo nell’outlet label. La soluzione più semplice è modificare la label solo quando l’utente chiude la flipside view. Aprite HWMainViewController.m e individuate il metodo flipsideViewControllerDidFinish:. Attualmente i file non sono molto lunghi. Probabilmente si può individuare il metodo semplicemente scorrendo il file. Tuttavia, è spesso più facile utilizzare la jump bar dell’editor (Figura 1.28). La barra lungo la sommità dell’area Editor vi mostra l’elemento in fase di modifica. Cliccate l’elemento più a destra e selezionate il metodo flipsideViewControllerDidFinish: (Figura 1.29). L’editor selezionerà il metodo e scorrerà in modo da renderlo visibile. Ora, modificate il metodo flipsideViewControllerDidFinish: come è mostrato qui. - (void)flipsideViewControllerDidFinish: (HWFlipsideViewController *)controller { self.label.text = controller.labelText.text; [self dismissModalViewControllerAnimated:YES]; }

Come abbiamo discusso prima, questa riga copia semplicemente il valore del testo dal text field della flipside view alla label della main view utilizzando gli outlet e le proprietà. È tutto. Eseguite nuovamente l’applicazione. All’avvio questa dovrà visualizzare il messaggio di default Hello World. Cliccate il pulsante info, e digitate il nuovo testo nel text field. Cliccate il pulsante Done. La label dovrà ora mostrare il nuovo testo.

RAFFINARE L’INTERFACCIA Funzionalmente va tutto bene, ma il risultato non è la più raffinata (o, per giunta, utile) app del mondo. Andiamo a migliorarla un pochino. Quando appare la flipside view, andiamo a popolare il text field con il valore corrente della label. Inoltre, proseguiamo e chiudiamo la flipside view quando l’utente tocca il tasto Return. Cominciamo col passare il testo dall’etichetta della main view alla flipside view. In teoria, dovremmo impostare questo valore nel metodo prepareForSegue:sender:. In pratica dovrebbe essere il contrario del codice scritto in flipsideViewControllerDidFinish:; copiamo il valore dalla label della main view direttamente nel text field della flipside view. Sfortunatamente, per via del modo in cui le view vengono caricate, il text field può non esistere ancora. Ci sono due 28

CAPITOLO 1

HELLO iPHONE


FIGURA 1.28 La jump bar FIGURA 1.29 Selezione di un metodo

modi per aggirare ciò. Andiamo ad aggiungere una proprietà alla classe HWFlipsideViewController e utilizziamola per contenere il testo finché non siamo certi che la view sia pronta. Aprite HWFlipsideViewController.h e aggiungete la seguente riga subito dopo le altre proprietà: @property (nonatomic, strong) NSString* startingText;

In HWFlipsideViewController.m, dobbiamo sintetizzare questa proprietà (per ulteriori informazioni si veda il paragrafo “Proprietà” del Capitolo 2). Aggiungete la seguente riga all’inizio del blocco @implementation: @synthesize startingText = _startingText;

Ora, scorrete verso il basso al metodo viewWillAppear: e modificatelo come è mostrato qui: - (void)viewWillAppear:(BOOL)animated { self.labelText.text = self.startingText; [super viewWillAppear:animated]; }

Questo metodo viene invocato ogni volta che appare la flipside view. Qui, stiamo giusto assegnando al text field il valore memorizzato nella proprietà startingText. Successivamente, aprite nuovamente HWMainViewController.m. Modificate prepareForSegue:sender: come è mostrato qui: - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@”showAlternate”]) { id flipsideViewController = [segue destinationViewController]; [flipsideViewController setDelegate:self]; [flipsideViewController setStartingText:self.label.text]; } }

MODIFICA DEL TEMPLATE

29


FIGURA 1.30 Disegno di una connessione dal dialogo Connections

Siccome stiamo impostando più proprietà, partiamo col ricavare un riferimento generico a HWFlipsideViewController. Poi utilizziamo questo riferimento per impostare il delegate, così

come abbiamo fatto prima. Infine, catturiamo la stringa dalla label della main view e la salviamo nella proprietà startingText. Per una miglioria introdotta, un’altra è ancora da apportare. Aprite nuovamente l’Assistant editor, con lo storyboard sulla sinistra e HWFlipsideViewController.h sulla destra. Vogliamo connettere l’evento Did End On Exit del text field con il metodo done: del controller. Ci sono diversi modi per tracciare i collegamenti. Ad esempio, potremmo premere Control-clic e trascinare dal text field al metodo done:. Sfortunatamente, ciò non ci permette di selezionare l’evento, e connetterà l’evento Editing Did End, non l’evento Did End On Exit. Invece, Control-clic sul text field per far apparire il dialogo Connections. Cliccate e trascinate dal cerchio che si trova sulla destra dell’evento Did End On Exit al metodo done:, come è mostrato

ACCEDERE CON SICUREZZA AGLI OUTLET DEL VIEW CONTROLLER In questo esempio, potremmo non modificare il contenuto del text field fin dopo che il sistema ha caricato completamente la view. Discuteremo di ciò in modo più approfondito nei Capitoli 3 e 4. Tuttavia, la spiegazione semplice è che UIKit non carica immediatamente le view quando crea i nostri view controller. Invece, attende finché è effettivamente necessaria la view (di solito, subito prima che il sistema la visualizzi sullo schermo). Benché UIKit lo faccia per motivi di prestazioni, può causare bug molto bizzarri se non si sta attenti. Tipicamente, questi bug si verificano quando si apportano modifiche nel codice all’outlet di un view controller: specialmente un outlet assegnato utilizzando Interface Builder. Quando si esegue l’applicazione, il codice viene attivato, ma le modifiche non sembra abbiano effetto. Ogni volta che accade ciò, ricontrollate bene per assicurarvi che l’outlet esiste effettivamente. Dovrà avere un valore diverso da nil. Se non è così, dovrete ritardare il codice di configurazione, in modo simile a come abbiamo fatto qui.

30

CAPITOLO 1

HELLO iPHONE


FIGURA 1.31 La main view completata FIGURA 1.32 La flipside view completata

ella Figura 1.30. Ci sono diverse altre alternative altrettanto valide. Si può effettuare Control-clic sull’icona HWFlipsideViewController nel dock della scena, e poi trascinare dall’action done: al text field. Xcode vi richiederà l’evento corretto. Si può anche utilizzare il Connection inspector nell’area Utilities, invece di utilizzare i dialoghi popup. Gran parte di questi approcci sono equivalenti. Siete liberi di sperimentare con i differenti approcci e vedere quale preferite.

NOTA: Ciascun evento può essere connesso a una sola action, ma una singola action può connettersi a più eventi. In questo caso, abbiamo sia il text field sia il pulsante Done connessi all’action done:.

Già che ci siamo, modifichiamo l’aspetto del tasto Return della tastiera. Selezionate il text field e aprite l’Attributes inspector. Impostate l’attributo Return Key a Done. Benché ciò non cambi la funzionalità della tastiera, aiuta a comunicare il nostro intento. Si può anche voler impostare l’attributo Clear Button a “Appears while editing.” In fin dei conti, l’utilizzo del backspace per cancellare grosse quantità del testo diventa ben presto noioso. È tutto. Eseguite l’applicazione. Dovrà apparire come è mostrato nelle Figure 1.31 e 1.32.

RIEPILOGO Ed è tutto per l’app Hello World. In questo capitolo, abbiamo esaminato come vengono assemblate le applicazioni iOS. Abbiamo costruito e modificato una (benché incredibilmente semplice) applicazione, e abbiamo fatto qualche esperienza pratica con l’utilizzo degli strumenti di sviluppo. Ciò dovrebbe fornirvi un buon punto di partenza per il resto del libro. Nel Capitolo 2, indagheremo su Objective-C. Descriveremo i differenti elementi fondamentali utilizzati nel linguaggio, e anche alcuni dei comuni design pattern presenti in Cocoa Touch. Poi, nel Capitolo 3, impiegheremo tutto ciò che abbiamo appreso e inizieremo a costruire una più complessa applicazione reale. RIEPILOGO

31

Crare App per iPhone e iPad  

Richard Warren spiega spiega come si possono scrivere applicazioni iOS di ultima generazione.

Advertisement