xcode4 tutorial completo

254
0 – Programmazione con Objective-C e Cocoa: iOS , , Mac , , OSX , , SDK , , Tech Room L’innovazione portata dall’iPhone non è solo legata all’arrivo di L’innovazione portata dall’iPhone non è solo legata all’arrivo di un nuovo dispositivo HW (in fondo di telefoni e smartphone ce un nuovo dispositivo HW (in fondo di telefoni e smartphone ce n’erano anche prima) ma alla possibilità di creare e vendere delle n’erano anche prima) ma alla possibilità di creare e vendere delle applicazioni in maniera semplice come mai prima. applicazioni in maniera semplice come mai prima. Questo percorso cerca di fornire gli strumenti concettuali alla base Questo percorso cerca di fornire gli strumenti concettuali alla base della (per qualcuno della (per qualcuno arte arte ) programmazione per iOS (iPhone e ) programmazione per iOS (iPhone e iPad). iPad). Non aspettatevi di imparare subito un linguaggio di Non aspettatevi di imparare subito un linguaggio di programmazione, lo vedremo solo più avanti; ma quando sarà programmazione, lo vedremo solo più avanti; ma quando sarà arrivato il momento spero che sarà abbastanza semplice impararlo. arrivato il momento spero che sarà abbastanza semplice impararlo. Il percorso che seguiremo può essere raggruppato in 4 fasi, anche Il percorso che seguiremo può essere raggruppato in 4 fasi, anche se non sempre sarà possibile separarle in maniera netta se non sempre sarà possibile separarle in maniera netta Introduzione alla programmazione Introduzione alla programmazione Il modello Object Oriented Il modello Object Oriented Il linguaggio Objective C Il linguaggio Objective C I fondamenti di Cocoa (il framework Apple per iOS) I fondamenti di Cocoa (il framework Apple per iOS) Non si tratta di un corso nel senso tradizionale del termine ma Non si tratta di un corso nel senso tradizionale del termine ma piuttosto di una serie di stimoli per una discussione a supporto piuttosto di una serie di stimoli per una discussione a supporto della crescita di una comunità di sviluppatori della crescita di una comunità di sviluppatori

Upload: ido69

Post on 02-Jan-2016

119 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Xcode4 Tutorial Completo

0 – Programmazione con Objective-C e Cocoa:

iOS, , Mac, , OSX, , SDK, , Tech Room

L’innovazione portata dall’iPhone non è solo legata all’arrivo diL’innovazione portata dall’iPhone non è solo legata all’arrivo di un nuovo dispositivo HW (in fondo di telefoni e smartphone ceun nuovo dispositivo HW (in fondo di telefoni e smartphone ce n’erano anche prima) ma alla possibilità di creare e vendere dellen’erano anche prima) ma alla possibilità di creare e vendere delle applicazioni in maniera semplice come mai prima.applicazioni in maniera semplice come mai prima.

Questo percorso cerca di fornire gli strumenti concettuali alla baseQuesto percorso cerca di fornire gli strumenti concettuali alla base della (per qualcuno della (per qualcuno artearte) programmazione per iOS (iPhone e) programmazione per iOS (iPhone e iPad).iPad).

Non aspettatevi di imparare subito un linguaggio diNon aspettatevi di imparare subito un linguaggio di programmazione, lo vedremo solo più avanti; ma quando saràprogrammazione, lo vedremo solo più avanti; ma quando sarà arrivato il momento spero che sarà abbastanza semplice impararlo.arrivato il momento spero che sarà abbastanza semplice impararlo.

Il percorso che seguiremo può essere raggruppato in 4 fasi, ancheIl percorso che seguiremo può essere raggruppato in 4 fasi, anche se non sempre sarà possibile separarle in maniera nettase non sempre sarà possibile separarle in maniera netta

Introduzione alla programmazioneIntroduzione alla programmazione

Il modello Object OrientedIl modello Object Oriented

Il linguaggio Objective CIl linguaggio Objective C

I fondamenti di Cocoa (il framework Apple per iOS)I fondamenti di Cocoa (il framework Apple per iOS)

Non si tratta di un corso nel senso tradizionale del termine maNon si tratta di un corso nel senso tradizionale del termine ma piuttosto di una serie di stimoli per una discussione a supportopiuttosto di una serie di stimoli per una discussione a supporto della crescita di una comunità di sviluppatoridella crescita di una comunità di sviluppatori

Page 2: Xcode4 Tutorial Completo

1 – Introduzione alla Programmazione: Cosa è la programmazione1 – Introduzione alla Programmazione: Cosa è la programmazione

Guide, , iOS, , Mac, , OSX, , SDK, , Tech Room

Scrivere un programma è come giocare con le costruzioni, èScrivere un programma è come giocare con le costruzioni, è necessario sapere cosa si vuole costruire, quali mattoncini si hannonecessario sapere cosa si vuole costruire, quali mattoncini si hanno a disposizione e come si possono collegare tra di essi.a disposizione e come si possono collegare tra di essi.

Cosa significa programmare? E’ inutile precisarlo? Forse; ma nonCosa significa programmare? E’ inutile precisarlo? Forse; ma non costa nulla ed è meglio chiarire i concetti base.costa nulla ed è meglio chiarire i concetti base.

Programmare significa dire, ad un dispositivo capace di eseguirle,Programmare significa dire, ad un dispositivo capace di eseguirle, quali azioni deve compiere.quali azioni deve compiere.

E’ una descrizione semplicistica? può darsi ma è quanto ci serveE’ una descrizione semplicistica? può darsi ma è quanto ci serve per il momento e ne rappresenta l’essenza.per il momento e ne rappresenta l’essenza.

Iniziamo subito con un programmaIniziamo subito con un programma

memorizza l’ora di inizio della registrazionememorizza l’ora di inizio della registrazione

memorizza l’ora di fine della registrazionememorizza l’ora di fine della registrazione

memorizza il canale da registrarememorizza il canale da registrare

leggi l’ora correnteleggi l’ora corrente

è l’ora fissata per l’inizio della registrazione?è l’ora fissata per l’inizio della registrazione?

se non lo èse non lo è

torna a 4torna a 4

Page 3: Xcode4 Tutorial Completo

altrimentialtrimenti

seleziona il canale da registrareseleziona il canale da registrare

avvia la registrazioneavvia la registrazione

leggi l’ora correnteleggi l’ora corrente

è l’ora fissata per la fine della registrazione?è l’ora fissata per la fine della registrazione?

se non lo èse non lo è

torna a 11torna a 11

altrimentialtrimenti

ferma la registrazioneferma la registrazione

Lo avete riconosciuto? Penso di si…è il Lo avete riconosciuto? Penso di si…è il programmaprogramma eseguito dal eseguito dal vostro decoder, video-registratore o altro per registrare la vostravostro decoder, video-registratore o altro per registrare la vostra trasmissione preferita. In effetti è un programma che potreste daretrasmissione preferita. In effetti è un programma che potreste dare scritto su un foglietto ad un vostro amico con un orologio escritto su un foglietto ad un vostro amico con un orologio e chiedergli di eseguirlo se avete un registratore senza timer.chiedergli di eseguirlo se avete un registratore senza timer.

Quali strutture logiche di base possiamo individuare in questoQuali strutture logiche di base possiamo individuare in questo programma?programma?

Abbiamo delle azioni:Abbiamo delle azioni:

memorizza l’oramemorizza l’ora

memorizza il canalememorizza il canale

leggi l’ora correnteleggi l’ora corrente

confronta due oreconfronta due ore

seleziona il canaleseleziona il canale

avvia la registrazioneavvia la registrazione

ferma la registrazioneferma la registrazione

abbiamo delle informazioni:abbiamo delle informazioni:

Page 4: Xcode4 Tutorial Completo

ora correnteora corrente

ora di inizio registrazioneora di inizio registrazione

canale da registrarecanale da registrare

ora di fine registrazioneora di fine registrazione

abbiamo un controllo della successione delle operazioni (flusso diabbiamo un controllo della successione delle operazioni (flusso di elaborazione)elaborazione)

le operazioni sono eseguite una dopo l’altra (1,2,3,4,…)le operazioni sono eseguite una dopo l’altra (1,2,3,4,…)

al verificarsi o meno di determinate condizioni può cambiareal verificarsi o meno di determinate condizioni può cambiare la sequenza di operazioni (se non è ora di registrare torniamola sequenza di operazioni (se non è ora di registrare torniamo a controllare l’ora altrimenti proseguiamo, dopo 7 torno a 4,a controllare l’ora altrimenti proseguiamo, dopo 7 torno a 4, dopo 5 possiamo andare a 9,…)dopo 5 possiamo andare a 9,…)

L’astrazione di ciò che vogliamo che il nostro dispositivo faccia èL’astrazione di ciò che vogliamo che il nostro dispositivo faccia è fondamentale ancora prima della conoscenza del linguaggio difondamentale ancora prima della conoscenza del linguaggio di programmazione. In sostanza dobbiamoprogrammazione. In sostanza dobbiamo prima sapere modellare il prima sapere modellare il nostro pensiero (producendo quello che si chiama nostro pensiero (producendo quello che si chiama algoritmoalgoritmo) e poi) e poi cercare di spiegarlo con un linguaggio specializzato.cercare di spiegarlo con un linguaggio specializzato.

2 – Introduzione alla Programmazione: Azioni e Informazioni2 – Introduzione alla Programmazione: Azioni e Informazioni

Guide, , iOS, , Mac, , OSX, , SDK, , Tech Room

I mattoncini elementari che possiamo usare per la nostraI mattoncini elementari che possiamo usare per la nostra costruzione sono le azioni e le informazioni messe a disposizionecostruzione sono le azioni e le informazioni messe a disposizione dall’ambiente nel quale vogliamo programmare.dall’ambiente nel quale vogliamo programmare.

LE AZIONI.LE AZIONI.

Riprendiamo le azioni individuate nel programma precedenteRiprendiamo le azioni individuate nel programma precedente

memorizza l’oramemorizza l’ora

Page 5: Xcode4 Tutorial Completo

memorizza il canalememorizza il canale

leggi l’ora correnteleggi l’ora corrente

confronta due oreconfronta due ore

seleziona il canaleseleziona il canale

avvia la registrazioneavvia la registrazione

ferma la registrazioneferma la registrazione

ed analizziamole per individuare i mattoncini che abbiamo usato.ed analizziamole per individuare i mattoncini che abbiamo usato.

Che tipo di operazioni abbiamo fatto?Che tipo di operazioni abbiamo fatto?

Abbiamo ricevuto delle informazioni dall’esterno (ora di inizio,Abbiamo ricevuto delle informazioni dall’esterno (ora di inizio, ora di fine, canale), le abbiamo memorizzate, abbiamo fatto delleora di fine, canale), le abbiamo memorizzate, abbiamo fatto delle operazioni su di esse, in particolare le abbiamo confrontate, eoperazioni su di esse, in particolare le abbiamo confrontate, e abbiamo fatto delle operazioni di uscita, cioè siamo intervenuti sulabbiamo fatto delle operazioni di uscita, cioè siamo intervenuti sul registratore (seleziona il canale, avvia la registrazione, ferma laregistratore (seleziona il canale, avvia la registrazione, ferma la registrazione).registrazione).

Quindi quali tipi di mattoncini abbiamo individuato?Quindi quali tipi di mattoncini abbiamo individuato?

Operazioni di ingressoOperazioni di ingresso

Operazioni di memorizzazioneOperazioni di memorizzazione

Operazioni sulle informazioniOperazioni sulle informazioni

Operazioni di uscitaOperazioni di uscita

LE INFORMAZIONI.LE INFORMAZIONI.

Ogni programma un minimo significativo lavora su informazioni,Ogni programma un minimo significativo lavora su informazioni, anzi se ci pensate un attimo si parla proprio di anzi se ci pensate un attimo si parla proprio di elaborazione delleelaborazione delle informazioni, informaticainformazioni, informatica, evidenziando il ruolo principe che, evidenziando il ruolo principe che hanno le informazioni quando si programma.hanno le informazioni quando si programma.

Riprendiamo le informazioni del nostro esempioRiprendiamo le informazioni del nostro esempio

Page 6: Xcode4 Tutorial Completo

ora correnteora corrente

ora di inizio registrazioneora di inizio registrazione

canale da registrarecanale da registrare

ora di fine registrazioneora di fine registrazione

Cosa sono?Cosa sono?

In pratica sono i valori sui quali lavora il nostro programma.In pratica sono i valori sui quali lavora il nostro programma.

La prima cosa da notare è che quando impostiamo la nostraLa prima cosa da notare è che quando impostiamo la nostra registrazione andiamo a riempire dei campi con le informazioniregistrazione andiamo a riempire dei campi con le informazioni che vogliamo…quindi possiamo dire che ogni informazione deveche vogliamo…quindi possiamo dire che ogni informazione deve essere memorizzataessere memorizzata per poter essere recuperata successivamente, per poter essere recuperata successivamente, quindi ogni informazione ha il suo nome con il quale vienequindi ogni informazione ha il suo nome con il quale viene richiamato il valore nel nostro programma (Es. richiamato il valore nel nostro programma (Es. nomenome: canale da: canale da registrare, registrare, valorevalore: 523).: 523).

Inoltre nel campo Inoltre nel campo destinato all’ora non possiamo scrivere ildestinato all’ora non possiamo scrivere il canale da registrare, le due informazioni hanno strutture diverse…canale da registrare, le due informazioni hanno strutture diverse…ogni informazione ha un proprio insieme di valori (tipo di dato).ogni informazione ha un proprio insieme di valori (tipo di dato).

Questi due aspettiQuesti due aspetti

nome dell’informazionenome dell’informazione

tipo di datotipo di dato

sono alla base della gestione delle informazioni in un programma;sono alla base della gestione delle informazioni in un programma; e definiscono una e definiscono una variabile:variabile: la componente fondamentale della la componente fondamentale della gestione dei dati in un programma.gestione dei dati in un programma.

TIPI DI DATOTIPI DI DATO

Riprendiamo ancora le informazioni del nostro esempioRiprendiamo ancora le informazioni del nostro esempio

ora correnteora corrente

Page 7: Xcode4 Tutorial Completo

ora di inizio registrazioneora di inizio registrazione

canale da registrarecanale da registrare

ora di fine registrazioneora di fine registrazione

Abbiamo alcune informazioni in forma di data e ora (corrente, diAbbiamo alcune informazioni in forma di data e ora (corrente, di inizio registrazione, di stop) e una informazione in forma diinizio registrazione, di stop) e una informazione in forma di numero (canale). Questa caratteristica delle informazioni, cioènumero (canale). Questa caratteristica delle informazioni, cioè l’insieme dei valori che può assumere, è indicato dal tipo di dato.l’insieme dei valori che può assumere, è indicato dal tipo di dato.

I tipi di dato rispondono ad una macro classificazione.I tipi di dato rispondono ad una macro classificazione.

Tipi di dato scalari.Tipi di dato scalari.

Le informazioni viste finora contengono ognuna un solo valoreLe informazioni viste finora contengono ognuna un solo valore (una data-ora, un numero di canale).(una data-ora, un numero di canale).

Questi tipi di dato che contengono un solo valore sono chiamatiQuesti tipi di dato che contengono un solo valore sono chiamati scalari; esempi tipici di tipi scalari sono:scalari; esempi tipici di tipi scalari sono:

numero intero (1,2,-3,287,…)numero intero (1,2,-3,287,…)

numero decimale (2.5, 98.435, 3.1415,…)numero decimale (2.5, 98.435, 3.1415,…)

carattere (‘a’, ‘b’, ‘g’,…)carattere (‘a’, ‘b’, ‘g’,…)

stringa o sequenza di caratteri (“questa è una stringa”,stringa o sequenza di caratteri (“questa è una stringa”, “un’altra”, “c”,…)“un’altra”, “c”,…)

Ogni linguaggio mette a disposizione un insieme di tipi di datoOgni linguaggio mette a disposizione un insieme di tipi di dato scalare.scalare.

Tipi di dato composti.Tipi di dato composti.

Sono detti composti i tipi di dato che contengono più valori.Sono detti composti i tipi di dato che contengono più valori.

Introduciamoli con un esempio.Introduciamoli con un esempio.

Riprendiamo una parte del nostro programmaRiprendiamo una parte del nostro programma

leggi l’ora correnteleggi l’ora corrente

è l’ora fissata per l’inizio della registrazione?è l’ora fissata per l’inizio della registrazione?

se non lo èse non lo è

Page 8: Xcode4 Tutorial Completo

torna a 1torna a 1

altrimentialtrimenti

seleziona il canale da registrareseleziona il canale da registrare

avvia la registrazioneavvia la registrazione

leggi l’ora correnteleggi l’ora corrente

è l’ora fissata per la fine della registrazione?è l’ora fissata per la fine della registrazione?

se non lo èse non lo è

torna a 8torna a 8

altrimentialtrimenti

ferma la registrazioneferma la registrazione

Questo programma, gestisce una sola registrazione; comeQuesto programma, gestisce una sola registrazione; come dovremmo fare per gestire più impostazioni di registrazione in undovremmo fare per gestire più impostazioni di registrazione in un elenco?elenco?

leggi l’ora correnteleggi l’ora corrente

per ogni impostazione nell’elenco delle registrazioniper ogni impostazione nell’elenco delle registrazioni

se l’ora di inizio è uguale all’ora correntese l’ora di inizio è uguale all’ora corrente

seleziona il canale da registrareseleziona il canale da registrare

avvia la registrazioneavvia la registrazione

per ogni registrazione in corsoper ogni registrazione in corso

se l’ora di fine è uguale all’ora correntese l’ora di fine è uguale all’ora corrente

ferma la registrazioneferma la registrazione

torna a 1torna a 1

Ora quale nuovo tipo di informazioni abbiamo inserito? L’elencoOra quale nuovo tipo di informazioni abbiamo inserito? L’elenco delle registrazioni.delle registrazioni.

E’ un tipo di dato particolare perchè non contiene un solo valoreE’ un tipo di dato particolare perchè non contiene un solo valore ma una molteplicità di valori: tutte le registrazioni programmate.ma una molteplicità di valori: tutte le registrazioni programmate.

Questo tipo di dato composto che contiene dati omogenei (delloQuesto tipo di dato composto che contiene dati omogenei (dello stesso tipo) è detto elenco (nei linguaggio di programmazionestesso tipo) è detto elenco (nei linguaggio di programmazione arrayarray).).

Page 9: Xcode4 Tutorial Completo

Un altro tipo di dato che possiamo individuare (anche se eraUn altro tipo di dato che possiamo individuare (anche se era presente anche prima ma ora è più evidente) è la presente anche prima ma ora è più evidente) è la registrazioneregistrazione; è; è un dato composto a sua volta da tre informazioni non omogenee:un dato composto a sua volta da tre informazioni non omogenee:

data di iniziodata di inizio

canale da registrarecanale da registrare

data di finedata di fine

Quindi i dati scalari (non omogenei a differenza degli elenchi)Quindi i dati scalari (non omogenei a differenza degli elenchi) sono stati raggruppati in un altro tipo di dato, questo tipo disono stati raggruppati in un altro tipo di dato, questo tipo di costruzione si chiama costruzione si chiama strutturastruttura..

Un altro esempio di struttura lo possiamo vedere con la data (cheUn altro esempio di struttura lo possiamo vedere con la data (che può essere interpretata a volte come scalare e a volte comepuò essere interpretata a volte come scalare e a volte come composto), costituita da:composto), costituita da:

annoanno

mesemese

giornogiorno

oraora

minutominuto

Bene, riepilogando, la caratteristica Bene, riepilogando, la caratteristica tipo di datotipo di dato delle informazioni delle informazioni è uno degli aspetti fondamentali della gestione delle informazioniè uno degli aspetti fondamentali della gestione delle informazioni e può essere analizzato rispetto alla sua molteplicità:e può essere analizzato rispetto alla sua molteplicità:

tipo scalaretipo scalare

tipo compostotipo composto

e per i tipi composti si individuano due famiglie principalie per i tipi composti si individuano due famiglie principali

elenchi: insiemi di informazioni omogenee, si usano perelenchi: insiemi di informazioni omogenee, si usano per modellare le liste di informazioni (l’elenco dellemodellare le liste di informazioni (l’elenco delle registrazioni);registrazioni);

strutture: insiemi di dati non necessariamente omogenei, sistrutture: insiemi di dati non necessariamente omogenei, si usano per modellare le caratteristiche di una informazione (leusano per modellare le caratteristiche di una informazione (le informazioni che descrivono una registrazione)informazioni che descrivono una registrazione)

Page 10: Xcode4 Tutorial Completo

Provate a riflettere un po’ su queste classificazioni ed adProvate a riflettere un po’ su queste classificazioni ed ad assegnarle a concetti con i quali avete a che fare nella vostra vitaassegnarle a concetti con i quali avete a che fare nella vostra vita quotidiana.quotidiana.

3 – Introduzione alla Programmazione: I flussi di controllo3 – Introduzione alla Programmazione: I flussi di controllo

iOS, , Mac, , OSX, , SDK, , Tech Room

Riprendiamo il segmento di programmaRiprendiamo il segmento di programma

leggi l’ora correnteleggi l’ora corrente

per ogni impostazione nell’elenco delle registrazioniper ogni impostazione nell’elenco delle registrazioni

se l’ora di inizio è uguale all’ora correntese l’ora di inizio è uguale all’ora corrente

seleziona il canale da registrareseleziona il canale da registrare

avvia la registrazioneavvia la registrazione

per ogni registrazione in corsoper ogni registrazione in corso

se l’ora di fine è uguale all’ora correntese l’ora di fine è uguale all’ora corrente

ferma la registrazioneferma la registrazione

torna a 1torna a 1

Dal punto di vista della sequenza elaborativa, cioè della sequenzaDal punto di vista della sequenza elaborativa, cioè della sequenza

Page 11: Xcode4 Tutorial Completo

nella quale sono eseguite le operazioni cosa possiamo osservare?nella quale sono eseguite le operazioni cosa possiamo osservare?

Innanzitutto abbiamo la Innanzitutto abbiamo la successionesuccessione: l’istruzione 2 segue la 1,: l’istruzione 2 segue la 1, l’azione 5 segue la 4,…l’azione 5 segue la 4,…

Poi abbiamo l’Poi abbiamo l’esecuzione condizionataesecuzione condizionata: : se l’ora di inizio…(riga 3)se l’ora di inizio…(riga 3) allora esegui alcune cose altrimenti noallora esegui alcune cose altrimenti no, cioè un blocco di azioni, cioè un blocco di azioni viene eseguito solo se si verifica una determinata condizioneviene eseguito solo se si verifica una determinata condizione (riesci a trovare un’altra esecuzione condizionata in questo(riesci a trovare un’altra esecuzione condizionata in questo esempio?)esempio?)

Infine abbiamo ilInfine abbiamo il ciclo ciclo: p: per ogni impostazione…(riga 2) eseguier ogni impostazione…(riga 2) esegui certe azionicerte azioni, cioè un gruppo di azioni è eseguito ripetutamente su, cioè un gruppo di azioni è eseguito ripetutamente su determinati elementi (riesci a trovare un altro ciclo in questodeterminati elementi (riesci a trovare un altro ciclo in questo esempio?)esempio?)

Abbiamo così individuato le principali strutture base relativamenteAbbiamo così individuato le principali strutture base relativamente al flusso di controlloal flusso di controllo

la sequenza o successionela sequenza o successione

l’esecuzione condizionatal’esecuzione condizionata

il cicloil ciclo

BLOCCHIBLOCCHI

Riguardiamo il programma sopra e vediamo che c’è un insieme diRiguardiamo il programma sopra e vediamo che c’è un insieme di istruzioni che possiamo raggruppare, quelle che vanno da 2 a 5;istruzioni che possiamo raggruppare, quelle che vanno da 2 a 5; sono istruzioni che sono eseguite per ogni registrazione impostatasono istruzioni che sono eseguite per ogni registrazione impostata nel nostro apparecchio. Ne possiamo poi individuare un altro (lenel nostro apparecchio. Ne possiamo poi individuare un altro (le righe 4 e 5) che sono eseguite ogni volta che è necessario avviarerighe 4 e 5) che sono eseguite ogni volta che è necessario avviare una registrazione. Questa caratteristica di raggruppamento delleuna registrazione. Questa caratteristica di raggruppamento delle istruzioni è la istruzioni è la strutturazione a blocchistrutturazione a blocchi del nostro programma. del nostro programma.

Vediamo conVediamo con un semplice esempio come si può usare questa un semplice esempio come si può usare questa caratteristica considerando che ad un blocco possiamo dare uncaratteristica considerando che ad un blocco possiamo dare un nome.nome.

Page 12: Xcode4 Tutorial Completo

Scriviamo un blocco che chiamiamo Scriviamo un blocco che chiamiamo controlla avviocontrolla avvio

se l’ora di inizio è uguale all’ora correntese l’ora di inizio è uguale all’ora corrente

seleziona il canale da registrareseleziona il canale da registrare

avvia la registrazioneavvia la registrazione

con l’ausilio di questo blocco il nostro programma principalecon l’ausilio di questo blocco il nostro programma principale diventadiventa

leggi l’ora correnteleggi l’ora corrente

per ogni impostazione nell’elenco delle registrazioniper ogni impostazione nell’elenco delle registrazioni

controlla avviocontrolla avvio della registrazione della registrazione

per ogni registrazione in corsoper ogni registrazione in corso

se l’ora di fine è uguale all’ora correntese l’ora di fine è uguale all’ora corrente

ferma la registrazioneferma la registrazione

torna a 1torna a 1

Abbiamo ottenuto una maggiore leggibilità del nostro programma,Abbiamo ottenuto una maggiore leggibilità del nostro programma, inoltre abbiamo isolato un insieme di istruzioni che possiamoinoltre abbiamo isolato un insieme di istruzioni che possiamo richiamare da diverse parti del programma pur avendole scritterichiamare da diverse parti del programma pur avendole scritte una volta sola.una volta sola.

Prova ad individuare altri blocchi e ad isolarli con un nome daProva ad individuare altri blocchi e ad isolarli con un nome da usare all’interno del programma.usare all’interno del programma.

4 – Introduzione alla Programmazione: Riepilogo4 – Introduzione alla Programmazione: Riepilogo

Page 13: Xcode4 Tutorial Completo

iOS, , Mac, , OSX, , SDK, , Tech Room

Rimettiamo in ordine le cose.Rimettiamo in ordine le cose.

Bene, abbiamo concluso la parte introduttiva sui concetti generaliBene, abbiamo concluso la parte introduttiva sui concetti generali della programmazione. Può sembrare una parte inutile e prolissadella programmazione. Può sembrare una parte inutile e prolissa ma non è così. E’ importante che siano chiari i concetti generalima non è così. E’ importante che siano chiari i concetti generali che abbiamo visto e che sono alla base della scrittura deiche abbiamo visto e che sono alla base della scrittura dei programmi.programmi.

Abbiamo tre costrutti di baseAbbiamo tre costrutti di base

AzioniAzioni

Operazioni di ingressoOperazioni di ingresso

Operazioni di memorizzazioneOperazioni di memorizzazione

Operazioni sulle informazioniOperazioni sulle informazioni

Operazioni di uscitaOperazioni di uscita

InformazioniInformazioni

Tipi di dato scalariTipi di dato scalari

Tipi di dato compostiTipi di dato composti

Flussi di controlloFlussi di controllo

SequenzaSequenza

Esecuzione condizionataEsecuzione condizionata

CicloCiclo

BlocchiBlocchi

Ora con questi costrutti puoi provare a scrivere dei programmiOra con questi costrutti puoi provare a scrivere dei programmi (come quello del decoder che abbiamo avuto come esempio) tratti(come quello del decoder che abbiamo avuto come esempio) tratti dalla vita di tutti i giorni. Nelle prossime sezioni vedremo comedalla vita di tutti i giorni. Nelle prossime sezioni vedremo come

Page 14: Xcode4 Tutorial Completo

tradurli in istruzioni comprensibili ad un computer.tradurli in istruzioni comprensibili ad un computer.

5 – Il modello Object-Oriented: I fondamenti5 – Il modello Object-Oriented: I fondamenti

iOS, , Mac, , OSX, , SDK, , Tech Room

Abbiamo visto che scrivere un programma consisteAbbiamo visto che scrivere un programma consiste essenzialmente nell’organizzare il nostro pensiero (ed ilessenzialmente nell’organizzare il nostro pensiero (ed il procedimento che vogliamo eseguire) in maniera strutturata ed inprocedimento che vogliamo eseguire) in maniera strutturata ed in termini di Informazioni, Azioni, Flusso.termini di Informazioni, Azioni, Flusso.Ora però dobbiamo porci il problema di come esprimere questeOra però dobbiamo porci il problema di come esprimere queste cose in modo comprensibile al nostro amico computer. Secose in modo comprensibile al nostro amico computer. Se dovessimo dare le istruzioni ad un nostro amico inglese comedovessimo dare le istruzioni ad un nostro amico inglese come faremmo? semplice le scriveremmo in inglese ( e se nonfaremmo? semplice le scriveremmo in inglese ( e se non conosciamo l’inglese? …lo dovremo studiare); e se il nostroconosciamo l’inglese? …lo dovremo studiare); e se il nostro amico fosse francese? le scriveremmo in francese (e se nonamico fosse francese? le scriveremmo in francese (e se non conosciamo il francese? …lo dovremo studiare); e se il nostroconosciamo il francese? …lo dovremo studiare); e se il nostro amico fosse tedesco?…amico fosse tedesco?…

Allora è semplice basta sapere che lingua conosce il nostroAllora è semplice basta sapere che lingua conosce il nostro computer, già… che lingua conosce il nostro computer? Impulsicomputer, già… che lingua conosce il nostro computer? Impulsi elettrici presenti o assenti. Bhe è un po’ troppo lontana dalla nostraelettrici presenti o assenti. Bhe è un po’ troppo lontana dalla nostra comprensione per metterci a studiarla, per questo motivo sonocomprensione per metterci a studiarla, per questo motivo sono stati definiti dei linguaggi (di programmazione) che sonostati definiti dei linguaggi (di programmazione) che sono abbastanza simili al linguaggio umano da renderne agevoleabbastanza simili al linguaggio umano da renderne agevole l’utilizzo.l’utilizzo.

I diversi linguaggi consentono la modellazione del mondo secondoI diversi linguaggi consentono la modellazione del mondo secondo diversi approcci. L’approccio che noi seguiremo è quello chiamatodiversi approcci. L’approccio che noi seguiremo è quello chiamato Object-Oriented ed il linguaggio nel quale scriveremo i nostriObject-Oriented ed il linguaggio nel quale scriveremo i nostri

Page 15: Xcode4 Tutorial Completo

programmi sarà Objective-C.programmi sarà Objective-C.

LE CARATTERISTICHE DEL MODELLO OOLE CARATTERISTICHE DEL MODELLO OO

Scrivere un programma secondo il modello Object-OrientedScrivere un programma secondo il modello Object-Oriented significa modellare il nostro pensiero secondo una struttura disignifica modellare il nostro pensiero secondo una struttura di oggetti cooperanti. Può sembrare una cosa difficile ma se cioggetti cooperanti. Può sembrare una cosa difficile ma se ci pensiamo un attimo tutta la nostra esperienza è basata suglipensiamo un attimo tutta la nostra esperienza è basata sugli oggetti, noi interagiamo continuamente con oggetti, la nostra autooggetti, noi interagiamo continuamente con oggetti, la nostra auto è un oggetto, la nostra tv è un oggetto, il nostro iPhone è unè un oggetto, la nostra tv è un oggetto, il nostro iPhone è un oggetto, e così via…oggetto, e così via…

Cosa caratterizza un oggetto? Principalmente due coseCosa caratterizza un oggetto? Principalmente due cose

ha uno stato interno, cioè ha dei valori che lo caratterizzano:ha uno stato interno, cioè ha dei valori che lo caratterizzano: la nostra auto ha una cilindrata, un colore, un certo numero dila nostra auto ha una cilindrata, un colore, un certo numero di km percorsi, un certo quantitativo di benzina nel serbatoio,km percorsi, un certo quantitativo di benzina nel serbatoio,…; la nostra tv ha una dimensione espressa in pollici, ha una…; la nostra tv ha una dimensione espressa in pollici, ha una serie di canali memorizzati, ha un canale selezionato per laserie di canali memorizzati, ha un canale selezionato per la visualizzazione, ha un livello di volume,…visualizzazione, ha un livello di volume,…

sa fare alcune cose, cioè è in grado di eseguire delle azioni insa fare alcune cose, cioè è in grado di eseguire delle azioni in risposta a stimoli esterni: la nostra tv sa cambiare canale, sarisposta a stimoli esterni: la nostra tv sa cambiare canale, sa alzare ed abbassare il volume,…alzare ed abbassare il volume,…

Questi due concetti (lo stato interno e gli stimoli cui è in grado diQuesti due concetti (lo stato interno e gli stimoli cui è in grado di rispondere un oggetto) sono le caratteristiche fondamentali dellarispondere un oggetto) sono le caratteristiche fondamentali della programmazione OO.programmazione OO.

Riflettiamo un attimo su una cosa (prendendo come esempio ilRiflettiamo un attimo su una cosa (prendendo come esempio il televisore): in effetti tutti i televisori avranno una dimensione, unatelevisore): in effetti tutti i televisori avranno una dimensione, una

Page 16: Xcode4 Tutorial Completo

serie di canali memorizzati, un canale selezionato, un certo livelloserie di canali memorizzati, un canale selezionato, un certo livello di volume, e così via e tutti sapranno cambiare canale, alzare eddi volume, e così via e tutti sapranno cambiare canale, alzare ed abbassare il volume. Possiamo quindi dire che c’è unaabbassare il volume. Possiamo quindi dire che c’è una caratterizzazione comune a tutti i televisori, anche se poi ognicaratterizzazione comune a tutti i televisori, anche se poi ogni singolo televisore avrà valori diversi per il proprio stato (ilsingolo televisore avrà valori diversi per il proprio stato (il televisore nel soggiorno avrà una lista di canali e quello in cameratelevisore nel soggiorno avrà una lista di canali e quello in camera potrà averne una diversa, in soggiorno guardo un canale mentre inpotrà averne una diversa, in soggiorno guardo un canale mentre in camera ce n’è un altro, in soggiorno ascolto con un certo volume ecamera ce n’è un altro, in soggiorno ascolto con un certo volume e in camera con un altro,…).in camera con un altro,…).

Le caratteristiche comuni a tutti i televisori determinano quellaLe caratteristiche comuni a tutti i televisori determinano quella che si chiama che si chiama ClasseClasse, mentre ogni singolo televisore si chiama, mentre ogni singolo televisore si chiama IstanzaIstanza (o Oggetto). Nel nostro mondo abbiamo quindi la Classe (o Oggetto). Nel nostro mondo abbiamo quindi la Classe Televisore che dice che ogni Oggetto televisore avrà una lista deiTelevisore che dice che ogni Oggetto televisore avrà una lista dei canali, un canale selezionato, un livello per il volume, e sapràcanali, un canale selezionato, un livello per il volume, e saprà cambiare canalecambiare canale e alzare e alzare ed abbassare il volume e poi avremoed abbassare il volume e poi avremo tante Istanze di Televisore ognuna con i propri valori per letante Istanze di Televisore ognuna con i propri valori per le caratteristiche definite dalla Classe.caratteristiche definite dalla Classe.

E’ importante capire la differenza tra la classe Televisore (esisteE’ importante capire la differenza tra la classe Televisore (esiste una sola classe Televisore) e le sue istanze (ogni singolo televisoreuna sola classe Televisore) e le sue istanze (ogni singolo televisore esistente).esistente).

Riprendiamo adesso in esame il nostro televisore per vedere qualiRiprendiamo adesso in esame il nostro televisore per vedere quali altre caratteristiche ha.altre caratteristiche ha.

Sicuramente ha un consumo espresso in KwH, ma anche il nostroSicuramente ha un consumo espresso in KwH, ma anche il nostro frigorifero ha un consumo e anche il nostro forno a microonde, efrigorifero ha un consumo e anche il nostro forno a microonde, e così via. Possiamo dire che qualunque elettrodomestico ha uncosì via. Possiamo dire che qualunque elettrodomestico ha un

Page 17: Xcode4 Tutorial Completo

consumo, quindi diciamo che un televisore è un elettrodomesticoconsumo, quindi diciamo che un televisore è un elettrodomestico ed in quanto tale ha un consumo.ed in quanto tale ha un consumo.

Continuando l’analisi vediamo che il televisore (come qualunqueContinuando l’analisi vediamo che il televisore (come qualunque elettrodomestico) ha un peso, ma anche la nostra sedia ha un pesoelettrodomestico) ha un peso, ma anche la nostra sedia ha un peso pur non essendo un elettrodomestico (a meno che non siamo nelpur non essendo un elettrodomestico (a meno che non siamo nel braccio della morte); in realtà qualunque oggetto fisico (sullabraccio della morte); in realtà qualunque oggetto fisico (sulla terra ) ha un peso, abbiamo quindi un’altra caratteristica che arrivaterra ) ha un peso, abbiamo quindi un’altra caratteristica che arriva ad ogni elettrodomestico in quanto oggetto fisico.ad ogni elettrodomestico in quanto oggetto fisico.

Bene, che conclusioni possiamo trarre da questa analisi:Bene, che conclusioni possiamo trarre da questa analisi:

la prima è che le classi che descrivono gli oggetti si possonola prima è che le classi che descrivono gli oggetti si possono strutturare in strutturare in gerarchie.gerarchie.

Per descrivere una gerarchia si usano i terminiPer descrivere una gerarchia si usano i termini superclasse/sottoclasse: un televisore è un elettrodomestico che èsuperclasse/sottoclasse: un televisore è un elettrodomestico che è un oggetto fisico; elettrodomestico è superclasse di televisore eun oggetto fisico; elettrodomestico è superclasse di televisore e sottoclasse di oggetto fisico, televisore è sottoclasse disottoclasse di oggetto fisico, televisore è sottoclasse di elettrodomestico e quindi anche di oggetto fisico;elettrodomestico e quindi anche di oggetto fisico;

la seconda è che le caratteristiche di una superclasse sonola seconda è che le caratteristiche di una superclasse sono ereditateereditate dalle sottoclassi: un televisore, oltre ad avere dei dalle sottoclassi: un televisore, oltre ad avere dei canali da mostrare ha un consumo in quanto elettrodomesticocanali da mostrare ha un consumo in quanto elettrodomestico ed un peso in quanto oggetto fisico;ed un peso in quanto oggetto fisico;

Page 18: Xcode4 Tutorial Completo

Vediamo un altro aspetto.Vediamo un altro aspetto.

Quando vogliamo cambiare il canale che stiamo guardando comeQuando vogliamo cambiare il canale che stiamo guardando come facciamo? Apriamo il televisore e mettiamo le mani sullefacciamo? Apriamo il televisore e mettiamo le mani sulle componenti elettroniche fino a quando non riusciamo a vedere ilcomponenti elettroniche fino a quando non riusciamo a vedere il canale desiderato?canale desiderato?

Immagino di no, anzi vi sconsiglio vivamente dal provarci. PerchèImmagino di no, anzi vi sconsiglio vivamente dal provarci. Perchè non possiamo farlo? Perchè in realtà noi non sappiamo come ilnon possiamo farlo? Perchè in realtà noi non sappiamo come il televisore faccia per cambiare la frequenza del sintonizzatoretelevisore faccia per cambiare la frequenza del sintonizzatore (inoltre ogni costruttore di televisori avrà un suo proprio circuito(inoltre ogni costruttore di televisori avrà un suo proprio circuito per farlo); quello che possiamo fare è usare la per farlo); quello che possiamo fare è usare la funzionalitàfunzionalità che il che il tuo televisore ci mette a disposizione per cambiare canale, poi satuo televisore ci mette a disposizione per cambiare canale, poi sa lui come aggiornare il suo stato interno. Lo stesso ragionamento silui come aggiornare il suo stato interno. Lo stesso ragionamento si applica alla modifica del volume, anche qui potremo solo usare leapplica alla modifica del volume, anche qui potremo solo usare le due funzioni per alzare ed abbassare il volume e non potremodue funzioni per alzare ed abbassare il volume e non potremo agire direttamente sullo stato elettronico del televisore.agire direttamente sullo stato elettronico del televisore.

Questa caratteristica della modellazione ad oggetti si chiamaQuesta caratteristica della modellazione ad oggetti si chiama incapsulamentoincapsulamento: impone che dall’esterno si possa agire sullo stato: impone che dall’esterno si possa agire sullo stato interno di un oggetto solo attraverso le funzionalità che espone.interno di un oggetto solo attraverso le funzionalità che espone. Solo l’oggetto stesso può accedere alla sua struttura interna perSolo l’oggetto stesso può accedere alla sua struttura interna per ottenere l’effetto voluto: noi possiamo solo chiedere al televisoreottenere l’effetto voluto: noi possiamo solo chiedere al televisore di passare dal canale 1 al canale 2; lui invece sa come risponderedi passare dal canale 1 al canale 2; lui invece sa come rispondere alla richiesta aggiornando la frequenza del sintonizzatore peralla richiesta aggiornando la frequenza del sintonizzatore per vedere il nuovo canale.vedere il nuovo canale.

Dalle prossime lezioni inizieremo a vedere come si implementanoDalle prossime lezioni inizieremo a vedere come si implementano nel linguaggio di programmazione Objective-C i concetti vistinel linguaggio di programmazione Objective-C i concetti visti finora.finora.

Page 19: Xcode4 Tutorial Completo

6 – Iniziamo con XCode6 – Iniziamo con XCode

iOS, , Mac, , OSX, , SDK, , Tech Room

A questo punto direi che è il momento di cominciare a vedere unA questo punto direi che è il momento di cominciare a vedere un po’ di programmazione sul computer. Iniziando quindi a vedere unpo’ di programmazione sul computer. Iniziando quindi a vedere un linguaggio di programmazione (Objective-C) e quali sono glilinguaggio di programmazione (Objective-C) e quali sono gli strumenti che si usano per scrivere un programma.strumenti che si usano per scrivere un programma.

La prima cosa di cui abbiamo bisogno è un ambiente nel qualeLa prima cosa di cui abbiamo bisogno è un ambiente nel quale digitare il programma, questa componente si chiama Editor, è ladigitare il programma, questa componente si chiama Editor, è la parte che in maniera più o meno evoluta ci aiuta scrivere il codiceparte che in maniera più o meno evoluta ci aiuta scrivere il codice del nostro programma ad esempio può supportare in manieradel nostro programma ad esempio può supportare in maniera automatica l’indentatura delle righe del codice per una maggioreautomatica l’indentatura delle righe del codice per una maggiore leggibilità o l’evidenziazione di parti del programma con colorileggibilità o l’evidenziazione di parti del programma con colori diversi per mostrarne il tipo.diversi per mostrarne il tipo.

Abbiamo detto che il nostro computer riconosce solo la presenza oAbbiamo detto che il nostro computer riconosce solo la presenza o assenza di corrente mentre noi useremo un linguaggio moltoassenza di corrente mentre noi useremo un linguaggio molto vicino a quello umano per scrivere il programma nell’Editor;vicino a quello umano per scrivere il programma nell’Editor; come colmiamo la distanza tra il linguaggio del computer e quellocome colmiamo la distanza tra il linguaggio del computer e quello di programmazione? Ci pensa un altro componente che prende ildi programmazione? Ci pensa un altro componente che prende il programma scritto nell’Editor e lo traduce in una formaprogramma scritto nell’Editor e lo traduce in una forma comprensibile al computer; questo componente si chiamacomprensibile al computer; questo componente si chiama Compilatore/Linker chiamato di solito solo Compilatore.Compilatore/Linker chiamato di solito solo Compilatore.

Page 20: Xcode4 Tutorial Completo

Questi 2 componenti (Editor e Compilatore) oltre a tanti altri che,Questi 2 componenti (Editor e Compilatore) oltre a tanti altri che, in parte, vedremo in seguito fanno parte di XCode: l’ambiente diin parte, vedremo in seguito fanno parte di XCode: l’ambiente di sviluppo integrato per OSX.sviluppo integrato per OSX.

Durante queste lezioni noi useremo XCode 4.Durante queste lezioni noi useremo XCode 4.

Bene ora lanciamo XCode, nella pagina di benvenuto scegliamoBene ora lanciamo XCode, nella pagina di benvenuto scegliamo Cancel.Cancel.

Ora dovremo creare un nuovo progetto XCodeOra dovremo creare un nuovo progetto XCode

il progetto è il contenitore del tuo programma per XCode.il progetto è il contenitore del tuo programma per XCode.

Andiamo nel MenuAndiamo nel Menu

File>New>New ProjectFile>New>New Project

e ci saranno proposti i template (modelli) di progetto giàe ci saranno proposti i template (modelli) di progetto già predisposti da Apple.predisposti da Apple.

Questo primo esempio sarà un progetto introduttivo sui concettiQuesto primo esempio sarà un progetto introduttivo sui concetti base che abbiamo visto e quindi non farà uso dell’interfacciabase che abbiamo visto e quindi non farà uso dell’interfaccia utente dell’iPhone (ancora un attimo di pazienza)utente dell’iPhone (ancora un attimo di pazienza) quindiquindi scegliamo il template Command Line Tool che troviamoscegliamo il template Command Line Tool che troviamo nel gruppo Application della sezione Mac OS X.nel gruppo Application della sezione Mac OS X.

Page 21: Xcode4 Tutorial Completo

Nella finestra successiva (Invio o Next) diamo un nome alNella finestra successiva (Invio o Next) diamo un nome al progetto, ad esempio PrimoProgetto, e nel menu a tendinaprogetto, ad esempio PrimoProgetto, e nel menu a tendina lasciamo selezionata la voce Foundation; infine scegliamo lalasciamo selezionata la voce Foundation; infine scegliamo la directory dove salvare il progetto.directory dove salvare il progetto.

A questo punto siamo dentro XCode con il template del progettoA questo punto siamo dentro XCode con il template del progetto predisposto, in realtà si tratta di un progetto già eseguibile ma perpredisposto, in realtà si tratta di un progetto già eseguibile ma per il momento non lo eseguiamo.il momento non lo eseguiamo.

Analizziamo invece cosa c’è.Analizziamo invece cosa c’è.

Page 22: Xcode4 Tutorial Completo

Nel frame di sinistra selezionando la vista Nel frame di sinistra selezionando la vista Gruppi e FilesGruppi e Files (la prima (la prima icona nella barra di controllo) possiamo vedere i file che fannoicona nella barra di controllo) possiamo vedere i file che fanno parte del progetto. Selezioniamo il file main.m ed analizziamone ilparte del progetto. Selezioniamo il file main.m ed analizziamone il contenuto.contenuto.

Un programma è costituito di istruzioni; in un programmaUn programma è costituito di istruzioni; in un programma Objective-C possiamo avere 3 tipi di istruzioni:Objective-C possiamo avere 3 tipi di istruzioni:

le istruzioni con la le istruzioni con la logicalogica del nostro programma (quello che del nostro programma (quello che vogliamo che il programma faccia); se usiamo la metaforavogliamo che il programma faccia); se usiamo la metafora della traduzione è il testo che vogliamo tradurre;della traduzione è il testo che vogliamo tradurre;

le istruzioni che diamo al compilatore (le istruzioni che diamo al compilatore (direttivedirettive), si), si riconoscono perché iniziano con #, sono istruzioni che nonriconoscono perché iniziano con #, sono istruzioni che non servono alla logica del programma ma sono indicazioni cheservono alla logica del programma ma sono indicazioni che diamo al compilatore; nella metafora è come se dopo averdiamo al compilatore; nella metafora è come se dopo aver dato il foglietto con il testo da tradurre al nostro amicodato il foglietto con il testo da tradurre al nostro amico traduttore gli dicessimo di usare la penna rossa per i verbi etraduttore gli dicessimo di usare la penna rossa per i verbi e quella blu per i nomi;quella blu per i nomi;

i i commenti,commenti, si riconoscono perchè iniziano con // o con /* se si riconoscono perchè iniziano con // o con /* se si sviluppano su più righe nel qual caso sono chiusi da */;si sviluppano su più righe nel qual caso sono chiusi da */; servono ad aiutare chi dovrà rileggere il nostro foglietto eservono ad aiutare chi dovrà rileggere il nostro foglietto e capire perché abbiamo scritto certe cose ad esempiocapire perché abbiamo scritto certe cose ad esempio potremmo scrivere all’inizio del nostro foglietto “Questo è unpotremmo scrivere all’inizio del nostro foglietto “Questo è un brano dall’atto 2 dell’Amleto”brano dall’atto 2 dell’Amleto”

Riesci a riconoscere a quale tipo di istruzione appartiene ognunaRiesci a riconoscere a quale tipo di istruzione appartiene ognuna di quelle del file main.m?di quelle del file main.m?

……

Ricordiamo che nel capitolo 6 abbiamo parlato dei blocchi aRicordiamo che nel capitolo 6 abbiamo parlato dei blocchi a proposito dei flussi di controllo.proposito dei flussi di controllo.

Bene in Objective-C i blocchi di istruzioni sono racchiusi traBene in Objective-C i blocchi di istruzioni sono racchiusi tra parentesi graffe { }, riesci ad individuare un blocco di istruzioni?parentesi graffe { }, riesci ad individuare un blocco di istruzioni?

……

Un particolare tipo di blocchi di istruzioni è chiamato Un particolare tipo di blocchi di istruzioni è chiamato funzionefunzione ed ed

Page 23: Xcode4 Tutorial Completo

ha la caratteristica di dare un nome al blocco in modo da poterloha la caratteristica di dare un nome al blocco in modo da poterlo richiamare da altre parti del tuo programma. La sintassi dellarichiamare da altre parti del tuo programma. La sintassi della funzione èfunzione è

tipoRisultato nomeFunzione(…) {tipoRisultato nomeFunzione(…) {

……

……

}}

Man mano che ne avremo bisogno analizzeremo le varie partiMan mano che ne avremo bisogno analizzeremo le varie parti della struttura di una funzione. Per ora è importante capire chedella struttura di una funzione. Per ora è importante capire che quella scritta significa che l’esecuzione delle istruzioni racchiusequella scritta significa che l’esecuzione delle istruzioni racchiuse tra le { } può essere richiesta dall’interno del tuo programmatra le { } può essere richiesta dall’interno del tuo programma semplicemente scrivendo il nome della funzione, comunque losemplicemente scrivendo il nome della funzione, comunque lo vedremo meglio in seguito.vedremo meglio in seguito.

Riesci a trovare una funzione, nel file main.m?Riesci a trovare una funzione, nel file main.m?

……

In realtà l’unica funzione che trovi (si chiama main) è il punto diIn realtà l’unica funzione che trovi (si chiama main) è il punto di partenza di ogni programma Objective-C; in ogni programmapartenza di ogni programma Objective-C; in ogni programma Objective-C Objective-C devedeve esserci una funzione main ed è quella che sarà esserci una funzione main ed è quella che sarà eseguita quando lanci la tua applicazione.eseguita quando lanci la tua applicazione.

Bene quindi, le istruzioni racchiuse tra le { } costituiscono inBene quindi, le istruzioni racchiuse tra le { } costituiscono in questo momento il tuo programma.questo momento il tuo programma.

La prima cosa che puoi notare è che tutte le istruzioni di logica,La prima cosa che puoi notare è che tutte le istruzioni di logica, quindi esclusi i commenti e le direttive (vedi sopra) terminano conquindi esclusi i commenti e le direttive (vedi sopra) terminano con il ‘;’. Ricordi che a proposito del flusso di controllo avevamoil ‘;’. Ricordi che a proposito del flusso di controllo avevamo parlato della sequenza? Bene in Objective-C la sequenza diparlato della sequenza? Bene in Objective-C la sequenza di istruzioni è segnalata dal ‘;’ stiamo cioè dicendo che finisceistruzioni è segnalata dal ‘;’ stiamo cioè dicendo che finisce un’istruzione e si passa alla successiva.un’istruzione e si passa alla successiva.

Ora, per concludere questa sessione, prova ad eseguire ilOra, per concludere questa sessione, prova ad eseguire il programma cliccando sul bottone Run.programma cliccando sul bottone Run.

Page 24: Xcode4 Tutorial Completo

Si aprirà un pannello inferiore nella finestra di XCode e avrai unaSi aprirà un pannello inferiore nella finestra di XCode e avrai una scritta tipo quella evidenziata in figura. Cioè oltre ad una serie discritta tipo quella evidenziata in figura. Cioè oltre ad una serie di informazioni di controllo che ignoriamo, il tuo programma scriveinformazioni di controllo che ignoriamo, il tuo programma scrive Hello, World!Hello, World!

Se torni a guardare nel codice vedrai che Hello, World! è scrittoSe torni a guardare nel codice vedrai che Hello, World! è scritto all’interno dell’istruzione NSLog(…). Bene ora prova a cancellareall’interno dell’istruzione NSLog(…). Bene ora prova a cancellare Hello, World! e a scriverci Ciao, …! al posto dei puntini scrivici ilHello, World! e a scriverci Ciao, …! al posto dei puntini scrivici il tuo nome, ed esegui di nuovo il programma. A questo punto iltuo nome, ed esegui di nuovo il programma. A questo punto il programma dovrebbe salutare solo te. Abbiamo quindi trovato laprogramma dovrebbe salutare solo te. Abbiamo quindi trovato la prima istruzione di Objective-C che è quella che consente alprima istruzione di Objective-C che è quella che consente al programma di scrivere qualcosa e quindi di comunicare conprogramma di scrivere qualcosa e quindi di comunicare con l’esterno; ricordi la sessione sulla Azioni? Bene abbiamo trovatol’esterno; ricordi la sessione sulla Azioni? Bene abbiamo trovato la prima delle azioni di uscita.la prima delle azioni di uscita.

Per ora basta così, dalla prossima sessione cominceremo aPer ora basta così, dalla prossima sessione cominceremo a modificare questo template per fare un piccolo programma che cimodificare questo template per fare un piccolo programma che ci consenta, passo passo di vedere alcuni aspetti di Objective-C.consenta, passo passo di vedere alcuni aspetti di Objective-C.

Page 25: Xcode4 Tutorial Completo

7 – Creiamo le nostre classi7 – Creiamo le nostre classi

iOS, , Mac, , OSX, , SDK, , Tech Room

Ora cominciamo a far crescere il nostro programma.Ora cominciamo a far crescere il nostro programma.Prima di scrivere del codice è necessario sapere cosa si vuole fare:Prima di scrivere del codice è necessario sapere cosa si vuole fare: noi scriveremo un programma che realizzi il modello semplificatonoi scriveremo un programma che realizzi il modello semplificato di televisore che abbiamo visto nel capitolo 5.di televisore che abbiamo visto nel capitolo 5.

Quindi cosa vogliamo che sia gestito nel nostro programma?Quindi cosa vogliamo che sia gestito nel nostro programma?

Oggetto fisicoOggetto fisico

Caratteristiche:Caratteristiche:

Peso (espresso in Kg): valore numerico decimalePeso (espresso in Kg): valore numerico decimale

Elettrodomestico Elettrodomestico (è un oggetto fisico)(è un oggetto fisico)

CaratteristicheCaratteristiche

Consumo (espresso in W/h): valore numerico interoConsumo (espresso in W/h): valore numerico intero

TelevisoreTelevisore (è un elettrodomestico) (è un elettrodomestico)

CaratteristicheCaratteristiche

canali memorizzati: elenco di nomi di canalicanali memorizzati: elenco di nomi di canali

canale sintonizzato: numero intero che indica lacanale sintonizzato: numero intero che indica la

Page 26: Xcode4 Tutorial Completo

posizione del canale sintonizzato nell’elenco deiposizione del canale sintonizzato nell’elenco dei memorizzatimemorizzati

livello del volume: numero intero da 0 a 100livello del volume: numero intero da 0 a 100

FunzionalitàFunzionalità (cosa sa fare il nostro televisore) (cosa sa fare il nostro televisore)

ricerca canali: valorizza l’elenco dei canali memorizzatiricerca canali: valorizza l’elenco dei canali memorizzati

mostra elenco: mostra i nomi dei canali memorizzatimostra elenco: mostra i nomi dei canali memorizzati

mostra il canale: scrive il nome del canale selezionatomostra il canale: scrive il nome del canale selezionato

alza il volume: aumenta di 1 il livello del volumealza il volume: aumenta di 1 il livello del volume

abbassa il volume: decrementa di 1 il livello del volumeabbassa il volume: decrementa di 1 il livello del volume

vai al canale x : sintonizza il televisore sul canale xvai al canale x : sintonizza il televisore sul canale x

Tutto chiaro? E’ inutile andare a vedere come si fanno le cose inTutto chiaro? E’ inutile andare a vedere come si fanno le cose in Objective-C se non sono chiare nella nostra mente.Objective-C se non sono chiare nella nostra mente.

……

Bene ora andiamo in XCode e creiamo le 3 classi e la loroBene ora andiamo in XCode e creiamo le 3 classi e la loro gerarchia.gerarchia.

Fai click con il bottone destro sulla cartella con il nome delFai click con il bottone destro sulla cartella con il nome del progetto, scegli New File e scegli Objective-C class tra i templateprogetto, scegli New File e scegli Objective-C class tra i template che trovi alla voce Cocoache trovi alla voce Cocoa

Page 27: Xcode4 Tutorial Completo

Nella finestra successiva ti viene proposta la superclasseNella finestra successiva ti viene proposta la superclasse NSObject .NSObject .

Page 28: Xcode4 Tutorial Completo

Si tratta di una classe particolare. E’ la classe fornita dal sistemaSi tratta di una classe particolare. E’ la classe fornita dal sistema come madre di tutte le classi (ci sono delle eccezioni ma si trattacome madre di tutte le classi (ci sono delle eccezioni ma si tratta di casi talmente particolari che per il momento li ignoriamo). Indi casi talmente particolari che per il momento li ignoriamo). In pratica è la radice di tutte le gerarchie, quindi tutte le classi sonopratica è la radice di tutte le gerarchie, quindi tutte le classi sono direttamente o indirettamente sottoclassi di NSObjectdirettamente o indirettamente sottoclassi di NSObject ereditandone (ricorda cosa abbiamo detto nel cap.5) quindi tutte leereditandone (ricorda cosa abbiamo detto nel cap.5) quindi tutte le caratteristiche e le funzionalità.caratteristiche e le funzionalità.

Nella finestra successiva dobbiamo dare un nome alla nostraNella finestra successiva dobbiamo dare un nome alla nostra classe, scriviamo OggettoFisico nel campo SaveAs.classe, scriviamo OggettoFisico nel campo SaveAs.

Bene, cosa abbiamo ora in XCode?Bene, cosa abbiamo ora in XCode?

Abbiamo 2 nuovi file OggettoFisico.h e OggettoFisico.m.Abbiamo 2 nuovi file OggettoFisico.h e OggettoFisico.m. Generalizzando possiamo dire cheGeneralizzando possiamo dire che

ogni classe in Objective-C viene codificata attraverso due file ilogni classe in Objective-C viene codificata attraverso due file il nomeClasse.h (chiamato header) e nomeClasse.m (chiamatonomeClasse.h (chiamato header) e nomeClasse.m (chiamato implementation); nello header si codifica la struttura e cosa fa laimplementation); nello header si codifica la struttura e cosa fa la nostra classe mentre nell’implementation si codifica come lo fa.nostra classe mentre nell’implementation si codifica come lo fa.

Selezioniamo il file OggettoFisico.h. e analizziamone il contenuto.Selezioniamo il file OggettoFisico.h. e analizziamone il contenuto.

Page 29: Xcode4 Tutorial Completo

La rigaLa riga

@interface OggettoFisico : NSObject@interface OggettoFisico : NSObject

dichiara al sistema la Classe dicendo qual è il suo nomedichiara al sistema la Classe dicendo qual è il suo nome (OggettoFisico) e qual è la sua superclasse (NSObject) in quanto(OggettoFisico) e qual è la sua superclasse (NSObject) in quanto tutte le classi in Objective-C devono avere una superclasse.tutte le classi in Objective-C devono avere una superclasse. Capiremo meglio le diverse parti di una Classe man mano che leCapiremo meglio le diverse parti di una Classe man mano che le useremo.useremo.

Ora, in maniera analoga a quanto fatto per OggettoFisico, creiamoOra, in maniera analoga a quanto fatto per OggettoFisico, creiamo le altre due classi Elettrodomestico e Televisore, sempre comele altre due classi Elettrodomestico e Televisore, sempre come sottoclassi di NSObject.sottoclassi di NSObject.

Page 30: Xcode4 Tutorial Completo

Abbiamo quindi creato le nostre tre classi (OggettoFisico,Abbiamo quindi creato le nostre tre classi (OggettoFisico, Elettrodomestico e Televisore) ma per il momento sono tutte e treElettrodomestico e Televisore) ma per il momento sono tutte e tre direttamente sottoclassi di NSObject e non c’è alcuna relazionedirettamente sottoclassi di NSObject e non c’è alcuna relazione gerarchica tra di esse mentre noi vogliamo che Televisore siagerarchica tra di esse mentre noi vogliamo che Televisore sia sottoclasse di Elettrodomestico, a sua volta sottoclasse disottoclasse di Elettrodomestico, a sua volta sottoclasse di OggettoFisico. Procediamo quindi alla creazione della gerarchia.OggettoFisico. Procediamo quindi alla creazione della gerarchia.

Iniziamo con il definire la relazione tra Elettrodomestico eIniziamo con il definire la relazione tra Elettrodomestico e OggettoFisico.OggettoFisico.

Selezioniamo Elettrodomestico.h e sostituiamo NSObject conSelezioniamo Elettrodomestico.h e sostituiamo NSObject con OggettoFisico, dicendo in tal modo che Elettrodomestico èOggettoFisico, dicendo in tal modo che Elettrodomestico è sottoclasse di OggettoFisico. Come possiamo vedere XCodesottoclasse di OggettoFisico. Come possiamo vedere XCode segnala un errore in quella riga. Questo perchè il compilatore,segnala un errore in quella riga. Questo perchè il compilatore, all’interno del file Elettrodomestico.h. non sa chi siaall’interno del file Elettrodomestico.h. non sa chi sia OggettoFisico. Allora facciamoglielo conoscere aggiungendo laOggettoFisico. Allora facciamoglielo conoscere aggiungendo la rigariga

#import “OggettoFisico.h”#import “OggettoFisico.h”

prima della rigaprima della riga

@interface…@interface…

L’istruzione che abbiamo appena inserito è una direttiva alL’istruzione che abbiamo appena inserito è una direttiva al

Page 31: Xcode4 Tutorial Completo

compilatore (inizia con #) che rende visibile la classecompilatore (inizia con #) che rende visibile la classe OggettoFisico. Osservando meglio il file ora possiamo notare cheOggettoFisico. Osservando meglio il file ora possiamo notare che c’era già un #import e lo stesso è all’inizio del file main.m, lac’era già un #import e lo stesso è all’inizio del file main.m, la struttura è leggermente diversa ma lo scopo è lo stesso; renderestruttura è leggermente diversa ma lo scopo è lo stesso; rendere visibili, all’interno dei programmi che scriveremo, tutta una serievisibili, all’interno dei programmi che scriveremo, tutta una serie di elementi che sono stati predisposti da Apple e che fanno partedi elementi che sono stati predisposti da Apple e che fanno parte del linguaggio Objective-C.del linguaggio Objective-C.

In maniera analoga creiamo la relazione tra Televisore edIn maniera analoga creiamo la relazione tra Televisore ed Elettrodomestico, aprendo il file Televisore.h e sostituendoElettrodomestico, aprendo il file Televisore.h e sostituendo NSObject con Elettrodomestico. Anche in questo caso per evitareNSObject con Elettrodomestico. Anche in questo caso per evitare l’errore e rendere visibile la classe Elettrodomestico dobbiamol’errore e rendere visibile la classe Elettrodomestico dobbiamo inserire la direttivainserire la direttiva

#import “Elettrodomestico.h”#import “Elettrodomestico.h”

A questo punto abbiamo creato le tre classi e la gerarchia tra diA questo punto abbiamo creato le tre classi e la gerarchia tra di esse.esse.

Nelle prossime lezioni diremo come sono fatte queste classi, inNelle prossime lezioni diremo come sono fatte queste classi, in termini di caratteristiche e funzionalità e creeremo alcuni oggetti.termini di caratteristiche e funzionalità e creeremo alcuni oggetti.

8 – Definiamo le Classi8 – Definiamo le Classi

iOS, , Mac, , OSX, , SDK, , Tech Room

Nella lezione precedente abbiamo creato le tre classiNella lezione precedente abbiamo creato le tre classi (OggettoFisico, Elettrodomestico e Televisore) e messe in(OggettoFisico, Elettrodomestico e Televisore) e messe in relazione gerarchica, ma non abbiamo ancora detto come sonorelazione gerarchica, ma non abbiamo ancora detto come sono fatte queste classi in termini di stato interno e di funzionalità ed èfatte queste classi in termini di stato interno e di funzionalità ed è quello che faremo ora; però prima di andare avanti cominciamoquello che faremo ora; però prima di andare avanti cominciamo con il dare il nome corretto alle cose.con il dare il nome corretto alle cose.Lo stato interno, come abbiamo detto nel cap 5, è costituito dalleLo stato interno, come abbiamo detto nel cap 5, è costituito dalle

Page 32: Xcode4 Tutorial Completo

informazioni gestite dell’oggetto (lista dei canali, canaleinformazioni gestite dell’oggetto (lista dei canali, canale selezionato, livello del volume,…)selezionato, livello del volume,…) e, come abbiamo detto nel cap. e, come abbiamo detto nel cap. 2, le informazioni sono modellate attraverso le variabili,2, le informazioni sono modellate attraverso le variabili, all’interno di una classe si indicano con il termine di all’interno di una classe si indicano con il termine di variabile divariabile di istanzaistanza e come tutte le variabili sono descritte da un nome ed un e come tutte le variabili sono descritte da un nome ed un tipo di dato.tipo di dato.

Le funzionalità sono definite attraverso blocchi di codice cui èLe funzionalità sono definite attraverso blocchi di codice cui è assegnato un nome e sono indicati con il nome di assegnato un nome e sono indicati con il nome di metodometodo.. L’esecuzione di un metodo (cioè del blocco di istruzioni associateL’esecuzione di un metodo (cioè del blocco di istruzioni associate a quel nome) è richiesta con la sintassia quel nome) è richiesta con la sintassi

[oggetto nomeMetodo][oggetto nomeMetodo]

lo vedremo molte volte in seguito e avremo modo dilo vedremo molte volte in seguito e avremo modo di approfondirne l’uso visto che è l’istruzione base dellaapprofondirne l’uso visto che è l’istruzione base della programmazione Object-Oriented.programmazione Object-Oriented.

Bene ora che abbiamo introdotto i termini corretti riprendiamo ilBene ora che abbiamo introdotto i termini corretti riprendiamo il nostro programma per definire le variabili di istanza ed i metodinostro programma per definire le variabili di istanza ed i metodi delle nostre tre classi.delle nostre tre classi.

Apriamo il nostro progetto in XCode. La prima cosa che faremo èApriamo il nostro progetto in XCode. La prima cosa che faremo è completare la definizione della classe OggettoFisico, ricordiamocompletare la definizione della classe OggettoFisico, ricordiamo che quello che vogliamo èche quello che vogliamo è

Oggetto fisicoOggetto fisico

CaratteristicheCaratteristiche

Peso (espresso in Kg): valore numerico decimalePeso (espresso in Kg): valore numerico decimale

Cioè vogliamo che un oggetto di questa classe sia caratterizzato daCioè vogliamo che un oggetto di questa classe sia caratterizzato da un Peso, per fare ciò dobbiamo dichiarare all’interno della nostraun Peso, per fare ciò dobbiamo dichiarare all’interno della nostra classe una variabile di istanza di tipo numerico.classe una variabile di istanza di tipo numerico.

Selezioniamo il file OggettoFisico.h e all’interno delle parentesiSelezioniamo il file OggettoFisico.h e all’interno delle parentesi {}, dopo la riga{}, dopo la riga

@private@private

scriviamoscriviamo

Page 33: Xcode4 Tutorial Completo

NSDecimalNumber *peso;NSDecimalNumber *peso;in questo modo dichiariamo una variabile di istanza di nome pesoin questo modo dichiariamo una variabile di istanza di nome peso e di tipo NSDecimalNumber, questo tipo è definito tramite unae di tipo NSDecimalNumber, questo tipo è definito tramite una classe Objective-C e si usa per modellare valori numerici, fra unclasse Objective-C e si usa per modellare valori numerici, fra un attimo vedremo come si usa appena dovremo lavorare con inattimo vedremo come si usa appena dovremo lavorare con in valori della variabile peso. Nella dichiarazione di questa variabilevalori della variabile peso. Nella dichiarazione di questa variabile osserva bene la presenza di un *, come vedremo innumerevoliosserva bene la presenza di un *, come vedremo innumerevoli volte è necessario usarlo sempre nella dichiarazione di variabilivolte è necessario usarlo sempre nella dichiarazione di variabili riferite a Classi. Non dimentichiamo di scrivere ‘;’ alla fine dellariferite a Classi. Non dimentichiamo di scrivere ‘;’ alla fine della riga per dire che è finita un’istruzione.riga per dire che è finita un’istruzione.

Passiamo ora a codificarePassiamo ora a codificare

Elettrodomestico Elettrodomestico (è un oggetto fisico)(è un oggetto fisico)

CaratteristicheCaratteristiche

Consumo (espresso in W/h): valore numerico interoConsumo (espresso in W/h): valore numerico intero

Quindi apriamo il file Elettrodomestico.h e all’interno delle {},Quindi apriamo il file Elettrodomestico.h e all’interno delle {}, dopo la rigadopo la riga

@private@private

scriviamoscriviamo

NSDecimalNumber *consumo;NSDecimalNumber *consumo;

in questo modo dichiariamo una variabile di istanza di nomein questo modo dichiariamo una variabile di istanza di nome consumo e di tipo NSDecimalNumber. Osserviamo che anche quiconsumo e di tipo NSDecimalNumber. Osserviamo che anche qui sono presenti il carattere ‘*’ prima del nome della variabile ed ilsono presenti il carattere ‘*’ prima del nome della variabile ed il ‘;’ alla fine della riga.‘;’ alla fine della riga.

Infine passiamo ora a codificareInfine passiamo ora a codificare

TelevisoreTelevisore (è un elettrodomestico) (è un elettrodomestico)

CaratteristicheCaratteristiche

canali memorizzati: elenco di nomi di canalicanali memorizzati: elenco di nomi di canali

canale sintonizzato: numero intero che indica lacanale sintonizzato: numero intero che indica la

Page 34: Xcode4 Tutorial Completo

posizione del canale sintonizzato nell’elenco deiposizione del canale sintonizzato nell’elenco dei memorizzatimemorizzati

livello del volume: numero intero da 0 a 100livello del volume: numero intero da 0 a 100

FunzionalitàFunzionalità (cosa sa fare il nostro televisore) (cosa sa fare il nostro televisore)

ricerca canali: valorizza l’elenco dei canaliricerca canali: valorizza l’elenco dei canali memorizzatimemorizzati

mostra elenco: mostra i nomi dei canalimostra elenco: mostra i nomi dei canali memorizzatimemorizzati

mostra il canale: scrive il nome del canalemostra il canale: scrive il nome del canale selezionatoselezionato

alza il volume: aumenta di 1 il livello del volumealza il volume: aumenta di 1 il livello del volume

abbassa il volume: decrementa di 1 il livello delabbassa il volume: decrementa di 1 il livello del volumevolume

vai al canale x : sintonizza il televisore sul canale xvai al canale x : sintonizza il televisore sul canale x

quindi apriamo il file Televisore.h e all’interno delle {}, dopo laquindi apriamo il file Televisore.h e all’interno delle {}, dopo la rigariga

@private@private

scriviamoscriviamo

NSArray *canaliMemorizzati;NSArray *canaliMemorizzati;NSDecimalNumber *canaleSintonizzato;NSDecimalNumber *canaleSintonizzato;NSDecimalNumber *livelloVolume;NSDecimalNumber *livelloVolume;

rispetto a quanto visto finora abbiamo introdotto un nuovo terminerispetto a quanto visto finora abbiamo introdotto un nuovo termine del linguaggio: NSArray. Anche questa è una classe di Objective-del linguaggio: NSArray. Anche questa è una classe di Objective-C, ed è la classe usata per modellare un elenco di elementiC, ed è la classe usata per modellare un elenco di elementi referenziabili per posizione: l’elemento in posizione 1, quello inreferenziabili per posizione: l’elemento in posizione 1, quello in posizione 2, quello in posizione 3,… Conosceremo meglio ancheposizione 2, quello in posizione 3,… Conosceremo meglio anche questa classe man mano che la useremo nel nostro programma.questa classe man mano che la useremo nel nostro programma.

Anche qui non mancano…’*’ e ‘;’.Anche qui non mancano…’*’ e ‘;’.

Rispetto alle classi OggettoFisico e Elettrodomestico, televisore haRispetto alle classi OggettoFisico e Elettrodomestico, televisore ha

Page 35: Xcode4 Tutorial Completo

anche delle funzionalità. Vediamo come si codificano. Per ognianche delle funzionalità. Vediamo come si codificano. Per ogni funzionalità dovremo implementare un metodo.funzionalità dovremo implementare un metodo.

Quindi apriamo il file Televisore.h e dopo la ‘}’ e prima della rigaQuindi apriamo il file Televisore.h e dopo la ‘}’ e prima della riga

@end@end

scriviamoscriviamo

-(void)ricercaCanali;-(void)ricercaCanali;-(void)mostraElenco;-(void)mostraElenco;-(void)mostraIlCanale;-(void)mostraIlCanale;-(void)alzaIlVolume;-(void)alzaIlVolume;-(void)abbassaIlVolume;-(void)abbassaIlVolume;-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare;-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare;Cosa sono queste istruzioni?Cosa sono queste istruzioni?

Sono dichiarazioni di metodi, cioè stiamo dicendo al sistema cheSono dichiarazioni di metodi, cioè stiamo dicendo al sistema che gli oggetti della classe Televisore sanno rispondere a questegli oggetti della classe Televisore sanno rispondere a queste richieste.richieste.

Analizziamo un po’ la struttura sintattica di queste dichiarazioni.Analizziamo un po’ la struttura sintattica di queste dichiarazioni.

Iniziano tutte con un segno ‘-’, questo segno indica che si tratta diIniziano tutte con un segno ‘-’, questo segno indica che si tratta di metodi di istanza, cioè sono eseguiti da un’istanza della classe edmetodi di istanza, cioè sono eseguiti da un’istanza della classe ed hanno quindi accesso alle variabili di istanza; sono invocati conhanno quindi accesso alle variabili di istanza; sono invocati con

[oggetto nomeMetodo][oggetto nomeMetodo]

se un metodo invece inizia con il segnose un metodo invece inizia con il segno ‘+’ è un metodo di classe ‘+’ è un metodo di classe cioè è eseguito da una classe e non da un’istanza viene quindicioè è eseguito da una classe e non da un’istanza viene quindi invocato coninvocato con

[classe nomeMetodo][classe nomeMetodo]

essendo eseguiti da una classe questi metodi non hanno accessoessendo eseguiti da una classe questi metodi non hanno accesso alle variabili di istanza; i metodi di classe sono solitamente usatialle variabili di istanza; i metodi di classe sono solitamente usati

Page 36: Xcode4 Tutorial Completo

per creare istanze della classe.per creare istanze della classe.

Dopo il segno ‘-’ deve esser indicato il tipo del risultato restituitoDopo il segno ‘-’ deve esser indicato il tipo del risultato restituito dal metodo. Cosa vuol dire che il metodo restituisce un valore? Ildal metodo. Cosa vuol dire che il metodo restituisce un valore? Il concetto di valore restituito si può esemplificare facendoconcetto di valore restituito si può esemplificare facendo riferimento alle classiche operazioni matematiche:riferimento alle classiche operazioni matematiche:

1+1 1+1 restituiscerestituisce il valore 2 il valore 2

cioè una l’esecuzione di una certa parte del nostro programmacioè una l’esecuzione di una certa parte del nostro programma restituisce un valore a chi l’ha invocata. Quindi se avessimo unrestituisce un valore a chi l’ha invocata. Quindi se avessimo un metodo che fa la somma scriveremmometodo che fa la somma scriveremmo

-(NSDecimalNumber *)somma…-(NSDecimalNumber *)somma…

per dire che il metodo somma restituisce un oggetto cheper dire che il metodo somma restituisce un oggetto che rappresenta un numero.rappresenta un numero.

Più avanti nel nostro esempio definiremo anche dei metodi chePiù avanti nel nostro esempio definiremo anche dei metodi che restituiscono dei valori e capiremo meglio questo concetto.restituiscono dei valori e capiremo meglio questo concetto.

Per ora i nostri metodi non restituiscono nulla, ma intervengonoPer ora i nostri metodi non restituiscono nulla, ma intervengono direttamente sul televisore per cambiare canale o il volume edirettamente sul televisore per cambiare canale o il volume e quindi si indica quindi si indica voidvoid per dire che non c’è risultato. per dire che non c’è risultato.

Infine, dopo il tipo del risultato, abbiamo il nome del metodo.Infine, dopo il tipo del risultato, abbiamo il nome del metodo.

Se osserviamo l’ultimo metodo, però, vediamo che c’è ancoraSe osserviamo l’ultimo metodo, però, vediamo che c’è ancora qualcosaqualcosa

-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare;-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare;Questo metodo ha bisogno di un’informazione per poter essereQuesto metodo ha bisogno di un’informazione per poter essere eseguito. Se il metodo deveeseguito. Se il metodo deve cambiare canale deve sapere a che cambiare canale deve sapere a che canale andare e quindi questa informazione la riceve dall’esterno canale andare e quindi questa informazione la riceve dall’esterno e la chiama canaleDaSintonizzare.e la chiama canaleDaSintonizzare.

Un altro esempio: il nostro metodo somma non può fare nulla seUn altro esempio: il nostro metodo somma non può fare nulla se non riceve i due operandi da sommare; avremo quindi qualcosanon riceve i due operandi da sommare; avremo quindi qualcosa del generedel genere

-(NSDecimalNumber *)somma:(NSDecimalNumber *)operando1-(NSDecimalNumber *)somma:(NSDecimalNumber *)operando1

Page 37: Xcode4 Tutorial Completo

allOperando:(NSDecimalNumber *)operando2allOperando:(NSDecimalNumber *)operando2

Bene, abbiamo finito con le dichiarazioni che descrivono il nostroBene, abbiamo finito con le dichiarazioni che descrivono il nostro mondo di Televisori.mondo di Televisori.

Nella prossima lezione definiremo i metodi, cioè scriveremo cosaNella prossima lezione definiremo i metodi, cioè scriveremo cosa devono fare ed inizieremo a far funzionare qualche istanza didevono fare ed inizieremo a far funzionare qualche istanza di Televisore.Televisore.

9 – Implementiamo i metodi9 – Implementiamo i metodi

iOS, , Mac, , OSX, , SDK, , Tech Room

Nella scorsa lezione abbiamo dichiarato quali sono i metodi dellaNella scorsa lezione abbiamo dichiarato quali sono i metodi della classe Televisore, ora inizieremo a scrivere cosa vogliamo checlasse Televisore, ora inizieremo a scrivere cosa vogliamo che questi metodi facciano. Ovviamente non aspettatevi di vedere la tvquesti metodi facciano. Ovviamente non aspettatevi di vedere la tv sul vostro Mac.sul vostro Mac.Apriamo il progetto e selezioniamo il file Televisore.m. In questoApriamo il progetto e selezioniamo il file Televisore.m. In questo

Page 38: Xcode4 Tutorial Completo

file dovrà essere scritto il codice per i metodi e come possiamofile dovrà essere scritto il codice per i metodi e come possiamo vedere ci sono delle cose già predisposte da Apple, levedere ci sono delle cose già predisposte da Apple, le analizzeremo fra un po’.analizzeremo fra un po’.

Ora dopo la ‘}’ del metodo init inseriamo, per ogni metodo cheOra dopo la ‘}’ del metodo init inseriamo, per ogni metodo che abbiamo dichiarato in Televisore.h, l’intestazione del metodo (laabbiamo dichiarato in Televisore.h, l’intestazione del metodo (la stessa che c’è nel .h) e una coppia di { }stessa che c’è nel .h) e una coppia di { } , dobbiamo però avere , dobbiamo però avere l’accortezza di togliere il ‘;’ dall’intestazione, perché in questo filel’accortezza di togliere il ‘;’ dall’intestazione, perché in questo file la sola intestazione non è un’istruzione.la sola intestazione non è un’istruzione.

Bene, in questo modo abbiamo preparato lo spazio per leBene, in questo modo abbiamo preparato lo spazio per le istruzioni dei diversi blocchi che costituiscono i nostri metodi.istruzioni dei diversi blocchi che costituiscono i nostri metodi.

Scriviamo ora una prima versione della nostra applicazione che ciScriviamo ora una prima versione della nostra applicazione che ci faccia vedere come un’oggetto della classe Televisore rispondefaccia vedere come un’oggetto della classe Televisore risponde alle nostre richieste; per rendere il più semplice possibile questaalle nostre richieste; per rendere il più semplice possibile questa prima versione non faremo altro che far scrivere a console unprima versione non faremo altro che far scrivere a console un messaggio di risposta alle nostre diverse richieste.messaggio di risposta alle nostre diverse richieste.

All’interno delle parentesi {} di ognuno dei nostri metodiAll’interno delle parentesi {} di ognuno dei nostri metodi scriviamoscriviamo

Page 39: Xcode4 Tutorial Completo

NSLog (@”Sto eseguendo il metodo …”);NSLog (@”Sto eseguendo il metodo …”);

mettendo il nome del metodo al posto dei ‘…’mettendo il nome del metodo al posto dei ‘…’

Ricordiamo che l’istruzione NSLog l’abbiamo già incontrata nelRicordiamo che l’istruzione NSLog l’abbiamo già incontrata nel main.m ed è l’istruzione che scrive sulla console il messaggio chemain.m ed è l’istruzione che scrive sulla console il messaggio che passiamo tra parentesi, quindi quello che otterremo è chepassiamo tra parentesi, quindi quello che otterremo è che all’esecuzione di un metodo sarà scritto a console il messaggioall’esecuzione di un metodo sarà scritto a console il messaggio relativo.relativo.

Inoltre all’interno del metodo init della classe Televisore, dopo laInoltre all’interno del metodo init della classe Televisore, dopo la { scriviamo{ scriviamo

NSLog (@”Sto inizializzando il televisore”);NSLog (@”Sto inizializzando il televisore”);

all’interno del metodo init della classe Elettrodomestico, dopo laall’interno del metodo init della classe Elettrodomestico, dopo la { scriviamo{ scriviamo

NSLog (@”Sto inizializzando l’elettrodomestico”);NSLog (@”Sto inizializzando l’elettrodomestico”);

all’interno del metodo init della classe OggettoFisico, dopo laall’interno del metodo init della classe OggettoFisico, dopo la { scriviamo{ scriviamo

NSLog (@”Sto inizializzando l’oggetto fisico”);NSLog (@”Sto inizializzando l’oggetto fisico”);

Fra un attimo vedremo l’effetto di queste ultime tre istruzioni.Fra un attimo vedremo l’effetto di queste ultime tre istruzioni.

Bene, diciamo che per questa prima versione siamo soddisfattiBene, diciamo che per questa prima versione siamo soddisfatti della modellazione delle nostre classi: abbiamo definito la classedella modellazione delle nostre classi: abbiamo definito la classe Televisore e le sue superclassi, abbiamo indicato le caratteristicheTelevisore e le sue superclassi, abbiamo indicato le caratteristiche (variabili di istanza) e le funzionalità (metodi) che per ora si(variabili di istanza) e le funzionalità (metodi) che per ora si limitano a scrivere un messaggio a console.limitano a scrivere un messaggio a console.

Page 40: Xcode4 Tutorial Completo

Andiamo ad eseguire il nostro programma cliccando sul bottoneAndiamo ad eseguire il nostro programma cliccando sul bottone Run ed otteniamo…ancora Hello, World! oppure Ciao,…! con ilRun ed otteniamo…ancora Hello, World! oppure Ciao,…! con il tuo nome se hai fatto le modifiche indicate nella lezione 6. Tuttatuo nome se hai fatto le modifiche indicate nella lezione 6. Tutta questa fatica per definire la classe questa fatica per definire la classe Televisore e non è cambiatoTelevisore e non è cambiato nulla.nulla.

Perchè?Perchè?

Perchè, come abbiamo detto il punto di ingresso del nostroPerchè, come abbiamo detto il punto di ingresso del nostro programma è sempre la funzione main che è rimasta quella diprogramma è sempre la funzione main che è rimasta quella di prima, cioè quella che fa solo NSLog(…prima, cioè quella che fa solo NSLog(…

Dobbiamo quindi intervenire sul codice interno alla funzioneDobbiamo quindi intervenire sul codice interno alla funzione main.main.

Quindi selezioniamo il file main.m e cancelliamo l’istruzioneQuindi selezioniamo il file main.m e cancelliamo l’istruzione NSLog(…NSLog(…

La prima cosa da fare è creare un oggetto televisore, per oraLa prima cosa da fare è creare un oggetto televisore, per ora abbiamo solo creato la classe, non l’oggetto, cioè abbiamo dettoabbiamo solo creato la classe, non l’oggetto, cioè abbiamo detto come è fato un televisore ma non ne abbiamo ancora nessuno income è fato un televisore ma non ne abbiamo ancora nessuno in casa.casa.

Il televisore è un’informazione del nostro programma e leIl televisore è un’informazione del nostro programma e le

Page 41: Xcode4 Tutorial Completo

informazioni abbiamo detto che si modellano con le variabili,informazioni abbiamo detto che si modellano con le variabili, abbiamo quindi bisogno di una variabile che si riferisca al nostroabbiamo quindi bisogno di una variabile che si riferisca al nostro televisore.televisore.

Quindi all’interno della funzione main, dopo la prima ‘{‘ cheQuindi all’interno della funzione main, dopo la prima ‘{‘ che inizia il blocco principale, dichiariamo la nostra variabile:inizia il blocco principale, dichiariamo la nostra variabile:

Televisore *tv1;Televisore *tv1;abbiamo dichiarato una variabile di nome tv1 e di tipo Televisore,abbiamo dichiarato una variabile di nome tv1 e di tipo Televisore, cioè la variabile tv1 si riferirà ad istanze della classe Televisore,cioè la variabile tv1 si riferirà ad istanze della classe Televisore, osserviamo come al solito il simbolo ‘*’ trattandosi di un oggetto.osserviamo come al solito il simbolo ‘*’ trattandosi di un oggetto.

A questo punto il compilatore ci segnala un errore che ci dice cheA questo punto il compilatore ci segnala un errore che ci dice che non conosce Televisore, ricordiamo che lo stesso errore lonon conosce Televisore, ricordiamo che lo stesso errore lo abbiamo ottenuto quando abbiamo cercato di dichiarare leabbiamo ottenuto quando abbiamo cercato di dichiarare le superclassi di Elettrodomestico e Televisore prima di fare l’import.superclassi di Elettrodomestico e Televisore prima di fare l’import. Anche in questo caso stiamo dicendo al compilatore che useremoAnche in questo caso stiamo dicendo al compilatore che useremo una variabile di tipo Televisore ma non gli abbiamo detto cosa èuna variabile di tipo Televisore ma non gli abbiamo detto cosa è Televisore. Quindi dobbiamo fare l’import. Sotto la rigaTelevisore. Quindi dobbiamo fare l’import. Sotto la riga

#import <Foundation/Foundation.h>#import <Foundation/Foundation.h>scriviamoscriviamo

#import "Televisore.h"#import "Televisore.h"in questo modo il compilatore conosce la classe Televisorein questo modo il compilatore conosce la classe Televisore all’interno della funzione main e la può usare.all’interno della funzione main e la può usare.

Bene, con la dichiarazione Televisore *tv1; abbiamo dato un nomeBene, con la dichiarazione Televisore *tv1; abbiamo dato un nome alla variabile ma non abbiamo ancora creato l’oggetto lo facciamoalla variabile ma non abbiamo ancora creato l’oggetto lo facciamo adesso scrivendo, dove prima c’era scritto NSLog(@”Hello,adesso scrivendo, dove prima c’era scritto NSLog(@”Hello, World!)”World!)”

// insert code here...// insert code here...

tv1=[[Televisore alloc] init];tv1=[[Televisore alloc] init];analizziamo questa istruzione.analizziamo questa istruzione.

Si tratta di una istruzione di assegnamento cioè si assegna unSi tratta di una istruzione di assegnamento cioè si assegna un valore ad una variabile, l’operatore di assegnamento in Objective-valore ad una variabile, l’operatore di assegnamento in Objective-

Page 42: Xcode4 Tutorial Completo

C è ‘=’. La nostra variabile è tv1, quindi dopo l’esecuzione diC è ‘=’. La nostra variabile è tv1, quindi dopo l’esecuzione di questa istruzione tv1 conterrà il valore risultato dalla valutazionequesta istruzione tv1 conterrà il valore risultato dalla valutazione di quello che c’è scritto a destra del simbolo ‘=’.di quello che c’è scritto a destra del simbolo ‘=’.

Vediamo allora cosa c’è scritto a destra del simbolo ‘=’. IniziamoVediamo allora cosa c’è scritto a destra del simbolo ‘=’. Iniziamo con la parte più internacon la parte più interna

[Televisore alloc][Televisore alloc]

Se ricordiamo la struttura sintattica per l’invocazione di unSe ricordiamo la struttura sintattica per l’invocazione di un metodo di classe [classe nomeMetodo] vediamo che ci troviamometodo di classe [classe nomeMetodo] vediamo che ci troviamo proprio in questa situazione, Televisore è la nostra classe e alloc èproprio in questa situazione, Televisore è la nostra classe e alloc è un metodo di classe predefinito da Objective-C nella classeun metodo di classe predefinito da Objective-C nella classe NSObject e quindi ereditato da tutte le sue sottoclassi il cuiNSObject e quindi ereditato da tutte le sue sottoclassi il cui obiettivo è quello di costruire l’oggetto della classe su cui èobiettivo è quello di costruire l’oggetto della classe su cui è invocato e restituisce l’oggetto creato, cioè un’istanza diinvocato e restituisce l’oggetto creato, cioè un’istanza di Televisore.Televisore.

Approfondimento: cerca NSObject nella documentazione AppleApprofondimento: cerca NSObject nella documentazione Apple ed individua il metodo alloc.ed individua il metodo alloc.

A questo punto l’espressione completaA questo punto l’espressione completa

[[Televisore alloc] init] la possiamo leggere come se fosse[[Televisore alloc] init] la possiamo leggere come se fosse

[[istanzadiTelevisoreistanzadiTelevisore init] init]

Se ricordiamo la struttura sintattica per l’invocazione di unSe ricordiamo la struttura sintattica per l’invocazione di un metodo di istanza [istanza nomeMetodo] vediamo che ci troviamometodo di istanza [istanza nomeMetodo] vediamo che ci troviamo proprio in questa situazione. Inoltre il metodo init è proprio quelloproprio in questa situazione. Inoltre il metodo init è proprio quello che noi troviamo nell’implementazione della classe Televisoreche noi troviamo nell’implementazione della classe Televisore (Televisore.m). Il metodo init è quello che viene di solito usato per(Televisore.m). Il metodo init è quello che viene di solito usato per assegnare i valori iniziali alle variabili di istanza degli oggettiassegnare i valori iniziali alle variabili di istanza degli oggetti appena creati e restituisce l’oggetto stesso. Riassumendoappena creati e restituisce l’oggetto stesso. Riassumendo

tv1=[[Televisore alloc] init];tv1=[[Televisore alloc] init];crea un’istanza di Televisore, su questa istanza invoca (richiedecrea un’istanza di Televisore, su questa istanza invoca (richiede l’esecuzione di) il metodo init ed assegna l’oggetto creato edl’esecuzione di) il metodo init ed assegna l’oggetto creato ed inizializzato alla variabile tv1.inizializzato alla variabile tv1.

Page 43: Xcode4 Tutorial Completo

Bene fermiamoci un attimo ed eseguiamo il programma cliccandoBene fermiamoci un attimo ed eseguiamo il programma cliccando sul bottone Run.sul bottone Run.

Abbiamo alcuni messaggi a console.Abbiamo alcuni messaggi a console.

Guardiamo il primoGuardiamo il primo

Sto inizializzando il televisoreSto inizializzando il televisore

Questo è il messaggio del NSLog del metodo init della classeQuesto è il messaggio del NSLog del metodo init della classe Televisore, ed in effetti è quello che ci saremmo dovuti aspettareTelevisore, ed in effetti è quello che ci saremmo dovuti aspettare di ottenere visto che [[Televisore alloc] init] invoca proprio ildi ottenere visto che [[Televisore alloc] init] invoca proprio il metodo init della classe Televisore. Ma perchè c’è anchemetodo init della classe Televisore. Ma perchè c’è anche

Sto inizializzando l’elettrodomesticoSto inizializzando l’elettrodomestico

il messaggio del metodo init della classe Elettrodomestico?il messaggio del metodo init della classe Elettrodomestico?

Andiamo a guardare come è fatto il metodo init della classeAndiamo a guardare come è fatto il metodo init della classe Televisore, nel file Televisore.m.Televisore, nel file Televisore.m.

Guardiamo l’istruzioneGuardiamo l’istruzione

self = [super init];self = [super init];è simile alla nostra tv1=…, cioè è un assegnamento che assegnaè simile alla nostra tv1=…, cioè è un assegnamento che assegna un valore alla variabile self. Self è una variabile predefinita inun valore alla variabile self. Self è una variabile predefinita in

Page 44: Xcode4 Tutorial Completo

Objective-C che si riferisce sempre all’istanza corrente che staObjective-C che si riferisce sempre all’istanza corrente che sta eseguendo l’istruzione nella quale è scritto self.eseguendo l’istruzione nella quale è scritto self.

Ma che valore assegnamo a self? Il risultato di [super init]. QuestaMa che valore assegnamo a self? Il risultato di [super init]. Questa forma sintattica dovrebbe inziare ad esserci familiare èforma sintattica dovrebbe inziare ad esserci familiare è l’invocazione di un metodo di istanza (init) su un oggetto (super).l’invocazione di un metodo di istanza (init) su un oggetto (super). Ma super chi è?Ma super chi è?

Anche super è una variabile predefinita di Objective-C ed ancheAnche super è una variabile predefinita di Objective-C ed anche super, come self, si riferisce sempre all’istanza che eseguesuper, come self, si riferisce sempre all’istanza che esegue l’istruzione nella quale è scritto super. Allora che differenza c’èl’istruzione nella quale è scritto super. Allora che differenza c’è con self?con self?

Quando si usa self l’oggetto viene visto come istanza della classeQuando si usa self l’oggetto viene visto come istanza della classe nella quale è stato invocato (nel nostro caso Televisore), mentrenella quale è stato invocato (nel nostro caso Televisore), mentre quando si usa super l’oggetto viene visto come istanza dellaquando si usa super l’oggetto viene visto come istanza della superclasse della classe nella quale è stato invocato (nel nostrosuperclasse della classe nella quale è stato invocato (nel nostro caso Elettrodomestico).caso Elettrodomestico).

Facciamo un esempio; se considero il televisore che ho nelFacciamo un esempio; se considero il televisore che ho nel soggiorno, quando ne cambio il canale o alzo e abbasso il volumesoggiorno, quando ne cambio il canale o alzo e abbasso il volume lo sto usando in quanto televisore, uso le funzioni proprie di unlo sto usando in quanto televisore, uso le funzioni proprie di un televisore; se lo collego alla corrente, lo accendo, lo spengo, lo stotelevisore; se lo collego alla corrente, lo accendo, lo spengo, lo sto usando in quanto elettrodomestico, uso le funzioni proprie di unusando in quanto elettrodomestico, uso le funzioni proprie di un elettrodomestico; ma l’oggetto è sempre lo stesso: il televisore delelettrodomestico; ma l’oggetto è sempre lo stesso: il televisore del soggiorno. Nel primo caso mi riferirò con self e nel secondo casosoggiorno. Nel primo caso mi riferirò con self e nel secondo caso con super. Fra un po’ faremo un esempio specifico di questocon super. Fra un po’ faremo un esempio specifico di questo aspetto che dovrebbe chiarirlo meglio.aspetto che dovrebbe chiarirlo meglio.

Tornando al nostro metodo init della classe Televisore quindiTornando al nostro metodo init della classe Televisore quindi

self = [super init];self = [super init];potremmo leggerlo come “potremmo leggerlo come “inizializza un elettrodomestico edinizializza un elettrodomestico ed assegnalo all’istanza del televisoreassegnalo all’istanza del televisore”.”.

In conclusione [super init] invoca il metod init della classeIn conclusione [super init] invoca il metod init della classe Elettrodomestico ed ecco spiegato perchè a console c’è anche ilElettrodomestico ed ecco spiegato perchè a console c’è anche il messaggiomessaggio

Page 45: Xcode4 Tutorial Completo

Sto inizializzando l’elettrodomesticoSto inizializzando l’elettrodomestico

ma perchè c’è anchema perchè c’è anche

Sto inizializzando l’oggetto fisicoSto inizializzando l’oggetto fisico

che è il messaggio dell’init di OggettoFisico?che è il messaggio dell’init di OggettoFisico?

Se guardiamo il metodo init di Elettrodomestico vediamo cheSe guardiamo il metodo init di Elettrodomestico vediamo che anche qui c’èanche qui c’è

self = [super init];self = [super init];e si applica quindi tutto il discorso precedente dove però supere si applica quindi tutto il discorso precedente dove però super adesso fa riferimento all’oggetto visto nella classe OggettoFisico.adesso fa riferimento all’oggetto visto nella classe OggettoFisico.

Però, siccome ci piace la precisione, sembrerebbe più correttoPerò, siccome ci piace la precisione, sembrerebbe più corretto leggere prima l’inizializzazione dell’oggetto fisico, poi quellaleggere prima l’inizializzazione dell’oggetto fisico, poi quella dell’elettrodomestico e poi quella del televisore, tanto perdell’elettrodomestico e poi quella del televisore, tanto per percorrere la gerarchia dalla classe più generale a quella piùpercorrere la gerarchia dalla classe più generale a quella più particolare; spostiamo i 3 NSLog che abbiamo inserito prima neiparticolare; spostiamo i 3 NSLog che abbiamo inserito prima nei metodo init, dopo la rigametodo init, dopo la riga

self = [super init];self = [super init];di ognuno dei metodi, in tal modo prima inizializziamo l’oggettodi ognuno dei metodi, in tal modo prima inizializziamo l’oggetto come istanza della superclasse e poi scriviamo il messaggio dicome istanza della superclasse e poi scriviamo il messaggio di inizializzazione della classe corrente.inizializzazione della classe corrente.

Analizziamo un ultimo aspetto.Analizziamo un ultimo aspetto.

Stiamo usando il metodo init sempre in espressioni a destra diStiamo usando il metodo init sempre in espressioni a destra di un’istruzione di assegnamento (‘=’), questo vuol dire che ilun’istruzione di assegnamento (‘=’), questo vuol dire che il metodo deve restituire un valore che sarà assegnato alla variabile ametodo deve restituire un valore che sarà assegnato alla variabile a sinistra del segno “=”. Ma quale valore restituisce il metodo init?sinistra del segno “=”. Ma quale valore restituisce il metodo init?

La risposta è nell’istruzioneLa risposta è nell’istruzione

return self;return self;L’istruzione L’istruzione returnreturn restituisce il valore che segue, quindi in questo restituisce il valore che segue, quindi in questo caso restituisce self. Cosa vale self? Vale l’oggetto inizializzatocaso restituisce self. Cosa vale self? Vale l’oggetto inizializzato dal metodo della superclasse ([super init]) ed eventualmente condal metodo della superclasse ([super init]) ed eventualmente con

Page 46: Xcode4 Tutorial Completo

ulteriori assegnamenti alle variabili di istanza fatti dentro ilulteriori assegnamenti alle variabili di istanza fatti dentro il metodo init stesso (lo faremo più avanti).metodo init stesso (lo faremo più avanti).

Un’ultima cosa riguardo a questo aspetto. Abbiamo detto che initUn’ultima cosa riguardo a questo aspetto. Abbiamo detto che init restituisce un valore, quindi nella sua intestazione deve essercirestituisce un valore, quindi nella sua intestazione deve esserci l’indicazione del tipo del valore restituitol’indicazione del tipo del valore restituito

-(id)init-(id)initRicordiamo che il simbolo ‘-’ indica che si tratta di un metodo diRicordiamo che il simbolo ‘-’ indica che si tratta di un metodo di istanza, infatti lo invochiamo su istanzeistanza, infatti lo invochiamo su istanze ([Televisore alloc] , ([Televisore alloc] , super). super). idid indica il tipo restituito, in realtà indica il tipo restituito, in realtà idid indica qualunque indica qualunque tipo; in questo modo questa intestazione del metodo si potrà usaretipo; in questo modo questa intestazione del metodo si potrà usare per qualunque classe accettando un qualunque tipo come risultatoper qualunque classe accettando un qualunque tipo come risultato (Televisore, Elettrodomestico, OggettoFisico e qualunque altra(Televisore, Elettrodomestico, OggettoFisico e qualunque altra classe predefinita da Objective-C).classe predefinita da Objective-C).

Approfondimento: cerca NSObject nella documentazione AppleApprofondimento: cerca NSObject nella documentazione Apple ed individua il metodo init.ed individua il metodo init.

Ora basta con il metodo init, lo abbiamo analizzato a sufficienza.Ora basta con il metodo init, lo abbiamo analizzato a sufficienza.

Torniamo al main, nel quale con l’istruzioneTorniamo al main, nel quale con l’istruzione

tv1=[[Televisore alloc] init];tv1=[[Televisore alloc] init];abbiamo creato un oggetto televisore referenziato dalla variabileabbiamo creato un oggetto televisore referenziato dalla variabile tv1.tv1.

A questo punto possiamo invocare i metodi definiti nella classeA questo punto possiamo invocare i metodi definiti nella classe Televisore su questa istanza.Televisore su questa istanza.

Scriviamo quindi.Scriviamo quindi.

[tv1 ricercaCanali];[tv1 ricercaCanali];dopo l’inizializzazione di tv1.dopo l’inizializzazione di tv1.

Cosa significa quella istruzione? Ha la solita struttura [istanzaCosa significa quella istruzione? Ha la solita struttura [istanza nomeMetodo]. Quindi consiste nell’invocazione del metodonomeMetodo]. Quindi consiste nell’invocazione del metodo ricercaCanali sull’istanza tv1.ricercaCanali sull’istanza tv1.

Poi aggiungiamoPoi aggiungiamo

Page 47: Xcode4 Tutorial Completo

[tv1 mostraElenco];[tv1 mostraElenco];Cosa significa quella istruzione? Ha la solita struttura [istanzaCosa significa quella istruzione? Ha la solita struttura [istanza nomeMetodo]. Quindi consiste nell’invocazione del metodonomeMetodo]. Quindi consiste nell’invocazione del metodo mostraElenco sull’istanza tv1.mostraElenco sull’istanza tv1.

Aggiungiamo ancheAggiungiamo anche

[tv1 mostraIlCanale];[tv1 mostraIlCanale];[tv1 alzaIlVolume];[tv1 alzaIlVolume];[tv1 abbassaIlVolume];[tv1 abbassaIlVolume];Cosa significano queste istruzioni?…Cosa significano queste istruzioni?…

Eseguiamo cliccando sul bottone Run, e guardiamo la console,Eseguiamo cliccando sul bottone Run, e guardiamo la console,

abbiamo tutti i messaggi dei metodi invocatiabbiamo tutti i messaggi dei metodi invocati

Bene, per ora ci fermiamo; nella prossima lezione aggiungeremoBene, per ora ci fermiamo; nella prossima lezione aggiungeremo nei metodi il codice per modificare lo stato interno del televisore,nei metodi il codice per modificare lo stato interno del televisore, cioè per assegnare e leggere i valori delle variabili di istanza.cioè per assegnare e leggere i valori delle variabili di istanza.

Page 48: Xcode4 Tutorial Completo

10 – Aggiungiamo la logica10 – Aggiungiamo la logica

iOS, , Mac, , OSX, , SDK, , Tech Room

Nella scorsa lezione abbiamo costruito le strutture dei metodi cheNella scorsa lezione abbiamo costruito le strutture dei metodi che dovranno implementare la logica della nostra applicazione. Indovranno implementare la logica della nostra applicazione. In questa lezione andremo a scrivere il codice necessario edquesta lezione andremo a scrivere il codice necessario ed analizzeremo i costrutti linguistici necessari. Analizziamo quindi ianalizzeremo i costrutti linguistici necessari. Analizziamo quindi i metodi uno per uno.metodi uno per uno.

Ricerca canali.Ricerca canali.

In questo metodo vogliamo modellare quello che succede quandoIn questo metodo vogliamo modellare quello che succede quando nel nostro televisore appena comperato facciamo lanel nostro televisore appena comperato facciamo la sintonizzazione dei canali. Il risultato finale è che nel nostrosintonizzazione dei canali. Il risultato finale è che nel nostro televisore (reale) troveremo la lista dei canali popolata con tuttitelevisore (reale) troveremo la lista dei canali popolata con tutti quelli ricevibili.quelli ricevibili.

Quindi quello che dobbiamo fare nel nostro metodo è popolare laQuindi quello che dobbiamo fare nel nostro metodo è popolare la struttura che modella l’elenco dei canali. Questa struttura è lastruttura che modella l’elenco dei canali. Questa struttura è la variabile di istanza, dichiarata nella classe Televisore,variabile di istanza, dichiarata nella classe Televisore,

NSArray *canaliMemorizzati;NSArray *canaliMemorizzati;Ricordate cosa abbiamo fatto con la nostra variabile tv1? PrimaRicordate cosa abbiamo fatto con la nostra variabile tv1? Prima l’abbiamo dichiarata conl’abbiamo dichiarata con

Televisore *tv1Televisore *tv1ma a questoma a questo punto era solo un nome senza ancora nessun oggetto punto era solo un nome senza ancora nessun oggetto associato, per creare l’oggetto ed associarlo a tv1 abbiamo usatoassociato, per creare l’oggetto ed associarlo a tv1 abbiamo usato

tv1=[[Televisore alloc] init]tv1=[[Televisore alloc] init]In maniera analoga noi per ora abbiamo solo la dichiarazione delIn maniera analoga noi per ora abbiamo solo la dichiarazione del nome dell’elenco, ma dobbiamo creare ed inizializzare l’oggettonome dell’elenco, ma dobbiamo creare ed inizializzare l’oggetto ed assegnarlo alla nostra variabile.ed assegnarlo alla nostra variabile.

Page 49: Xcode4 Tutorial Completo

Approfondimento: cerca NSArray nella documentazione Apple edApprofondimento: cerca NSArray nella documentazione Apple ed individua i metodi di inizializzazione (iniziano con init).individua i metodi di inizializzazione (iniziano con init).

Nel metodo ricercaCanali, dopo la riga NSLog(… scriviamoNel metodo ricercaCanali, dopo la riga NSLog(… scriviamo

canaliMemorizzati=[[NSArray alloc] initWithObjects:@"Onda1",canaliMemorizzati=[[NSArray alloc] initWithObjects:@"Onda1", @"Onda2", @"Onda3", @"Onda4", @"Onda5", nil];@"Onda2", @"Onda3", @"Onda4", @"Onda5", nil];Analizziamo questa istruzione.Analizziamo questa istruzione.

E’ un’istruzione di assegnamento (stiamo usando il segno ‘=’),E’ un’istruzione di assegnamento (stiamo usando il segno ‘=’), quindi a sinistra c’è la variabile cui vogliamo assegnare un valorequindi a sinistra c’è la variabile cui vogliamo assegnare un valore e a destra c’è il valore da assegnare. Cosa c’è a destra? La partee a destra c’è il valore da assegnare. Cosa c’è a destra? La parte interna è [NSArray alloc], è l’invocazione del metodo alloc sullainterna è [NSArray alloc], è l’invocazione del metodo alloc sulla classe NSArray; è un metodo che abbiamo già incontrato e checlasse NSArray; è un metodo che abbiamo già incontrato e che serve a creare un’istanza della classe su cui è invocato e restituisceserve a creare un’istanza della classe su cui è invocato e restituisce l’istanza creata. Quindi l’istanza creata. Quindi sull’istanza creata da [NSArray alloc] sisull’istanza creata da [NSArray alloc] si invoca il metodo initWithObjects, questo è un metodoinvoca il metodo initWithObjects, questo è un metodo che che inserisce nell’elenco su cui viene invocato gli oggetti che seguono.inserisce nell’elenco su cui viene invocato gli oggetti che seguono. Nel nostro caso inserirà nell’elenco appena creato le 5 paroleNel nostro caso inserirà nell’elenco appena creato le 5 parole (oggetti anche esse e poi vedremo di cosa) Onda1, Onda2, Onda3,(oggetti anche esse e poi vedremo di cosa) Onda1, Onda2, Onda3, Onda4, Onda5 che possiamo immaginare siano i nomi dei canaliOnda4, Onda5 che possiamo immaginare siano i nomi dei canali ricevibili. L’elenco deve essere terminato da ricevibili. L’elenco deve essere terminato da nilnil che è una keyword che è una keyword Objective-C per indicare Objective-C per indicare nessun oggettonessun oggetto e si usa in questo caso per e si usa in questo caso per indicare quando finisce la lista degli oggetti da caricareindicare quando finisce la lista degli oggetti da caricare nell’elenco.nell’elenco.

Quindi dopo questa istruzione la nostra variabile di istanzaQuindi dopo questa istruzione la nostra variabile di istanza canaliMemorizzati si riferisce ad un oggetto della classe NSArraycanaliMemorizzati si riferisce ad un oggetto della classe NSArray che modella l’ elenco (Onda1, Onda2, Onda3, Onda4, Onda5).che modella l’ elenco (Onda1, Onda2, Onda3, Onda4, Onda5).

Mostra elenco.Mostra elenco.

In questo metodo ci faremo scrivere a console il nome dei canaliIn questo metodo ci faremo scrivere a console il nome dei canali che abbiamo inserito nel nostro elenco. Quindi vogliamo scrivereche abbiamo inserito nel nostro elenco. Quindi vogliamo scrivere il codice per modellare una logica del tipoil codice per modellare una logica del tipo

per ogni nome presente nell’elenco canaliMemorizzati scrivi ilper ogni nome presente nell’elenco canaliMemorizzati scrivi il nomenome

Page 50: Xcode4 Tutorial Completo

la frase la frase per ogniper ogni è indicativa di un costrutto linguistico che è indicativa di un costrutto linguistico che abbiamo introdotto quando abbiamo parlato dei flussi di controlloabbiamo introdotto quando abbiamo parlato dei flussi di controllo e che si chiama e che si chiama ciclociclo. Cioè quello che dobbiamo implementare è. Cioè quello che dobbiamo implementare è un ciclo che ripete un certo blocco di istruzioni per ogni elementoun ciclo che ripete un certo blocco di istruzioni per ogni elemento dell’elenco.dell’elenco.

Scriviamo il codice e poi lo analizziamo.Scriviamo il codice e poi lo analizziamo.

All’interno del metodo mostraElenco dopo la riga NSLog(…All’interno del metodo mostraElenco dopo la riga NSLog(… scriviamoscriviamo

for (nomeCanale in canaliMemorizzati) {for (nomeCanale in canaliMemorizzati) { NSLog(@"Canale: %@",nomeCanale); NSLog(@"Canale: %@",nomeCanale);}}(Per ora non preoccupatevi dell’errore che vi viene segnalato, ci(Per ora non preoccupatevi dell’errore che vi viene segnalato, ci arriviamo fra un attimo).arriviamo fra un attimo).

L’istruzione for è una delle istruzioni per implementare un ciclo eL’istruzione for è una delle istruzioni per implementare un ciclo e ha due forme, quella che stiamo usando scorre un elenco di oggettiha due forme, quella che stiamo usando scorre un elenco di oggetti ed assegna di volta in volta uno degli oggetti dell’elenco ad unaed assegna di volta in volta uno degli oggetti dell’elenco ad una variabile e poi esegue un blocco di istruzioni. La sua strutturavariabile e poi esegue un blocco di istruzioni. La sua struttura generale ègenerale è

for (variabile in elenco) {for (variabile in elenco) { …… <usa variabile><usa variabile> …… }}

in pratica ad ogni iterazione del ciclo, alla variabile vienein pratica ad ogni iterazione del ciclo, alla variabile viene assegnato uno degli oggetti dell’elenco e poi viene eseguito ilassegnato uno degli oggetti dell’elenco e poi viene eseguito il blocco di istruzioni (ricordiamo che un blocco è una sequenza diblocco di istruzioni (ricordiamo che un blocco è una sequenza di istruzioni racchiuse tra {}).istruzioni racchiuse tra {}).

Bene da cosa è composto il nostro blocco di istruzioni? QualiBene da cosa è composto il nostro blocco di istruzioni? Quali istruzioni ci sono dentro le {} che seguono l’intestazione del for?istruzioni ci sono dentro le {} che seguono l’intestazione del for?

Prima di rispondere vediamo cosa vorremmo fare. Diciamo chePrima di rispondere vediamo cosa vorremmo fare. Diciamo che per il momento ci interessa che per ogni canale nell’elenco ilper il momento ci interessa che per ogni canale nell’elenco il programma scriveprogramma scrive

Canale: …Canale: …

con il nome del canale al posto dei puntini. Esattamente concon il nome del canale al posto dei puntini. Esattamente con

Page 51: Xcode4 Tutorial Completo

questo concetto dovremo riuscire a costruire un’istruzione chequesto concetto dovremo riuscire a costruire un’istruzione che possa scrivere una stringa di possa scrivere una stringa di caratteri che però sia compostacaratteri che però sia composta (almeno in parte) solo al momento dell’esecuzione; cioè al(almeno in parte) solo al momento dell’esecuzione; cioè al momento in cui scriviamo il programma noi non siamo in grado dimomento in cui scriviamo il programma noi non siamo in grado di preparare completamente la stringa poichè solo al momentopreparare completamente la stringa poichè solo al momento dell’esecuzione avremo valorizzato il nome del canale chedell’esecuzione avremo valorizzato il nome del canale che vogliamo stampare. In Objective-C si utilizzano delle stringhevogliamo stampare. In Objective-C si utilizzano delle stringhe particolari, chiamate particolari, chiamate format stringsformat strings, che contengono al loro interno, che contengono al loro interno dei caratteri speciali che indicano un segnaposto, cioè unadei caratteri speciali che indicano un segnaposto, cioè una posizione della stringa che sarà valorizzata solo al momentoposizione della stringa che sarà valorizzata solo al momento dell’esecuzione. Guardiamo l’istruzione che abbiamo nel nostrodell’esecuzione. Guardiamo l’istruzione che abbiamo nel nostro blocco del cicloblocco del ciclo

NSLog(@"Canale: %@",nomeCanale);NSLog(@"Canale: %@",nomeCanale);NSLog la dovremmo conoscere, è l’istruzione per scrivere aNSLog la dovremmo conoscere, è l’istruzione per scrivere a console, l’abbiamo usata ormai diverse volte. Ma cosa scriveconsole, l’abbiamo usata ormai diverse volte. Ma cosa scrive questa volta? “Canale: %@” questa volta? “Canale: %@” i caratteri %@ individuano un segnaposto, cioè una posizionei caratteri %@ individuano un segnaposto, cioè una posizione della stringa che sarà valorizzata al momento dell’esecuzione; madella stringa che sarà valorizzata al momento dell’esecuzione; ma da che valore sarà sostituito il segnaposto? Dalla variabile cheda che valore sarà sostituito il segnaposto? Dalla variabile che segue la format string all’interno delle parentesi () della NSLog,segue la format string all’interno delle parentesi () della NSLog, quindi nel nostro caso nomeCanale. Cioè scriverà Canale: seguitoquindi nel nostro caso nomeCanale. Cioè scriverà Canale: seguito dal valore della variabile nomeCanale che viene valorizzata daldal valore della variabile nomeCanale che viene valorizzata dal ciclo for con i diversi oggetto presenti nell’elenco uno alla volta. ciclo for con i diversi oggetto presenti nell’elenco uno alla volta. Veniamo adesso all’errore segnalato da XCode. Abbiamo dettoVeniamo adesso all’errore segnalato da XCode. Abbiamo detto che nel ciclo for stiamo usando la variabile nomeCanale; ma nonche nel ciclo for stiamo usando la variabile nomeCanale; ma non abbiamo detto al compilatore chi è nomeCanale, cioè non abbiamoabbiamo detto al compilatore chi è nomeCanale, cioè non abbiamo dichiarato la variabile; ricordate che per usare una variabile èdichiarato la variabile; ricordate che per usare una variabile è sempre necessario prima dichiararla. Cioè dire che esiste (il nome)sempre necessario prima dichiararla. Cioè dire che esiste (il nome) e dire di che tipo è. Quindi prima della riga con l’istruzione fore dire di che tipo è. Quindi prima della riga con l’istruzione for aggiungiamoaggiungiamo

NSString *nomeCanale;NSString *nomeCanale;Questa è una dichiarazione di variabile come ne abbiamo visteQuesta è una dichiarazione di variabile come ne abbiamo viste diverse quando abbiamo dichiarato le variabili di istanza dellediverse quando abbiamo dichiarato le variabili di istanza delle nostre classi. Stiamo dichiarando che esiste una variabile che sinostre classi. Stiamo dichiarando che esiste una variabile che si

Page 52: Xcode4 Tutorial Completo

chiama nomeCanale che si riferisce a oggetti di tipo NSString.chiama nomeCanale che si riferisce a oggetti di tipo NSString. NSString è una classe predefinita in Objective-C e serve aNSString è una classe predefinita in Objective-C e serve a modellare le informazioni di testo, cioè le sequenze di caratterimodellare le informazioni di testo, cioè le sequenze di caratteri (parole, frasi).(parole, frasi).

Approfondimento: cerca e studia NSString nella documentazioneApprofondimento: cerca e studia NSString nella documentazione Apple.Apple.

Ma perchè abbiamo scelto questo tipo? Ricordiamo che gli oggettiMa perchè abbiamo scelto questo tipo? Ricordiamo che gli oggetti che abbiamo usato per popolare il nostro elenco sonoche abbiamo usato per popolare il nostro elenco sono @”Onda1″, @”Onda2″, @”Onda3″, @”Onda4″,@”Onda1″, @”Onda2″, @”Onda3″, @”Onda4″, @”Onda5″, (riguardiamo il metodo ricercaCanali).@”Onda5″, (riguardiamo il metodo ricercaCanali). Sono cioè Sono cioè sequenze di caratteri precedute dall’operatore @, questo operatoresequenze di caratteri precedute dall’operatore @, questo operatore rende quelle sequenze di caratteri proprio degli oggetti della classerende quelle sequenze di caratteri proprio degli oggetti della classe NSString per cui la nostra variabile del ciclo, nomeCanale, saràNSString per cui la nostra variabile del ciclo, nomeCanale, sarà dello stesso tipo degli oggetti.dello stesso tipo degli oggetti.

Bene, ora proviamo ad eseguire il nostro programma cliccando sulBene, ora proviamo ad eseguire il nostro programma cliccando sul bottone Run e guardiamo la console.bottone Run e guardiamo la console.

Page 53: Xcode4 Tutorial Completo

Appaiono le stesse scritte di prima, cioè quelle che indicano qualeAppaiono le stesse scritte di prima, cioè quelle che indicano quale metodo è in esecuzione, in più durante l’esecuzione del metodometodo è in esecuzione, in più durante l’esecuzione del metodo mostraElenco troviamo le righemostraElenco troviamo le righe

2011-05-23 18:49:42.765 PrimoProgetto[19098:903] Canale:2011-05-23 18:49:42.765 PrimoProgetto[19098:903] Canale: Onda1Onda1 2011-05-23 18:49:42.765 PrimoProgetto[19098:903]2011-05-23 18:49:42.765 PrimoProgetto[19098:903] Canale: Onda2Canale: Onda2 2011-05-23 18:49:42.7662011-05-23 18:49:42.766 PrimoProgetto[19098:903] Canale: Onda3PrimoProgetto[19098:903] Canale: Onda3 2011-05-232011-05-23 18:49:42.766 PrimoProgetto[19098:903] Canale: Onda418:49:42.766 PrimoProgetto[19098:903] Canale: Onda4 2011-05-2011-05-23 18:49:42.767 PrimoProgetto[19098:903] Canale: Onda523 18:49:42.767 PrimoProgetto[19098:903] Canale: Onda5

queste stampe sono l’effetto dall’istruzione NSLog(“Canale:queste stampe sono l’effetto dall’istruzione NSLog(“Canale: %@”, nome Canale) all’interno del ciclo nel quale la variabile%@”, nome Canale) all’interno del ciclo nel quale la variabile nomeCanale viene di volta in volta assegnata ad un elementonomeCanale viene di volta in volta assegnata ad un elemento dell’elenco canaliMemorizzati e poi va a sostituire il segnapostodell’elenco canaliMemorizzati e poi va a sostituire il segnaposto %@ durante l’esecuzione dell’applicazione.%@ durante l’esecuzione dell’applicazione.

mostraIlCanale.mostraIlCanale.

In questo metodo ci faremo scrivere su quale canale siamoIn questo metodo ci faremo scrivere su quale canale siamo sintonizzati.sintonizzati.

Ricordiamo che abbiamo scelto di modellare il canale sul qualeRicordiamo che abbiamo scelto di modellare il canale sul quale siamo sintonizzati con una variabile canaleSintonizzato di tiposiamo sintonizzati con una variabile canaleSintonizzato di tipo numerico (si riferirà ad istanze di NSDecimalNumber) chenumerico (si riferirà ad istanze di NSDecimalNumber) che conterrà il numero della posizione del canale che stiamoconterrà il numero della posizione del canale che stiamo guardando nell’elenco dei canali memorizzati. Poiché gli oggettiguardando nell’elenco dei canali memorizzati. Poiché gli oggetti NSArray iniziano a contare dalla posizione 0, se siamoNSArray iniziano a contare dalla posizione 0, se siamo sintonizzati sul canale Onda1 la variabile canaleSintonizzatosintonizzati sul canale Onda1 la variabile canaleSintonizzato conterrà il numero 0, se siamo sintonizzati sul canale Onda2conterrà il numero 0, se siamo sintonizzati sul canale Onda2 canaleSintonizzato conterrà il numero 1, e così via.canaleSintonizzato conterrà il numero 1, e così via.

In questo metodo però vogliamo solo farci scrivere il nome delIn questo metodo però vogliamo solo farci scrivere il nome del canale sintonizzato.canale sintonizzato.

All’interno delle parentesi { } del metodo nel file Televisore.m,All’interno delle parentesi { } del metodo nel file Televisore.m, dopo la riga NSLog(… aggiungiamodopo la riga NSLog(… aggiungiamo

NSLog(@"Stai guardando il canale %@",[canaliMemorizzatiNSLog(@"Stai guardando il canale %@",[canaliMemorizzati

Page 54: Xcode4 Tutorial Completo

objectAtIndex:[canaleSintonizzato intValue]]);objectAtIndex:[canaleSintonizzato intValue]]);Ora analizziamo questa istruzione.Ora analizziamo questa istruzione.

Si tratta di una NSLog, ormai dovremmo conoscerla èSi tratta di una NSLog, ormai dovremmo conoscerla è un’istruzione per scrivere qualcosa a console. Cosa scriviamo? Laun’istruzione per scrivere qualcosa a console. Cosa scriviamo? La stringa passata come primo argomento. Si tratta di una formatstringa passata come primo argomento. Si tratta di una format string cioè di una stringa che contiene un segnaposto (%@), lostring cioè di una stringa che contiene un segnaposto (%@), lo abbiamo già visto nel metodo precedente. Cioè quando ilabbiamo già visto nel metodo precedente. Cioè quando il programma eseguirà questa istruzione scriveràprogramma eseguirà questa istruzione scriverà

Stai guardano il canaleStai guardano il canale

Seguito dal valore restituito dall’espressione che segue la formatSeguito dal valore restituito dall’espressione che segue la format stringstring

[canaliMemorizzati objectAtIndex:[canaleSintonizzato intValue]][canaliMemorizzati objectAtIndex:[canaleSintonizzato intValue]]

Questa espressione Questa espressione ha la forma del tipoha la forma del tipo

[istanza metodo][istanza metodo]

cioè è l’invocazione di un metodo su un’istanza. In particolare ilcioè è l’invocazione di un metodo su un’istanza. In particolare il nostro oggetto è canaliMemorizzati, cioè un’istanza della classenostro oggetto è canaliMemorizzati, cioè un’istanza della classe NSArray ed il metodo che invochiamo, è un metodo predefinito diNSArray ed il metodo che invochiamo, è un metodo predefinito di Objective-C che si chiama objectAtIndex che restituisceObjective-C che si chiama objectAtIndex che restituisce l’elemento dell’elenco che si trova nella posizione indicata.l’elemento dell’elenco che si trova nella posizione indicata.

Approfondimento: cerca NSArray nella documentazione Apple eApprofondimento: cerca NSArray nella documentazione Apple e studia il metodo objectAtIndex.studia il metodo objectAtIndex.

Come si fa ad indicare la posizione dell’elemento che vogliamo?Come si fa ad indicare la posizione dell’elemento che vogliamo?

Un metodo riceve un’informazione in ingresso per poterUn metodo riceve un’informazione in ingresso per poter funzionare (in questo caso la posizione da leggere) facendofunzionare (in questo caso la posizione da leggere) facendo seguire al nome del metodo il simbolo ‘:’ e poi l’informazioneseguire al nome del metodo il simbolo ‘:’ e poi l’informazione necessarianecessaria

[istanza nomeMetodo:argomento][istanza nomeMetodo:argomento]

Se ha bisogno di più di un argomento avremo diverse coppieSe ha bisogno di più di un argomento avremo diverse coppie nome:argomento, ad esempio se volessimo scrivere un metodo chenome:argomento, ad esempio se volessimo scrivere un metodo che

Page 55: Xcode4 Tutorial Completo

fa la somma e quindi restituisce un oggetto numerico, questofa la somma e quindi restituisce un oggetto numerico, questo metodo dovrà ricevere due operandi di tipo numerico, sarà quindimetodo dovrà ricevere due operandi di tipo numerico, sarà quindi dichiarato comedichiarato come

-(NSDecimalNumber *)somma:(NSDecimalNumber *)operando1-(NSDecimalNumber *)somma:(NSDecimalNumber *)operando1 allOperando: (NSDecimalNumber *)operando2allOperando: (NSDecimalNumber *)operando2

e sarà invocato dae sarà invocato da

[… somma:valoreA allOperando:valoreB][… somma:valoreA allOperando:valoreB]

dove al momento dell’invocazione valoreA e valoreB saranno duedove al momento dell’invocazione valoreA e valoreB saranno due variabili di tipo NSDecimalNumber che rappresentano i duevariabili di tipo NSDecimalNumber che rappresentano i due operandi da sommare.operandi da sommare.

Bene chiudiamo la parentesi sui metodi con parametri (neBene chiudiamo la parentesi sui metodi con parametri (ne vedremo molti altri in seguito, quindi riprenderemo questivedremo molti altri in seguito, quindi riprenderemo questi concetti) e torniamo al nostro objectAtIndex:.concetti) e torniamo al nostro objectAtIndex:.

Questo metodo abbiamo detto che deve ricevere in ingresso unQuesto metodo abbiamo detto che deve ricevere in ingresso un numero intero (in senso aritmetico) che indica la posizionenumero intero (in senso aritmetico) che indica la posizione dell’elenco da leggere. Ma noi non abbiamo un numero, in sensodell’elenco da leggere. Ma noi non abbiamo un numero, in senso aritmetico, ma un oggetto della classe NSDecimalNumber chearitmetico, ma un oggetto della classe NSDecimalNumber che rappresenta un numero; come passiamo da questo oggetto alrappresenta un numero; come passiamo da questo oggetto al numero intero rappresentato? Si tratta di un oggetto, quindinumero intero rappresentato? Si tratta di un oggetto, quindi avremo bisogno di un metodo; infatti noi scriviamoavremo bisogno di un metodo; infatti noi scriviamo

[canaleSintonizzato intValue][canaleSintonizzato intValue]

che vuol dire invoca sull’oggetto canaleSintonizzato il metodoche vuol dire invoca sull’oggetto canaleSintonizzato il metodo intValue; si tratta di un metodo predefinito che restituisce ilintValue; si tratta di un metodo predefinito che restituisce il numero intero rappresentato dall’oggetto su cui è invocato.numero intero rappresentato dall’oggetto su cui è invocato.

Quindi, riepilogando, l’istruzioneQuindi, riepilogando, l’istruzione

NSLog(@"Stai guardando il canale %@",[canaliMemorizzatiNSLog(@"Stai guardando il canale %@",[canaliMemorizzati objectAtIndex:[canaleSintonizzato intValue]]);objectAtIndex:[canaleSintonizzato intValue]]);prende l’oggetto canaleSintonizzato e ne restituisce il numeroprende l’oggetto canaleSintonizzato e ne restituisce il numero

intero rappresentatointero rappresentato

il risultato diil risultato di 1. viene passato al metodo objectAtIndex come1. viene passato al metodo objectAtIndex come

Page 56: Xcode4 Tutorial Completo

indicazione della posizione da leggere nell’elencoindicazione della posizione da leggere nell’elenco canaliMemorizzaticanaliMemorizzati

il valore letto dall’elenco va a sostituire il segnaposto nella formatil valore letto dall’elenco va a sostituire il segnaposto nella format stringstring

la stringa con il nuovo valore viene scritta a consolela stringa con il nuovo valore viene scritta a console

Possiamo chiudere con questo metodo, ma prima di eseguirePossiamo chiudere con questo metodo, ma prima di eseguire l’applicazione, dobbiamo osservare una cosa.l’applicazione, dobbiamo osservare una cosa.

Scorrendo i metodi scritti finora, noi non abbiamo mai creato unScorrendo i metodi scritti finora, noi non abbiamo mai creato un oggetto da assegnare alla variabile canaleSintonizzato, noi finoraoggetto da assegnare alla variabile canaleSintonizzato, noi finora abbiamo solo dichiarato questa variabile (cioè abbiamo detto cheabbiamo solo dichiarato questa variabile (cioè abbiamo detto che esiste e che si riferirà ad un istanza della classeesiste e che si riferirà ad un istanza della classe NSDecimalNumber). Quindi visto che abbiamo appena scritto ilNSDecimalNumber). Quindi visto che abbiamo appena scritto il metodo per far scrivere il canale che stiamo guardando,metodo per far scrivere il canale che stiamo guardando, preoccupiamoci anche di assegnargli un valore. Diciamo chepreoccupiamoci anche di assegnargli un valore. Diciamo che modelliamo un comportamento del tipomodelliamo un comportamento del tipo

Ogni volta che creo un televisore lo faccio partire dal canale 0Ogni volta che creo un televisore lo faccio partire dal canale 0

La creazione di un oggetto è riconducibile al metodo La creazione di un oggetto è riconducibile al metodo initinit, quindi, quindi vuol dire che dobbiamo scrivere qualcosa nel metodo vuol dire che dobbiamo scrivere qualcosa nel metodo initinit della della classe Televisore, in particolare dovremo assegnare alla variabileclasse Televisore, in particolare dovremo assegnare alla variabile canaleSintonizzato l’indice 0 come oggetto di NSDecimalNumber.canaleSintonizzato l’indice 0 come oggetto di NSDecimalNumber. Quindi selezioniamo il file Televisore.m e posizioniamociQuindi selezioniamo il file Televisore.m e posizioniamoci all’interno del blocco (ricordiamo che un blocco è racchiuso traall’interno del blocco (ricordiamo che un blocco è racchiuso tra {}) che segue{}) che segue

if (self)if (self)

nel metodo nel metodo initinit e scriviamo e scriviamo

canaleSintonizzato=[NSDecimalNumber zero];canaleSintonizzato=[NSDecimalNumber zero];è un’istruzione di assegnamento (c’è il simbolo ‘=’ ne abbiamoè un’istruzione di assegnamento (c’è il simbolo ‘=’ ne abbiamo viste già alcune) quindi alla variabile a sinistra del simbolo =viste già alcune) quindi alla variabile a sinistra del simbolo = viene assegnato il valore dell’espressione a destra del simbolo =.viene assegnato il valore dell’espressione a destra del simbolo =.

Quindi alla variabile canaleSintonizzato viene assegnato ilQuindi alla variabile canaleSintonizzato viene assegnato il risultato dirisultato di

Page 57: Xcode4 Tutorial Completo

[NSDecimalNumber zero][NSDecimalNumber zero]

Questa espressione ha la forma diQuesta espressione ha la forma di

[nomeClasse metodo][nomeClasse metodo]

si tratta quindi dell’invocazione di un metodo di classe; insi tratta quindi dell’invocazione di un metodo di classe; in particolare particolare zerozero è un metodo predefinito in Objective-C che è un metodo predefinito in Objective-C che restituisce un’istanza di NSDecimalNumber che rappresenta ilrestituisce un’istanza di NSDecimalNumber che rappresenta il valore 0.valore 0.

Approfondimento: cerca NSDecimalNumber nellaApprofondimento: cerca NSDecimalNumber nella documentazione Apple e studia i metodi zero e one.documentazione Apple e studia i metodi zero e one.

Bene, in tal modo abbiamo dato un valore iniziale alla variabileBene, in tal modo abbiamo dato un valore iniziale alla variabile canaleSintonizzato e possiamo ora eseguire nuovamentecanaleSintonizzato e possiamo ora eseguire nuovamente l’applicazione (cliccando sul bottone Run) e guardare il risultatol’applicazione (cliccando sul bottone Run) e guardare il risultato nella console.nella console.

Ai messaggi precedenti si è aggiunto il messaggioAi messaggi precedenti si è aggiunto il messaggio

2011-05-24 18:57:43.912 PrimoProgetto[99193:903] Stai2011-05-24 18:57:43.912 PrimoProgetto[99193:903] Stai

Page 58: Xcode4 Tutorial Completo

guardando il canale Onda1guardando il canale Onda1

e Onda1 è proprio il canale nella posizione 0 dell’elenco.e Onda1 è proprio il canale nella posizione 0 dell’elenco.

alzaIlVoumealzaIlVoume

In questo metodo andiamo ad aumentare il livello del volume delIn questo metodo andiamo ad aumentare il livello del volume del nostro ipotetico televisore.nostro ipotetico televisore.

Per modellare il livello del volume abbiamo dichiarato unaPer modellare il livello del volume abbiamo dichiarato una variabile di istanza, livelloVolume, di tipo numerico (istanza divariabile di istanza, livelloVolume, di tipo numerico (istanza di NSDecimalNumber). Quindi il nostro metodo dovràNSDecimalNumber). Quindi il nostro metodo dovrà semplicemente aumentare di 1 il valore rappresentato da questosemplicemente aumentare di 1 il valore rappresentato da questo oggetto. Per comodità ci faremo anche scrivere il livello deloggetto. Per comodità ci faremo anche scrivere il livello del volume prima e dopo l’incremento.volume prima e dopo l’incremento.

Quindi all’interno del metodo alzaIlVolume, nel file Televisore.m,Quindi all’interno del metodo alzaIlVolume, nel file Televisore.m, scriviamoscriviamo

NSLog (@"Il volume è a livello %@", livelloVolume);NSLog (@"Il volume è a livello %@", livelloVolume);livelloVolume= [livelloVolume decimalNumberByAdding:livelloVolume= [livelloVolume decimalNumberByAdding:[NSDecimalNumber one]];[NSDecimalNumber one]];NSLog (@"Il volume è a livello %@",livelloVolume);NSLog (@"Il volume è a livello %@",livelloVolume);La prima istruzione è un NSLog (ne abbiamo viste tante) cioèLa prima istruzione è un NSLog (ne abbiamo viste tante) cioè scriviamo a console qualcosa; in particolare abbiamo una formatscriviamo a console qualcosa; in particolare abbiamo una format string costituita da una parte fissa “Il volume è a livello” ed unstring costituita da una parte fissa “Il volume è a livello” ed un segnaposto (%@) che durante l’esecuzione sarà sostituito dalsegnaposto (%@) che durante l’esecuzione sarà sostituito dal valore dell’oggetto referenziato dalla variabile livelloVolume.valore dell’oggetto referenziato dalla variabile livelloVolume.

La seconda istruzione è un…assegnamento (anche qui dovremmoLa seconda istruzione è un…assegnamento (anche qui dovremmo essere ormai in grado di riconoscerla dal simbolo ‘=’. Quindi allaessere ormai in grado di riconoscerla dal simbolo ‘=’. Quindi alla variabile a sinistra (livelloVolume) assegnamo il valore risultantevariabile a sinistra (livelloVolume) assegnamo il valore risultante dall’espressione a destradall’espressione a destra

[livelloVolume decimalNumberByAdding:[NSDecimalNumber[livelloVolume decimalNumberByAdding:[NSDecimalNumber one]]one]]

Cosa è questa istruzione? Siamo ancora nella struttura tipica per laCosa è questa istruzione? Siamo ancora nella struttura tipica per la

Page 59: Xcode4 Tutorial Completo

programmazione Objective-Cprogrammazione Objective-C

[istanza metodo][istanza metodo]

Cioè è l’invocazione di un metodo su un’istanza di una classe; inCioè è l’invocazione di un metodo su un’istanza di una classe; in particolare stiamo invocando il metodo decimalNumberByAddingparticolare stiamo invocando il metodo decimalNumberByAdding sull’istanza livelloVolume. Si tratta di un metodo predefinito dellasull’istanza livelloVolume. Si tratta di un metodo predefinito della classe NSDecimalNumber che restituisce una nuova istanza dellaclasse NSDecimalNumber che restituisce una nuova istanza della classe ottenuta sommando al valore dell’istanza riceventeclasse ottenuta sommando al valore dell’istanza ricevente (livelloVolume) il valore dell’argomento del metodo, cioè, nel(livelloVolume) il valore dell’argomento del metodo, cioè, nel nostro casonostro caso

[NSDecimalNumber one][NSDecimalNumber one]

Anche qui siamo davanti all’invocazione di un metodo, questaAnche qui siamo davanti all’invocazione di un metodo, questa volta di classe in quanto il primo termine è una classe e nonvolta di classe in quanto il primo termine è una classe e non un’istanza, che restituisce l’oggetto che rappresenta il valore 1.un’istanza, che restituisce l’oggetto che rappresenta il valore 1.

Approfondimento: cerca NSDecimalNumber nellaApprofondimento: cerca NSDecimalNumber nella documentazione Apple e studia il metododocumentazione Apple e studia il metodo decimalNumberByAdding.decimalNumberByAdding.

Quindi, riepilogando,Quindi, riepilogando,

livelloVolume= [livelloVolume decimalNumberByAdding:livelloVolume= [livelloVolume decimalNumberByAdding:[NSDecimalNumber one]];[NSDecimalNumber one]];assegna alla variabile livelloVolume un oggetto che rappresenta ilassegna alla variabile livelloVolume un oggetto che rappresenta il valore che si ottiene sommando 1 al valore precedente divalore che si ottiene sommando 1 al valore precedente di livelloVolume.livelloVolume.

La terza istruzione è un NSLog identica alla prima che scrive ilLa terza istruzione è un NSLog identica alla prima che scrive il nuovo valore del livello del volume.nuovo valore del livello del volume.

Tutto qui? E’ un po’ troppo semplice, manca qualcosa. CosaTutto qui? E’ un po’ troppo semplice, manca qualcosa. Cosa succede nel nostro televisore di casa se alziamo il volume?succede nel nostro televisore di casa se alziamo il volume? Ovviamente il volume aumenta, e se lo alziamo ancora? Aumenta,Ovviamente il volume aumenta, e se lo alziamo ancora? Aumenta, e così via fino a che …raggiunge il livello massimo, a quel puntoe così via fino a che …raggiunge il livello massimo, a quel punto non aumenta più anche se premiamo il tasto aumenta volume.non aumenta più anche se premiamo il tasto aumenta volume.

Bene allora dobbiamo modellare anche questo comportamento nelBene allora dobbiamo modellare anche questo comportamento nel

Page 60: Xcode4 Tutorial Completo

nostro programma. Come procediamo? Iniziamo osservando chenostro programma. Come procediamo? Iniziamo osservando che abbiamo una nuova informazione che è il livello massimo delabbiamo una nuova informazione che è il livello massimo del nostro televisore, abbiamo quindi bisogno di una nuova variabilenostro televisore, abbiamo quindi bisogno di una nuova variabile di istanza. Selezioniamo il file Televisore.h e dopo la rigadi istanza. Selezioniamo il file Televisore.h e dopo la riga

NSDecimalNumber *livelloVolume;NSDecimalNumber *livelloVolume;scriviamo (prima della })scriviamo (prima della })

NSDecimalNumber *volumeMassimo;NSDecimalNumber *volumeMassimo;in questo modo abbiamo dichiarato la variabile che conterrà ilin questo modo abbiamo dichiarato la variabile che conterrà il valore massimo per il livello del volume. Dobbiamo però ancoravalore massimo per il livello del volume. Dobbiamo però ancora creare l’oggetto cui questa variabile si riferisce; il posto piùcreare l’oggetto cui questa variabile si riferisce; il posto più naturale dove inserire questa creazione è il metodo init della classenaturale dove inserire questa creazione è il metodo init della classe Televisore, il senso è che quando viene creato un televisore èTelevisore, il senso è che quando viene creato un televisore è anche definito qual è il livello massimo di volume. Per il nostroanche definito qual è il livello massimo di volume. Per il nostro esempio assumiamo che il volume massimo sia 100, e nel metodoesempio assumiamo che il volume massimo sia 100, e nel metodo init init diamo anche un valore iniziale al livello del volumediamo anche un valore iniziale al livello del volume posizionandolo a 50.posizionandolo a 50.

Quindi apriamo il file Televisore.m e posizioniamoci all’internoQuindi apriamo il file Televisore.m e posizioniamoci all’interno del metodo init e subito dopo la riga che abbiamo inseritodel metodo init e subito dopo la riga che abbiamo inserito precedentemente precedentemente canaleSintonizzato=[NSDecimalNumber zero];canaleSintonizzato=[NSDecimalNumber zero]; scriviamoscriviamo

volumeMassimo=[[NSDecimalNumber alloc]volumeMassimo=[[NSDecimalNumber alloc] initWithString:@"100"];initWithString:@"100"];è un’istruzione di assegnamento, la variabile volumeMassimoè un’istruzione di assegnamento, la variabile volumeMassimo prende il valore risultato dall’espressioneprende il valore risultato dall’espressione

[[NSDecimalNumber alloc] initWithString:@”100″][[NSDecimalNumber alloc] initWithString:@”100″]

è una forma che abbiamo già incontrato e si tratta di una chiamataè una forma che abbiamo già incontrato e si tratta di una chiamata del metodo di classe del metodo di classe allocalloc

[NSDecimalNumber alloc][NSDecimalNumber alloc]

che crea un’istanza della classe NSDecimalNumber e su questache crea un’istanza della classe NSDecimalNumber e su questa istanza si invoca il metodoistanza si invoca il metodo

Page 61: Xcode4 Tutorial Completo

initWithString:@”100″initWithString:@”100″

questo è un metodo predefinito della classe NSDecimalNumberquesto è un metodo predefinito della classe NSDecimalNumber che restituisce l’istanza sulla quale è invocato valorizzata con ilche restituisce l’istanza sulla quale è invocato valorizzata con il valore scritto tra gli “”, nel nostro caso 100.valore scritto tra gli “”, nel nostro caso 100.

Approfondimento: cerca NSDecimalNumber nellaApprofondimento: cerca NSDecimalNumber nella documentazione Apple e studia i metodi initWithString edocumentazione Apple e studia i metodi initWithString e initWithMantissa:exponent:isNegative:initWithMantissa:exponent:isNegative:

In maniera analoga inizializziamo a 50 il valore del livelloIn maniera analoga inizializziamo a 50 il valore del livello corrente del volumecorrente del volume

livelloVolume=[[NSDecimalNumber alloc]livelloVolume=[[NSDecimalNumber alloc] initWithString:@"50"];initWithString:@"50"];Bene ora che abbiamo ulteriormente configurato il nostroBene ora che abbiamo ulteriormente configurato il nostro televisore con il livello massimo ed il livello corrente del volumetelevisore con il livello massimo ed il livello corrente del volume possiamo tornare al nostro metodo alzaIlVolume.possiamo tornare al nostro metodo alzaIlVolume.

Come dobbiamo cambiare il codice? Abbiamo detto che il volumeCome dobbiamo cambiare il codice? Abbiamo detto che il volume si alza solo se non è al massimo, quindi dobbiamo scriveresi alza solo se non è al massimo, quindi dobbiamo scrivere qualcosa del tipoqualcosa del tipo

se il volume non è al massimo alza il volume altrimenti non farese il volume non è al massimo alza il volume altrimenti non fare nullanulla

Ricordiamo che nel capitolo sui flussi di controllo abbiamo parlatoRicordiamo che nel capitolo sui flussi di controllo abbiamo parlato di flusso condizionale cioè della possibilità di eseguire un bloccodi flusso condizionale cioè della possibilità di eseguire un blocco di istruzioni solo se una determinata condizione è vera. E’ propriodi istruzioni solo se una determinata condizione è vera. E’ proprio il costrutto di cui abbiamo bisogno; vediamo come si implementail costrutto di cui abbiamo bisogno; vediamo come si implementa in Objective-C.in Objective-C.

La struttura generale èLa struttura generale è

if (condizione) { … } else { … }if (condizione) { … } else { … }

se la condizione è vera viene eseguito il primo blocco di istruzioni,se la condizione è vera viene eseguito il primo blocco di istruzioni, quello racchiuso tra la prima coppia di {} altrimenti viene eseguitoquello racchiuso tra la prima coppia di {} altrimenti viene eseguito il secondo, quello racchiuso tra la seconda coppia di {}. Esiste unail secondo, quello racchiuso tra la seconda coppia di {}. Esiste una forma semplificata che comprende solo il blocco relativo allaforma semplificata che comprende solo il blocco relativo alla

Page 62: Xcode4 Tutorial Completo

condizione veracondizione vera

if (condizione) { … }if (condizione) { … }

nella quale se la condizione è falsa non viene eseguita nessunanella quale se la condizione è falsa non viene eseguita nessuna istruzione.istruzione.

Noi adotteremo la seconda forma.Noi adotteremo la seconda forma.

Nel metodo alzaIlVolume sostituiamo la rigaNel metodo alzaIlVolume sostituiamo la riga

livelloVolume= [livelloVolume decimalNumberByAdding:livelloVolume= [livelloVolume decimalNumberByAdding:[NSDecimalNumber one]];[NSDecimalNumber one]];concon

if ([livelloVolumeif ([livelloVolume compare:volumeMassimo]==NSOrderedAscending) {compare:volumeMassimo]==NSOrderedAscending) { livelloVolume= [livelloVolume decimalNumberByAdding: livelloVolume= [livelloVolume decimalNumberByAdding:[NSDecimalNumber one]];[NSDecimalNumber one]];}}in pratica abbiamo racchiuso l’istruzione di incremento del livelloin pratica abbiamo racchiuso l’istruzione di incremento del livello del volume dentro un blocco controllato dalla veridicità di unadel volume dentro un blocco controllato dalla veridicità di una condizione. Analizziamo ora la condizionecondizione. Analizziamo ora la condizione

[livelloVolume[livelloVolume compare:volumeMassimo]==NSOrderedAscendingcompare:volumeMassimo]==NSOrderedAscending

E’ un controllo di uguaglianza, l’operatore == confronta i dueE’ un controllo di uguaglianza, l’operatore == confronta i due termini, quello a sinistra e quello a destra e restituisce vero se sonotermini, quello a sinistra e quello a destra e restituisce vero se sono uguali, falso se sono diversi. Cosa c’è a sinistra?uguali, falso se sono diversi. Cosa c’è a sinistra?

[livelloVolume compare:volumeMassimo][livelloVolume compare:volumeMassimo]

solita invocazione di un metodo di istanza su un oggettosolita invocazione di un metodo di istanza su un oggetto (livelloVolume). Il metodo è (livelloVolume). Il metodo è comparecompare che prende in ingresso un che prende in ingresso un oggetto NSDecimalNumber (volumeMassimo) e lo confronta conoggetto NSDecimalNumber (volumeMassimo) e lo confronta con l’oggetto ricevente l’invocazione, cioè livelloVolume. In pratical’oggetto ricevente l’invocazione, cioè livelloVolume. In pratica confronta i valori di livelloVolume e volumeMassimo e restituisceconfronta i valori di livelloVolume e volumeMassimo e restituisce NSOrderedAscending se il primo è minore del secondo,NSOrderedAscending se il primo è minore del secondo, NSOrderedSame se sono uguali e NSOrderedDescending se ilNSOrderedSame se sono uguali e NSOrderedDescending se il

Page 63: Xcode4 Tutorial Completo

primo è maggiore del secondo. Quindi la condizioneprimo è maggiore del secondo. Quindi la condizione

[livelloVolume[livelloVolume compare:volumeMassimo]==NSOrderedAscendingcompare:volumeMassimo]==NSOrderedAscending

sarà vera quando livelloVolume è minore di volumeMassimo, edsarà vera quando livelloVolume è minore di volumeMassimo, ed in questo caso sarà eseguito il blocco di istruzioni, cioèin questo caso sarà eseguito il blocco di istruzioni, cioè l’incremento del volume.l’incremento del volume.

QuindiQuindi

if ([livelloVolumeif ([livelloVolume compare:volumeMassimo]==NSOrderedAscending) {compare:volumeMassimo]==NSOrderedAscending) { livelloVolume= [livelloVolume decimalNumberByAdding: livelloVolume= [livelloVolume decimalNumberByAdding:[NSDecimalNumber one]];[NSDecimalNumber one]];}}confronta livelloVolume e volumeMassimo e solo se il primo èconfronta livelloVolume e volumeMassimo e solo se il primo è minore del secondo incrementa il volume.minore del secondo incrementa il volume.

Prima di eseguire il programma, per vedere meglio l’effetto diPrima di eseguire il programma, per vedere meglio l’effetto di quello che abbiamo fatto, torniamo nel main.m e ripetiamo per trequello che abbiamo fatto, torniamo nel main.m e ripetiamo per tre volte l’istruzione alzaIlVolumevolte l’istruzione alzaIlVolume

[tv1 alzaIlVolume];[tv1 alzaIlVolume];[tv1 alzaIlVolume];[tv1 alzaIlVolume];[tv1 alzaIlVolume];[tv1 alzaIlVolume];Ora eseguiamo il programma cliccando sul bottone Run edOra eseguiamo il programma cliccando sul bottone Run ed osserviamo la console.osserviamo la console.

Page 64: Xcode4 Tutorial Completo
Page 65: Xcode4 Tutorial Completo
Page 66: Xcode4 Tutorial Completo
Page 67: Xcode4 Tutorial Completo
Page 68: Xcode4 Tutorial Completo

Vediamo i messaggi effetto del volume che parte da 50 (il valoreVediamo i messaggi effetto del volume che parte da 50 (il valore nel metodo init) ed aumenta.nel metodo init) ed aumenta.

abbassaIlVoumeabbassaIlVoume

In questo metodo andiamo a diminuire il livello del volume delIn questo metodo andiamo a diminuire il livello del volume del nostro ipotetico televisore. Come potete immaginare è un metodonostro ipotetico televisore. Come potete immaginare è un metodo molto simile al precedente. Anche questo metodo interviene sullamolto simile al precedente. Anche questo metodo interviene sulla variabile di istanza livelloVolume, e dovrà semplicementevariabile di istanza livelloVolume, e dovrà semplicemente diminuire di 1 il suo valore. Ovviamente anche qui dovremodiminuire di 1 il suo valore. Ovviamente anche qui dovremo introdurre un controllo per impedire che il volume possa scendereintrodurre un controllo per impedire che il volume possa scendere sotto il valore 0.sotto il valore 0.

Quindi all’interno del metodo, nel file Televisore.m, scriviamoQuindi all’interno del metodo, nel file Televisore.m, scriviamo

NSLog (@"Il volume è a livello %@", livelloVolume);NSLog (@"Il volume è a livello %@", livelloVolume);if ([livelloVolume compare:[NSDecimalNumberif ([livelloVolume compare:[NSDecimalNumber zero]]==NSOrderedDescending) {zero]]==NSOrderedDescending) { livelloVolume= [livelloVolume decimalNumberBySubtracting: livelloVolume= [livelloVolume decimalNumberBySubtracting:[NSDecimalNumber one]];[NSDecimalNumber one]];}}NSLog (@"Il volume è a livello %@",livelloVolume);NSLog (@"Il volume è a livello %@",livelloVolume);

Page 69: Xcode4 Tutorial Completo

La prima eLa prima e l’ultima istruzione sono gli NSLog, con format string l’ultima istruzione sono gli NSLog, con format string per far scrivere il livello del volume e sono le stesse del metodoper far scrivere il livello del volume e sono le stesse del metodo precedente.precedente.

if ([livelloVolume compare:[NSDecimalNumberif ([livelloVolume compare:[NSDecimalNumber zero]]==NSOrderedDescending) {zero]]==NSOrderedDescending) {

livelloVolume= [livelloVolume decimalNumberBySubtracting:livelloVolume= [livelloVolume decimalNumberBySubtracting:[NSDecimalNumber one]];[NSDecimalNumber one]];

}}

è un’istruzione per un flusso di controllo condizionale, cioè ilè un’istruzione per un flusso di controllo condizionale, cioè il blocco di istruzioni, racchiuso tra le {} è eseguito solo se lablocco di istruzioni, racchiuso tra le {} è eseguito solo se la condizione racchiusa tra le ( ) è vera.condizione racchiusa tra le ( ) è vera.

La condizioneLa condizione

[livelloVolume compare:[NSDecimalNumber[livelloVolume compare:[NSDecimalNumber zero]]==NSOrderedDescendingzero]]==NSOrderedDescending

è un controllo di uguaglianza, l’operatore == confronta i dueè un controllo di uguaglianza, l’operatore == confronta i due termini, quello a sinistra e quello a destra e restituisce vero se sonotermini, quello a sinistra e quello a destra e restituisce vero se sono uguali,uguali, falso se sono diversi. Cosa c’è a sinistra? falso se sono diversi. Cosa c’è a sinistra?

[livelloVolume compare:[NSDecimalNumber zero]][livelloVolume compare:[NSDecimalNumber zero]]

è la solita invocazione di un metodo di istanza su un oggettoè la solita invocazione di un metodo di istanza su un oggetto (livelloVolume). Il metodo è (livelloVolume). Il metodo è comparecompare che prende in ingresso un che prende in ingresso un oggetto NSDecimalNumber ([NSDecimalNumber zero]) e looggetto NSDecimalNumber ([NSDecimalNumber zero]) e lo confronta con l’oggetto ricevente l’invocazione, cioèconfronta con l’oggetto ricevente l’invocazione, cioè livelloVolume.livelloVolume.

[NSDecimalNumber zero][NSDecimalNumber zero]

lo abbiamo già incontrato ed è l’invocazione del metodo di classelo abbiamo già incontrato ed è l’invocazione del metodo di classe zero zero sulla classe NSDecimalNumber; questo metodo restituiscesulla classe NSDecimalNumber; questo metodo restituisce un’istanza di NSDecimalNumber che rappresenta il numero 0.un’istanza di NSDecimalNumber che rappresenta il numero 0.

In pratica confronta il valore di livelloVolume con 0 e restituisceIn pratica confronta il valore di livelloVolume con 0 e restituisce NSOrderedAscending se il primo è minore del secondo,NSOrderedAscending se il primo è minore del secondo,

Page 70: Xcode4 Tutorial Completo

NSOrderedSame se sono uguali e NSOrderedDescending se ilNSOrderedSame se sono uguali e NSOrderedDescending se il primo è maggiore del secondo. Quindi la condizioneprimo è maggiore del secondo. Quindi la condizione

[livelloVolume compare:[NSDecimalNumber[livelloVolume compare:[NSDecimalNumber zero]]==NSOrderedDescendingzero]]==NSOrderedDescending

sarà vera quando il valore di livelloVolume è maggiore di 0,sarà vera quando il valore di livelloVolume è maggiore di 0, ed in ed in questo caso sarà eseguito il blocco di istruzioni, cioè laquesto caso sarà eseguito il blocco di istruzioni, cioè la diminuzione del volume.diminuzione del volume.

Prima di eseguire il programma, per vedere meglio l’effetto diPrima di eseguire il programma, per vedere meglio l’effetto di quello che abbiamo fatto, torniamo nel main.m e ripetiamo per trequello che abbiamo fatto, torniamo nel main.m e ripetiamo per tre volte l’istruzione abbassaIlVolumevolte l’istruzione abbassaIlVolume

[tv1 abbassaIlVolume];[tv1 abbassaIlVolume];[tv1 abbassaIlVolume];[tv1 abbassaIlVolume];[tv1 abbassaIlVolume];[tv1 abbassaIlVolume];Eseguiamo il programma e guardiamo la console.Eseguiamo il programma e guardiamo la console.

Vediamo i messaggi relativi al volume che dopo esser arrivato aVediamo i messaggi relativi al volume che dopo esser arrivato a 53, per effetto d alzaIlVolume, torna a 50 con le invocazioni di53, per effetto d alzaIlVolume, torna a 50 con le invocazioni di abbassa il volume.abbassa il volume.

vaiAlCanalevaiAlCanale

Page 71: Xcode4 Tutorial Completo

In questo metodo sintonizziamo il televisore ad un canaleIn questo metodo sintonizziamo il televisore ad un canale specifico.specifico.

Una prima considerazione è che questo metodo per funzionare haUna prima considerazione è che questo metodo per funzionare ha bisogno di ricevere un’informazione al momento dell’invocazione,bisogno di ricevere un’informazione al momento dell’invocazione, cioè il numero del canale da sintonizzare.cioè il numero del canale da sintonizzare.

Per fare questo è necessario implementare un metodo che ricevaPer fare questo è necessario implementare un metodo che riceva dei parametri, infatti la sua dichiarazione, la troviamo nel filedei parametri, infatti la sua dichiarazione, la troviamo nel file Televisore.h, èTelevisore.h, è

-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare;-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare;

che vuol dire che il metodo che vuol dire che il metodo vaiAlCanalevaiAlCanale al momento al momento dell’invocazione riceverà un valore istanza di NSDecimalNumberdell’invocazione riceverà un valore istanza di NSDecimalNumber che sarà referenziato all’interno del metodo con il nomeche sarà referenziato all’interno del metodo con il nome canaleDaSintonizzare.canaleDaSintonizzare.

All’interno del blocco del metodo vaiAlCanale nel fileAll’interno del blocco del metodo vaiAlCanale nel file Televisore.m, dopo l’istruzioneTelevisore.m, dopo l’istruzione

NSLog(@"Sto eseguendo il metodo vaiAlCanale");NSLog(@"Sto eseguendo il metodo vaiAlCanale");scriviamoscriviamo

canaleSintonizzato=canaleDaSintonizzare;canaleSintonizzato=canaleDaSintonizzare;E’ un’istruzione di assegnamento, in pratica alla variabileE’ un’istruzione di assegnamento, in pratica alla variabile d’istanza canaleSintonizzato assegnamo il valore che riceverà ald’istanza canaleSintonizzato assegnamo il valore che riceverà al momento dell’invocazione (canaleDaSintonizzare). Vediamo oramomento dell’invocazione (canaleDaSintonizzare). Vediamo ora come sarà l’invocazione.come sarà l’invocazione.

Torniamo nel file main.m e, dopo l’ultima istruzioneTorniamo nel file main.m e, dopo l’ultima istruzione

[tv1 abbassaIlVolume];[tv1 abbassaIlVolume];scriviamoscriviamo

[tv1 vaiAlCanale:[[NSDecimalNumber alloc][tv1 vaiAlCanale:[[NSDecimalNumber alloc] initWithString:@"3"]];initWithString:@"3"]];cioè diciamo all’istanza tv1 di eseguire il metodo vaiAlCanalecioè diciamo all’istanza tv1 di eseguire il metodo vaiAlCanale passandogli come argomentopassandogli come argomento

Page 72: Xcode4 Tutorial Completo

[[NSDecimalNumber alloc] initWithString:@”3″][[NSDecimalNumber alloc] initWithString:@”3″]

è un’espressione che abbiamo già incontrato, [NSDecimalNumberè un’espressione che abbiamo già incontrato, [NSDecimalNumber alloc] crea un’istanza di NSDecimalNumber, su questa istanzaalloc] crea un’istanza di NSDecimalNumber, su questa istanza invochiamo il metodo initWithString:@”3″ che la inizializza coninvochiamo il metodo initWithString:@”3″ che la inizializza con il valore 3.il valore 3.

Infine per verificare se effettivamente il televisore ha cambiatoInfine per verificare se effettivamente il televisore ha cambiato canale, invochiamo nuovamente il metodo per farci scrivere ilcanale, invochiamo nuovamente il metodo per farci scrivere il canale sintonizzato, aggiungendo l’istruzionecanale sintonizzato, aggiungendo l’istruzione

[tv1 mostraIlCanale];[tv1 mostraIlCanale];Eseguiamo il programma ed osserviamo la console.Eseguiamo il programma ed osserviamo la console.

Vediamo che c’è il messaggioVediamo che c’è il messaggio

2011-05-25 22:39:46.358 PrimoProgetto[98714:903] Stai2011-05-25 22:39:46.358 PrimoProgetto[98714:903] Stai guardando il canale Onda4guardando il canale Onda4

ed effettivamete Onda4 è il canale in posizione 3.ed effettivamete Onda4 è il canale in posizione 3.

Per ora fermiamoci, questo capitolo è stato veramente lungo ePer ora fermiamoci, questo capitolo è stato veramente lungo e pieno di nuovi concetti, per cui vi invito a ripercorrerlo conpieno di nuovi concetti, per cui vi invito a ripercorrerlo con attenzione, facendo anche delle prove per vedere gli effetti delleattenzione, facendo anche delle prove per vedere gli effetti delle

Page 73: Xcode4 Tutorial Completo

vostre modifiche.vostre modifiche.

11 – Riepilogo ed altro11 – Riepilogo ed altro

iOS, , Mac, , OSX, , SDK, , Tech Room

Facciamo un po’ il punto focalizzando i concetti principali vistiFacciamo un po’ il punto focalizzando i concetti principali visti finora e facendo alcuni approfondimenti.finora e facendo alcuni approfondimenti.Il modello OOIl modello OO

Abbiamo visto che la programmazione secondo il modello OOAbbiamo visto che la programmazione secondo il modello OO prevede la descrizione del contesto secondo il meccanismo delleprevede la descrizione del contesto secondo il meccanismo delle classi e delle istanze.classi e delle istanze.

Le classi descrivono la struttura degli oggetti in termini di attributiLe classi descrivono la struttura degli oggetti in termini di attributi (variabili di istanza) e funzionalità (metodi). La definizione di una(variabili di istanza) e funzionalità (metodi). La definizione di una classe è effettuata attraverso due fileclasse è effettuata attraverso due file

<nome classe>.h: che contiene solo le dichiarazioni degli<nome classe>.h: che contiene solo le dichiarazioni degli elementi caratterizzanti la classe;elementi caratterizzanti la classe;

<nome classe>.m: che contiene l’implementazione dei<nome classe>.m: che contiene l’implementazione dei metodi;metodi;

La sintassi per l’invocazione di un metodo, e quindi per richiedereLa sintassi per l’invocazione di un metodo, e quindi per richiedere una certa funzionalità ad un oggetto o ad una classe èuna certa funzionalità ad un oggetto o ad una classe è

[oggetto metodoDiIstanza];[oggetto metodoDiIstanza];

[classe metodoDiClasse];[classe metodoDiClasse];

I metodi di istanza sono quelli che sono eseguiti dagli oggetti diI metodi di istanza sono quelli che sono eseguiti dagli oggetti di una classe e che modellano il comportamento stesso degli oggettiuna classe e che modellano il comportamento stesso degli oggetti (alzaIlVolume, vaiAlCanale,…). I metodi di classe invece sono(alzaIlVolume, vaiAlCanale,…). I metodi di classe invece sono eseguiti dalle classi (e non dalle istanze) e di solito si usano pereseguiti dalle classi (e non dalle istanze) e di solito si usano per creare istanze della classe (alloc,…).creare istanze della classe (alloc,…).

Facciamo un piccolo esercizio per chiarire ancora meglio laFacciamo un piccolo esercizio per chiarire ancora meglio la differenza tra classe (una) e istanze (tante). Nel nostro esempiodifferenza tra classe (una) e istanze (tante). Nel nostro esempio abbiamo per ora un solo televisore, ora creiamo altri due televisoriabbiamo per ora un solo televisore, ora creiamo altri due televisori

Page 74: Xcode4 Tutorial Completo

dichiarando due altra variabili tv2 e tv3, allocando eddichiarando due altra variabili tv2 e tv3, allocando ed inizializzando gli oggetti assegnati alle variabili (guarda comeinizializzando gli oggetti assegnati alle variabili (guarda come abbiamo fatto per tv1) e su queste due nuove istanze invoca iabbiamo fatto per tv1) e su queste due nuove istanze invoca i metodi che vuoi analizzando l’output e osservando che lemetodi che vuoi analizzando l’output e osservando che le modifiche fatte su un’istanza non si ripercuotono sulle altre: semodifiche fatte su un’istanza non si ripercuotono sulle altre: se cambio il canale di tv1non cambia quello in tv2, se alzo il volumecambio il canale di tv1non cambia quello in tv2, se alzo il volume in tv2 non cambia il volume in tv3,…in tv2 non cambia il volume in tv3,…

GerarchiaGerarchia

Nel nostro programma abbiamo definito la classe Televisore comeNel nostro programma abbiamo definito la classe Televisore come sottoclasse di Elettrodomestico (caratterizzato da un consumo) asottoclasse di Elettrodomestico (caratterizzato da un consumo) a sua volta sottoclasse di OggettoFisico (caratterizzato da un peso)sua volta sottoclasse di OggettoFisico (caratterizzato da un peso) ma durante lo sviluppo del codice non abbiamo mai fatto uso dima durante lo sviluppo del codice non abbiamo mai fatto uso di queste informazioni.queste informazioni.

Ora supponiamo di voler modellare il fatto che ogni televisoreOra supponiamo di voler modellare il fatto che ogni televisore della nostra classe consumi 500W e pesi 20Kg.della nostra classe consumi 500W e pesi 20Kg.

Apriamo il file Televisore.m e andiamo nel metodo init, dopoApriamo il file Televisore.m e andiamo nel metodo init, dopo l’istruzionel’istruzione

livelloVolume=[[NSDecimalNumber alloc]livelloVolume=[[NSDecimalNumber alloc] initWithString:@"50"];initWithString:@"50"];aggiungiamoaggiungiamo

consumo = [[NSDecimalNumber alloc] initWithString:@"500"];consumo = [[NSDecimalNumber alloc] initWithString:@"500"];Cosa abbiamo fatto? Abbiamo cercato di assegnare un valore (unCosa abbiamo fatto? Abbiamo cercato di assegnare un valore (un oggetto che rappresenta il valore 500) ad una variabile dellaoggetto che rappresenta il valore 500) ad una variabile della superclasse. In effetti in questo momento XCode ci segnala unsuperclasse. In effetti in questo momento XCode ci segnala un errore e se guardiamo l’errore segnalato, ci dice che errore e se guardiamo l’errore segnalato, ci dice che consumoconsumo è è una variabile privata.una variabile privata.

Page 75: Xcode4 Tutorial Completo

Quando costruiamo una gerarchia possiamo decidere qualiQuando costruiamo una gerarchia possiamo decidere quali elementi della superclasse possono essere ereditati dalleelementi della superclasse possono essere ereditati dalle sottoclassi e questo si fa facendo precederesottoclassi e questo si fa facendo precedere

@private@private

all’elenco delle variabili che non vogliamo che siano ereditate.all’elenco delle variabili che non vogliamo che siano ereditate.

Se torniamo sul file Elettrodomestico.h vediamo che prima dellaSe torniamo sul file Elettrodomestico.h vediamo che prima della dichiarazione della variabile dichiarazione della variabile consumoconsumo, c’è proprio la keyword, c’è proprio la keyword @private@private, cancelliamola e verifichiamo che l’errore sia sparito., cancelliamola e verifichiamo che l’errore sia sparito.

Facciamo la stessa cosa per il peso aggiungendo la rigaFacciamo la stessa cosa per il peso aggiungendo la riga

peso = [[NSDecimalNumber alloc] initWithString:@"20"];peso = [[NSDecimalNumber alloc] initWithString:@"20"];nel metodo Init della classe Televisore e togliendo @private danel metodo Init della classe Televisore e togliendo @private da OggettoFisico.h.OggettoFisico.h.

Infine, tanto per vedere il risultato di quello che abbiamo fatto, ciInfine, tanto per vedere il risultato di quello che abbiamo fatto, ci definiamo un nuovo metodo che chiamiamo mostraInfo che cidefiniamo un nuovo metodo che chiamiamo mostraInfo che ci stamperà le caratteristiche del televisore.stamperà le caratteristiche del televisore.

Nel file Televisore.h aggiungiamo alla lista dei metodi laNel file Televisore.h aggiungiamo alla lista dei metodi la dichiarazione del nuovo metododichiarazione del nuovo metodo

Page 76: Xcode4 Tutorial Completo

-(void)mostraInfo;-(void)mostraInfo;che ci stamperà le caratteristiche del televisore; in Televisore.mche ci stamperà le caratteristiche del televisore; in Televisore.m aggiungiamo (tra la } di un metodo e l’inizio del metodoaggiungiamo (tra la } di un metodo e l’inizio del metodo successivo) la sua implementazionesuccessivo) la sua implementazione

-(void)mostraInfo {-(void)mostraInfo {NSLog(@"Il televisore pesa %@ Kg e consuma %@NSLog(@"Il televisore pesa %@ Kg e consuma %@ W",peso,consumo);W",peso,consumo);}}Questo metodo non fa altro che scrivere a console la fraseQuesto metodo non fa altro che scrivere a console la frase

Il Televisore pesa … Kg e consuma … WIl Televisore pesa … Kg e consuma … W

come vedete abbiamo usato una format string con 2 segnapostocome vedete abbiamo usato una format string con 2 segnaposto che durante l’esecuzione saranno sostituiti con il valore di peso eche durante l’esecuzione saranno sostituiti con il valore di peso e consumo.consumo.

Bene, per veder l’effetto di questo metodo che dobbiamo fare?Bene, per veder l’effetto di questo metodo che dobbiamo fare? Ovviamente lo dobbiamo invocare quindi torniamo nel nostroOvviamente lo dobbiamo invocare quindi torniamo nel nostro main.m e aggiungiamo, in un punto qualsiasi, purchè sia dopomain.m e aggiungiamo, in un punto qualsiasi, purchè sia dopo l’inizializzazione di tv1l’inizializzazione di tv1

[tv1 mostraInfo];[tv1 mostraInfo];Eseguiamo il programma tramite il bottone Run e guardiamo laEseguiamo il programma tramite il bottone Run e guardiamo la console sulla quale adesso apparirà anche il messaggioconsole sulla quale adesso apparirà anche il messaggio

Il televisore pesa 20 Kg e consuma 500 WIl televisore pesa 20 Kg e consuma 500 W

Abbiamo appena visto cheAbbiamo appena visto che tramite una gerarchia una sottoclasse tramite una gerarchia una sottoclasse può usare anche le variabili di istanza (non private) dellepuò usare anche le variabili di istanza (non private) delle superclassi. Ma cosa succede dei metodi?superclassi. Ma cosa succede dei metodi?

Aggiungiamo un nuovo comportamento al nostro esempio,Aggiungiamo un nuovo comportamento al nostro esempio, supponiamo di voler modellare il fatto che un elettrodomestico sisupponiamo di voler modellare il fatto che un elettrodomestico si può accendere o spegnere; questo significa che in ogni istante ilpuò accendere o spegnere; questo significa che in ogni istante il mio elettrodomestico è in uno dei due stati e inoltre che ho unmio elettrodomestico è in uno dei due stati e inoltre che ho un meccanismo per fargli cambiare stato.meccanismo per fargli cambiare stato.

Dal punto di vista della programmazione abbiamo quindi unaDal punto di vista della programmazione abbiamo quindi una

Page 77: Xcode4 Tutorial Completo

nuova informazione da gestire, lo stato. Una nuova informazionenuova informazione da gestire, lo stato. Una nuova informazione richiede una nuova variabile di istanza; abbiamo diverserichiede una nuova variabile di istanza; abbiamo diverse opportunità di scelta che dipendono dai gusti personaliopportunità di scelta che dipendono dai gusti personali

una stringa che può assumere i valori Acceso o Spentouna stringa che può assumere i valori Acceso o Spento

una variabile che può assumere i valore Vero o Falso per dirciuna variabile che può assumere i valore Vero o Falso per dirci se è accesose è acceso

una variabile che può assumere i valore Vero o Falso per dirciuna variabile che può assumere i valore Vero o Falso per dirci se è spentose è spento

……

scegliamo la seconda, cioè avremo una variabile che ci dirà se èscegliamo la seconda, cioè avremo una variabile che ci dirà se è vero o falso che è acceso.vero o falso che è acceso.

In Objective-C i valori vero/falso determinano un tipo di datoIn Objective-C i valori vero/falso determinano un tipo di dato chiamato BOOL. Cioè le variabili di tipo BOOL possonochiamato BOOL. Cioè le variabili di tipo BOOL possono assumere solo due valori che il computer interpreta come Vero oassumere solo due valori che il computer interpreta come Vero o Falso. Attenzione: a differenza dei tipi visti finora, BOOL non èFalso. Attenzione: a differenza dei tipi visti finora, BOOL non è un a classe e le sue variabili non sono oggetti; si tratta di un tipo diun a classe e le sue variabili non sono oggetti; si tratta di un tipo di dato scalare come ne vedremo altri più avanti per quanto riguardadato scalare come ne vedremo altri più avanti per quanto riguarda i numeri.i numeri.

Quindi andiamo a dichiarare la nostra variabile; nel fileQuindi andiamo a dichiarare la nostra variabile; nel file Elettrodomestico.h (sempre all’interno delle {}) aggiungiamoElettrodomestico.h (sempre all’interno delle {}) aggiungiamo

BOOL acceso;BOOL acceso;abbiamo dichiarato una variabile di nome abbiamo dichiarato una variabile di nome accesoacceso e di tipo e di tipo BOOLBOOL (Osserviamo che non trattandosi di una classe ma di un tipo(Osserviamo che non trattandosi di una classe ma di un tipo elementare non c’è il carattere * prima del nome della variabile).elementare non c’è il carattere * prima del nome della variabile).

Poi la prima cosa che facciamo è che quando costruiamo unPoi la prima cosa che facciamo è che quando costruiamo un elettrodomestico questo è spento, quindi nel metodo init dielettrodomestico questo è spento, quindi nel metodo init di Elettrodomestico.m, all’interno delle {} dopo l’istruzione if (self)Elettrodomestico.m, all’interno delle {} dopo l’istruzione if (self) aggiungiamoaggiungiamo

acceso=FALSE;acceso=FALSE;cioè è falso che l’elettrodomestico sia acceso.cioè è falso che l’elettrodomestico sia acceso.

Page 78: Xcode4 Tutorial Completo

Adesso però dobbiamo modellare il nostro interruttore; nel fileAdesso però dobbiamo modellare il nostro interruttore; nel file Elettrodomestico.h aggiungiamo la dichiarazione di 2 metodiElettrodomestico.h aggiungiamo la dichiarazione di 2 metodi

-(void)accendi;-(void)accendi;-(void)spegni;-(void)spegni;con questi due metodi accenderemo e spegneremo il nostrocon questi due metodi accenderemo e spegneremo il nostro elettrodomestico andando semplicemente a cambiare il valoreelettrodomestico andando semplicemente a cambiare il valore Vero/Falso della variabile acceso, per comodità ci faremo ancheVero/Falso della variabile acceso, per comodità ci faremo anche scrivere in che stato è l’elettrodomestico.scrivere in che stato è l’elettrodomestico.

Nel file Elettrodomestico.m aggiungiamo le due definizioni deiNel file Elettrodomestico.m aggiungiamo le due definizioni dei metodimetodi

-(void)accendi {-(void)accendi {acceso=TRUE;acceso=TRUE;NSLog(@"L'elettrodomestico è acceso");NSLog(@"L'elettrodomestico è acceso");}}

-(void)spegni {-(void)spegni {acceso=FALSE;acceso=FALSE;NSLog(@"L'elettrodomestico è spento");NSLog(@"L'elettrodomestico è spento");}}e infine nel file main.m aggiungiamo, dopo [tv1 mostraInfo]e infine nel file main.m aggiungiamo, dopo [tv1 mostraInfo]

[tv1 accendi];[tv1 accendi];e prima di [pool drain]e prima di [pool drain]

[tv1 spegni];[tv1 spegni];Eseguiamo e osserviamo i messaggi, vediamo quindi che grazieEseguiamo e osserviamo i messaggi, vediamo quindi che grazie all’ereditarietà, anche i metodi delle superclassi (metodi definiti inall’ereditarietà, anche i metodi delle superclassi (metodi definiti in Elettrodomestico) si possono usare nelle sottoclassi (tv1 è unElettrodomestico) si possono usare nelle sottoclassi (tv1 è un Televisore).Televisore).

Un meccanismo importante legato a questo aspetto è il fatto che èUn meccanismo importante legato a questo aspetto è il fatto che è possibile comunque specializzare un metodo ereditato.possibile comunque specializzare un metodo ereditato.

Supponiamo di voler modellare il fatto che quando accendiamo unSupponiamo di voler modellare il fatto che quando accendiamo un televisore, oltre cambiare di stato acceso/spento ricerca i canali etelevisore, oltre cambiare di stato acceso/spento ricerca i canali e

Page 79: Xcode4 Tutorial Completo

ci mostra l’elenco di quelli sintonizzati.ci mostra l’elenco di quelli sintonizzati.

Andiamo in Televisore.mAndiamo in Televisore.m

-(void)accendi {-(void)accendi {[super accendi];[super accendi];[self ricercaCanali];[self ricercaCanali];[self mostraElenco];[self mostraElenco];}}come vedete non abbiamo avuto bisogno di dichiarare il metodocome vedete non abbiamo avuto bisogno di dichiarare il metodo accendi all’interno di Televisore.h, in quanto lo eredita daaccendi all’interno di Televisore.h, in quanto lo eredita da Elettrodomestico, ma ne abbiamo cambiato la suaElettrodomestico, ma ne abbiamo cambiato la sua implementazione. In particolare la prima cosa che facciamo è cheimplementazione. In particolare la prima cosa che facciamo è che accendiamo il nostro televisore in quanto elettrodomesticoaccendiamo il nostro televisore in quanto elettrodomestico

[super accendi];[super accendi];

questo ne cambia lo stato della variabile acceso, poi invochiamo iquesto ne cambia lo stato della variabile acceso, poi invochiamo i metodi propri della classe Televisoremetodi propri della classe Televisore

[self ricercaCanali];[self ricercaCanali];

[self mostraElenco];[self mostraElenco];

Ripeto una cosa già detta, super e self sono sempre lo stessoRipeto una cosa già detta, super e self sono sempre lo stesso oggetto (il mio televisore) ma una voltaoggetto (il mio televisore) ma una volta (super) agisco su di esso (super) agisco su di esso come Elettrodomestico e l’altra invece come Televisore macome Elettrodomestico e l’altra invece come Televisore ma l’oggetto è sempre lo stesso, non esiste alcun super-oggettol’oggetto è sempre lo stesso, non esiste alcun super-oggetto Elettrodomestico e un altro super-oggetto OggettoFisico, ma soloElettrodomestico e un altro super-oggetto OggettoFisico, ma solo superclassi.superclassi.

Ora per semplificare un po’ l’output, visto che l’invocazione deiOra per semplificare un po’ l’output, visto che l’invocazione dei ricercaCanali e mostraElenco l’abbiamo inserite in accendiricercaCanali e mostraElenco l’abbiamo inserite in accendi togliamo le righe corrispondenti dal main.m, eseguimo edtogliamo le righe corrispondenti dal main.m, eseguimo ed osserviamo l’output.osserviamo l’output.

Infine, per quanto riguarda la gerarchia, dobbiamo dare un sensoInfine, per quanto riguarda la gerarchia, dobbiamo dare un senso allo stato accesso/spento. Noi abbiamo tanti metodi,allo stato accesso/spento. Noi abbiamo tanti metodi,

-(void)ricercaCanali;-(void)ricercaCanali;

Page 80: Xcode4 Tutorial Completo

-(void)mostraElenco;-(void)mostraElenco;

-(void)mostraIlCanale;-(void)mostraIlCanale;

……

che in questo momento non tengono conto se il televisore è accesoche in questo momento non tengono conto se il televisore è acceso o spento; per fare una cosa corretta dovremo tutti trasformarli ino spento; per fare una cosa corretta dovremo tutti trasformarli in

se il televisore è acceso fai quello che devi fare altrimenti non farese il televisore è acceso fai quello che devi fare altrimenti non fare nullanulla

è una struttura di tipo condizionale, cioè un blocco di istruzioniè una struttura di tipo condizionale, cioè un blocco di istruzioni viene eseguito solo se si verifica una determinata condizione, nelviene eseguito solo se si verifica una determinata condizione, nel nostro caso solo se la variabile acceso è Vero.nostro caso solo se la variabile acceso è Vero.

Vi mostro come cambia il metodo mostraIlCanale, e vi lascio laVi mostro come cambia il metodo mostraIlCanale, e vi lascio la modifica degli altri.modifica degli altri.

-(void)mostraIlCanale {-(void)mostraIlCanale {NSLog(@"Sto eseguendo il metodo mostraIlCanale");NSLog(@"Sto eseguendo il metodo mostraIlCanale");if (acceso) {if (acceso) {NSLog(@"Stai guardando il canale %@",[canaliMemorizzatiNSLog(@"Stai guardando il canale %@",[canaliMemorizzati objectAtIndex:[canaleSintonizzato intValue]]);objectAtIndex:[canaleSintonizzato intValue]]);}}}}cioè l’istruzione che scrive il canale che stiamo guardando vienecioè l’istruzione che scrive il canale che stiamo guardando viene racchiusa in un blocco controllato dal valore della variabileracchiusa in un blocco controllato dal valore della variabile acceso, quindi se acceso vale Vero, sarà eseguita altrimenti no.acceso, quindi se acceso vale Vero, sarà eseguita altrimenti no.

Una volta fatte le modifiche provate ad eseguire il programma siaUna volta fatte le modifiche provate ad eseguire il programma sia con l’istruzione [tv1 accendi] nel main.m sia senza ed osservate icon l’istruzione [tv1 accendi] nel main.m sia senza ed osservate i diversi output.diversi output.

AritmeticaAritmetica

Quando abbiamo avuto bisogno di informazioni numeriche nelQuando abbiamo avuto bisogno di informazioni numeriche nel nostro programma (numero del canale, livello del volume, peso,nostro programma (numero del canale, livello del volume, peso,

Page 81: Xcode4 Tutorial Completo

consumo) abbiamo sempre usato variabili cui associare oggetticonsumo) abbiamo sempre usato variabili cui associare oggetti della classedella classe NSDecimalNumber, in effetti questa è la classe che NSDecimalNumber, in effetti questa è la classe che Objective-C mette a disposizione proprio per questo scopo.Objective-C mette a disposizione proprio per questo scopo.

Come abbiamo creato finora gli oggetti di questa classe?Come abbiamo creato finora gli oggetti di questa classe?

[[NSDecimalNumber alloc] initWithString:@”…”][[NSDecimalNumber alloc] initWithString:@”…”]

scrivendo il valore che volevamo rappresentare al posto deiscrivendo il valore che volevamo rappresentare al posto dei puntini.puntini.

Per l’accesso ai valori abbiamo invece usatoPer l’accesso ai valori abbiamo invece usato

[nomeIstanza intValue][nomeIstanza intValue]

per ottenere il valore intero contenuto nell’oggetto.per ottenere il valore intero contenuto nell’oggetto.

Approfondimento: Cerca sulla documentazione Apple la classeApprofondimento: Cerca sulla documentazione Apple la classe NSDecimalNumber, osserva che è sottoclasse di NSNumber eNSDecimalNumber, osserva che è sottoclasse di NSNumber e guarda la classe NSNumber, in particolare i metodi costruttori edguarda la classe NSNumber, in particolare i metodi costruttori ed inizializzatoriinizializzatori

Solitamente nei programmi Objective-C per la gestione delleSolitamente nei programmi Objective-C per la gestione delle informazioni numeriche si fa ricorso a tipi di dato scalari, cioè ainformazioni numeriche si fa ricorso a tipi di dato scalari, cioè a tipi dii dato che fanno riferimento a valori piuttosto che a oggetti.tipi dii dato che fanno riferimento a valori piuttosto che a oggetti.

Quando trattiamo un’informazione numerica con i tipi scalari, laQuando trattiamo un’informazione numerica con i tipi scalari, la prima considerazione che dobbiamo fare è se si tratta di unprima considerazione che dobbiamo fare è se si tratta di un numero intero o decimale.numero intero o decimale.

Nel primo caso in Objective-C, è disponibile il tipoNel primo caso in Objective-C, è disponibile il tipo

intint

nel secondo caso sono disponibili i tipinel secondo caso sono disponibili i tipi

float e doublefloat e double

Per valori particolarmente grandi si antepone la keyword long,Per valori particolarmente grandi si antepone la keyword long, inoltre è possibile usare la keyword unsigned per indicare variabiliinoltre è possibile usare la keyword unsigned per indicare variabili che assumeranno solo valori positivi. In generale i valoriche assumeranno solo valori positivi. In generale i valori rappresentabili in questi tipi di dato dipendono dalle diverserappresentabili in questi tipi di dato dipendono dalle diverse

Page 82: Xcode4 Tutorial Completo

implementazioni dei SO e del HW su cui sono compilati;implementazioni dei SO e del HW su cui sono compilati; possiamo in linea di massima dire che gli intervalli di valoripossiamo in linea di massima dire che gli intervalli di valori possibili sono:possibili sono:

int : [-2^31..2^31-1]int : [-2^31..2^31-1]

unsigned int: [0..2^32-1]unsigned int: [0..2^32-1]

long int: [-2^63..2^63-1]long int: [-2^63..2^63-1]

unsigned long int: [0..2^64-1]unsigned long int: [0..2^64-1]

Per quanto riguarda i valori float e double, non è mia intenzionePer quanto riguarda i valori float e double, non è mia intenzione entrare in questa guida nel merito della rappresentazione in virgolaentrare in questa guida nel merito della rappresentazione in virgola mobile per cui vi rimando alla descrizione nella documentazionemobile per cui vi rimando alla descrizione nella documentazione ed in particolare aprendo l’applicazione ed in particolare aprendo l’applicazione TerminalTerminal e digitando e digitando manman floatfloat..

L’uso dei tipi scalari ha un impatto anche sulle format string.L’uso dei tipi scalari ha un impatto anche sulle format string. Ricordate che per far stampare un numero (istanza diRicordate che per far stampare un numero (istanza di NSDecimalNumber) noi abbiamo usato come segnaposto ilNSDecimalNumber) noi abbiamo usato come segnaposto il simbolo %@. Se invece stiamo usando valori int, il segnaposto dasimbolo %@. Se invece stiamo usando valori int, il segnaposto da usare sarà %d; nel caso di valori float (o double) sarà %f. Neiusare sarà %d; nel caso di valori float (o double) sarà %f. Nei segnaposto %f, per i numeri con una parte decimale, possiamosegnaposto %f, per i numeri con una parte decimale, possiamo anche specificare quante cifre decimali vogliamo vedere, cioè ilanche specificare quante cifre decimali vogliamo vedere, cioè il segnaposto sarà del tipo %.2f che vorrà dire che vogliamo chesegnaposto sarà del tipo %.2f che vorrà dire che vogliamo che siano stampate 2 cifre decimali.siano stampate 2 cifre decimali.

Tornando al nostro programma vediamo come si sarebbero potutiTornando al nostro programma vediamo come si sarebbero potuti usare i valori scalari int invece degli oggetti NSDecimalNumber.usare i valori scalari int invece degli oggetti NSDecimalNumber.

Modifichiamo ad esempio il livello del volume.Modifichiamo ad esempio il livello del volume.

Prima cosa andiamo nel file Televisore.h e modifichiamo le duePrima cosa andiamo nel file Televisore.h e modifichiamo le due righe di livelloVolume e livelloMassimo come seguerighe di livelloVolume e livelloMassimo come segue

//// NSDecimalNumber *livelloVolume; NSDecimalNumber *livelloVolume;//// NSDecimalNumber *volumeMassimo; NSDecimalNumber *volumeMassimo;cioè le facciamo precedere da una coppia di slash //; questocioè le facciamo precedere da una coppia di slash //; questo trasforma le due righe in commenti, cioè il compilatore non letrasforma le due righe in commenti, cioè il compilatore non le prende in considerazione, ma per noi sarà utile tenerle lì se poi inprende in considerazione, ma per noi sarà utile tenerle lì se poi in

Page 83: Xcode4 Tutorial Completo

seguito vorremo tornare a questa modalità di programmazione.seguito vorremo tornare a questa modalità di programmazione.

Subito dopo le due righe appena commentate aggiungiamoSubito dopo le due righe appena commentate aggiungiamo

int livelloVolume;int livelloVolume;int volumeMassimo;int volumeMassimo;cioè abbiamo dichiarato due variabili con lo stesso nome dellecioè abbiamo dichiarato due variabili con lo stesso nome delle precedenti però stavolta di tipo int. Osserviamo che nonprecedenti però stavolta di tipo int. Osserviamo che non trattandosi di classi non c’è bisogno del simbolo * prima del nometrattandosi di classi non c’è bisogno del simbolo * prima del nome della variabile.della variabile.

Ora andiamo nel file Televisore.m a modificare il codice deiOra andiamo nel file Televisore.m a modificare il codice dei metodi init, alzaIlVolume ed abbassaIlVolume.metodi init, alzaIlVolume ed abbassaIlVolume.

Iniziamo con il metodo init. Individuiamo le due righe cheIniziamo con il metodo init. Individuiamo le due righe che assegnano i valori iniziali a livelloVolume e livelloMassimo e cheassegnano i valori iniziali a livelloVolume e livelloMassimo e che ora sono segnalate da un warning in quanto gli assegnamenti diora sono segnalate da un warning in quanto gli assegnamenti di oggetti non sono più compatibili con il tipo delle due variabili cheoggetti non sono più compatibili con il tipo delle due variabili che abbiamo dichiarato int. Come prima, commentiamo le dueabbiamo dichiarato int. Come prima, commentiamo le due istruzioni anteponendo // ad entrambe le righe e subito dopo esseistruzioni anteponendo // ad entrambe le righe e subito dopo esse aggiungiamo le nuove inizializzazioniaggiungiamo le nuove inizializzazioni

volumeMassimo=100;volumeMassimo=100;livelloVolume=50;livelloVolume=50;alla variabile viene assegnato direttamente il valore numerico.alla variabile viene assegnato direttamente il valore numerico.

Passiamo ai due metodi per alzare ed abbassare il volume, comePassiamo ai due metodi per alzare ed abbassare il volume, come abbiamo fatto per le variabili però teniamoci da parte la versioneabbiamo fatto per le variabili però teniamoci da parte la versione attuale facendola diventare un commento. Per fare questo,attuale facendola diventare un commento. Per fare questo, trattandosi di più righe, invece di anteporre // ad ogni rigatrattandosi di più righe, invece di anteporre // ad ogni riga aggiungiamo /* prima della definizione del metodo alzaIlVolume eaggiungiamo /* prima della definizione del metodo alzaIlVolume e aggiungiamo */ dopo la fine della definizione del metodoaggiungiamo */ dopo la fine della definizione del metodo abbassaIlVolume; dovrete avere qualcosa del tipo illustrato inabbassaIlVolume; dovrete avere qualcosa del tipo illustrato in figura.figura.

Page 84: Xcode4 Tutorial Completo
Page 85: Xcode4 Tutorial Completo
Page 86: Xcode4 Tutorial Completo

Bene a questo punto dobbiamo ridefinirci i due metodi che peròBene a questo punto dobbiamo ridefinirci i due metodi che però stavolta faranno uso di variabili di tipo int. Questo è il nuovostavolta faranno uso di variabili di tipo int. Questo è il nuovo alzaIlVolumealzaIlVolume

-(void)alzaIlVolume {-(void)alzaIlVolume {NSLog(@"Sto eseguendo il metodo alzaIlVolume");NSLog(@"Sto eseguendo il metodo alzaIlVolume");if (acceso) {if (acceso) {NSLog (@"Il volume è a livello %d", livelloVolume);NSLog (@"Il volume è a livello %d", livelloVolume);if (livelloVolume < volumeMassimo) {if (livelloVolume < volumeMassimo) {livelloVolume= livelloVolume+1;livelloVolume= livelloVolume+1;}}NSLog (@"Il volume è a livello %d",livelloVolume);NSLog (@"Il volume è a livello %d",livelloVolume);}}}}Prima cosa da notare è la format string che ora contiene ilPrima cosa da notare è la format string che ora contiene il segnaposto %d dovendo ricevere un valore int.segnaposto %d dovendo ricevere un valore int.

Il test di condizione usa l’operatore < (minore) che restituisce veroIl test di condizione usa l’operatore < (minore) che restituisce vero se il termine a sinistra è minore di quello a destra. Gli operatori dise il termine a sinistra è minore di quello a destra. Gli operatori di confronto sonoconfronto sono

L’istruzione di incremento del livello del volume usa l’operatoreL’istruzione di incremento del livello del volume usa l’operatore aritmetico +. Gli operatori aritmetici sonoaritmetico +. Gli operatori aritmetici sono

Page 87: Xcode4 Tutorial Completo

Un’attenzione particolare merita la divisione /. Se la divisioneUn’attenzione particolare merita la divisione /. Se la divisione viene applicata a due valori interi l’operazione sarà una divisioneviene applicata a due valori interi l’operazione sarà una divisione intera, cioè con quoziente intero; se invece uno dei due operandi èintera, cioè con quoziente intero; se invece uno dei due operandi è float l’operazione restituirà un quoziente decimale.float l’operazione restituirà un quoziente decimale.

Infine il metodoInfine il metodo

-(void)abbassaIlVolume {-(void)abbassaIlVolume {NSLog(@"Sto eseguendo il metodo abbassaIlVolume");NSLog(@"Sto eseguendo il metodo abbassaIlVolume");if (acceso) {if (acceso) {NSLog (@"Il volume è a livello %d", livelloVolume);NSLog (@"Il volume è a livello %d", livelloVolume);if (livelloVolume > 0) {if (livelloVolume > 0) {livelloVolume= livelloVolume-1;livelloVolume= livelloVolume-1;}}NSLog (@"Il volume è a livello %d",livelloVolume);NSLog (@"Il volume è a livello %d",livelloVolume);}}}}valgono le stesse considerazioni del metodo precedente.valgono le stesse considerazioni del metodo precedente.

Bene, se ora proviamo ad eseguire il programma vediamo cheBene, se ora proviamo ad eseguire il programma vediamo che otteniamo gli stessi risultati precedenti. Questa è una lezioneotteniamo gli stessi risultati precedenti. Questa è una lezione importante, quando facciamo un programma non c’è un unicoimportante, quando facciamo un programma non c’è un unico modo di fare le cose.modo di fare le cose.

Per esercizio ora provate a cambiare tutti gli NSDecimalNumberPer esercizio ora provate a cambiare tutti gli NSDecimalNumber in int.in int.

Nel prossimo articolo introdurremo la comunicazione tra dueNel prossimo articolo introdurremo la comunicazione tra due oggetti.oggetti.

Page 88: Xcode4 Tutorial Completo

12 – Comunicazione tra oggetti12 – Comunicazione tra oggetti

iOS, , Mac, , OSX, , SDK, , Tech Room

Riprendiamo lo sviluppo del nostro televisore.Riprendiamo lo sviluppo del nostro televisore.

Abbiamo implementato le funzioni principali che ci interessano,Abbiamo implementato le funzioni principali che ci interessano, ma per fargli fare qualcosa agiamo direttamente sulle suema per fargli fare qualcosa agiamo direttamente sulle sue funzionalità, invochiamo direttamente i metodi di Televisore, èfunzionalità, invochiamo direttamente i metodi di Televisore, è come se per cambiare canale o alzare il volume ogni voltacome se per cambiare canale o alzare il volume ogni volta dovessimo alzarci dalla poltrona e girare qualche manopola delladovessimo alzarci dalla poltrona e girare qualche manopola della tv. E’ il momento di acquistare un telecomando e farlo comunicaretv. E’ il momento di acquistare un telecomando e farlo comunicare con la nostra tv.con la nostra tv.

Abbiamo bisogno quindi di un nuovo tipo di oggetto; inAbbiamo bisogno quindi di un nuovo tipo di oggetto; in Objective-C questo si traduce nella definizione di una nuovaObjective-C questo si traduce nella definizione di una nuova classe.classe.

Facendo click con il bottone destro sulla cartella con il nome delFacendo click con il bottone destro sulla cartella con il nome del progetto in XCode scegliamo New File e poi il templateprogetto in XCode scegliamo New File e poi il template Objective-C Class del gruppo Cocoa Touch nella sezione iOS,Objective-C Class del gruppo Cocoa Touch nella sezione iOS, lasciamo NSObject come superclasse e chiamiamo la nuova classelasciamo NSObject come superclasse e chiamiamo la nuova classe Telecomando. Alla fine della procedura di creazione avremo dueTelecomando. Alla fine della procedura di creazione avremo due nuovi file nel nostro progetto Telecomando.h dove scriveremo lenuovi file nel nostro progetto Telecomando.h dove scriveremo le dichiarazioni e Telecomando.m dove scriveremo ledichiarazioni e Telecomando.m dove scriveremo le implementazioni.implementazioni.

Iniziamo con il file header (Telecomando.h); ricordiamo cheIniziamo con il file header (Telecomando.h); ricordiamo che all’interno delle {} dovremo dichiarare le variabili di istanza cheall’interno delle {} dovremo dichiarare le variabili di istanza che modellano le informazioni caratteristiche della classe, mentremodellano le informazioni caratteristiche della classe, mentre prima di @end dovremo dichiarare i metodi che modellano leprima di @end dovremo dichiarare i metodi che modellano le funzionalità della classe.funzionalità della classe.

Page 89: Xcode4 Tutorial Completo

Che informazioni gestisce un telecomando?…il volume, la listaChe informazioni gestisce un telecomando?…il volume, la lista dei canali, il canale sintonizzato, sono tutte informazioni deldei canali, il canale sintonizzato, sono tutte informazioni del televisore, non sono nel telecomando; quindi come prima ipotesitelevisore, non sono nel telecomando; quindi come prima ipotesi (più avanti vedremo che non è vera) diciamo che il telecomando(più avanti vedremo che non è vera) diciamo che il telecomando non ha informazioni proprie e quindi non dichiariamo nessunanon ha informazioni proprie e quindi non dichiariamo nessuna variabile di istanza.variabile di istanza.

Passiamo ai metodi. Il nostro telecomando dovrà essere in gradoPassiamo ai metodi. Il nostro telecomando dovrà essere in grado di accendere e spegnere di accendere e spegnere il televisoreil televisore, di alzar, di alzarnene o abbassar o abbassarnene il il volume e di cambiarvolume e di cambiarnene il canale. il canale. Ho evidenziato alcune parti della Ho evidenziato alcune parti della descrizione che implicano che i metodi del telecomando dovrannodescrizione che implicano che i metodi del telecomando dovranno modificare lo stato del televisore, ma per la regolamodificare lo stato del televisore, ma per la regola dell’incapsulamento, lo potrà fare solo invocando i metodi deldell’incapsulamento, lo potrà fare solo invocando i metodi del televisore.televisore.

Iniziamo quindi a dichiarare i metodi del telecomando, nel fileIniziamo quindi a dichiarare i metodi del telecomando, nel file Telecomando.h prima della rigaTelecomando.h prima della riga

@end@end

scriviamoscriviamo

-(void)accendiIlTelevisore;-(void)accendiIlTelevisore;-(void)spegniIlTelevisore;-(void)spegniIlTelevisore;-(void)mostraIlCanaleDelTelevisore;-(void)mostraIlCanaleDelTelevisore;-(void)alzaIlVolumeDelTelevisore;-(void)alzaIlVolumeDelTelevisore;-(void)abbassaIlVolumeDelTelevisore;-(void)abbassaIlVolumeDelTelevisore;-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare;-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare;Ricordiamo brevemente la struttura della dichiarazione di unRicordiamo brevemente la struttura della dichiarazione di un metodo:metodo:

Il simbolo ‘-’ indica che stiamo dichiarando un metodo diIl simbolo ‘-’ indica che stiamo dichiarando un metodo di istanza, cioè un metodo che sarà eseguito da un’istanza dellaistanza, cioè un metodo che sarà eseguito da un’istanza della classe;classe;

(void) indica il tipo del risultato dell’esecuzione del metodo,(void) indica il tipo del risultato dell’esecuzione del metodo, in particolare void indica nessun tipo, poichè i nostri metodiin particolare void indica nessun tipo, poichè i nostri metodi non restituiscono nessun dato, ma agiscono solo sullo statonon restituiscono nessun dato, ma agiscono solo sullo stato degli oggetti;degli oggetti;

Page 90: Xcode4 Tutorial Completo

poi c’è il nome del metodopoi c’è il nome del metodo

se un metodo richiede un’informazione al momentose un metodo richiede un’informazione al momento dell’invocazione per poter essere eseguito (vaiAlCanale), ildell’invocazione per poter essere eseguito (vaiAlCanale), il nome è seguita da ‘:’ e l’indicazione del parametro espressonome è seguita da ‘:’ e l’indicazione del parametro espresso comecome

(tipo del parametro), quando si tratta di una classe(tipo del parametro), quando si tratta di una classe metteremo il simbolo ‘*’metteremo il simbolo ‘*’

nome del parametronome del parametro

Ho tralasciato l’implementazione di alcuni metodi che potrannoHo tralasciato l’implementazione di alcuni metodi che potranno essere invocati solo direttamente dal televisore, ma se preferiteessere invocati solo direttamente dal televisore, ma se preferite potete fare un telecomando più potente ed implementarli tutti;potete fare un telecomando più potente ed implementarli tutti; un’altra osservazione riguarda il metodo vaiAlCanale che prendeun’altra osservazione riguarda il metodo vaiAlCanale che prende come parametro un numero, come abbiamo visto nell’articolocome parametro un numero, come abbiamo visto nell’articolo precedente per gestire informazioni numeriche si possono usareprecedente per gestire informazioni numeriche si possono usare anche tipi di dato elementare (int, float, double), per gustoanche tipi di dato elementare (int, float, double), per gusto personale io preferisco in genere usare oggetti per gestirepersonale io preferisco in genere usare oggetti per gestire informazioni numeriche non espressamente aritmetiche visto cheinformazioni numeriche non espressamente aritmetiche visto che stiamo programmando in OO e ricorrere ai tipi elementari quandostiamo programmando in OO e ricorrere ai tipi elementari quando avremo bisogno di variabili di supporto (ad esempio contatori)avremo bisogno di variabili di supporto (ad esempio contatori) come vedremo nei prossimi articoli. Ma se preferite usare lecome vedremo nei prossimi articoli. Ma se preferite usare le variabili semplici vi lascio l’adattamento a questo modello comevariabili semplici vi lascio l’adattamento a questo modello come esercizio.esercizio.

Ora andiamo a preparare la struttura dei metodi nel file diOra andiamo a preparare la struttura dei metodi nel file di implementazione (Telecomando.m). Per ognuno dei metodiimplementazione (Telecomando.m). Per ognuno dei metodi ripetiamo l’intestazione (senza il ed aggiungiamo una coppia diripetiamo l’intestazione (senza il ed aggiungiamo una coppia di { } per racchiudere il blocco di istruzioni del metodo.{ } per racchiudere il blocco di istruzioni del metodo.

Es.Es.

-(void)accendiIlTelevisore {-(void)accendiIlTelevisore {

}}Bene, iniziamo proprio dall’implementazione del metodoBene, iniziamo proprio dall’implementazione del metodo accendiIlTelevisore.accendiIlTelevisore.

Page 91: Xcode4 Tutorial Completo

Prima cosa ci facciamo scrivere che il metodo è in esecuzione, traPrima cosa ci facciamo scrivere che il metodo è in esecuzione, tra le { } scriviamole { } scriviamo

NSLog(@"Il telecomando sta accendendo il televisore");NSLog(@"Il telecomando sta accendendo il televisore");Poi quello che dobbiamo fare è far accendere il televisore dalPoi quello che dobbiamo fare è far accendere il televisore dal telecomando, questo lo sappiamo fare, è sufficiente invocare iltelecomando, questo lo sappiamo fare, è sufficiente invocare il metodo metodo accendiaccendi di Televisore. di Televisore. Già ma su quale istanza lo Già ma su quale istanza lo invochiamo?invochiamo?

La risposta a questa domanda nasconde una regola importante:La risposta a questa domanda nasconde una regola importante: perchè due perchè due oggetti comunichino è necessario che uno abbia iloggetti comunichino è necessario che uno abbia il riferimento dell’altroriferimento dell’altro. Come tutte le regole avrà la sua eccezione. Come tutte le regole avrà la sua eccezione ma ci arriveremo solo più avanti.ma ci arriveremo solo più avanti.

Cosa succede quando vogliamo usare il nostro telecomando?Cosa succede quando vogliamo usare il nostro telecomando? Dobbiamo puntarlo verso il ricevitore ad infrarossi del televisore;Dobbiamo puntarlo verso il ricevitore ad infrarossi del televisore; cioè il telecomando deve cioè il telecomando deve conoscereconoscere e e vederevedere il televisore che sta il televisore che sta pilotando. Quindi il telecomando deve avere un riferimento alpilotando. Quindi il telecomando deve avere un riferimento al televisore.televisore.

Sembrerebbe naturale dire che noi abbiamo già un riferimento cheSembrerebbe naturale dire che noi abbiamo già un riferimento che è tv1; quindi proviamo a scrivere, sotto l’istruzione NSLog cheè tv1; quindi proviamo a scrivere, sotto l’istruzione NSLog che abbiamo appena inseritoabbiamo appena inserito

[tv1 accendi];[tv1 accendi];cioè diciamo che il nostro telecomando, quando invocheremo ilcioè diciamo che il nostro telecomando, quando invocheremo il metodo accendiIlTelevisore, accenderà tv1; mametodo accendiIlTelevisore, accenderà tv1; ma XCode ci segnalaXCode ci segnala un errore,un errore, che ci dice che non conosce tv1. che ci dice che non conosce tv1.

Nell’uso delle variabili c’è una caratteristica fondamentale che èNell’uso delle variabili c’è una caratteristica fondamentale che è quella della quella della visibilitàvisibilità. Cioè in quali parti del programma è. Cioè in quali parti del programma è utilizzabile una variabile. La regola è abbastanza semplice, unautilizzabile una variabile. La regola è abbastanza semplice, una variabile è visibile all’interno del blocco ( { } ) nel quale è statavariabile è visibile all’interno del blocco ( { } ) nel quale è stata dichiarata. La nostra variabile tv1 è dichiarata nel blocco dichiarata. La nostra variabile tv1 è dichiarata nel blocco mainmain ed ed ora siamo nel blocco ora siamo nel blocco accendiIlTelevisoreaccendiIlTelevisore quindi qui la variabile quindi qui la variabile tv1 non è visibile e XCode ce lo dice.tv1 non è visibile e XCode ce lo dice.

Implementiamo quindi una nostra variabile di istanza, nella classeImplementiamo quindi una nostra variabile di istanza, nella classe

Page 92: Xcode4 Tutorial Completo

Telecomando, che conterrà il riferimento al televisore da pilotare eTelecomando, che conterrà il riferimento al televisore da pilotare e che sarà usata da tutti i metodi di Telecomando.che sarà usata da tutti i metodi di Telecomando.

Nel file Telecomando.h, all’interno delle { } scriviamoNel file Telecomando.h, all’interno delle { } scriviamo

Televisore *myTv;Televisore *myTv;XCode segnala ancora un errore, perchè? Perchè la classeXCode segnala ancora un errore, perchè? Perchè la classe Telecomando non conosce la classe Televisore, quindi non si puòTelecomando non conosce la classe Televisore, quindi non si può usarla per dichiarare una variabile di istanza. Per risolvere l’errore,usarla per dichiarare una variabile di istanza. Per risolvere l’errore, dopodopo

#import <Foundation/Foundation.h>#import <Foundation/Foundation.h>aggiungiamoaggiungiamo

#import "Televisore.h"#import "Televisore.h"in questo modo la classe Televisore sarà conosciuta ed utilizzabilein questo modo la classe Televisore sarà conosciuta ed utilizzabile all’interno della classe Telecomando (ricordate? lo abbiamo giàall’interno della classe Telecomando (ricordate? lo abbiamo già fatto nella classe Televisore a proposito di Elettrodomestico ed infatto nella classe Televisore a proposito di Elettrodomestico ed in Elettrodomestico a proposito di OggettoFisico).Elettrodomestico a proposito di OggettoFisico).

Adesso che abbiamo la variabile che avrà il riferimento alAdesso che abbiamo la variabile che avrà il riferimento al televisore, possiamo riprendere la scrittura del codice ditelevisore, possiamo riprendere la scrittura del codice di accendiIlTelevisore.accendiIlTelevisore.

SostituiamoSostituiamo

[tv1 accendi];[tv1 accendi];concon

[myTv accendi];[myTv accendi];Quindi ogni volta che invochiamo il metodo accendiIlTelevisoreQuindi ogni volta che invochiamo il metodo accendiIlTelevisore del telecomando, questo non farà altro che scrivere una stringa eddel telecomando, questo non farà altro che scrivere una stringa ed invocare il metodo accendi del televisore. invocare il metodo accendi del televisore. Con questa regola viCon questa regola vi lascio come esercizio l’implementazione degli altri metodi dellascio come esercizio l’implementazione degli altri metodi del telecomando.telecomando.

Resta un’ultima cosa da fare prima di usare il telecomando,Resta un’ultima cosa da fare prima di usare il telecomando, bisogna modellare il fatto che nella realtà, telecomando ebisogna modellare il fatto che nella realtà, telecomando e televisore si vedono per poter dialogare, cioè il telecomandotelevisore si vedono per poter dialogare, cioè il telecomando

Page 93: Xcode4 Tutorial Completo

aggancia il televisore; noi per ora ci siamo solo creatila variabileaggancia il televisore; noi per ora ci siamo solo creatila variabile di istanza per gestire questo fatto ma non abbiamo ancora costruitodi istanza per gestire questo fatto ma non abbiamo ancora costruito nessun legame.nessun legame.

Per far questo poichè durante l’esecuzione dobbiamo modificarePer far questo poichè durante l’esecuzione dobbiamo modificare una variabile di istanza (myTv) per agganciarla al nostrouna variabile di istanza (myTv) per agganciarla al nostro televisore (tv1) abbiamo bisogno di un metodo che ricevatelevisore (tv1) abbiamo bisogno di un metodo che riceva un’istanza di Televisore e la assegni a myTv, costruendo così ilun’istanza di Televisore e la assegni a myTv, costruendo così il collegamento tra televisore e telecomando.collegamento tra televisore e telecomando.

Quindi in Telecomando.h aggiungiamo la dichiarazione di unQuindi in Telecomando.h aggiungiamo la dichiarazione di un metodometodo

-(void)agganciaIlTelevisore:(Televisore *)ilTelevisore;-(void)agganciaIlTelevisore:(Televisore *)ilTelevisore;in Telecomando.m la sua implementazione saràin Telecomando.m la sua implementazione sarà

-(void)agganciaIlTelevisore:(Televisore *)ilTelevisore {-(void)agganciaIlTelevisore:(Televisore *)ilTelevisore {NSLog(@"Il telecomando sta agganciando il televisore");NSLog(@"Il telecomando sta agganciando il televisore");myTv=ilTelevisore;myTv=ilTelevisore;}}cioè, a parte la solita scritta a console, alla variabile di istanzacioè, a parte la solita scritta a console, alla variabile di istanza viene assegnato il riferimento all’oggetto passato al momentoviene assegnato il riferimento all’oggetto passato al momento dell’invocazione.dell’invocazione.

A questo punto torniamo al main.m, aggiungiamo in testaA questo punto torniamo al main.m, aggiungiamo in testa

#import "Telecomando.h"#import "Telecomando.h"Poi, dopo la dichiarazione della variabile tv1 aggiungiamoPoi, dopo la dichiarazione della variabile tv1 aggiungiamo

Telecomando *tc1;Telecomando *tc1;cioè, dichiariamo una variabile di nome tc1 che conterrà uncioè, dichiariamo una variabile di nome tc1 che conterrà un riferimento ad un’istanza della classe Telecomando.riferimento ad un’istanza della classe Telecomando.

Dopo la creazione dell’oggetto associato a tv1Dopo la creazione dell’oggetto associato a tv1

tv1=[[Televisore alloc] init];tv1=[[Televisore alloc] init];creiamo anche un oggetto telecomando scrivendocreiamo anche un oggetto telecomando scrivendo

tc1=[[Telecomando alloc] init];tc1=[[Telecomando alloc] init];Vi ricordo brevemente il significato di quello che abbiamo scritto:Vi ricordo brevemente il significato di quello che abbiamo scritto:

Page 94: Xcode4 Tutorial Completo

[Telecomando alloc][Telecomando alloc] crea un’istanza della classe Telecomando; crea un’istanza della classe Telecomando; [[Telecomando alloc] init][[Telecomando alloc] init] sull’istanza appena creata viene sull’istanza appena creata viene invocato il metodo init che restituisce l’istanza stessa; invocato il metodo init che restituisce l’istanza stessa; tc1=…tc1=… l’istanza restituita da init è assegnata alla variabile tc1.l’istanza restituita da init è assegnata alla variabile tc1.

Abbiamo adesso i nostri due oggetti, il televisore ed ilAbbiamo adesso i nostri due oggetti, il televisore ed il telecomando; la prima cosa da fare è agganciarli l’uno con l’altrotelecomando; la prima cosa da fare è agganciarli l’uno con l’altro (dobbiamo far sì che si vedano) quindi, dopo l’istruzione tc1=…(dobbiamo far sì che si vedano) quindi, dopo l’istruzione tc1=… aggiungiamoaggiungiamo

[tc1 agganciaIlTelevisore:tv1];[tc1 agganciaIlTelevisore:tv1];è l’invocazione del metodo della classe Telecomando con il qualeè l’invocazione del metodo della classe Telecomando con il quale assegnamo alla variabile di istanza myTv il riferimento alassegnamo alla variabile di istanza myTv il riferimento al televisore tv1.televisore tv1.

A questo punto possiamo iniziare ad usare il nostro telecomando,A questo punto possiamo iniziare ad usare il nostro telecomando, quindi cancelliamo tutte le istruzioni che erano invocazioni diquindi cancelliamo tutte le istruzioni che erano invocazioni di metodi sul televisore e sostituiamole conmetodi sul televisore e sostituiamole con

[tc1 accendiIlTelevisore];[tc1 accendiIlTelevisore];[tc1 mostraIlCanaleDelTelevisore];[tc1 mostraIlCanaleDelTelevisore];[tc1 alzaIlVolumeDelTelevisore];[tc1 alzaIlVolumeDelTelevisore];[tc1 alzaIlVolumeDelTelevisore];[tc1 alzaIlVolumeDelTelevisore];[tc1 alzaIlVolumeDelTelevisore];[tc1 alzaIlVolumeDelTelevisore];[tc1 abbassaIlVolumeDelTelevisore];[tc1 abbassaIlVolumeDelTelevisore];[tc1 abbassaIlVolumeDelTelevisore];[tc1 abbassaIlVolumeDelTelevisore];[tc1 abbassaIlVolumeDelTelevisore];[tc1 abbassaIlVolumeDelTelevisore];[tc1 vaiAlCanale:[[NSDecimalNumber alloc][tc1 vaiAlCanale:[[NSDecimalNumber alloc] initWithString:@"3"]];initWithString:@"3"]];[tc1 mostraIlCanaleDelTelevisore];[tc1 mostraIlCanaleDelTelevisore];[tc1 spegniIlTelevisore];[tc1 spegniIlTelevisore];o con la sequenza di comandi che preferite.o con la sequenza di comandi che preferite.

Bene, ci fermiamo qui e direi che le basi della programmazioneBene, ci fermiamo qui e direi che le basi della programmazione OO le abbiamo viste; il prossimo capitolo lo dedichiamo ad unaOO le abbiamo viste; il prossimo capitolo lo dedichiamo ad una overview su un aspetto importante che sono le properties e poi cioverview su un aspetto importante che sono le properties e poi ci sposteremo su Cocoa x iOS, cioè sempre sfruttando televisori esposteremo su Cocoa x iOS, cioè sempre sfruttando televisori e

Page 95: Xcode4 Tutorial Completo

telecomandi andremo ad analizzare i principali framework per lotelecomandi andremo ad analizzare i principali framework per lo sviluppo su iPhone e avremo anche modo di rivedere edsviluppo su iPhone e avremo anche modo di rivedere ed approfondire tutti i concetti visti finora.approfondire tutti i concetti visti finora.

13 – Proprietà13 – Proprietà

iOS, , Mac, , OSX, , SDK, , Tech Room

La spiegazione di cosa è una proprietà (property) partirà dal loroLa spiegazione di cosa è una proprietà (property) partirà dal loro uso più comune (a mio avviso anche il più fuorviante) e poi peruso più comune (a mio avviso anche il più fuorviante) e poi per passi successivi arriveremo alla forma generale. Riprendiamo ilpassi successivi arriveremo alla forma generale. Riprendiamo il nostro programma e prendiamo in considerazione la variabile dinostro programma e prendiamo in considerazione la variabile di istanza istanza canaleSintonizzatocanaleSintonizzato. Scriviamo due metodi, uno che. Scriviamo due metodi, uno che restituisce il valore della variabile e uno che lo scrive (in realtàrestituisce il valore della variabile e uno che lo scrive (in realtà quest’ultimo lo abbiamo già e si chiama vaiAlCanale ma per scopiquest’ultimo lo abbiamo già e si chiama vaiAlCanale ma per scopi didattici ne scriviamo un altro). Andiamo nel file Televisore.h edidattici ne scriviamo un altro). Andiamo nel file Televisore.h e dichiariamo i due metodidichiariamo i due metodi

Page 96: Xcode4 Tutorial Completo

-(NSDecimalNumber *)canaleSintonizzato; questa è la-(NSDecimalNumber *)canaleSintonizzato; questa è la dichiarazione di un metodo di istanza (è preceduto dal segnodichiarazione di un metodo di istanza (è preceduto dal segno ‘-‘)‘-‘) che ha lo stesso nome della variabile di istanza che ha lo stesso nome della variabile di istanza (canaleSintonizzato) e che restituisce un valore dello stesso(canaleSintonizzato) e che restituisce un valore dello stesso tipo della variabile di istanza (NSDecimalNumber *).tipo della variabile di istanza (NSDecimalNumber *).

-(void)setCanaleSintonizzato:(NSDecimalNumber-(void)setCanaleSintonizzato:(NSDecimalNumber *)canaleDaSintonizzare; questa è la dichiarazione di un*)canaleDaSintonizzare; questa è la dichiarazione di un metodo di istanza (è preceduto dal segno ‘-‘)metodo di istanza (è preceduto dal segno ‘-‘) che ha lo stesso che ha lo stesso nome della variabile di istanza (canaleSintonizzato) con lanome della variabile di istanza (canaleSintonizzato) con la prima lettera in maiuscolo e preceduto da ‘set’; il metodoprima lettera in maiuscolo e preceduto da ‘set’; il metodo riceve in ingresso al momento dell’invocazione un valorericeve in ingresso al momento dell’invocazione un valore dello stesso tipo della variabile di istanza (NSDecimaNumberdello stesso tipo della variabile di istanza (NSDecimaNumber *) che sarà conosciuto, all’interno del metodo con il nome*) che sarà conosciuto, all’interno del metodo con il nome canaleDaSintonizzare e che sarà il nuovo valore checanaleDaSintonizzare e che sarà il nuovo valore che vogliamo assegnare alla variabile di istanza (come ho giàvogliamo assegnare alla variabile di istanza (come ho già detto avrà la stessa logica di vaiAlCanale).detto avrà la stessa logica di vaiAlCanale).

Ora andiamo ad implementare questi metodi in Televisore.m.Ora andiamo ad implementare questi metodi in Televisore.m. Iniziamo con l’aggiungere il metodo di letturaIniziamo con l’aggiungere il metodo di lettura

-(NSDecimalNumber *)canaleSintonizzato {-(NSDecimalNumber *)canaleSintonizzato {NSLog (@”Esecuzione del metodo di lettura”);NSLog (@”Esecuzione del metodo di lettura”);return canaleSintonizzato;return canaleSintonizzato;}}Vi ricordo che l’implementazione di un metodo è costituitaVi ricordo che l’implementazione di un metodo è costituita dall’intestazione del metodo stesso (dall’intestazione del metodo stesso (-(NSDecimalNumber-(NSDecimalNumber *)canale Sintonizzato*)canale Sintonizzato) seguita dal blocco di istruzioni ({..}) che) seguita dal blocco di istruzioni ({..}) che devono essere eseguite nel metodo. L’istruzione devono essere eseguite nel metodo. L’istruzione return…return… l’abbiamo già incontrata e serve per uscire da un metodol’abbiamo già incontrata e serve per uscire da un metodo restituendo un valore a chi lo ha invocato, quindi questo metodorestituendo un valore a chi lo ha invocato, quindi questo metodo non fa altro che restituire l’oggetto referenziato dalla variabile dinon fa altro che restituire l’oggetto referenziato dalla variabile di istanza canaleSintonizzato, così come richiesto. Passiamo alistanza canaleSintonizzato, così come richiesto. Passiamo al metodo di scrittura ed aggiungiamo in Televisore.mmetodo di scrittura ed aggiungiamo in Televisore.m

-(void)setCanaleSintonizzato:(NSDecimalNumber-(void)setCanaleSintonizzato:(NSDecimalNumber *)canaleDaSintonizzare {*)canaleDaSintonizzare {

Page 97: Xcode4 Tutorial Completo

NSLog (@”Esecuzione del metodo di scrittura”);NSLog (@”Esecuzione del metodo di scrittura”);canaleSintonizzato= canaleDaSintonizzare;canaleSintonizzato= canaleDaSintonizzare;}}E’ un metodo di istanza (c’è il ‘-‘) che non restituisce alcun valoreE’ un metodo di istanza (c’è il ‘-‘) che non restituisce alcun valore (il tipo restituito è void) che riceve in ingresso un’istanza di(il tipo restituito è void) che riceve in ingresso un’istanza di NSDecimalNumber. L’unica cosa che fa è assegnare alla variabileNSDecimalNumber. L’unica cosa che fa è assegnare alla variabile di istanza canaleSintonizzato l’oggetto ricevuto al momentodi istanza canaleSintonizzato l’oggetto ricevuto al momento dell’invocazione canaleDaSintonizzare. Bene ora proviamo addell’invocazione canaleDaSintonizzare. Bene ora proviamo ad usare questi metodi. Andiamo nel file Telecomando.m ed andiamousare questi metodi. Andiamo nel file Telecomando.m ed andiamo a modificare il metodoa modificare il metodo

-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare{-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare{NSLog(@"Il telecomando sta cambiando il canale");NSLog(@"Il telecomando sta cambiando il canale");NSLog(@"Sei passato dal canale %@",[myTvNSLog(@"Sei passato dal canale %@",[myTv canaleSintonizzato]);canaleSintonizzato]);[myTv setCanaleSintonizzato:canaleDaSintonizzare];[myTv setCanaleSintonizzato:canaleDaSintonizzare];NSLog(@"al canale %@",[myTv canaleSintonizzato]);NSLog(@"al canale %@",[myTv canaleSintonizzato]);}}Cioè, dopo la prima istruzione che scrive un messaggio genericoCioè, dopo la prima istruzione che scrive un messaggio generico per individuare il metodo, abbiamo l’istruzioneper individuare il metodo, abbiamo l’istruzione

NSLog(@"Sei passato dal canale %@",[myTvNSLog(@"Sei passato dal canale %@",[myTv canaleSintonizzato]);canaleSintonizzato]);la format string ha un segnaposto (%@) che durante l’esecuzionela format string ha un segnaposto (%@) che durante l’esecuzione sarà sostituito dal risultato dell’invocazione di [myTvsarà sostituito dal risultato dell’invocazione di [myTv canaleSintonizzato]. Questo è proprio il metodo di lettura checanaleSintonizzato]. Questo è proprio il metodo di lettura che abbiamo appena definito in Televisore.m e che restituisce il valoreabbiamo appena definito in Televisore.m e che restituisce il valore della variabile canaleSintonizzato. Quindi questa istruzionedella variabile canaleSintonizzato. Quindi questa istruzione scriverà il messaggioscriverà il messaggio

Sei passato dal canale …Sei passato dal canale …

con il numero del canale al posto dei ‘…’ Poi abbiamo l’istruzionecon il numero del canale al posto dei ‘…’ Poi abbiamo l’istruzione

[myTv setCanaleSintonizzato:canaleDaSintonizzare];[myTv setCanaleSintonizzato:canaleDaSintonizzare];che è l’invocazione del metodo di scrittura che abbiamo appenache è l’invocazione del metodo di scrittura che abbiamo appena definito nella classe Televisore al quale passiamo l’oggetto chedefinito nella classe Televisore al quale passiamo l’oggetto che

Page 98: Xcode4 Tutorial Completo

rappresenta il numero del nuovo canale da sintonizzare. Infine, perrappresenta il numero del nuovo canale da sintonizzare. Infine, per verificare che è cambiato il canaleverificare che è cambiato il canale

NSLog(@"al canale %@",[myTv canaleSintonizzato]);NSLog(@"al canale %@",[myTv canaleSintonizzato]);anche qui la format string ha un segnaposto (%@) che duranteanche qui la format string ha un segnaposto (%@) che durante l’esecuzione sarà sostituito dal risultato dell’invocazione di [myTvl’esecuzione sarà sostituito dal risultato dell’invocazione di [myTv canaleSintonizzato] cioè dal risultato del metodo di lettura e checanaleSintonizzato] cioè dal risultato del metodo di lettura e che restituisce il valore della variabile canaleSintonizzato. Quindirestituisce il valore della variabile canaleSintonizzato. Quindi questa istruzione scriverà il messaggioquesta istruzione scriverà il messaggio

al canale …al canale …

con il numero del canale al posto di ‘…’ .con il numero del canale al posto di ‘…’ .

Proviamo ad eseguire il programma ed osserviamo i messaggi. Proviamo ad eseguire il programma ed osserviamo i messaggi. In In particolare troverete la sequenzaparticolare troverete la sequenza

2011-06-10 11:44:16.649 PrimoProgetto[98830:903] Il2011-06-10 11:44:16.649 PrimoProgetto[98830:903] Il telecomando sta cambiando il canaletelecomando sta cambiando il canale del metodo vaiAlCanale di del metodo vaiAlCanale di TelecomandoTelecomando

2011-06-10 11:44:16.650 PrimoProgetto[98830:903] Esecuzione2011-06-10 11:44:16.650 PrimoProgetto[98830:903] Esecuzione del metodo di letturadel metodo di lettura del metodo getter della property del metodo getter della property canaleSintonizzato invocato dall’istruzione canaleSintonizzato invocato dall’istruzione NSLog(@”Sei passatoNSLog(@”Sei passato dal canale %@”,[myTv canaleSintonizzato]);dal canale %@”,[myTv canaleSintonizzato]);

2011-06-10 11:44:16.651 PrimoProgetto[98830:903] Sei passato2011-06-10 11:44:16.651 PrimoProgetto[98830:903] Sei passato dal canale 0dal canale 0 del metodo vaiAlCanale di Telecomando del metodo vaiAlCanale di Telecomando

2011-06-10 11:44:16.664 PrimoProgetto[98830:903] Esecuzione2011-06-10 11:44:16.664 PrimoProgetto[98830:903] Esecuzione del metodo di scritturadel metodo di scrittura del metodo setter della property del metodo setter della property canaleSintonizzato invocato dall’istruzione canaleSintonizzato invocato dall’istruzione [myTv[myTv setCanaleSintonizzato:canaleDaSintonizzare];setCanaleSintonizzato:canaleDaSintonizzare];

2011-06-10 11:44:16.665 PrimoProgetto[98830:903] Esecuzione2011-06-10 11:44:16.665 PrimoProgetto[98830:903] Esecuzione del metodo di letturadel metodo di lettura del metodo getter della property del metodo getter della property canaleSintonizzato invocato dall’istruzione canaleSintonizzato invocato dall’istruzione NSLog(@”al canaleNSLog(@”al canale %@”,[myTv canaleSintonizzato]);%@”,[myTv canaleSintonizzato]);

2011-06-10 11:44:16.667 PrimoProgetto[98830:903] al canale 32011-06-10 11:44:16.667 PrimoProgetto[98830:903] al canale 3

Page 99: Xcode4 Tutorial Completo

del metodo vaiAlCanale di Telecomando funziona tutto comedel metodo vaiAlCanale di Telecomando funziona tutto come previsto…previsto…

ma non dovevamo parlare di proprietà? Finora abbiamo soloma non dovevamo parlare di proprietà? Finora abbiamo solo scritto ed usato altri due metodi, nulla di nuovo rispetto a quantoscritto ed usato altri due metodi, nulla di nuovo rispetto a quanto avevamo già visto. Questa situazione, cioè una coppia di metodiavevamo già visto. Questa situazione, cioè una coppia di metodi uno che legge un valore e l’altro che lo scrive, è proprio iluno che legge un valore e l’altro che lo scrive, è proprio il concetto base delle proprietà e la coppia di metodi, chiamataconcetto base delle proprietà e la coppia di metodi, chiamata metodi di accesso, è costituita dal metodo metodi di accesso, è costituita dal metodo gettergetter (quello che legge) (quello che legge) e dal metodo e dal metodo settersetter (quello che scrive). La definizione generale di (quello che scrive). La definizione generale di una propertyuna property è @property (attributi) tipoProprietà nomeProprietà; è @property (attributi) tipoProprietà nomeProprietà; Trascuriamo per il momento cosa sono gli attributi tra le ( ). LaTrascuriamo per il momento cosa sono gli attributi tra le ( ). La dichiarazione precedente implicitamente dichiara una coppia didichiarazione precedente implicitamente dichiara una coppia di metodi di accessometodi di accesso

gettergetter: -(tipoProprietà)nomeProprietà;: -(tipoProprietà)nomeProprietà; cioè un metodo che ha cioè un metodo che ha lo stesso nome della proprietà e restituisce un valore dellolo stesso nome della proprietà e restituisce un valore dello stesso tipo;stesso tipo;

settersetter: -(void)setNomeProprietà:(tipoProprietà)valore; cioè: -(void)setNomeProprietà:(tipoProprietà)valore; cioè un metodo che ha il nome della proprietà con la prima letteraun metodo che ha il nome della proprietà con la prima lettera in maiuscolo e preceduto da set, che prende come argomentoin maiuscolo e preceduto da set, che prende come argomento un valore del tipo della proprietà e che sarà il valore daun valore del tipo della proprietà e che sarà il valore da assegnare.assegnare.

Allora cominciamo ad usare la property. Nel file Televisore.h,Allora cominciamo ad usare la property. Nel file Televisore.h, prima della lista dei metodi, ma fuori dal blocco di dichiarazioniprima della lista dei metodi, ma fuori dal blocco di dichiarazioni delle variabili di istanza, scriviamo @property (nonatomic, retain)delle variabili di istanza, scriviamo @property (nonatomic, retain) NSDecimalNumber *canaleSintonizzato; In linea con quantoNSDecimalNumber *canaleSintonizzato; In linea con quanto abbiamo appena detto, questa implicitamente comporta laabbiamo appena detto, questa implicitamente comporta la dichiarazione dei metodi getter/setter esattamente uguali a quellidichiarazione dei metodi getter/setter esattamente uguali a quelli che abbiamo dichiarato noi esplicitamenteche abbiamo dichiarato noi esplicitamente

-(NSDecimalNumber *)canale Sintonizzato;-(NSDecimalNumber *)canale Sintonizzato;-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare;-(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare;quindi avendo dichiarato la property, possiamo cancellare le nostrequindi avendo dichiarato la property, possiamo cancellare le nostre due dichiarazioni dei metodi da Televisore.h. Proviamo addue dichiarazioni dei metodi da Televisore.h. Proviamo ad eseguire il programma e vediamo che funziona tutto come prima.eseguire il programma e vediamo che funziona tutto come prima.

Page 100: Xcode4 Tutorial Completo

Un effetto dell’adozione delle property è l’introduzione di unaUn effetto dell’adozione delle property è l’introduzione di una nuova forma sintattica. Vediamola ed analizziamola. Innuova forma sintattica. Vediamola ed analizziamola. In Telecomando.m cambiamo il metodo -(void)vaiAlCanale:Telecomando.m cambiamo il metodo -(void)vaiAlCanale:(NSDecimalNumber *)canaleDaSintonizzare nel quale abbiamo(NSDecimalNumber *)canaleDaSintonizzare nel quale abbiamo usato la property canaleSintonizzato del Televisore, l’istruzioneusato la property canaleSintonizzato del Televisore, l’istruzione

NSLog(@"Sei passato dal canale %@",[myTvNSLog(@"Sei passato dal canale %@",[myTv canaleSintonizzato]);canaleSintonizzato]);la sostituiamo conla sostituiamo con

NSLog(@"Sei passato dal canale %@",myTv.canaleSintonizzato);NSLog(@"Sei passato dal canale %@",myTv.canaleSintonizzato);cioè invece di scrivere l’invocazione di un metodo facciamocioè invece di scrivere l’invocazione di un metodo facciamo seguire alla variabile che referenzia l’oggetto il nome dellaseguire alla variabile che referenzia l’oggetto il nome della property separato da un ‘.’, questa notazione si traduceproperty separato da un ‘.’, questa notazione si traduce nell’invocazione del metodo getter. L’istruzionenell’invocazione del metodo getter. L’istruzione

[myTv setCanaleSintonizzato:canaleDaSintonizzare];[myTv setCanaleSintonizzato:canaleDaSintonizzare];la sostituiamo conla sostituiamo con

myTv.canaleSintonizzato=canaleDaSintonizzare;myTv.canaleSintonizzato=canaleDaSintonizzare;cioè alla nuova notazione cioè alla nuova notazione istanza.proprietàistanza.proprietà viene assegnato un viene assegnato un valore, questo si traduce nell’invocazione del metodo setter a cuivalore, questo si traduce nell’invocazione del metodo setter a cui viene passato il valore a destra del segno ‘=’ come parametro. Inviene passato il valore a destra del segno ‘=’ come parametro. In maniera analoga sostituiamo l’istruzionemaniera analoga sostituiamo l’istruzione

NSLog(@"al canale %@",[myTv canaleSintonizzato]);NSLog(@"al canale %@",[myTv canaleSintonizzato]);concon

NSLog(@"al canale %@",myTv.canaleSintonizzato);NSLog(@"al canale %@",myTv.canaleSintonizzato);che invoca di nuovo il metodo getter. Eseguiamo di nuovo ilche invoca di nuovo il metodo getter. Eseguiamo di nuovo il programma e verifichiamo che anche con la nuova notazione sonoprogramma e verifichiamo che anche con la nuova notazione sono eseguiti i metodi di accesso. Tuttavia finora abbiamo risparmiatoeseguiti i metodi di accesso. Tuttavia finora abbiamo risparmiato due righe di codice e ne abbiamo inserito una, non è un grandue righe di codice e ne abbiamo inserito una, non è un gran vantaggio. L’introduzione della property in realtà ci consentevantaggio. L’introduzione della property in realtà ci consente anche di non definire i metodi in Televisore.m, cancelliamo quindianche di non definire i metodi in Televisore.m, cancelliamo quindi la definizione dei metodi da Televisore.m, ma dopo la rigala definizione dei metodi da Televisore.m, ma dopo la riga

@implementation Televisore@implementation Televisore

Page 101: Xcode4 Tutorial Completo

aggiungiamoaggiungiamo

@synthesize canaleSintonizzato;@synthesize canaleSintonizzato;l’istruzione l’istruzione @synthesize proprietà@synthesize proprietà non fa altro che dire al non fa altro che dire al compilatore di predisporre lui i metodi standard getter/setter;compilatore di predisporre lui i metodi standard getter/setter; quindi non abbiamo più bisogno di scriverli noi. Ora in main.mquindi non abbiamo più bisogno di scriverli noi. Ora in main.m invochiamo più volte il metodo per cambiare il canale e eseguendoinvochiamo più volte il metodo per cambiare il canale e eseguendo il programma verifichiamo che i messaggi a console siano corretti,il programma verifichiamo che i messaggi a console siano corretti, ovviamente non ci sono più i messaggi che avevamo scritto neiovviamente non ci sono più i messaggi che avevamo scritto nei nostri getter/setter ma il funzionamento è corretto. Bene questo ènostri getter/setter ma il funzionamento è corretto. Bene questo è l’uso principale che si fa delle property:l’uso principale che si fa delle property:

si dichiara una property con lo stesso tipo e nome di unasi dichiara una property con lo stesso tipo e nome di una variabile di istanzavariabile di istanza

si inserisce l’istruzione synthesize della propertysi inserisce l’istruzione synthesize della property

si ottengono automaticamente due metodi per leggere e scrivere ilsi ottengono automaticamente due metodi per leggere e scrivere il valore della variabile di istanza accessibili attraverso la notazionevalore della variabile di istanza accessibili attraverso la notazione (dot notation) istanza.proprietà. Fissati questi punti e se non sono(dot notation) istanza.proprietà. Fissati questi punti e se non sono chiari vi invito a ripercorrere il capitolo, passiamo a vedere lachiari vi invito a ripercorrere il capitolo, passiamo a vedere la prima variante. Cosa succede se la property non ha lo stesso nomeprima variante. Cosa succede se la property non ha lo stesso nome di una variabile di istanza? Andiamo in Televisore.h edi una variabile di istanza? Andiamo in Televisore.h e modifichiamo la dichiarazione della property inmodifichiamo la dichiarazione della property in

@property (nonatomic, retain) NSNumber *canale;@property (nonatomic, retain) NSNumber *canale;È una proprietà che non ha il nome di alcuna variabile di istanzaÈ una proprietà che non ha il nome di alcuna variabile di istanza (noi abbiamo canaleSintonizzato) ed è di un tipo NSNumber che(noi abbiamo canaleSintonizzato) ed è di un tipo NSNumber che non abbiamo ancora incontrato.non abbiamo ancora incontrato.

Approfondimento: andate sulla documentazione Apple relativa aApprofondimento: andate sulla documentazione Apple relativa a NSDecimalNumber ed osservate la sua gerarchia.NSDecimalNumber ed osservate la sua gerarchia.

Poi andiamo in Televisore.m e modifichiamo l’istruzionePoi andiamo in Televisore.m e modifichiamo l’istruzione synthesize insynthesize in

@synthesize canale=canaleSintonizzato;@synthesize canale=canaleSintonizzato;cioè diciamo che la property canale cioè diciamo che la property canale usausa la variabile di istanza la variabile di istanza canaleSintonizzato. A questo punto andiamo nei metodicanaleSintonizzato. A questo punto andiamo nei metodi

Page 102: Xcode4 Tutorial Completo

mostraIlCanaleDelTelevisore e vaiAlCanale di Telecomando.mmostraIlCanaleDelTelevisore e vaiAlCanale di Telecomando.m dove abbiamo usato la precedente property e scriviamoci il nuovodove abbiamo usato la precedente property e scriviamoci il nuovo nome, cioè dove abbiamo usato nome, cioè dove abbiamo usato myTv.canaleSintonizzatomyTv.canaleSintonizzato ora ora useremo useremo myTv.canalemyTv.canale. Eseguiamo ed osserviamo i messaggi; tutto. Eseguiamo ed osserviamo i messaggi; tutto uguale, quindi possiamo anche usare proprietà con un nomeuguale, quindi possiamo anche usare proprietà con un nome diverso da quello delle variabili di istanza e poi al momento deldiverso da quello delle variabili di istanza e poi al momento del synthesize dire quale variabile si usa. Quindi una property devesynthesize dire quale variabile si usa. Quindi una property deve sempre essere riferita ad una variabile di istanza? Torniamo insempre essere riferita ad una variabile di istanza? Torniamo in Televisore.h ed aggiungiamo la dichiarazioneTelevisore.h ed aggiungiamo la dichiarazione

@property (readonly) NSString *marca;@property (readonly) NSString *marca;abbiamo dichiarato una property di nome marca e che si riferisceabbiamo dichiarato una property di nome marca e che si riferisce ad istanze di NSString. La classe NSString l’abbiamo giàad istanze di NSString. La classe NSString l’abbiamo già incontrata ed è quella che, in Cocoa gestisce le stringhe (sequenze)incontrata ed è quella che, in Cocoa gestisce le stringhe (sequenze) di caratteri, cioè parole o frasi. L’attributo readonly indica che sidi caratteri, cioè parole o frasi. L’attributo readonly indica che si tratta di una proprietà in sola lettura e questo vuol dire che cometratta di una proprietà in sola lettura e questo vuol dire che come metodo di accesso avrà solo il getter. Andiamo in Televisore.m emetodo di accesso avrà solo il getter. Andiamo in Televisore.m e questa volta non facciamo la synthesize ma definiamo noi ilquesta volta non facciamo la synthesize ma definiamo noi il metodo getter coma abbiamo fatto all’inizio del capitolo, quindimetodo getter coma abbiamo fatto all’inizio del capitolo, quindi aggiungiamoaggiungiamo

-(NSString *)marca {-(NSString *)marca {return @"Apple";return @"Apple";}}è il tipico metodo di accesso getter, ha lo stesso nome dellaè il tipico metodo di accesso getter, ha lo stesso nome della property e restituisce un oggetto dello stesso tipo. In particolare ilproperty e restituisce un oggetto dello stesso tipo. In particolare il nostro metodo restituirà sempre la stringa Apple, tutti i nostrinostro metodo restituirà sempre la stringa Apple, tutti i nostri televisori hanno marca Apple. Per vedere l’effetto modifichiamo iltelevisori hanno marca Apple. Per vedere l’effetto modifichiamo il metodo mostraInfo di Televisoremetodo mostraInfo di Televisore

-(void)mostraInfo {-(void)mostraInfo {NSLog(@"Il televisore %@ pesa %@ Kg e consuma %@NSLog(@"Il televisore %@ pesa %@ Kg e consuma %@ W",self.marca,peso,consumo);W",self.marca,peso,consumo);}}Cioè abbiamo aggiunto un nuovo segnaposto dopo la parolaCioè abbiamo aggiunto un nuovo segnaposto dopo la parola televisoretelevisore e in corrispondenza abbiamo inserito un nuovo e in corrispondenza abbiamo inserito un nuovo

Page 103: Xcode4 Tutorial Completo

parametro il cui valore sarà visualizzato al suo posto, self.marca;parametro il cui valore sarà visualizzato al suo posto, self.marca; questa forma è quella che abbiamo già incontrato e corrispondequesta forma è quella che abbiamo già incontrato e corrisponde all’invocazione del metodo getter della property marca.all’invocazione del metodo getter della property marca.Eseguiamo ed osserviamo i messaggi, potremo vedere che ilEseguiamo ed osserviamo i messaggi, potremo vedere che il nuovo messaggio di informazioni sarànuovo messaggio di informazioni sarà2011-06-10 15:12:55.915 PrimoProgetto[39487:903] Il televisore2011-06-10 15:12:55.915 PrimoProgetto[39487:903] Il televisore Apple pesa 20 Kg e consuma 500 WApple pesa 20 Kg e consuma 500 Wè comparso il valore della property.è comparso il valore della property.Quindi….abbiamo definito una property non associata ad alcunaQuindi….abbiamo definito una property non associata ad alcuna variabile di istanza. Per semplicità didattica in questo momentovariabile di istanza. Per semplicità didattica in questo momento abbiamo solo una property readonly.abbiamo solo una property readonly.Questo è il concetto finale al quale volevo arrivare. Una propertyQuesto è il concetto finale al quale volevo arrivare. Una property implica sempre una coppia di metodi getter/setter (se readonlyimplica sempre una coppia di metodi getter/setter (se readonly solo getter) che può o meno essere associata ad una variabile disolo getter) che può o meno essere associata ad una variabile di istanza anche se questo è l’uso più diffuso.istanza anche se questo è l’uso più diffuso.Come esercizio vi invito a riscrivere tutto il programma togliendoCome esercizio vi invito a riscrivere tutto il programma togliendo tutte le variabili di istanza e sostituendole tutte con property.tutte le variabili di istanza e sostituendole tutte con property. Dopo averci provato potete scaricareDopo averci provato potete scaricare qui il mio codice.qui il mio codice.

14 – Model-View-…14 – Model-View-…

iOS, , OSX, , SDK, , Tech Room

Abbiamo detto più volte ed abbiamo anche visto con il nostroAbbiamo detto più volte ed abbiamo anche visto con il nostro esempio, che un’applicazione Objective-C è costituita da unesempio, che un’applicazione Objective-C è costituita da un insieme di oggetti cooperanti (tv1, tc1) definiti tramite classiinsieme di oggetti cooperanti (tv1, tc1) definiti tramite classi (Televisore, Telecomando, Elettrodomestico,…).(Televisore, Telecomando, Elettrodomestico,…).

La realizzazione di un’applicazione complessa in iOS prevede laLa realizzazione di un’applicazione complessa in iOS prevede la cooperazione di un certo numero di oggetti e di diverse classi, percooperazione di un certo numero di oggetti e di diverse classi, per cui per rendere più semplice lo sviluppo e i successivicui per rendere più semplice lo sviluppo e i successivi aggiornamenti del programma è necessario trovare un modo peraggiornamenti del programma è necessario trovare un modo per organizzare gli oggetti. Il modo con il quale organizzare gli oggettiorganizzare gli oggetti. Il modo con il quale organizzare gli oggetti

Page 104: Xcode4 Tutorial Completo

di un’applicazione prende il nome di paradigma o pattern ed ildi un’applicazione prende il nome di paradigma o pattern ed il principale è il Model-View-Controller (MVC).principale è il Model-View-Controller (MVC).

In questo pattern gli oggetti sono raggruppati (solo logicamente)In questo pattern gli oggetti sono raggruppati (solo logicamente) inin

Model:Model: il gruppo degli oggetti che rappresentano il contesto il gruppo degli oggetti che rappresentano il contesto della nostra applicazione (televisori, telecomandi,…)della nostra applicazione (televisori, telecomandi,…)

View:View: il gruppo degli oggetti che costituiscono l’interfaccia il gruppo degli oggetti che costituiscono l’interfaccia utente (viste, bottoni,…)utente (viste, bottoni,…)

Controller:Controller: il gruppo degli oggetti che collegano l’interfaccia il gruppo degli oggetti che collegano l’interfaccia al modello implementando la logica elaborativa e gestendoal modello implementando la logica elaborativa e gestendo gli eventi.gli eventi.

Vediamo cosa significa in pratica. Creiamo un nuovo progetto,Vediamo cosa significa in pratica. Creiamo un nuovo progetto, lanciamo XCode e, nella finestra dei template selezioniamo lalanciamo XCode e, nella finestra dei template selezioniamo la voce Application del gruppo iOS; qui vediamo diversi schemi divoce Application del gruppo iOS; qui vediamo diversi schemi di applicazione già predisposti da Apple e che potrete approfondireapplicazione già predisposti da Apple e che potrete approfondire nella documentazione, noi per motivi didattici scegliamo ilnella documentazione, noi per motivi didattici scegliamo il template base Window-based Applicationtemplate base Window-based Application

Page 105: Xcode4 Tutorial Completo
Page 106: Xcode4 Tutorial Completo

proseguiamo, diamo un nome al progetto (es. myTv) eproseguiamo, diamo un nome al progetto (es. myTv) e selezioniamo iPhone come target, scegliamo la directory eselezioniamo iPhone come target, scegliamo la directory e creiamo il progetto.creiamo il progetto.

Potete vedere che anche stavolta XCode ha già preparato dei file,Potete vedere che anche stavolta XCode ha già preparato dei file, li analizzeremo man mano che ne avremo bisogno.li analizzeremo man mano che ne avremo bisogno.

ModelModel

Tutto il lavoro fatto fino al cap 13 con il nostro programmaTutto il lavoro fatto fino al cap 13 con il nostro programma precedente possiamo dire che coincide con la definizione delprecedente possiamo dire che coincide con la definizione del modello (il livello Model), cioè abbiamo creato le classi chemodello (il livello Model), cioè abbiamo creato le classi che modellano il nostro contesto fatto di televisori e telecomandi; siamodellano il nostro contesto fatto di televisori e telecomandi; sia in termini di proprietà che di comportamenti. Non abbiamo quindiin termini di proprietà che di comportamenti. Non abbiamo quindi molto da dire se non che useremo le classi che abbiamo disegnatomolto da dire se non che useremo le classi che abbiamo disegnato nel progetto già fatto così come le abbiamo lasciate alla fine delnel progetto già fatto così come le abbiamo lasciate alla fine del capitolo precedente, cioè con l’utilizzo delle proprietà e senzacapitolo precedente, cioè con l’utilizzo delle proprietà e senza variabili di istanza esplicite.variabili di istanza esplicite.

Quello di cui abbiamo quindi bisogno è di portarci in questoQuello di cui abbiamo quindi bisogno è di portarci in questo

Page 107: Xcode4 Tutorial Completo

nuovo progetto le classi che abbiamo già definito.nuovo progetto le classi che abbiamo già definito.

Con il clic destro del mouse sulla cartella myTv nell’albero diCon il clic destro del mouse sulla cartella myTv nell’albero di XCode selezioniamo XCode selezioniamo add Files to “myTv” add Files to “myTv” , nella finestra che ci, nella finestra che ci viene presentata navighiamo il disco fino a trovare la cartella delviene presentata navighiamo il disco fino a trovare la cartella del nostro vecchio progetto (primoProgetto) e selezioniamo i filenostro vecchio progetto (primoProgetto) e selezioniamo i file relativi alle classi OggettoFisico, Elettrodomestico, Televisore, siarelativi alle classi OggettoFisico, Elettrodomestico, Televisore, sia i .h che i .m, assicuriamoci che sia attivo il flag i .h che i .m, assicuriamoci che sia attivo il flag Destination.Destination.

Dal menu di XCode selezioniamo Product>Build in modo daDal menu di XCode selezioniamo Product>Build in modo da compilare l’applicazione per assicurarci che abbiamo importatocompilare l’applicazione per assicurarci che abbiamo importato correttamente i file.correttamente i file.

Bene, il Model è pronto.Bene, il Model è pronto.

ViewView

Nella componente View dobbiamo innanzitutto disegnare la nostraNella componente View dobbiamo innanzitutto disegnare la nostra interfaccia per capire di quali elementi grafici abbiamo bisogno.interfaccia per capire di quali elementi grafici abbiamo bisogno.

Decidiamo che il nostro programma visualizza un televisore comeDecidiamo che il nostro programma visualizza un televisore come quello rappresentato in figura.quello rappresentato in figura.

Page 108: Xcode4 Tutorial Completo

Quindi di quali elementi grafici abbiamo bisogno?Quindi di quali elementi grafici abbiamo bisogno?

A.A. Un contenitore generale dei controlli dell’interfaccia, occupa Un contenitore generale dei controlli dell’interfaccia, occupa tutto lo spazio dello schermo dell’iPhone; tutto lo spazio dello schermo dell’iPhone; B.B. Un riquadro grigio Un riquadro grigio che simulerà lo schermo del televisore; lo schermo diventerà neroche simulerà lo schermo del televisore; lo schermo diventerà nero quando il televisore è spento; quando il televisore è spento; C.C. Un testo dentro lo schermo che Un testo dentro lo schermo che indicherà il canale sintonizzato; indicherà il canale sintonizzato; D.D. Un controllo a scorrimento per Un controllo a scorrimento per la regolazione del volume; la regolazione del volume; E.E. Un testo che indicherà il livello del Un testo che indicherà il livello del volume volume F.F. Un selettore a 5 tasti per selezionare i canali del Un selettore a 5 tasti per selezionare i canali del televisore televisore G.G. Un bottone di tipo interruttore per accendere e Un bottone di tipo interruttore per accendere e spegnere il televisorespegnere il televisore

Passiamo ora ad implementare questa struttura in XCode.Passiamo ora ad implementare questa struttura in XCode.

Per creare una Per creare una schermataschermata di interfaccia grafica, come quella che di interfaccia grafica, come quella che abbiamo disegnato, dobbiamo creare un altro file; con il clicabbiamo disegnato, dobbiamo creare un altro file; con il clic destro sulla cartella myTv destro sulla cartella myTv scegliamo New File e nel gruppo iOSscegliamo New File e nel gruppo iOS selezioniamo User Interface e scegliamo il template View (è quellaselezioniamo User Interface e scegliamo il template View (è quella

Page 109: Xcode4 Tutorial Completo

che possiamo approssimare ad una schermata) e chiamiamolache possiamo approssimare ad una schermata) e chiamiamola TelevisoreView. XCode crea un file dedicato alla creazione graficaTelevisoreView. XCode crea un file dedicato alla creazione grafica dell’interfaccia chiamato TelevisoreView.xib.dell’interfaccia chiamato TelevisoreView.xib.

Finita la creazione XCode vi presenta una vista vuota (bianca) conFinita la creazione XCode vi presenta una vista vuota (bianca) con le proporzioni dell’iPhone, questa prima View è l’elemento A delle proporzioni dell’iPhone, questa prima View è l’elemento A del nostro disegno. Nella toolbar della finestra di XCode selezioniamonostro disegno. Nella toolbar della finestra di XCode selezioniamo l’icona l’icona frame destroframe destro nel gruppo nel gruppo View; questo frame contiene 2 View; questo frame contiene 2 parti: quella superiore è l’inspector con il quale si configurano gliparti: quella superiore è l’inspector con il quale si configurano gli attributi dell’elemento di interfaccia selezionato (come vedremo inattributi dell’elemento di interfaccia selezionato (come vedremo in seguito); quella inferiore è la libreria degli oggetti utilizzabili.seguito); quella inferiore è la libreria degli oggetti utilizzabili.

Page 110: Xcode4 Tutorial Completo
Page 111: Xcode4 Tutorial Completo

Andiamo ora a popolare la nostra interfaccia.Andiamo ora a popolare la nostra interfaccia.

L’elemento B, è un riquadro che simula lo schermo e che cambieràL’elemento B, è un riquadro che simula lo schermo e che cambierà colore da nero a grigio a seconda che il televisore sia spento ocolore da nero a grigio a seconda che il televisore sia spento o acceso.acceso.

Per creare questo elemento di interfaccia usiamo una View, quindiPer creare questo elemento di interfaccia usiamo una View, quindi scorriamo la Library (parte inferiore del frame di destra) escorriamo la Library (parte inferiore del frame di destra) e cerchiamo una View; la trasciniamo su quella esistente e lasciamocerchiamo una View; la trasciniamo su quella esistente e lasciamo il mouse. Con la nuova view selezionata andiamo nel pannelloil mouse. Con la nuova view selezionata andiamo nel pannello Attributes dell’Inspector (parte superiore del frame di destra) eAttributes dell’Inspector (parte superiore del frame di destra) e alla voce background cambiamo il colore da bianco a grigioalla voce background cambiamo il colore da bianco a grigio chiaro. Ora però dobbiamo dire che lo schermo non deve occuparechiaro. Ora però dobbiamo dire che lo schermo non deve occupare tutta la view sottostante, quindi ridimensioniamola o con itutta la view sottostante, quindi ridimensioniamola o con i connettori che trovate intorno alla view stessa quando èconnettori che trovate intorno alla view stessa quando è selezionata, oppure andando nel pannello Size dell’Inspector.selezionata, oppure andando nel pannello Size dell’Inspector.

Passiamo all’elemento C, il testo che mostrerà il nome del canalePassiamo all’elemento C, il testo che mostrerà il nome del canale dentro lo schermo.dentro lo schermo.

Page 112: Xcode4 Tutorial Completo

Per modellare questo elemento useremo una Label. PrendiamoPer modellare questo elemento useremo una Label. Prendiamo quindi una Label dalla Library e trasciniamola dentro lo schermo.quindi una Label dalla Library e trasciniamola dentro lo schermo.

Passiamo all’elemento D, il regolatore di volume.Passiamo all’elemento D, il regolatore di volume.

Usiamo un controllo di tipo Slider, quindi prendiamolo dallaUsiamo un controllo di tipo Slider, quindi prendiamolo dalla Library e posizioniamolo sotto lo schermo e diamogli unaLibrary e posizioniamolo sotto lo schermo e diamogli una dimensione adeguata rispetto a come abbiamo disegnato la nostradimensione adeguata rispetto a come abbiamo disegnato la nostra interfaccia. Se ricordate, nel nostro modello il volume ha un valoreinterfaccia. Se ricordate, nel nostro modello il volume ha un valore massimo di 100 ed un valore di default di 50. Quindi con lo slidermassimo di 100 ed un valore di default di 50. Quindi con lo slider che abbiamo inserito selezionato, andiamo nel pannello Attributesche abbiamo inserito selezionato, andiamo nel pannello Attributes e cambiamo i valori Maximum e Current mettendoci 100 e 50.e cambiamo i valori Maximum e Current mettendoci 100 e 50.

Passiamo all’elemento E, il testo con il valore corrente delPassiamo all’elemento E, il testo con il valore corrente del volume.volume.

Abbiamo bisogno di un’altra Label, quindi la prendiamo dallaAbbiamo bisogno di un’altra Label, quindi la prendiamo dalla Library e la posizioniamo a fianco allo slider.Library e la posizioniamo a fianco allo slider.

Passiamo all’elemento F, il selettore dei canali.Passiamo all’elemento F, il selettore dei canali.

Per questo tipo di controlli si usa un Segmented Control;Per questo tipo di controlli si usa un Segmented Control; prendiamolo dalla Library e posizioniamolo nella nostraprendiamolo dalla Library e posizioniamolo nella nostra interfaccia. Con questo controllo selezionato, andiamo nelinterfaccia. Con questo controllo selezionato, andiamo nel pannello Attributes e cambiamo il valore di Segments da 2 a 5.pannello Attributes e cambiamo il valore di Segments da 2 a 5. Adesso facciamo doppio clic su ognuno dei pulsanti del controlloAdesso facciamo doppio clic su ognuno dei pulsanti del controllo e scriviamoci i numeri da 1 a 5; se durante questa operazione ile scriviamoci i numeri da 1 a 5; se durante questa operazione il controllo dovesse ridimensionarsi risistemiamo tutto o con icontrollo dovesse ridimensionarsi risistemiamo tutto o con i connettori sui bordi o con i valori del pannello Size dell’Inspector.connettori sui bordi o con i valori del pannello Size dell’Inspector.

Infine aggiungiamo l’elemento G, l’interruttore.Infine aggiungiamo l’elemento G, l’interruttore.

Questo tipo di controllo è ben modellato da uno Switch, quindiQuesto tipo di controllo è ben modellato da uno Switch, quindi prendiamolo dalla Library e trasciniamolo nella posizioneprendiamolo dalla Library e trasciniamolo nella posizione desiderata. Nel pannello Attributes cambiamo State in Offdesiderata. Nel pannello Attributes cambiamo State in Off (l’interruttore è spento).(l’interruttore è spento).

Alla fine dovremmo avere qualcosa del genereAlla fine dovremmo avere qualcosa del genere

Page 113: Xcode4 Tutorial Completo
Page 114: Xcode4 Tutorial Completo

Bene analizziamo un po’ meglio cosa abbiamo fatto.Bene analizziamo un po’ meglio cosa abbiamo fatto.

Abbiamo preso degli elementi grafici di interfaccia e li abbiamoAbbiamo preso degli elementi grafici di interfaccia e li abbiamo sistemati come desideravamo. Questi elementi non solo solo dellesistemati come desideravamo. Questi elementi non solo solo delle immagini ma sono dei veri e propri oggetti di Objective-C. Seimmagini ma sono dei veri e propri oggetti di Objective-C. Se sono oggetti vuol dire che hanno una classe che li caratterizza.sono oggetti vuol dire che hanno una classe che li caratterizza. Selezioniamo la View che abbiamo usato per disegnare lo schermoSelezioniamo la View che abbiamo usato per disegnare lo schermo e poi andiamo sul tab Quick Help dell’Inspector, qui ci viene dettoe poi andiamo sul tab Quick Help dell’Inspector, qui ci viene detto a quale classe appartiene l’oggetto (UIView) con una brevea quale classe appartiene l’oggetto (UIView) con una breve descrizione della classe. Inoltre alla voce Reference abbiamo undescrizione della classe. Inoltre alla voce Reference abbiamo un link alla documentazione dove è compiutamente descritta la classelink alla documentazione dove è compiutamente descritta la classe e dove sono elencati e descritti tutti i metodi e proprietà dellee dove sono elencati e descritti tutti i metodi e proprietà delle istanze della classe.istanze della classe.

Approfondimento: selezionare gli elementi di interfaccia uno allaApprofondimento: selezionare gli elementi di interfaccia uno alla volta e studiare la documentazione della classe, in particolarevolta e studiare la documentazione della classe, in particolare osservate a quale superclasse appartiene ognuna di esse.osservate a quale superclasse appartiene ognuna di esse.

Un’altra considerazione da fare, parte dall’osservare il frameUn’altra considerazione da fare, parte dall’osservare il frame Objects che trovate a sinistra della nostra interfaccia. PoteteObjects che trovate a sinistra della nostra interfaccia. Potete

Page 115: Xcode4 Tutorial Completo

vedere tutti gli elementi di interfaccia con una struttura gerarchicavedere tutti gli elementi di interfaccia con una struttura gerarchica che rappresenta la relazione di contenimento. C’è una View (ilche rappresenta la relazione di contenimento. C’è una View (il nostro elemento A) che contiene tutti gli altri elementi, tra cui lanostro elemento A) che contiene tutti gli altri elementi, tra cui la View dello schermo che a sua volta contiene la Label del nome delView dello schermo che a sua volta contiene la Label del nome del canale. Questa gerarchia di oggetti, superview/subview, è uncanale. Questa gerarchia di oggetti, superview/subview, è un meccanismo importante da conoscere per poter gestire al meglio lemeccanismo importante da conoscere per poter gestire al meglio le interfacce via codice. Notiamo che la gerarchia tra gli oggettiinterfacce via codice. Notiamo che la gerarchia tra gli oggetti (contenimento) non ha alcuna relazione con la gerarchia tra le(contenimento) non ha alcuna relazione con la gerarchia tra le classi (specializzazione).classi (specializzazione).

Nel prossimo capitolo vedremo come creare il controller perNel prossimo capitolo vedremo come creare il controller per correlare il modello correlare il modello con l’interfaccia.con l’interfaccia.

15 – …- Controller15 – …- Controller

iOS, , OSX, , SDK, , Tech Room

Riprendiamo il nostro progetto myTv.Riprendiamo il nostro progetto myTv.

Abbiamo da un lato Model, definito dalle nostre classi Televisore,Abbiamo da un lato Model, definito dalle nostre classi Televisore, Telecomando,… dall’altro lato abbiamo View, costituito dallaTelecomando,… dall’altro lato abbiamo View, costituito dalla nostra Interfaccia Utente e da tutti i suoi oggetti (bottoni, viste,nostra Interfaccia Utente e da tutti i suoi oggetti (bottoni, viste, label,…). Adesso dobbiamo collegarli insieme; per fare questolabel,…). Adesso dobbiamo collegarli insieme; per fare questo abbiamo bisogno di un controller.abbiamo bisogno di un controller.

Cosa è un controller? Siamo in un ambiente Object-OrientedCosa è un controller? Siamo in un ambiente Object-Oriented quindi al risposta è facile: è un oggetto. La sua caratteristica è diquindi al risposta è facile: è un oggetto. La sua caratteristica è di poter vedere sia gli oggetti del modello, quindi di leggere epoter vedere sia gli oggetti del modello, quindi di leggere e scrivere le loro proprietà,scrivere le loro proprietà, che quelli dell’interfaccia Utente. Con che quelli dell’interfaccia Utente. Con questi ultimi il controller stabilisce due modalità di relazionequesti ultimi il controller stabilisce due modalità di relazione

dalla UI verso il controller (Action): in risposta ad eventi chedalla UI verso il controller (Action): in risposta ad eventi che intervengono sull’interfaccia (bottoni premuti, touch sulleintervengono sull’interfaccia (bottoni premuti, touch sulle viste,…) si invocano particolari metodi del controllerviste,…) si invocano particolari metodi del controller

dal controller verso la UI (Outlet):dal controller verso la UI (Outlet): dal controller posso leggere e dal controller posso leggere e scrivere proprietà degli oggetti della UI (il colore di unascrivere proprietà degli oggetti della UI (il colore di una

Page 116: Xcode4 Tutorial Completo

vista, lo stato di un bottone,…) o eseguire i metodi chevista, lo stato di un bottone,…) o eseguire i metodi che espongonoespongono

Con gli oggetti del modello invece avrà una sola relazione, cioèCon gli oggetti del modello invece avrà una sola relazione, cioè sarà in grado di invocare i loro metodi o di leggere/scrivere lesarà in grado di invocare i loro metodi o di leggere/scrivere le proprietàproprietà

con il termine send/target si indica il patterncon il termine send/target si indica il pattern di programmazionedi programmazione per il quale un oggetto invia un messaggio ad un altro oggetto perper il quale un oggetto invia un messaggio ad un altro oggetto per richiedere l’esecuzione di un metodo; nulla di diverso da quantorichiedere l’esecuzione di un metodo; nulla di diverso da quanto abbiamo fatto finora ad esempio tra il telecomando tc1 e ilabbiamo fatto finora ad esempio tra il telecomando tc1 e il televisore tv1, dall’oggetto tc1 abbiamo invocato i metodi di tv1;televisore tv1, dall’oggetto tc1 abbiamo invocato i metodi di tv1; questo schema si chiama send/target. Nel disegno precedentequesto schema si chiama send/target. Nel disegno precedente abbiamo lasciato una linea tratteggiata, quando sarà il momentoabbiamo lasciato una linea tratteggiata, quando sarà il momento vedremo che pattern si usa per quel tipo di comunicazione.vedremo che pattern si usa per quel tipo di comunicazione.

Andiamo ora a creare il nostro oggetto controller. Per creare unAndiamo ora a creare il nostro oggetto controller. Per creare un oggetto in un ambiente OO di cosa abbiamo bisogno? Dellaoggetto in un ambiente OO di cosa abbiamo bisogno? Della definizione della sua classe. Per l’oggetto controller dobbiamodefinizione della sua classe. Per l’oggetto controller dobbiamo creare una nuova classe in quanto è qualcosa di diverso dai tipi dicreare una nuova classe in quanto è qualcosa di diverso dai tipi di oggetto visti finora (Televisori, telecomandi,…).oggetto visti finora (Televisori, telecomandi,…).

Andiamo sul nostro progetto in XCode e con il click destro sullaAndiamo sul nostro progetto in XCode e con il click destro sulla cartella myTv scegliamo New File. Nel gruppo iOS selezioniamocartella myTv scegliamo New File. Nel gruppo iOS selezioniamo

Page 117: Xcode4 Tutorial Completo

Cocoa Touch e scegliamo il template UIViewController. NelCocoa Touch e scegliamo il template UIViewController. Nel pannello successivo lasciamo come subclass UIViewController epannello successivo lasciamo come subclass UIViewController e togliamo i flag Targeted for iPad, visto che noi stiamo sviluppandotogliamo i flag Targeted for iPad, visto che noi stiamo sviluppando per iOS, e with XIB for user interface, visto che noi il file con alper iOS, e with XIB for user interface, visto che noi il file con al user interface lo abbiamo già creato. Diamogli il nomeuser interface lo abbiamo già creato. Diamogli il nome TVController e salviamo.TVController e salviamo.

Se andiamo a guardare il file TVController.h vediamo che ci èSe andiamo a guardare il file TVController.h vediamo che ci è stata creata la classe come sottoclasse di UIViewController, questastata creata la classe come sottoclasse di UIViewController, questa è una classe predefinita in Cocoa ed è quella che si usa quando laè una classe predefinita in Cocoa ed è quella che si usa quando la root della nostra interfaccia è un UIView, come nel nostro caso; seroot della nostra interfaccia è un UIView, come nel nostro caso; se riapriamo il file TelevisoreView.xib vediamo che l’oggetto cheriapriamo il file TelevisoreView.xib vediamo che l’oggetto che contiene tutti gli altri è un’istanza di UIView.contiene tutti gli altri è un’istanza di UIView.

Torniamo al nostro TVController.h; qui dobbiamo dichiarare tutteTorniamo al nostro TVController.h; qui dobbiamo dichiarare tutte le property ed i metodi che ci servono per far parlare l’oggettole property ed i metodi che ci servono per far parlare l’oggetto controller con il modello e con l’interfaccia.controller con il modello e con l’interfaccia.

Andiamo adesso a dichiarare i flussi con l’interfaccia. IniziamoAndiamo adesso a dichiarare i flussi con l’interfaccia. Iniziamo con quelli che vanno dall’interfaccia verso il controller, quelli checon quelli che vanno dall’interfaccia verso il controller, quelli che abbiamo chiamato Action. Questi flussi si implementanoabbiamo chiamato Action. Questi flussi si implementano dichiarando dei metodi che saranno invocati in risposta alle nostredichiarando dei metodi che saranno invocati in risposta alle nostre interazioni con l’interfaccia.interazioni con l’interfaccia.

La prima interazione è quella con l’interruttore, dovremo avere unLa prima interazione è quella con l’interruttore, dovremo avere un metodo che viene invocato quando accendiamo ometodo che viene invocato quando accendiamo o spegnamo il spegnamo il televisore. Prima della riga @end scriviamotelevisore. Prima della riga @end scriviamo

-(IBAction)accendiSpegni:(id)sender;-(IBAction)accendiSpegni:(id)sender;questa è una dichiarazione di un metodo, ne abbiamo viste tantequesta è una dichiarazione di un metodo, ne abbiamo viste tante nei capitoli precedenti; è un metodo di istanza (inizia connei capitoli precedenti; è un metodo di istanza (inizia con il -), il il -), il tipo del risultato è IBAction, si tratta di un’indicazione particolaretipo del risultato è IBAction, si tratta di un’indicazione particolare nel senso che dal punto di vista linguistico il tipo restituito è void,nel senso che dal punto di vista linguistico il tipo restituito è void, cioè il metodo non restituisce nulla a chi lo ha invocato, ma,cioè il metodo non restituisce nulla a chi lo ha invocato, ma, usando IBAction il controller potrà usare questo metod in rispostausando IBAction il controller potrà usare questo metod in risposta alle nostre interazioni con l’interfaccia. Poi c’è il nome chealle nostre interazioni con l’interfaccia. Poi c’è il nome che abbiamo scelto, accendiSpegni; infine c’è il parametro che ilabbiamo scelto, accendiSpegni; infine c’è il parametro che il

Page 118: Xcode4 Tutorial Completo

metodo riceve quando viene invocato, (id)sender, tutti i metodimetodo riceve quando viene invocato, (id)sender, tutti i metodi che devono rispondere ad eventi di interfaccia ricevono unche devono rispondere ad eventi di interfaccia ricevono un parametro che sarà valorizzato con l’oggetto che ha generatoparametro che sarà valorizzato con l’oggetto che ha generato l’evento, nel nostro caso sarà l’oggetto interruttore. L’indicazionel’evento, nel nostro caso sarà l’oggetto interruttore. L’indicazione (id)ender vi ricordo che vuol dire che il metodo potrà usare al suo(id)ender vi ricordo che vuol dire che il metodo potrà usare al suo interno il nome sender per riferirsi all’argomento associato e cheinterno il nome sender per riferirsi all’argomento associato e che questo argomento sarà un oggetto di tipo id. Il tipo id indica unquesto argomento sarà un oggetto di tipo id. Il tipo id indica un qualunque tipo , potrà essere un bottone, uno slider, o unqualunque tipo , potrà essere un bottone, uno slider, o un qualunque altro oggetto.qualunque altro oggetto.

Andiamo ora da implementare questo metodo.Andiamo ora da implementare questo metodo. Andiamo nel file Andiamo nel file TVController.m e prima di @end scriviamoTVController.m e prima di @end scriviamo

-(IBAction)accendiSpegni:(id)sender {-(IBAction)accendiSpegni:(id)sender {NSLog(@"Metodo accendi/spegni");NSLog(@"Metodo accendi/spegni");}}Abbiamo cioè definito il metodo dichiarato nel header (il file .h) eAbbiamo cioè definito il metodo dichiarato nel header (il file .h) e per ora quello che facciamo è solo fargli scrivere una stringa. Oraper ora quello che facciamo è solo fargli scrivere una stringa. Ora ci resta da legare questo metodo all’evento dell’interruttore.ci resta da legare questo metodo all’evento dell’interruttore. Andiamo nel file TelevisoreView.xib (quello dove c’è disegnata laAndiamo nel file TelevisoreView.xib (quello dove c’è disegnata la nostra interfaccia); nel pannello di sinistra c’è una sezionenostra interfaccia); nel pannello di sinistra c’è una sezione placeholders all’interno della qual e trovate un oggetto che siplaceholders all’interno della qual e trovate un oggetto che si chiama File’s owner, bene quello è il segnaposto che a runtimechiama File’s owner, bene quello è il segnaposto che a runtime sarà il nostro controller, ora dobbiamo solo dirgli di quale classesarà il nostro controller, ora dobbiamo solo dirgli di quale classe sarà il nostro controller. Selezioniamo il File’s owner ed andiamosarà il nostro controller. Selezioniamo il File’s owner ed andiamo nel pannello Identity Inspector (ricordatevi di aprire i pannellinel pannello Identity Inspector (ricordatevi di aprire i pannelli destro e sinistro se non lo sono già), nel menù a tendina del campodestro e sinistro se non lo sono già), nel menù a tendina del campo Class selezionate TVController. In questo modo abbiamo dettoClass selezionate TVController. In questo modo abbiamo detto che l’oggetto controller apparterrà alla nostra classe.che l’oggetto controller apparterrà alla nostra classe.

La prima cosa che dobbiamo fare è dire al controller qual è la vistaLa prima cosa che dobbiamo fare è dire al controller qual è la vista che deve mostrare nell’iPhone. Per fare questo clicchiamo con ilche deve mostrare nell’iPhone. Per fare questo clicchiamo con il bottone destro sul File’s owner, appare una finestra con lebottone destro sul File’s owner, appare una finestra con le proprietà ed i metodi del controller, individuate la property view eproprietà ed i metodi del controller, individuate la property view e cliccando sul cerchietto corrispondente, trascinate il mouse sopracliccando sul cerchietto corrispondente, trascinate il mouse sopra la view principale dell’interfaccia (fate attenzione a nonla view principale dell’interfaccia (fate attenzione a non

Page 119: Xcode4 Tutorial Completo

agganciarla invece ad uno degli oggetti contenuti).agganciarla invece ad uno degli oggetti contenuti).

Ora con il bottone destro premuto trasciniamo il mouseOra con il bottone destro premuto trasciniamo il mouse dall’oggetto interruttore a File’s owner, vi verrà presentata unadall’oggetto interruttore a File’s owner, vi verrà presentata una finestra con il metodo accendiSpegni che selezionerete. In questofinestra con il metodo accendiSpegni che selezionerete. In questo modo abbiamo detto che in risposta al cambio di statomodo abbiamo detto che in risposta al cambio di stato dell’interruttore verrà invocato il nostro metodo.dell’interruttore verrà invocato il nostro metodo.

Bene, ora l’ultima cosa che dobbiamo fare è dire al sistema cheBene, ora l’ultima cosa che dobbiamo fare è dire al sistema che questa è l’interfaccia da caricare all’inizio.questa è l’interfaccia da caricare all’inizio.

Aprite il file MainWindow.xib, qui trovate l’oggetto Window,Aprite il file MainWindow.xib, qui trovate l’oggetto Window, questo oggetto rappresenta la finestra di iPhone nella quale siquesto oggetto rappresenta la finestra di iPhone nella quale si alternano le diverse view dell’interfaccia, la Window ha unaalternano le diverse view dell’interfaccia, la Window ha una property rootViewController che deve esser agganciato alproperty rootViewController che deve esser agganciato al viewController che gestisce la view da mostrare.viewController che gestisce la view da mostrare.

Procediamo in questo modo: nella Library degli oggetti (la sezioneProcediamo in questo modo: nella Library degli oggetti (la sezione inferiore del pannello di destra) cercate l’oggetto ViewController einferiore del pannello di destra) cercate l’oggetto ViewController e trascinatelo nella sezione Objects.trascinatelo nella sezione Objects.

Selezionate l’oggetto appena inserito e nell’Identity Inspector nelSelezionate l’oggetto appena inserito e nell’Identity Inspector nel campo Class scegliete TVController. Nell’Attributes Inspector nelcampo Class scegliete TVController. Nell’Attributes Inspector nel campo NibName scrivete TelevisoreView, in questo modo glicampo NibName scrivete TelevisoreView, in questo modo gli diciamo che questo viewController caricherà il nostro file xibdiciamo che questo viewController caricherà il nostro file xib dell’interfaccia.dell’interfaccia.

Infine cliccate con il bottone destro sull’oggetto Window e nellaInfine cliccate con il bottone destro sull’oggetto Window e nella finestra che si apre trascinate il mouse con il bottone sinistro dalfinestra che si apre trascinate il mouse con il bottone sinistro dal cerchietto di rooViewController sull’oggetto viewController checerchietto di rooViewController sull’oggetto viewController che abbiamo appena inserito.abbiamo appena inserito.

Siamo ora pronti per provare l’interruttore, clicchiamo su Run eSiamo ora pronti per provare l’interruttore, clicchiamo su Run e vediamo che parte il simulatore di iPhone presentandocivediamo che parte il simulatore di iPhone presentandoci l’interfaccia che abbiamo preparato. Spostiamo l’interruttore edl’interfaccia che abbiamo preparato. Spostiamo l’interruttore ed osserviamo la console dove dovrebbe apparire il messaggio cheosserviamo la console dove dovrebbe apparire il messaggio che abbiamo scritto nel metodo accendiSpegni.abbiamo scritto nel metodo accendiSpegni.

Riepiloghiamo i punti che abbiamo visto:Riepiloghiamo i punti che abbiamo visto:

Page 120: Xcode4 Tutorial Completo

Abbiamo creato la classe del nostro controller, ci dichiareremoAbbiamo creato la classe del nostro controller, ci dichiareremo dentro gli oggetti di interfaccia e di modello da collegare;dentro gli oggetti di interfaccia e di modello da collegare;

nel file xib della nostra interfaccia abbiamo associato questa classenel file xib della nostra interfaccia abbiamo associato questa classe al segnaposto File’s owner, a run time questo segnapostoal segnaposto File’s owner, a run time questo segnaposto rappresenterà il nostro oggetto controller;rappresenterà il nostro oggetto controller;

abbiamo legato la property view del controller alla viewabbiamo legato la property view del controller alla view contenitore della nostra interfaccia;contenitore della nostra interfaccia;

abbiamo legato il bottone al metodo accendiSpegni di File’sabbiamo legato il bottone al metodo accendiSpegni di File’s owner, in tal modo quando agiremo sull’interruttore saràowner, in tal modo quando agiremo sull’interruttore sarà invocato il metodo accendiSpegni;invocato il metodo accendiSpegni;

in MainWindow.xib abbiamo inserito on oggetto ViewControllerin MainWindow.xib abbiamo inserito on oggetto ViewController

abbiamo assegnato la classe di questo oggetto alla nostra classeabbiamo assegnato la classe di questo oggetto alla nostra classe controllercontroller

abbiamo definito che il controller carica il nostro xibabbiamo definito che il controller carica il nostro xib

abbiamo legato la property rootViewController dell’oggettoabbiamo legato la property rootViewController dell’oggetto Window al viewController appena inserito; in tal modoWindow al viewController appena inserito; in tal modo all’avvio dell’applicazione sarà visualizzata la nostraall’avvio dell’applicazione sarà visualizzata la nostra interfaccia;interfaccia;

Il passo successivo che facciamo è vedere l’effetto di accendere eIl passo successivo che facciamo è vedere l’effetto di accendere e spegnere sulla view che usiamo come schermo della tv. Cioèspegnere sulla view che usiamo come schermo della tv. Cioè faremo sì che quando accendiamo lo schermo diventi grigio chiarofaremo sì che quando accendiamo lo schermo diventi grigio chiaro e quando spegniamo diventi nero.e quando spegniamo diventi nero.

Per fare questo dovremo chiedere alla view di cambiare colorePer fare questo dovremo chiedere alla view di cambiare colore abbiamo quindi bisogno di un riferimento alla view.abbiamo quindi bisogno di un riferimento alla view.

Prima di procedere però dobbiamo rendere coerente la nostraPrima di procedere però dobbiamo rendere coerente la nostra interfaccia; in questo momento abbiamo l’interruttore su Off e lointerfaccia; in questo momento abbiamo l’interruttore su Off e lo schermo grigio chiaro come se fosse acceso; sistemiamo le coseschermo grigio chiaro come se fosse acceso; sistemiamo le cose facendo diventare nero lo schermo. Apriamo il filefacendo diventare nero lo schermo. Apriamo il file TelevisoreView.xib, selezioniamo la view che ci fa da schermo,TelevisoreView.xib, selezioniamo la view che ci fa da schermo, quella grigia che contiene il nome del canale, nel pannelloquella grigia che contiene il nome del canale, nel pannello Attibutes dell’Inspector selzioniamo Black Color nel menuAttibutes dell’Inspector selzioniamo Black Color nel menu relativo a Background.relativo a Background.

Page 121: Xcode4 Tutorial Completo

Riprendiamo ora con il nostro codice.Riprendiamo ora con il nostro codice.

Nel file TVController.h, dopo la } scriviamoNel file TVController.h, dopo la } scriviamo

@property (nonatomic, retain) IBOutlet UIView *schermo;@property (nonatomic, retain) IBOutlet UIView *schermo;cioè abbiamo dichiarato una property di nome schermo che sicioè abbiamo dichiarato una property di nome schermo che si riferirà ad istanze di UIView; rispetto alla dichiarazione solitariferirà ad istanze di UIView; rispetto alla dichiarazione solita delle property abbiamo aggiunto la keyword IBOutlet, questo fa sìdelle property abbiamo aggiunto la keyword IBOutlet, questo fa sì che questa property siche questa property si possa riferire ad oggetti dell’interfaccia possa riferire ad oggetti dell’interfaccia grafica. Vediamo come.grafica. Vediamo come.

Torniamo sul file TelevisoreView.xib e clicchiamo con il destro sulTorniamo sul file TelevisoreView.xib e clicchiamo con il destro sul segnaposto File’s owner, nella finestra che appare clicchiamo sulsegnaposto File’s owner, nella finestra che appare clicchiamo sul cerchietto della property schermo e trasciniamo il mouse sullacerchietto della property schermo e trasciniamo il mouse sulla view grigia (quella che usiamo per lo schermo); in questo modoview grigia (quella che usiamo per lo schermo); in questo modo abbiamo collegato la nostra property del codice con un elementoabbiamo collegato la nostra property del codice con un elemento di interfaccia. Ora nel codice usando la variabile schermodi interfaccia. Ora nel codice usando la variabile schermo possiamo agire sull’oggetto di interfaccia.possiamo agire sull’oggetto di interfaccia.

Andiamo nel file TVController.m e la prima cosa da fare avendoAndiamo nel file TVController.m e la prima cosa da fare avendo dichiarato una property è quella di far definire i metodi di accessodichiarato una property è quella di far definire i metodi di accesso (getter/setter), per far questo dopo(getter/setter), per far questo dopo

@implementation TVController@implementation TVControllerscriviamoscriviamo

@synthesize schermo;@synthesize schermo;come abbiamo visto nel capitolo precedente, con questa istruzionecome abbiamo visto nel capitolo precedente, con questa istruzione il compilatore prepara i metodi di accesso alla property (il getteril compilatore prepara i metodi di accesso alla property (il getter schermo ed il setter setSchermo:).schermo ed il setter setSchermo:).

Ora nel metodo accendiSpegni, dopo l’istruzione NSLog(…)Ora nel metodo accendiSpegni, dopo l’istruzione NSLog(…) scriviamoscriviamo

schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor è la schermo.backgroundColor è la dot notationdot notation per accedere alla per accedere alla property backgroundColor, quindi con questa istruzioneproperty backgroundColor, quindi con questa istruzione assegnamo un valore alla property.assegnamo un valore alla property.

Page 122: Xcode4 Tutorial Completo

Approfondimento: Guardate sulla documentazione la classeApprofondimento: Guardate sulla documentazione la classe UIView e la sua property backgroundColor.UIView e la sua property backgroundColor.

Che valore assegnamo a backgrounColor? Il risultato di [UIColorChe valore assegnamo a backgrounColor? Il risultato di [UIColor lightGrayColor]. Questa è l’invocazione di un metodo di classelightGrayColor]. Questa è l’invocazione di un metodo di classe predefinito, blackColor, sulla classe UIColor; questo metodopredefinito, blackColor, sulla classe UIColor; questo metodo restituisce un’istanza di UIColor che rappresenta il colore grigiorestituisce un’istanza di UIColor che rappresenta il colore grigio chiaro.chiaro.

Approfondimento: Guardate sulla documentazione la classeApprofondimento: Guardate sulla documentazione la classe UIColor ed i metodi per restituire i colori.UIColor ed i metodi per restituire i colori.

Abbiamo fatto un passo avanti, ora il nostro televisore si accenda,Abbiamo fatto un passo avanti, ora il nostro televisore si accenda, ma non si spegne; in effetti nel nostro metodo noi assegnamoma non si spegne; in effetti nel nostro metodo noi assegnamo sempre il colore grigio chiaro, senza sapere se stiamosempre il colore grigio chiaro, senza sapere se stiamo accendendolo o spegnendolo. Dobbiamo cioè implementareaccendendolo o spegnendolo. Dobbiamo cioè implementare qualcosa del tipoqualcosa del tipo

se il televisore è acceso spegnilo altrimenti accendilo;se il televisore è acceso spegnilo altrimenti accendilo;

questa è un’istruzione condizionale, l’abbiamo già incontrata, chequesta è un’istruzione condizionale, l’abbiamo già incontrata, che esegue blocchi diversi di codice a seconda del valore di unaesegue blocchi diversi di codice a seconda del valore di una determinata condizione.determinata condizione.

La prima cosa che dobbiamo definire è come traduciamo in codiceLa prima cosa che dobbiamo definire è come traduciamo in codice la verifica dello stato del televisore (se è acceso o spento); sila verifica dello stato del televisore (se è acceso o spento); si possono fare diverse scelte, quella che andremo ad implementarepossono fare diverse scelte, quella che andremo ad implementare noi è quella di avere una variabile di istanza nella qualenoi è quella di avere una variabile di istanza nella quale manteniamo lo stato del televisore.manteniamo lo stato del televisore.

In TVController.h all’interno delle {} scriviamoIn TVController.h all’interno delle {} scriviamo

BOOL acceso;BOOL acceso;cioè abbiamo dichiarato una variabile di nome acceso e di tipocioè abbiamo dichiarato una variabile di nome acceso e di tipo BOOL. Il tipo BOOL è predefinito in Cocoa e rappresenta i valoriBOOL. Il tipo BOOL è predefinito in Cocoa e rappresenta i valori logici Vero o Falso; non si tratta di una classe e pertanto non c’èlogici Vero o Falso; non si tratta di una classe e pertanto non c’è bisogno del * prima del suo nome.bisogno del * prima del suo nome.

Ora dobbiamo dare un valore iniziale a questa variabile coerenteOra dobbiamo dare un valore iniziale a questa variabile coerente

Page 123: Xcode4 Tutorial Completo

con lo stato iniziale del nostro televisore e cioè Falso. Ma dove locon lo stato iniziale del nostro televisore e cioè Falso. Ma dove lo inizializziamo questo valore? Ricordate quando abbiamo lavoratoinizializziamo questo valore? Ricordate quando abbiamo lavorato con i nostro oggetti tv1, tc1? Avevamo un metodo init della classecon i nostro oggetti tv1, tc1? Avevamo un metodo init della classe che veniva eseguito quando l’oggetto veniva creato; bene neche veniva eseguito quando l’oggetto veniva creato; bene ne abbiamo anche nel nostro controller leggermente più complessoabbiamo anche nel nostro controller leggermente più complesso ma con lo stesso comportamento e si chiamama con lo stesso comportamento e si chiama initWithNibName:bundle: quindi, in TVController.m andiamoinitWithNibName:bundle: quindi, in TVController.m andiamo all’interno di questo metodo tra le {} dopo all’interno di questo metodo tra le {} dopo if(self)if(self) scriviamo scriviamo

acceso=FALSE;acceso=FALSE;A questo punto possiamo cambiare il metodo accendiSpegni,A questo punto possiamo cambiare il metodo accendiSpegni, come seguecome segue

-(IBAction)accendiSpegni:(id)sender {-(IBAction)accendiSpegni:(id)sender {NSLog(@"Metodo accendi/spegni");NSLog(@"Metodo accendi/spegni");acceso=!acceso;acceso=!acceso;if (acceso) {if (acceso) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];} else {} else {schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];}}}}La prima istruzione (dopo NSLog) èLa prima istruzione (dopo NSLog) è

acceso=!acceso;acceso=!acceso;è un istruzione di assegnamento che assegna adè un istruzione di assegnamento che assegna ad acceso il risultato acceso il risultato di !acceso. L’operatore ! è un operatore che agisce su un valoredi !acceso. L’operatore ! è un operatore che agisce su un valore logico (Vero/Falso) e ne restituisce la sua negazione. Cioè selogico (Vero/Falso) e ne restituisce la sua negazione. Cioè se acceso è Falso restituisce Vero mentre se acceso è Vero restituisceacceso è Falso restituisce Vero mentre se acceso è Vero restituisce Falso. Quindi dopo questa istruzione accesso conterrà il contrarioFalso. Quindi dopo questa istruzione accesso conterrà il contrario di quello che conteneva prima (se il televisore era spento ora èdi quello che conteneva prima (se il televisore era spento ora è acceso e se era acceso ora è spento).acceso e se era acceso ora è spento).

L’istruzione condizionale si implementa conL’istruzione condizionale si implementa con

if (<espressione vero/falso>) {if (<espressione vero/falso>) {

<blocco Vero><blocco Vero>

Page 124: Xcode4 Tutorial Completo

} else {} else {

<blocco Falso><blocco Falso>

}}

cioè si stabilisce una espressione che restituisce Vero o Falso e acioè si stabilisce una espressione che restituisce Vero o Falso e a seconda del risultato della sua valutazione si esegue il primo o ilseconda del risultato della sua valutazione si esegue il primo o il secondo blocco.secondo blocco.

Quindi nel nostro caso il blocco Vero è semplicemente l’istruzioneQuindi nel nostro caso il blocco Vero è semplicemente l’istruzione che fa diventare grigio lo schermo ed il blocco Falso è quello cheche fa diventare grigio lo schermo ed il blocco Falso è quello che lo fa diventare nero. Mentre la condizione è semplicemente basatalo fa diventare nero. Mentre la condizione è semplicemente basata sullo stato della variabile sullo stato della variabile accesoacceso..

Bene, a questo punto andiamo a definire gli altri collegamenti conBene, a questo punto andiamo a definire gli altri collegamenti con l’interfaccia.l’interfaccia.

Prima cosa individuiamo quelli che richiedono un’action cioè iPrima cosa individuiamo quelli che richiedono un’action cioè i controlli che devono comunicare con il controller invocandone deicontrolli che devono comunicare con il controller invocandone dei metodi. In questa categoria ci sono lo slider per regolare ilmetodi. In questa categoria ci sono lo slider per regolare il volumevolume e la pulsantiera per il cambio dei canali. e la pulsantiera per il cambio dei canali.

Poi ci sono i controlli che invece devono ricevere un’invocazionePoi ci sono i controlli che invece devono ricevere un’invocazione dal controller, e che quindi avranno bisogno di Outlet; questi sonodal controller, e che quindi avranno bisogno di Outlet; questi sono la label dentro lo schermo che indicherà il nome del canale e lala label dentro lo schermo che indicherà il nome del canale e la label vicino allo slider del volume che invece indicherà il livellolabel vicino allo slider del volume che invece indicherà il livello del volume.del volume.

Andiamo quindi in TVController.h e dichiariamo i metodiAndiamo quindi in TVController.h e dichiariamo i metodi (regolaVolume e cambiaCanale) e le proprietà (nomeCanale e(regolaVolume e cambiaCanale) e le proprietà (nomeCanale e livelloVolume) di cui abbiamo bisogno. Alla fine dovreste averelivelloVolume) di cui abbiamo bisogno. Alla fine dovreste avere

#import <UIKit/UIKit.h>#import <UIKit/UIKit.h>@interface TVController : UIViewController {@interface TVController : UIViewController {}}@property (nonatomic, retain) IBOutlet UIView *schermo;@property (nonatomic, retain) IBOutlet UIView *schermo;@property (nonatomic, retain) IBOutlet UILabel *nomeCanale;@property (nonatomic, retain) IBOutlet UILabel *nomeCanale;@property (nonatomic, retain) IBOutlet UILabel *livelloVolume;@property (nonatomic, retain) IBOutlet UILabel *livelloVolume;

Page 125: Xcode4 Tutorial Completo

-(IBAction)accendiSpegni:(id)sender;-(IBAction)accendiSpegni:(id)sender;-(IBAction)regolaVolume:(UISlider *)sender;-(IBAction)regolaVolume:(UISlider *)sender;-(IBAction)cambiaCanale:(UISegmentedControl *)sender;-(IBAction)cambiaCanale:(UISegmentedControl *)sender;

@end@endOra andiamo in TelevisoreView.xib e facciamo i collegamenti traOra andiamo in TelevisoreView.xib e facciamo i collegamenti tra il controller e gli elementi dell’interfaccia grafica. Quindi con ilil controller e gli elementi dell’interfaccia grafica. Quindi con il bottone destro premuto trasciniamo il mouse dallo slider su File’sbottone destro premuto trasciniamo il mouse dallo slider su File’s owner e selezioniamo il metodo regolaVolume, facciamo lo stessoowner e selezioniamo il metodo regolaVolume, facciamo lo stesso con il selettore dei canali scegliendo il metodo cambiaCanale.con il selettore dei canali scegliendo il metodo cambiaCanale.

Andiamo ora ad implementare questi ultimi metodi nel nostroAndiamo ora ad implementare questi ultimi metodi nel nostro controller. In TVController.m per prima cosa dobbiamo fare icontroller. In TVController.m per prima cosa dobbiamo fare i synthesize per le property che abbiamo dichiarato, quindisynthesize per le property che abbiamo dichiarato, quindi aggiungiamo al syntesize già presenteaggiungiamo al syntesize già presente

@synthesize nomeCanale;@synthesize nomeCanale;@synthesize livelloVolume;@synthesize livelloVolume;Dopo il metodo accendiSpegni aggiungiamoDopo il metodo accendiSpegni aggiungiamo

-(IBAction)regolaVolume:(UISlider *)sender {-(IBAction)regolaVolume:(UISlider *)sender {if (acceso) {if (acceso) {NSLog(@"Il volume è a livello %f", sender.value);NSLog(@"Il volume è a livello %f", sender.value);livelloVolume.text=[NSStringlivelloVolume.text=[NSString stringWithFormat:@"%f",sender.value];stringWithFormat:@"%f",sender.value];}}}}questo è il metodo che sarà invocato quando lo slider del volumequesto è il metodo che sarà invocato quando lo slider del volume cambia posizione.cambia posizione.

La prima cosa che facciamo è controllare se il televisore è accesoLa prima cosa che facciamo è controllare se il televisore è acceso (la variabile acceso vale Vero), altrimenti non facciamo nulla.(la variabile acceso vale Vero), altrimenti non facciamo nulla.

Iniziamo con il farci scrivere un messaggio a console. La stringaIniziamo con il farci scrivere un messaggio a console. La stringa che usiamo è una format string (ormai dovreste sapere cosa è) cioèche usiamo è una format string (ormai dovreste sapere cosa è) cioè una stringa con un segnaposto %f. Questo segnaposto indica che auna stringa con un segnaposto %f. Questo segnaposto indica che a

Page 126: Xcode4 Tutorial Completo

runtime sarà sostituito da un numero frazionario. Il valore che saràruntime sarà sostituito da un numero frazionario. Il valore che sarà scritto al posto di %f è il risultato discritto al posto di %f è il risultato di sender.valuesender.value questo è questo è l’accesso ad una property l’accesso ad una property valuevalue dell’oggetto dell’oggetto sendersender. E’ una. E’ una property predefinita per la classe UISlider, cui appartiene sender,property predefinita per la classe UISlider, cui appartiene sender, che restituisce il valore corrispondente alla posizione dello slider.che restituisce il valore corrispondente alla posizione dello slider.

Approfondimento: Guardate sulla documentazione Apple la classeApprofondimento: Guardate sulla documentazione Apple la classe UISlider e la property value.UISlider e la property value.

L’altra cosa che dobbiamo fare è aggiornare la label a fianco alloL’altra cosa che dobbiamo fare è aggiornare la label a fianco allo slider che ci indica il livello del volume; Questo lo otteniamo conslider che ci indica il livello del volume; Questo lo otteniamo con

livelloVolume.text=[NSStringlivelloVolume.text=[NSString stringWithFormat:@"%f",sender.value];stringWithFormat:@"%f",sender.value];è un assegnamento a livelloVolume.text; questo è l’accesso allaè un assegnamento a livelloVolume.text; questo è l’accesso alla property text, predefinita per UILabel, che prende un’istanza diproperty text, predefinita per UILabel, che prende un’istanza di NSString come valore.NSString come valore.

Approfondimento: Guardate sulla documentazione Apple la classeApprofondimento: Guardate sulla documentazione Apple la classe UILabel e la property text.UILabel e la property text.

Noi assegnamo il risultato diNoi assegnamo il risultato di

[NSString stringWithFormat:@"%f",sender.value][NSString stringWithFormat:@"%f",sender.value]questa è l’invocazione di un metodo di classe di NSString,questa è l’invocazione di un metodo di classe di NSString, stringWithFormat:, che prende in input una format string ed i suoistringWithFormat:, che prende in input una format string ed i suoi parametri e restituisce l’istanza di NSString che la rappresenta.parametri e restituisce l’istanza di NSString che la rappresenta.

Approfondimento: Guardate sulla documentazione Apple la classeApprofondimento: Guardate sulla documentazione Apple la classe NSString ed i metodi di classe stringWith….NSString ed i metodi di classe stringWith….

Aggiungiamo l’ultimo metodoAggiungiamo l’ultimo metodo

-(IBAction)cambiaCanale:(UISegmentedControl *)sender {-(IBAction)cambiaCanale:(UISegmentedControl *)sender {if (acceso) {if (acceso) {NSLog(@"Il canale selezionato è il numeroNSLog(@"Il canale selezionato è il numero %d",sender.selectedSegmentIndex);%d",sender.selectedSegmentIndex);}}}}

Page 127: Xcode4 Tutorial Completo

questo è il metodo che sarà invocato quando selezioniamo unquesto è il metodo che sarà invocato quando selezioniamo un bottone del selettore dei canali ed in questo momento no fa altrobottone del selettore dei canali ed in questo momento no fa altro che scrivere un messaggio (solo se acceso è Vero) con al solitache scrivere un messaggio (solo se acceso è Vero) con al solita format string con un segnaposto questa volta %d, cioè a runtimeformat string con un segnaposto questa volta %d, cioè a runtime sarà sostituito da un numero intero. In particolare sarà sostituito dasarà sostituito da un numero intero. In particolare sarà sostituito da sender.selectedSegmentIndex , questo è l’accesso alla propertysender.selectedSegmentIndex , questo è l’accesso alla property selectedSegmentIndexselectedSegmentIndex dell’oggetto dell’oggetto sendersender. E’ una property. E’ una property predefinita per la predefinita per la classe UISegmentedControl di cui sender èclasse UISegmentedControl di cui sender è istanza, che restituisce il numero del pulsante premuto. Il punto èistanza, che restituisce il numero del pulsante premuto. Il punto è che lui comincia a contare da 0 quindi quando noi selezioniamo ilche lui comincia a contare da 0 quindi quando noi selezioniamo il canale 1 lui indica 0, quando selezioniamo 2 lui indica 1 e cosìcanale 1 lui indica 0, quando selezioniamo 2 lui indica 1 e così via; per sistema le cose sommiamo 1, quindi il nostro metodovia; per sistema le cose sommiamo 1, quindi il nostro metodo diventadiventa

-(IBAction)cambiaCanale:(UISegmentedControl *)sender {-(IBAction)cambiaCanale:(UISegmentedControl *)sender {if (acceso) {if (acceso) {NSLog(@"Il canale selezionato è il numeroNSLog(@"Il canale selezionato è il numero %d",sender.selectedSegmentIndex+1);%d",sender.selectedSegmentIndex+1);}}}}Ora proviamo ad eseguire il nostro programma e guardiamo ilOra proviamo ad eseguire il nostro programma e guardiamo il funzionamento sia con l’interruttore su On che su Off.funzionamento sia con l’interruttore su On che su Off.

Bene, abbiamo completato la definizione dei canali dal controllerBene, abbiamo completato la definizione dei canali dal controller verso l’interfaccia cioè verso il View del pattern MVC.verso l’interfaccia cioè verso il View del pattern MVC. Ricordando che il controller mette in comunicazione il Model conRicordando che il controller mette in comunicazione il Model con View passiamo a definire il canale verso il Model.View passiamo a definire il canale verso il Model.

Quindi prima cosa, il nostro controller dovrà parlare con unQuindi prima cosa, il nostro controller dovrà parlare con un oggetto televisore, ha quindi bisogno di gestire un riferimento adoggetto televisore, ha quindi bisogno di gestire un riferimento ad un’istanza di Televisore, quindi in TVController.h aggiungiamoun’istanza di Televisore, quindi in TVController.h aggiungiamo

@property (nonatomic,retain) Televisore *ilTelevisore;@property (nonatomic,retain) Televisore *ilTelevisore;abbiamo cioè dichiarato una property con la quale gestiremo ilabbiamo cioè dichiarato una property con la quale gestiremo il riferimento al televisore. XCode in questo momento vi segnala unriferimento al televisore. XCode in questo momento vi segnala un errore, perchè? Perchè la classe controller non conosce il termineerrore, perchè? Perchè la classe controller non conosce il termine Televisore, dobbiamo quindi farglielo conoscere aggiungendoTelevisore, dobbiamo quindi farglielo conoscere aggiungendo

Page 128: Xcode4 Tutorial Completo

#import "Televisore.h"#import "Televisore.h"dopo la prima riga di import già presente.dopo la prima riga di import già presente.

Passiamo a TVController.m e avendo una nuova propertyPassiamo a TVController.m e avendo una nuova property aggiungiamoaggiungiamo

@synthesize ilTelevisore;@synthesize ilTelevisore;Poi cambiamo il metodo accendiSpegni per comunicare con ilPoi cambiamo il metodo accendiSpegni per comunicare con il Model, cioè lo facciamo diventareModel, cioè lo facciamo diventare

-(IBAction)accendiSpegni:(id)sender {-(IBAction)accendiSpegni:(id)sender {NSLog(@"Metodo accendi/spegni");NSLog(@"Metodo accendi/spegni");acceso=!acceso;acceso=!acceso;if (acceso) {if (acceso) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];[ilTelevisore accendi];[ilTelevisore accendi];} else {} else {schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];[ilTelevisore spegni];[ilTelevisore spegni];}}}}cioè, nel ramo di accensione (il primo) abbiamo aggiuntocioè, nel ramo di accensione (il primo) abbiamo aggiunto [ilTelevisore accendi] cioè l’invocazione del metodo accendi della[ilTelevisore accendi] cioè l’invocazione del metodo accendi della classe Televisore, e nel ramo di spegnimento abbiamo aggiuntoclasse Televisore, e nel ramo di spegnimento abbiamo aggiunto [ilTelevisore spegni][ilTelevisore spegni] cioè l’invocazione del metodo spegni della cioè l’invocazione del metodo spegni della classe Televisore;classe Televisore; in questo modo attraverso i metodi del in questo modo attraverso i metodi del controller gli eventi sull’interruttore si propagano fino al modello.controller gli eventi sull’interruttore si propagano fino al modello.

In maniera analoga cambiamo il metodo regolaVolume inIn maniera analoga cambiamo il metodo regolaVolume in

-(IBAction)regolaVolume:(UISlider *)sender {-(IBAction)regolaVolume:(UISlider *)sender {if (acceso) {if (acceso) {NSLog(@"Il volume è a livello %f", sender.value);NSLog(@"Il volume è a livello %f", sender.value);livelloVolume.text=[NSStringlivelloVolume.text=[NSString stringWithFormat:@"%f",sender.value];stringWithFormat:@"%f",sender.value];ilTelevisore.livelloVolume=(NSDecimalNumber *)ilTelevisore.livelloVolume=(NSDecimalNumber *)[NSDecimalNumber numberWithFloat:sender.value];[NSDecimalNumber numberWithFloat:sender.value];

Page 129: Xcode4 Tutorial Completo

}}}}abbiamo cioè aggiuntoabbiamo cioè aggiunto

ilTelevisore.livelloVolume=(NSDecimalNumber *)ilTelevisore.livelloVolume=(NSDecimalNumber *)[NSDecimalNumber [NSDecimalNumber

numberWithFloat:sender.value];numberWithFloat:sender.value];ilTelevisore.livelloVolume è l’accesso alla property che abbiamoilTelevisore.livelloVolume è l’accesso alla property che abbiamo definito nella classe Televisore per l’oggetto ilTelevisore; quindidefinito nella classe Televisore per l’oggetto ilTelevisore; quindi con questa istruzione assegnamo un valore alla propertycon questa istruzione assegnamo un valore alla property livelloVolume del modello. Il valore che assegnamo è il risultatolivelloVolume del modello. Il valore che assegnamo è il risultato didi

(NSDecimalNumber *)[NSDecimalNumber(NSDecimalNumber *)[NSDecimalNumber numberWithFloat:sender.value]numberWithFloat:sender.value]Prendiamo prima in considerazione Prendiamo prima in considerazione [NSDecimalNumber[NSDecimalNumber numberWithFloat:sender.value] numberWithFloat:sender.value] questo è un metodo di classe chequesto è un metodo di classe che NSDecimalNumber eredita dalla sua superclasse NSNumber e cheNSDecimalNumber eredita dalla sua superclasse NSNumber e che restituisce un NSNumber che rappresenta il numero frazionariorestituisce un NSNumber che rappresenta il numero frazionario che gli viene passato (sender.value).che gli viene passato (sender.value).

Il punto è che la nostra property livelloVolume è di tipoIl punto è che la nostra property livelloVolume è di tipo NSDecimalNumber mentre questo metodo restituisce unNSDecimalNumber mentre questo metodo restituisce un NSNumber, quindi ricorriamo al type casting inserendoNSNumber, quindi ricorriamo al type casting inserendo (NSDecimalNumber *)(NSDecimalNumber *) che dice che il risultato va interpretato che dice che il risultato va interpretato come istanza di NSDecimalNumber.come istanza di NSDecimalNumber.

Il type casting è un meccanismo che ci consente di forzare un tipoIl type casting è un meccanismo che ci consente di forzare un tipo su un dato, ovviamente resta nostra la responsabilità di controllaresu un dato, ovviamente resta nostra la responsabilità di controllare che il nuovo tipo e la struttura dati siano compatibili, in quanto ilche il nuovo tipo e la struttura dati siano compatibili, in quanto il type casting,non trasforma un dato in un altro, non crea nuovitype casting,non trasforma un dato in un altro, non crea nuovi oggetti di un altro tipo ma forza solo la tipizzazione del dato.oggetti di un altro tipo ma forza solo la tipizzazione del dato.

Infine cambiamo il metodo cambiaCanale inInfine cambiamo il metodo cambiaCanale in

-(IBAction)cambiaCanale:(UISegmentedControl *)sender {-(IBAction)cambiaCanale:(UISegmentedControl *)sender {if (acceso) {if (acceso) {NSLog(@"Il canale selezionato è il numeroNSLog(@"Il canale selezionato è il numero

Page 130: Xcode4 Tutorial Completo

%d",sender.selectedSegmentIndex+1);%d",sender.selectedSegmentIndex+1);ilTelevisore.canaleSintonizzato=(NSDecimalNumber *)ilTelevisore.canaleSintonizzato=(NSDecimalNumber *)[NSDecimalNumber[NSDecimalNumber numberWithInt:sender.selectedSegmentIndex];numberWithInt:sender.selectedSegmentIndex];}}}}In maniera analoga al caso precedente abbiamo aggiunto la rigaIn maniera analoga al caso precedente abbiamo aggiunto la riga per l’aggiornamento della property canaleSintonizzato con ilper l’aggiornamento della property canaleSintonizzato con il valore del segmented control dell’interfaccia.valore del segmented control dell’interfaccia.

Ma in realtà noi in questo metodo dobbiamo fare anche un’altraMa in realtà noi in questo metodo dobbiamo fare anche un’altra cosa e cioè aggiornare la label nella view che fa da schermocosa e cioè aggiornare la label nella view che fa da schermo visualizzando il nome del canale, dobbiamo cioè aggiungerevisualizzando il nome del canale, dobbiamo cioè aggiungere un’istruzione comeun’istruzione come

nomeCanale.text=...nomeCanale.text=...cioè l’assegnamento alla property text della label nomeCanalecioè l’assegnamento alla property text della label nomeCanale di….già di cosa? cosa ci restituisce il nome del canaledi….già di cosa? cosa ci restituisce il nome del canale sintonizzato? A questo punto ci accorgiamo che non abbiamosintonizzato? A questo punto ci accorgiamo che non abbiamo nessun metodo che lo faccia, quindi il nostro modello non ènessun metodo che lo faccia, quindi il nostro modello non è allineato con la nostra vista…che facciamo? Possiamo decidere oallineato con la nostra vista…che facciamo? Possiamo decidere o di modificare la View (togliere la label) o di modificare il Modeldi modificare la View (togliere la label) o di modificare il Model (accedere al nome del canale). Noi scegliamo la seconda strada.(accedere al nome del canale). Noi scegliamo la seconda strada. Quindi nella nostra classe televisore dovremo trovar eil modo diQuindi nella nostra classe televisore dovremo trovar eil modo di farci restituire il nome del canale, per far questo usiamo unafarci restituire il nome del canale, per far questo usiamo una property, avremmo potuto usare un metodo e sarebbe statoproperty, avremmo potuto usare un metodo e sarebbe stato ugualmente corretto.ugualmente corretto.

Andiamo in Televisore.h e aggiungiamoAndiamo in Televisore.h e aggiungiamo

@property(readonly) NSString *nomeDelCanale;@property(readonly) NSString *nomeDelCanale;cioè dichiariamo una property in sola lettura nomeDelCanale checioè dichiariamo una property in sola lettura nomeDelCanale che sarà un’istanza di NSString.sarà un’istanza di NSString.

In Televisore.m, invece di fare il synthesize che creerebbeIn Televisore.m, invece di fare il synthesize che creerebbe automaticamente il getter, lo definiamo noi aggiungendoautomaticamente il getter, lo definiamo noi aggiungendo

-(NSString *)nomeDelCanale {-(NSString *)nomeDelCanale {

Page 131: Xcode4 Tutorial Completo

return [canaliMemorizzati objectAtIndex:[canaleSintonizzatoreturn [canaliMemorizzati objectAtIndex:[canaleSintonizzato intValue]];intValue]];}}cioè il metodo getter della property restituisce l’elementocioè il metodo getter della property restituisce l’elemento dell’array con i nomi dei canali (canaliMemorizzati) che si trovadell’array con i nomi dei canali (canaliMemorizzati) che si trova alla posizione indicata da canaleSintonizzato; esattamente quelloalla posizione indicata da canaleSintonizzato; esattamente quello che cercavamo.che cercavamo.

A questo punto possiamo tornare al nostro metodo cambiaCanale eA questo punto possiamo tornare al nostro metodo cambiaCanale e modificarlo cosìmodificarlo così

-(IBAction)cambiaCanale:(UISegmentedControl *)sender {-(IBAction)cambiaCanale:(UISegmentedControl *)sender {if (acceso) {if (acceso) {NSLog(@"Il canale selezionato è il numeroNSLog(@"Il canale selezionato è il numero %d",sender.selectedSegmentIndex+1);%d",sender.selectedSegmentIndex+1);ilTelevisore.canaleSintonizzato=(NSDecimalNumber *)ilTelevisore.canaleSintonizzato=(NSDecimalNumber *)[NSDecimalNumber[NSDecimalNumber numberWithInt:sender.selectedSegmentIndex];numberWithInt:sender.selectedSegmentIndex];nomeCanale.text=ilTelevisore.nomeDelCanale;nomeCanale.text=ilTelevisore.nomeDelCanale;}}}}Abbiamo aggiunto l’istruzioneAbbiamo aggiunto l’istruzione

nomeCanale.text=ilTelevisore.nomeDelCanale;nomeCanale.text=ilTelevisore.nomeDelCanale;cioè alla property text della label nomeCanale assegnamo il valorecioè alla property text della label nomeCanale assegnamo il valore della property nomeDelCanale dell’oggetto ilTelevisore.della property nomeDelCanale dell’oggetto ilTelevisore.

Bene, basta così per ora per il nostro controller e per le sueBene, basta così per ora per il nostro controller e per le sue interazioni con il Model e con il View; l’ultima cosa cheinterazioni con il Model e con il View; l’ultima cosa che dobbiamo fare parte da una domanda: ma l’oggetto della classedobbiamo fare parte da una domanda: ma l’oggetto della classe Televisore dov’è? Noi nel controller abbiamo usato una propertyTelevisore dov’è? Noi nel controller abbiamo usato una property ilTelevisore, ma non abbiamo mai creato un’istanza assegnata ailTelevisore, ma non abbiamo mai creato un’istanza assegnata a questa property. Abbiamo quindi bisogno di creare il nostroquesta property. Abbiamo quindi bisogno di creare il nostro televisore. Ricordate che nella versione precedente avevamo neltelevisore. Ricordate che nella versione precedente avevamo nel main.mmain.m

tv1=[[Televisore alloc] init];tv1=[[Televisore alloc] init];

Page 132: Xcode4 Tutorial Completo

Dovremo fare qualcosa del genere anche ora. A differenza delDovremo fare qualcosa del genere anche ora. A differenza del programma precedente però, nei programmi x iOS il main.m èprogramma precedente però, nei programmi x iOS il main.m è meglio lasciarlo perdere ed il punto più simile è il metodomeglio lasciarlo perdere ed il punto più simile è il metodo

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

che trovate in myTvAppDelegate.m; questo è il metodo che vieneche trovate in myTvAppDelegate.m; questo è il metodo che viene invocato quando l’app ha completato le procedure di attivazione.invocato quando l’app ha completato le procedure di attivazione. In myTvAppDelegate.h aggiungiamoIn myTvAppDelegate.h aggiungiamo

#import "Televisore.h"#import "Televisore.h"#import "TVController.h"#import "TVController.h"In myTvAppDelegate.m, nel metodoIn myTvAppDelegate.m, nel metodo didFinishLaunchingWithOptions prima della rigadidFinishLaunchingWithOptions prima della riga

[self.window makeKeyAndVisible];[self.window makeKeyAndVisible];aggiungiamoaggiungiamo

((TVController *)self.window.rootViewController).ilTelevisore=((TVController *)self.window.rootViewController).ilTelevisore= [[Televisore alloc] init];[[Televisore alloc] init];Questa istruzione è un po’ più articolata, analizziamolaQuesta istruzione è un po’ più articolata, analizziamola

selfself: l’abbiamo già incontrata ed è il nome con il quale si individua: l’abbiamo già incontrata ed è il nome con il quale si individua sempre l’istanza che la cita, quindi nel nostro caso è l’istanza disempre l’istanza che la cita, quindi nel nostro caso è l’istanza di appDelegate;appDelegate;

self.windowself.window: è l’accesso alla property window di appDelegate che: è l’accesso alla property window di appDelegate che in mainWindow.xib è collegata all’oggetto Window;in mainWindow.xib è collegata all’oggetto Window;

self.window.rootViewControllerself.window.rootViewController: è l’accesso alla property: è l’accesso alla property rootViewController di Window che nel file mainWindow.xibrootViewController di Window che nel file mainWindow.xib abbiamo collegato ad un’istanza di viewController della nostraabbiamo collegato ad un’istanza di viewController della nostra classeclasse

(TVController *)self.window.rootViewController(TVController *)self.window.rootViewController:è il type casting:è il type casting del rootViewController con il quale forziamo il tipo adel rootViewController con il quale forziamo il tipo a TVController;TVController;

(((TVController *)self.window.rootViewController).ilTelevisore: (TVController *)self.window.rootViewController).ilTelevisore: èè

Page 133: Xcode4 Tutorial Completo

l’accesso alla property ilTelevisore del rootViewController;l’accesso alla property ilTelevisore del rootViewController;

quindi in conclusione con l’istruzione precedente assegnamo allaquindi in conclusione con l’istruzione precedente assegnamo alla property ilTelevisore un’istanza appena creata, property ilTelevisore un’istanza appena creata, [[Televisore alloc][[Televisore alloc] init],init], come abbiamo già visto nel programma precedente. come abbiamo già visto nel programma precedente.

Bene; eseguiamo l’app e guardiamone l’effetto sull’interfaccia eBene; eseguiamo l’app e guardiamone l’effetto sull’interfaccia e sulla console.sulla console.

16 – Opzioni16 – Opzioni

iOS, , Mac, , SDK, , Tech Room

Prima di proseguire con il nuovo codice facciamo una piccolaPrima di proseguire con il nuovo codice facciamo una piccola ottimizzazione a quanto realizzato nel capitolo precedente.ottimizzazione a quanto realizzato nel capitolo precedente.

Nella prima parte, mentre collegavamo il controller conNella prima parte, mentre collegavamo il controller con l’interfaccia e non avevamo ancora disposizione il Model,l’interfaccia e non avevamo ancora disposizione il Model, abbiamo avuto bisogno di sapere se il televisore fosse già accesoabbiamo avuto bisogno di sapere se il televisore fosse già acceso oppure no e lo abbiamo fatto definendo una variabile di istanzaoppure no e lo abbiamo fatto definendo una variabile di istanza (acceso) dentro al controller e mettendo nei metodi del controller(acceso) dentro al controller e mettendo nei metodi del controller la condizionela condizione

if (acceso) {…if (acceso) {…

nel momento in cui colleghiamo il controller al Model questanel momento in cui colleghiamo il controller al Model questa variabile diventa superflua perchè c’è già la proprietà variabile diventa superflua perchè c’è già la proprietà accesoacceso che che ilTelevisore eredita da Elettrodomestico che ha la stessailTelevisore eredita da Elettrodomestico che ha la stessa informazione.informazione.

Quindi andiamo in TVController.m e nel metodo Quindi andiamo in TVController.m e nel metodo initinit togliamo togliamo l’istruzione di inizializzazione di accesol’istruzione di inizializzazione di acceso

acceso=False;acceso=False;negli altri metodi, sostituiamonegli altri metodi, sostituiamo

if (acceso)if (acceso)concon

Page 134: Xcode4 Tutorial Completo

if (ilTelevisore.acceso)if (ilTelevisore.acceso)cioè testiamo la condizione sulla property cioè testiamo la condizione sulla property accesoacceso di di ilTelevisoreilTelevisore..

Nel metodo Nel metodo accendiSpegniaccendiSpegni, invece usiamo la condizione, invece usiamo la condizione

if ([sender isOn])if ([sender isOn])ricordiamo che ricordiamo che sendersender è istanza di UISwitch ed è l’oggetto che è istanza di UISwitch ed è l’oggetto che invoca l’azione, cioè l’interruttore dell’interfaccia. La classeinvoca l’azione, cioè l’interruttore dell’interfaccia. La classe UISwitch fornisce il metodo UISwitch fornisce il metodo isOnisOn che restituisce Vero se che restituisce Vero se l’interruttore è in posizione On e Falso se è in posizione Off.l’interruttore è in posizione On e Falso se è in posizione Off.

Poi andiamo in TVController.h e cancelliamo la dichiarazionePoi andiamo in TVController.h e cancelliamo la dichiarazione della variabile della variabile accesoacceso..

Eseguiamo il programma per verificare che sia tutto a posto eEseguiamo il programma per verificare che sia tutto a posto e riprendiamo il nostro percorso.riprendiamo il nostro percorso.

Nel capitolo precedente abbiamo creato l’interfaccia verso ilNel capitolo precedente abbiamo creato l’interfaccia verso il nostro televisore, ma rispetto alla versione senza interfaccianostro televisore, ma rispetto alla versione senza interfaccia grafica ci siamo persi un pezzo: il telecomando.grafica ci siamo persi un pezzo: il telecomando.

Attualmente noi abbiamo dei controlli di interfaccia che agisconoAttualmente noi abbiamo dei controlli di interfaccia che agiscono direttamente su un oggetto della classe Televisore…direttamente su un oggetto della classe Televisore…

sapreste dire perché questa affermazione è vera? Per eserciziosapreste dire perché questa affermazione è vera? Per esercizio ripercorrete il percorso dall’evento sull’interfaccia fino al Model.ripercorrete il percorso dall’evento sull’interfaccia fino al Model.

In questo capitolo estenderemo la nostra app per gestire anche unIn questo capitolo estenderemo la nostra app per gestire anche un telecomando ed esploreremo un altro elemento di Cocoa: la Tabtelecomando ed esploreremo un altro elemento di Cocoa: la Tab Bar.Bar.

Ripartiamo dal pattern MVC.Ripartiamo dal pattern MVC.

Model.Model.

L’estensione del Model l’abbiamo già scritta nella versione senzaL’estensione del Model l’abbiamo già scritta nella versione senza interfaccia ma non l’abbiamo ancora portata in questo progetto: èinterfaccia ma non l’abbiamo ancora portata in questo progetto: è la nostra classe Telecomando con le sue relazioni con la classela nostra classe Telecomando con le sue relazioni con la classe Televisore.Televisore.

Con il bottone destro su myTv scegliamo Add Files to…Con il bottone destro su myTv scegliamo Add Files to…

Page 135: Xcode4 Tutorial Completo

navighiamo il disco fino a trovare la cartella del nostro vecchionavighiamo il disco fino a trovare la cartella del nostro vecchio progetto e selezioniamo i due file Telecomando.h eprogetto e selezioniamo i due file Telecomando.h e Telecomando.m.Telecomando.m.

Vedremo in seguito se abbiamo bisogno di qualche adeguamento.Vedremo in seguito se abbiamo bisogno di qualche adeguamento.

View.View.

Dobbiamo decidere come realizzare l’interfaccia Utente per ilDobbiamo decidere come realizzare l’interfaccia Utente per il telecomando. La definizione di un’interfaccia Utente segue moltotelecomando. La definizione di un’interfaccia Utente segue molto il gusto personale, vediamo alcune possibilità.il gusto personale, vediamo alcune possibilità.

La prima è quella di mantenere l’interfaccia Utente attuale eLa prima è quella di mantenere l’interfaccia Utente attuale e spostare il flusso degli eventi di interfaccia verso il telecomandospostare il flusso degli eventi di interfaccia verso il telecomando invece che direttamente verso il televisore. Cioè, ad esempio,invece che direttamente verso il televisore. Cioè, ad esempio, quando l’interruttore si sposta si invocano i metodi di accendi equando l’interruttore si sposta si invocano i metodi di accendi e spegni del telecomando invece che quelli del televisore. Questospegni del telecomando invece che quelli del televisore. Questo però cosa comporterebbe? Che perdiamo la possibilità diperò cosa comporterebbe? Che perdiamo la possibilità di comandare direttamente il televisore, è come se avessimo a casacomandare direttamente il televisore, è come se avessimo a casa un televisore senza comandi, ma con la sola possibilità di essereun televisore senza comandi, ma con la sola possibilità di essere pilotato da un telecomando.pilotato da un telecomando.

Una seconda possibilità, è quella di inserire nella vista attuale,Una seconda possibilità, è quella di inserire nella vista attuale, oltre ai controlli già presenti verso il televisore, anche altrioltre ai controlli già presenti verso il televisore, anche altri controlli che leghiamo al telecomando; eventualmentecontrolli che leghiamo al telecomando; eventualmente ingrandendo la view e facendola diventare una scrollView.ingrandendo la view e facendola diventare una scrollView.

Una terza possibilità, quella che scegliamo, è quella di definireUna terza possibilità, quella che scegliamo, è quella di definire una vista dedicata ai controlli per il telecomando.una vista dedicata ai controlli per il telecomando.

Page 136: Xcode4 Tutorial Completo
Page 137: Xcode4 Tutorial Completo
Page 138: Xcode4 Tutorial Completo

Poi metteremo insieme le due viste in un TabBar. La TabBar è unaPoi metteremo insieme le due viste in un TabBar. La TabBar è una forma di aggregazione delle viste, come quella utilizzata dall’appforma di aggregazione delle viste, come quella utilizzata dall’app iPod, nella quale in basso è presente una barra di selettori (Tab)iPod, nella quale in basso è presente una barra di selettori (Tab)

ognuno dei quali fa accedere ad una vista diversa.ognuno dei quali fa accedere ad una vista diversa.

Avremo quindi una cosa del tipoAvremo quindi una cosa del tipo

Page 139: Xcode4 Tutorial Completo

Con la tab bar potremo spostarci tra la vista con i controlli delCon la tab bar potremo spostarci tra la vista con i controlli del telecomando e la vista con il televisore.telecomando e la vista con il televisore.

Iniziamo quindi a creare la View per il nostro telecomando. Con ilIniziamo quindi a creare la View per il nostro telecomando. Con il bottone destro sulla cartella myTv in XCode, scegliamo New File,bottone destro sulla cartella myTv in XCode, scegliamo New File, nella sezione iOS selezioniamo il gruppo User Interface enella sezione iOS selezioniamo il gruppo User Interface e scegliamo il template View; nella finestra successiva scegliamoscegliamo il template View; nella finestra successiva scegliamo iPhone come device e infine salviamolo con il nomeiPhone come device e infine salviamolo con il nome TelecomandoView.TelecomandoView.

Se è necessario apriamo, selezionandolo, il fileSe è necessario apriamo, selezionandolo, il file TelecomandoView.xib appena creato. Abbiamo la ViewTelecomandoView.xib appena creato. Abbiamo la View contenitore nella quale andremo a disporre i controlli delcontenitore nella quale andremo a disporre i controlli del telecomando.telecomando.

Page 140: Xcode4 Tutorial Completo

Nella Library degli oggetti (pannello inferiore del frame di destra)Nella Library degli oggetti (pannello inferiore del frame di destra) prendiamo un Round Rect Button e posizioniamolo in alto alprendiamo un Round Rect Button e posizioniamolo in alto al centro della View. Con un doppio click sul bottone andiamo in editcentro della View. Con un doppio click sul bottone andiamo in edit del testo e scriviamo On/Off. Con questo controllo comanderemodel testo e scriviamo On/Off. Con questo controllo comanderemo l’accensione e lo spegnimento del televisore attraverso ill’accensione e lo spegnimento del televisore attraverso il telecomando.telecomando.

Nella Library prendiamo un SegmentedNella Library prendiamo un Segmented Control e posizioniamolo Control e posizioniamolo sotto il Button. Nel pannello Attributes del’Inspector mettiamo 5sotto il Button. Nel pannello Attributes del’Inspector mettiamo 5 in Segments, e poi con un doppio click su ogni segmentoin Segments, e poi con un doppio click su ogni segmento scriviamo i numeri da 1 a 5. Risistemiamo la dimensione delscriviamo i numeri da 1 a 5. Risistemiamo la dimensione del controllo se necessario. Con questo controllo comanderemo ilcontrollo se necessario. Con questo controllo comanderemo il passaggio diretto da un canale all’altro. Ma, questa volta, ilpassaggio diretto da un canale all’altro. Ma, questa volta, il funzionamento di questo controllo è di tipo a pulsante, cioè ilfunzionamento di questo controllo è di tipo a pulsante, cioè il bottone non resta selezionato una volta premuto. Per ottenerebottone non resta selezionato una volta premuto. Per ottenere questo comportamento selezioniamo il controllo e nel pannelloquesto comportamento selezioniamo il controllo e nel pannello Attributes dell’Inspector attiviamo il flag State Momentary; poiAttributes dell’Inspector attiviamo il flag State Momentary; poi togliamo il flag Selected alla voce Behavior contogliamo il flag Selected alla voce Behavior con il segmento il segmento Segment 0 selezionato.Segment 0 selezionato.

Nella Library, prendiamo una Label e posizioniamolo sotto ilNella Library, prendiamo una Label e posizioniamolo sotto il Segmented Control, con il doppio click sulla Label editiamo ilSegmented Control, con il doppio click sulla Label editiamo il testo e scriviamoci Programma.testo e scriviamoci Programma.

Nella Library prendiamo un Segmented Control e trasciniamoloNella Library prendiamo un Segmented Control e trasciniamolo sotto la Label, con il doppio click su ognuno dei due segmentisotto la Label, con il doppio click su ognuno dei due segmenti scriviamo ‘-‘ sul primo e ‘+’ sul secondo. Con questo controlloscriviamo ‘-‘ sul primo e ‘+’ sul secondo. Con questo controllo passeremo da un canale al successivo o al precedente. Anche ilpasseremo da un canale al successivo o al precedente. Anche il funzionamento di questo controllo è di tipo a pulsante, cioè ilfunzionamento di questo controllo è di tipo a pulsante, cioè il bottone non resta selezionato una volta premuto. Per ottenerebottone non resta selezionato una volta premuto. Per ottenere questo comportamento selezioniamo il controllo e nel pannelloquesto comportamento selezioniamo il controllo e nel pannello Attributes dell’Inspector attiviamo il flag State Momentary; poiAttributes dell’Inspector attiviamo il flag State Momentary; poi togliamo il flag Selected alla voce Behavior contogliamo il flag Selected alla voce Behavior con il segmento il segmento Segment 0 selezionato.Segment 0 selezionato.

Nella Library, prendiamo una Label e posizioniamolo sotto ilNella Library, prendiamo una Label e posizioniamolo sotto il Segmented Control, con il doppio click sulla Label editiamo ilSegmented Control, con il doppio click sulla Label editiamo il

Page 141: Xcode4 Tutorial Completo

testo e scriviamoci Volume.testo e scriviamoci Volume.

Nella Library prendiamo un Segmented Control e trasciniamoloNella Library prendiamo un Segmented Control e trasciniamolo sotto la Label, con il doppio click su ognuno dei due segmentisotto la Label, con il doppio click su ognuno dei due segmenti scriviamo ‘-‘ sul primo e ‘+’ sul secondo. Con questo controlloscriviamo ‘-‘ sul primo e ‘+’ sul secondo. Con questo controllo alzeremo o abbasseremo il volume di 1 alla volta. Anche ilalzeremo o abbasseremo il volume di 1 alla volta. Anche il funzionamento di questo controllo è di tipo a pulsante, cioè ilfunzionamento di questo controllo è di tipo a pulsante, cioè il bottone non resta selezionato una volta premuto. Per ottenerebottone non resta selezionato una volta premuto. Per ottenere questo comportamento selezioniamo il controllo e nel pannelloquesto comportamento selezioniamo il controllo e nel pannello Attributes dell’Inspector attiviamo il flag State Momentary; poiAttributes dell’Inspector attiviamo il flag State Momentary; poi togliamo il flag Selected alla voce Behavior contogliamo il flag Selected alla voce Behavior con il segmento il segmento Segment 0 selezionato.Segment 0 selezionato.

Alla fine dovreste avere qualcosa di simile alla figuraAlla fine dovreste avere qualcosa di simile alla figura

Page 142: Xcode4 Tutorial Completo

ControlControl

Dobbiamo implementare un controller per gestire gli eventi suDobbiamo implementare un controller per gestire gli eventi su questa interfaccia.questa interfaccia.

Cliccando con il bottone destro sulla cartella myTv in XCodeCliccando con il bottone destro sulla cartella myTv in XCode scegliamo il template UIViewController subclass nel grupposcegliamo il template UIViewController subclass nel gruppo Cocoa Touch. Lasciamo UIViewController come Superclasse eCocoa Touch. Lasciamo UIViewController come Superclasse e deselezioniamo deselezioniamo targeted for iPadtargeted for iPad e e With XIB for user interfaceWith XIB for user interface dal dal momento che noi lo XIB lo abbiamo già creato. Salviamolo con ilmomento che noi lo XIB lo abbiamo già creato. Salviamolo con il nome TCController.nome TCController.

Analizziamo ora che tipo di connessione dobbiamo stabilire tra ilAnalizziamo ora che tipo di connessione dobbiamo stabilire tra il controller e l’interfacciacontroller e l’interfaccia

Abbiamo un Button che deve comunicare al controller l’eventoAbbiamo un Button che deve comunicare al controller l’evento che è stato premuto (Action).che è stato premuto (Action).

Page 143: Xcode4 Tutorial Completo

Abbiamo un Segmented Control che deve comunicare al controllerAbbiamo un Segmented Control che deve comunicare al controller il numero del canale (Action).il numero del canale (Action).

Abbiamo un Segmented Control che deve richiedere al controllerAbbiamo un Segmented Control che deve richiedere al controller di passare al programma successivo o a quello precedentedi passare al programma successivo o a quello precedente (Action).(Action).

Abbiamo un Segmented Control che deve richiedere al controllerAbbiamo un Segmented Control che deve richiedere al controller di aumentare o diminuire il volume (Action).di aumentare o diminuire il volume (Action).

Bene, quindi dobbiamo definire dei metodi cui collegare gliBene, quindi dobbiamo definire dei metodi cui collegare gli oggetti dell’interfaccia, mentre non abbiamo bisogno di Outlet,oggetti dell’interfaccia, mentre non abbiamo bisogno di Outlet, cioè di stabilire dei collegamenti dal controller verso l’interfaccia.cioè di stabilire dei collegamenti dal controller verso l’interfaccia.

In TCController.h dichiariamo i metodiIn TCController.h dichiariamo i metodi

-(IBAction)accendiSpegni:(UIButton *)sender;-(IBAction)accendiSpegni:(UIButton *)sender;-(IBAction)cambiaCanale:(UISegmentedControl *)sender;-(IBAction)cambiaCanale:(UISegmentedControl *)sender;-(IBAction)incrementaDecrementaProgramma:-(IBAction)incrementaDecrementaProgramma:(UISegmentedControl *)sender;(UISegmentedControl *)sender;-(IBAction)incrementaDecrementaVolume:(UISegmentedControl-(IBAction)incrementaDecrementaVolume:(UISegmentedControl *)sender;*)sender;e dichiariamo la property verso il Modele dichiariamo la property verso il Model

@property(nonatomic,retain) Telecomando *ilTelecomando;@property(nonatomic,retain) Telecomando *ilTelecomando;dobbiamo far conoscere alla nostra classe controller, la classedobbiamo far conoscere alla nostra classe controller, la classe Telecomando che stiamo usando con il solitoTelecomando che stiamo usando con il solito

#import "Telecomando.h"#import "Telecomando.h"messo in testa a Telecomando.h.messo in testa a Telecomando.h.

Andiamo ora definire i nostri metodi in TCController.m.Andiamo ora definire i nostri metodi in TCController.m.

Prima cosa, visto che abbiamo una property, scriviamoPrima cosa, visto che abbiamo una property, scriviamo

@synthesize ilTelecomando;@synthesize ilTelecomando;vi ricordo che con questa istruzione diciamo ad XCode divi ricordo che con questa istruzione diciamo ad XCode di preparare (automaticamente) i metodi di accesso alla propertypreparare (automaticamente) i metodi di accesso alla property (getter e setter); la usiamo tutte le volte che non abbiamo bisogno(getter e setter); la usiamo tutte le volte che non abbiamo bisogno di personalizzare i metodi di accesso.di personalizzare i metodi di accesso.

Page 144: Xcode4 Tutorial Completo

Passiamo ad implementare il metodoPassiamo ad implementare il metodo

-(IBAction)accendiSpegni:(UIButton *)sender {-(IBAction)accendiSpegni:(UIButton *)sender {

}}questo metodo sarà invocato ogni volta che il bottone vienequesto metodo sarà invocato ogni volta che il bottone viene premuto; se guardiamo il nostro Model, cioè la classepremuto; se guardiamo il nostro Model, cioè la classe Telecomando vediamo che abbiamo due metodiTelecomando vediamo che abbiamo due metodi (accendiIlTelevisore e spegniIlTelevisore) che dovranno essere(accendiIlTelevisore e spegniIlTelevisore) che dovranno essere richiamati da questo a seconda dell’azione da fare.richiamati da questo a seconda dell’azione da fare.

Abbiamo quindi bisogno di un’istruzione condizionaleAbbiamo quindi bisogno di un’istruzione condizionale

ifif (condizione) { (condizione) {

<blocco Vero><blocco Vero>

} else {} else {

<blocco Falso><blocco Falso>

}}

dobbiamo allora sapere se il televisore è acceso o spento perdobbiamo allora sapere se il televisore è acceso o spento per capire se ora lo stiamo spegnendo o accendendo; ma, a differenzacapire se ora lo stiamo spegnendo o accendendo; ma, a differenza del controller TVController che parla con il televisore e quindi hadel controller TVController che parla con il televisore e quindi ha accesso alle sue property, il nostro TCController parla con ilaccesso alle sue property, il nostro TCController parla con il telecomando; tuttavia andando ad esaminare bene la classetelecomando; tuttavia andando ad esaminare bene la classe Telecomando vediamo che ha una property (Telecomando vediamo che ha una property (myTvmyTv) che si riferisce) che si riferisce al televisore pilotato, possiamo quindi verificarne lo statoal televisore pilotato, possiamo quindi verificarne lo stato attraverso questa property; il nostro metodo sarà quindiattraverso questa property; il nostro metodo sarà quindi

-(IBAction)accendiSpegni:(UIButton *)sender {-(IBAction)accendiSpegni:(UIButton *)sender {if (ilTelecomando.myTv.acceso) {if (ilTelecomando.myTv.acceso) {[ilTelecomando spegniIlTelevisore];[ilTelecomando spegniIlTelevisore];} else {} else {[ilTelecomando accendiIlTelevisore];[ilTelecomando accendiIlTelevisore];}}

Page 145: Xcode4 Tutorial Completo

}}osservate che nel test della condizione abbiamo acceduto ad unaosservate che nel test della condizione abbiamo acceduto ad una property della classe Televisore, in maniera analoga avremmoproperty della classe Televisore, in maniera analoga avremmo potuto scriverepotuto scrivere

-(IBAction)accendiSpegni:(UIButton *)sender {-(IBAction)accendiSpegni:(UIButton *)sender {if (ilTelecomando.myTv.acceso) {if (ilTelecomando.myTv.acceso) {[ilTelecomando.myTv spegni];[ilTelecomando.myTv spegni];} else {} else {[ilTelecomando.myTv accendi];[ilTelecomando.myTv accendi];}}}}cioè avremmo potuto invocare direttamente anche i metodi dicioè avremmo potuto invocare direttamente anche i metodi di accensione e spegnimento che la classe Televisore eredita daaccensione e spegnimento che la classe Televisore eredita da Elettrodomestico; cioè dal TCController avremmo potutoElettrodomestico; cioè dal TCController avremmo potuto bypassare l’invocazione dei metodi del Telecomando; qual èbypassare l’invocazione dei metodi del Telecomando; qual è giusta e qual è sbagliata? Nessuna delle due è ne giusta nègiusta e qual è sbagliata? Nessuna delle due è ne giusta nè sbagliata dal punto di vista del linguaggio; però a livello Modelsbagliata dal punto di vista del linguaggio; però a livello Model potremmo aver inserito delle istruzioni (di tracciamento, dipotremmo aver inserito delle istruzioni (di tracciamento, di controllo, …) nei metodi di Telecomando che nel secondo casocontrollo, …) nei metodi di Telecomando che nel secondo caso non verrebbero eseguite; valutate quindi di volta in volta qualenon verrebbero eseguite; valutate quindi di volta in volta quale schema utilizzare.schema utilizzare.

Passiamo al metodo successivoPassiamo al metodo successivo

-(IBAction)cambiaCanale:(UISegmentedControl *)sender {-(IBAction)cambiaCanale:(UISegmentedControl *)sender {if (ilTelecomando.myTv.acceso) {if (ilTelecomando.myTv.acceso) {[ilTelecomando vaiAlCanale:[ilTelecomando vaiAlCanale:(NSDecimalNumber *)[NSDecimalNumber(NSDecimalNumber *)[NSDecimalNumber numberWithInt:sender.selectedSegmentIndex]];numberWithInt:sender.selectedSegmentIndex]];}}}}Prima cosa controlliamo che il televisore sia acceso, se lo è l’unicaPrima cosa controlliamo che il televisore sia acceso, se lo è l’unica cosa che facciamo è invocare il metodo vaiAlCanale dell’istanzacosa che facciamo è invocare il metodo vaiAlCanale dell’istanza ilTelecomando passandogli l’indice del selettore del canaleilTelecomando passandogli l’indice del selettore del canale selezionato trasformato in NSDecimalNumber; la discussione sulselezionato trasformato in NSDecimalNumber; la discussione sul

Page 146: Xcode4 Tutorial Completo

modo con il quale creiamo il NSDecimalNumber e sul typemodo con il quale creiamo il NSDecimalNumber e sul type casting l’abbiamo già vista nel cap. precedente a proposito delcasting l’abbiamo già vista nel cap. precedente a proposito del metodo regolaVolume e vi rimando a quella parte se necessario.metodo regolaVolume e vi rimando a quella parte se necessario.

Passiamo al metodo con il quale ci spostiamo sui canali di 1 perPassiamo al metodo con il quale ci spostiamo sui canali di 1 per voltavolta

-(IBAction)incrementaDecrementaProgramma:-(IBAction)incrementaDecrementaProgramma:(UISegmentedControl *)sender {(UISegmentedControl *)sender {

}}In questo metodo dobbiamo come prima cosa sapere se dobbiamoIn questo metodo dobbiamo come prima cosa sapere se dobbiamo passare al precedente (bottone -, segmento 0) o al successivopassare al precedente (bottone -, segmento 0) o al successivo (bottone +, segmento 1).(bottone +, segmento 1).

Introduciamo un nuovo statement chiamato switch/case. E’ unaIntroduciamo un nuovo statement chiamato switch/case. E’ una forma di istruzione condizionale a più condizioni che ha laforma di istruzione condizionale a più condizioni che ha la strutturastruttura

switch (espressione) {switch (espressione) {

case valore1:case valore1:

……

break;break;

case valore2:case valore2:

……

break;break;

case valore3:case valore3:

……

break;break;

……

default:default:

Page 147: Xcode4 Tutorial Completo

……

break;break;

}}

Il suo comportamento è: si valuta l’espressione (deve essereIl suo comportamento è: si valuta l’espressione (deve essere un’espressione che restituisce un valore intero) e se il risultato èun’espressione che restituisce un valore intero) e se il risultato è valore1valore1 si eseguono le istruzioni che si trovano dopo si eseguono le istruzioni che si trovano dopo valore1:valore1: fino fino al primo break incontrato; se il risultato è al primo break incontrato; se il risultato è valore2valore2 si eseguono le si eseguono le istruzioni che si trovano dopo istruzioni che si trovano dopo valore2:valore2: fino al primo break fino al primo break incontrato, e così via; se il risultato non è nessuno dei valoriincontrato, e così via; se il risultato non è nessuno dei valori indicati si eseguono le istruzioni indicati si eseguono le istruzioni dopo dopo defaultdefault. E’ necessario porre. E’ necessario porre attenzione all’uso dei attenzione all’uso dei breakbreak; perché se non si mette un ; perché se non si mette un breakbreak in in una condizione, saranno eseguite anche le istruzioni delleuna condizione, saranno eseguite anche le istruzioni delle condizioni successive fino ad incontrare un condizioni successive fino ad incontrare un breakbreak o la }. o la }.

Con questa istruzione il nostro metodo inizia ad assumereCon questa istruzione il nostro metodo inizia ad assumere l’aspettol’aspetto

-(IBAction)incrementaDecrementaProgramma:-(IBAction)incrementaDecrementaProgramma:(UISegmentedControl *)sender {(UISegmentedControl *)sender {switch (sender.selectedSegmentIndex) {switch (sender.selectedSegmentIndex) {case 0:case 0:

break;break;case 1:case 1:

break;break;default:default:

Page 148: Xcode4 Tutorial Completo

break;break;}}}}Abbiamo già visto che Abbiamo già visto che selectedSegmentIndexselectedSegmentIndex, è una property di, è una property di UISegmentedControl, che restituisce l’indice del selettoreUISegmentedControl, che restituisce l’indice del selettore selezionato. Sulla base dell’indice selezionato, se è stato premutoselezionato. Sulla base dell’indice selezionato, se è stato premuto il primo tasto viene eseguito il gruppo case 0, dove gestiremo ilil primo tasto viene eseguito il gruppo case 0, dove gestiremo il passaggio al canale precedente; mentre se è stato premuto ilpassaggio al canale precedente; mentre se è stato premuto il secondo tasto viene eseguito il gruppo case 1 dove gestiremo ilsecondo tasto viene eseguito il gruppo case 1 dove gestiremo il passaggio al canale successivo.passaggio al canale successivo.

Prima di fare la variazione di canale dobbiamo però verificare sePrima di fare la variazione di canale dobbiamo però verificare se questa è possibile cioè dobbiamo sapere se siamo già al numeroquesta è possibile cioè dobbiamo sapere se siamo già al numero minimo o massimo di canale e nel caso non fare nulla; altrimentiminimo o massimo di canale e nel caso non fare nulla; altrimenti passeremo al canale corretto. La prima informazione di cuipasseremo al canale corretto. La prima informazione di cui abbiamo bisogno è il numero del canale sintonizzato attualmente;abbiamo bisogno è il numero del canale sintonizzato attualmente; questa informazione è una property del televisore e quindi laquesta informazione è una property del televisore e quindi la recupereremo attraversorecupereremo attraverso

ilTelecomando.myTv.canaleSintonizzatoilTelecomando.myTv.canaleSintonizzato

cioè attraverso la property myTv di ilTelecomando accediamo alcioè attraverso la property myTv di ilTelecomando accediamo al televisore pilotato e da questo possiamo accedere alla propertytelevisore pilotato e da questo possiamo accedere alla property canaleSintonizzatocanaleSintonizzato..

Aggiungiamo quindi il controllo sui limiti del numero di canale alAggiungiamo quindi il controllo sui limiti del numero di canale al nostro metodo che diventanostro metodo che diventa

-(IBAction)incrementaDecrementaProgramma:-(IBAction)incrementaDecrementaProgramma:(UISegmentedControl *)sender {(UISegmentedControl *)sender {switch (sender.selectedSegmentIndex) {switch (sender.selectedSegmentIndex) {case 0:case 0:if ([ilTelecomando.myTv.canaleSintonizzato intValue] > 0) {if ([ilTelecomando.myTv.canaleSintonizzato intValue] > 0) {

}}break;break;

Page 149: Xcode4 Tutorial Completo

case 1:case 1:if ([ilTelecomando.myTv.canaleSintonizzato intValue] < 4) {if ([ilTelecomando.myTv.canaleSintonizzato intValue] < 4) {

}}break;break;default:default:

break;break;}}}}Nel case 0 abbiamo introdotto il testNel case 0 abbiamo introdotto il test

[ilTelecomando.myTv.canaleSintonizzato intValue] > 0[ilTelecomando.myTv.canaleSintonizzato intValue] > 0

con con ilTelecomando.myTv.canaleSintonizzato,ilTelecomando.myTv.canaleSintonizzato, come abbiamo detto come abbiamo detto sopra, accediamo alla property sopra, accediamo alla property canaleSintonizzatocanaleSintonizzato della classe della classe Televisore; questa property referenzia un’istanza diTelevisore; questa property referenzia un’istanza di NSDecimalNumber (sottoclasse di NSNumber) può quindiNSDecimalNumber (sottoclasse di NSNumber) può quindi ricevere l’invocazione del metodo intValue (ereditato daricevere l’invocazione del metodo intValue (ereditato da NSNumber ) che restituisce il valore intero rappresentatoNSNumber ) che restituisce il valore intero rappresentato dall’istanza stessa. A questo punto controlliamo che il valore siadall’istanza stessa. A questo punto controlliamo che il valore sia maggiore (>) di 0, che vuol dire che possiamo ancora diminuire ilmaggiore (>) di 0, che vuol dire che possiamo ancora diminuire il numero del canale.numero del canale.

In maniera analoga, nel case 1, verifichiamoIn maniera analoga, nel case 1, verifichiamo

[ilTelecomando.myTv.canaleSintonizzato intValue] < 4[ilTelecomando.myTv.canaleSintonizzato intValue] < 4

che il valore sia minore (<) di 4, che vuol dire che possiamoche il valore sia minore (<) di 4, che vuol dire che possiamo ancora aumentare il numero del canale.ancora aumentare il numero del canale.

Iniziamo ora a gestire il case 0, cioè dobbiamo passare al canaleIniziamo ora a gestire il case 0, cioè dobbiamo passare al canale precedente. Andando a guardare il nostro Model, vediamo che laprecedente. Andando a guardare il nostro Model, vediamo che la classe Telecomando non ha metodi di incremento e decrementoclasse Telecomando non ha metodi di incremento e decremento canali ma ha solo il metodo vaiAlCanale che prende comecanali ma ha solo il metodo vaiAlCanale che prende come parametro il numero del canale da sintonizzare.parametro il numero del canale da sintonizzare.

Potremmo decidere di estendere il modello con una coppia diPotremmo decidere di estendere il modello con una coppia di

Page 150: Xcode4 Tutorial Completo

metodi di incremento e decremento canali oppure di continuare admetodi di incremento e decremento canali oppure di continuare ad usare il solo metodo già disponibile, e noi adottiamo questa scelta.usare il solo metodo già disponibile, e noi adottiamo questa scelta.

Quello che dovremo scrivere sarà dunque qualcosa del tipoQuello che dovremo scrivere sarà dunque qualcosa del tipo

[ilTelecomando vaiAlCanale:…];[ilTelecomando vaiAlCanale:…];

con al posto dei … un NSDecimalNumber che rappresenti uncon al posto dei … un NSDecimalNumber che rappresenti un valore inferiore di 1 rispetto a quello attualmente selezionato.valore inferiore di 1 rispetto a quello attualmente selezionato.

Osserviamo che Osserviamo che ilTelecomando.myTv.canaleSintonizzatoilTelecomando.myTv.canaleSintonizzato è è l’istanza di NSDecimalNumber che rappresenta il valorel’istanza di NSDecimalNumber che rappresenta il valore correntemente sintonizzato, andando sulla documentazione acorrentemente sintonizzato, andando sulla documentazione a guardare la classe NSDecimalNUmebr vediamo che è disponibileguardare la classe NSDecimalNUmebr vediamo che è disponibile un metodo di istanza (decimalNumberBySubtracting) per sottrarreun metodo di istanza (decimalNumberBySubtracting) per sottrarre un NSDecimalNumber da un altro; quello che dobbiamo costruireun NSDecimalNumber da un altro; quello che dobbiamo costruire quindi èquindi è

[ilTelecomando.myTv.canaleSintonizzato[ilTelecomando.myTv.canaleSintonizzato decimalNumberBySubtracting:…]decimalNumberBySubtracting:…]

con al posto dei … un NSDecimalNumber che rappresenti ilcon al posto dei … un NSDecimalNumber che rappresenti il valore 1; osservando ancora la classe NSDecimalNumber notiamovalore 1; osservando ancora la classe NSDecimalNumber notiamo il metodo di classe il metodo di classe oneone, questo , questo metodo (che tra l’altro abbiamo giàmetodo (che tra l’altro abbiamo già usato) restituisce proprio l’oggetto NSDecimalNumber cheusato) restituisce proprio l’oggetto NSDecimalNumber che rappresenta il valore 1; quindi l’espressione per ottenererappresenta il valore 1; quindi l’espressione per ottenere un’istanza di NSDecimalNumber che rappresenta il canaleun’istanza di NSDecimalNumber che rappresenta il canale precedente saràprecedente sarà

[ilTelecomando.myTv.canaleSintonizzato[ilTelecomando.myTv.canaleSintonizzato decimalNumberBySubtracting:[NSDecimalNumber one]]decimalNumberBySubtracting:[NSDecimalNumber one]]

e questo sarà l’argomento del metodo vaiAlCanalee questo sarà l’argomento del metodo vaiAlCanale

case 0:case 0:if ([ilTelecomando.myTv.canaleSintonizzato intValue] > 0) {if ([ilTelecomando.myTv.canaleSintonizzato intValue] > 0) {[ilTelecomando vaiAlCanale:[ilTelecomando vaiAlCanale:[ilTelecomando.myTv.canaleSintonizzato[ilTelecomando.myTv.canaleSintonizzatodecimalNumberBySubtracting:[NSDecimalNumber one]]];decimalNumberBySubtracting:[NSDecimalNumber one]]];

Page 151: Xcode4 Tutorial Completo

}}break;break;In maniera analoga, in case 1, usiamo la stessa istruzioneIn maniera analoga, in case 1, usiamo la stessa istruzione sostituendo però al metodo sostituendo però al metodo decimalNumberBySubtractingdecimalNumberBySubtracting il il metodo metodo decimalNumberByAddingdecimalNumberByAdding, per sommare 1 al canale, per sommare 1 al canale corrente.corrente.

Il metodo diventa quindiIl metodo diventa quindi

-(IBAction)incrementaDecrementaProgramma:-(IBAction)incrementaDecrementaProgramma:(UISegmentedControl *)sender {(UISegmentedControl *)sender {if (ilTelecomando.myTv.acceso) {if (ilTelecomando.myTv.acceso) {switch (sender.selectedSegmentIndex) {switch (sender.selectedSegmentIndex) {case 0:case 0:if ([ilTelecomando.myTv.canaleSintonizzato intValue] > 0) {if ([ilTelecomando.myTv.canaleSintonizzato intValue] > 0) {[ilTelecomando vaiAlCanale:[ilTelecomando vaiAlCanale:[ilTelecomando.myTv.canaleSintonizzato[ilTelecomando.myTv.canaleSintonizzatodecimalNumberBySubtracting:[NSDecimalNumber one]]];decimalNumberBySubtracting:[NSDecimalNumber one]]];}}break;break;case 1:case 1:if ([ilTelecomando.myTv.canaleSintonizzato intValue] < 4) {if ([ilTelecomando.myTv.canaleSintonizzato intValue] < 4) {[ilTelecomando vaiAlCanale:[ilTelecomando vaiAlCanale:[ilTelecomando.myTv.canaleSintonizzato[ilTelecomando.myTv.canaleSintonizzatodecimalNumberByAdding:[NSDecimalNumber one]]];decimalNumberByAdding:[NSDecimalNumber one]]];}}break;break;default:default:break;break;}}}}}}Osservate che ho inserito anche qui il controllo se il televisore èOsservate che ho inserito anche qui il controllo se il televisore è acceso.acceso.

Teniamo per ora questa come versione finale, anche se presenta unTeniamo per ora questa come versione finale, anche se presenta un

Page 152: Xcode4 Tutorial Completo

problema che per ora non vi segnalo.problema che per ora non vi segnalo.

Passiamo al metodo con il quale aumentiamo o diminuiamo ilPassiamo al metodo con il quale aumentiamo o diminuiamo il volume di 1 per voltavolume di 1 per volta

-(IBAction)incrementaDecrementaVolume:(UISegmentedControl-(IBAction)incrementaDecrementaVolume:(UISegmentedControl *)sender {*)sender {

}}Anche in questo metodo dobbiamo come prima cosa sapere seAnche in questo metodo dobbiamo come prima cosa sapere se dobbiamo diminuire (bottone -, segmento 0) o aumentare (bottonedobbiamo diminuire (bottone -, segmento 0) o aumentare (bottone +, segmento 1) il volume.+, segmento 1) il volume.

Anche qui useremo lo statement switch/case.Anche qui useremo lo statement switch/case.

-(IBAction)incrementaDecrementaVolume:(UISegmentedControl-(IBAction)incrementaDecrementaVolume:(UISegmentedControl *)sender {*)sender {switch (sender.selectedSegmentIndex) {switch (sender.selectedSegmentIndex) {case 0:case 0:break;break;case 1:case 1:break;break;default:default:break;break;}}}}In questo caso non abbiamo bisogno di verificare che il livello delIn questo caso non abbiamo bisogno di verificare che il livello del volume si mantenga entro i limiti perché questo è già garantito dalvolume si mantenga entro i limiti perché questo è già garantito dal Model nei metodi Model nei metodi alzaIlVolmealzaIlVolme e e abbassaIlVolumeabbassaIlVolume della classe della classe Televisore.Televisore.

Inoltre la classe Telecomando espone già due metodiInoltre la classe Telecomando espone già due metodi alzaIlVolumeDelTelevisorealzaIlVolumeDelTelevisore e e abbassaIlVolumeDelTelevisoreabbassaIlVolumeDelTelevisore che che possiamo invocare in risposta ai due eventi di interfaccia, quindi ilpossiamo invocare in risposta ai due eventi di interfaccia, quindi il nostro metodo diventanostro metodo diventa

-(IBAction)incrementaDecrementaVolume:(UISegmentedControl-(IBAction)incrementaDecrementaVolume:(UISegmentedControl *)sender {*)sender {

Page 153: Xcode4 Tutorial Completo

if (ilTelecomando.myTv.acceso) {if (ilTelecomando.myTv.acceso) {switch (sender.selectedSegmentIndex) {switch (sender.selectedSegmentIndex) {case 0:case 0:[ilTelecomando abbassaIlVolumeDelTelevisore];[ilTelecomando abbassaIlVolumeDelTelevisore];break;break;case 1:case 1:[ilTelecomando alzaIlVolumeDelTelevisore];[ilTelecomando alzaIlVolumeDelTelevisore];break;break;default:default:break;break;}}}}}}con il solito inserimento del controllo sul televisore acceso.con il solito inserimento del controllo sul televisore acceso.

Abbiamo così finito di definire il controller (per ora).Abbiamo così finito di definire il controller (per ora).

Andiamo ora a stabilire i collegamenti con l’interfaccia. ApriamoAndiamo ora a stabilire i collegamenti con l’interfaccia. Apriamo il file TelecomandoView.xib e selezioniamo File’s owner nelil file TelecomandoView.xib e selezioniamo File’s owner nel gruppo placeholders. Vi ricordo che questo è il segnaposto che a sigruppo placeholders. Vi ricordo che questo è il segnaposto che a si riferirà al nostro controller, quindi la prima cosa da fare è dire diriferirà al nostro controller, quindi la prima cosa da fare è dire di che classe sarà il nostro controller. Nel pannello Identityche classe sarà il nostro controller. Nel pannello Identity dell’Inspector, nel campo Class scegliamo la classe TCController.dell’Inspector, nel campo Class scegliamo la classe TCController.

Il primo collegamento da fare è quello per dire quale View èIl primo collegamento da fare è quello per dire quale View è gestita da questo controller, clicchiamo con il bottone destro sugestita da questo controller, clicchiamo con il bottone destro su File’s owner e nella finestra che si apre colleghiamo l’Outlet viewFile’s owner e nella finestra che si apre colleghiamo l’Outlet view con la view generale dell’interfaccia, cioè quella che contiene tutticon la view generale dell’interfaccia, cioè quella che contiene tutti gli altri controlli.gli altri controlli.

A questo punto trasciniamo, con il bottone destro premuto, ilA questo punto trasciniamo, con il bottone destro premuto, il mouse dal Button a File’s owner e scegliamo mouse dal Button a File’s owner e scegliamo accendiSpegniaccendiSpegni (dovrebbe esservi proposto solo questo); facciamo lo stesso dal(dovrebbe esservi proposto solo questo); facciamo lo stesso dal selettore dei canali e dagli altri due controlli e scegliamo di voltaselettore dei canali e dagli altri due controlli e scegliamo di volta in volta il metodo che vogliamo che sia eseguito per gestirein volta il metodo che vogliamo che sia eseguito per gestire l’evento corrispondente.l’evento corrispondente.

Page 154: Xcode4 Tutorial Completo

Abbiamo così completato la prima parte dell’estensione, cioè ilAbbiamo così completato la prima parte dell’estensione, cioè il pattern MVC per gestire il telecomando. Adesso dobbiamopattern MVC per gestire il telecomando. Adesso dobbiamo mettere insieme la gestione del televisore con la gestione delmettere insieme la gestione del televisore con la gestione del telecomando in un TabBar.telecomando in un TabBar.

Andiamo in MainWindow.xib; in questo momento ilAndiamo in MainWindow.xib; in questo momento il rootViewController è quello del televisore e a noi non va bene.rootViewController è quello del televisore e a noi non va bene. Clicchiamo con il destro sull’oggetto Window e nel popUp cheClicchiamo con il destro sull’oggetto Window e nel popUp che appare clicchiamo sulla x della property rootViewController inappare clicchiamo sulla x della property rootViewController in modo da sganciare il controller attualmente presente. A questomodo da sganciare il controller attualmente presente. A questo punto cancelliamo, nell’area Objects, l’oggetto viewController chepunto cancelliamo, nell’area Objects, l’oggetto viewController che avevamo inserito precedentemente e che era usato comeavevamo inserito precedentemente e che era usato come rootViewController.rootViewController.

Nella Library scegliamo Tab Bar Controller e trasciniamoloNella Library scegliamo Tab Bar Controller e trasciniamolo nell’area Objects sotto Window. A questo punto con il bottonenell’area Objects sotto Window. A questo punto con il bottone destro su Window riapriamo il popUp e colleghiamodestro su Window riapriamo il popUp e colleghiamo rootViewController al Tab Bar Controller.rootViewController al Tab Bar Controller.

Selezioniamo l’oggetto Tab Bar Controller e sarà visualizzata laSelezioniamo l’oggetto Tab Bar Controller e sarà visualizzata la View con la Tab Bar in basso, con due elementi. Facciamo doppioView con la Tab Bar in basso, con due elementi. Facciamo doppio click sul primo per andare in edit sulla label dell’item eclick sul primo per andare in edit sulla label dell’item e scriviamoci Telecomando, poi facciamo doppio click sul secondoscriviamoci Telecomando, poi facciamo doppio click sul secondo e scriviamoci Televisore. Sono solo nomi che diamo alle sezioni ee scriviamoci Televisore. Sono solo nomi che diamo alle sezioni e non hanno nessuna relazione con le classi che abbiamo creato,non hanno nessuna relazione con le classi che abbiamo creato, quindi potete anche chiamarli in altro modo.quindi potete anche chiamarli in altro modo.

Ora selezionate il primo dei due View Controller contenuti nel TabOra selezionate il primo dei due View Controller contenuti nel Tab Bar Controller (se necessario esplodetelo con il triangolino vicinoBar Controller (se necessario esplodetelo con il triangolino vicino all’oggetto) e nell’Identity Inspector alla voce Class selezionateall’oggetto) e nell’Identity Inspector alla voce Class selezionate TCController. Nell’Attributes Inspector alla voce NIB NameTCController. Nell’Attributes Inspector alla voce NIB Name scrivete TelecomandoView.scrivete TelecomandoView.

Ora selezionate il secondo View Controller e fate la stessa cosaOra selezionate il secondo View Controller e fate la stessa cosa ovviamente dando TVController come Class e TelevisoreViewovviamente dando TVController come Class e TelevisoreView come NIB Name.come NIB Name.

Se ora provate ad eseguire l’applicazione otterrete un errore allaSe ora provate ad eseguire l’applicazione otterrete un errore alla

Page 155: Xcode4 Tutorial Completo

rigariga

((TVController((TVController *)self.window.rootViewController).ilTelevisore=[[Televisore*)self.window.rootViewController).ilTelevisore=[[Televisore alloc] init];alloc] init];del metodo del metodo application:didFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions:, perché?, perché?

Perché ora il nostro rootViewController non è più un’istanza diPerché ora il nostro rootViewController non è più un’istanza di TVController ma di UITabBarController e quindi non conosce laTVController ma di UITabBarController e quindi non conosce la property ilTelevisore. Per ora cancelliamo questa riga per provareproperty ilTelevisore. Per ora cancelliamo questa riga per provare l’interfaccia poi vedremo cosa dobbiamo ancora fare.l’interfaccia poi vedremo cosa dobbiamo ancora fare.

Quindi cancelliamo la riga ed eseguiamo l’app, vediamo cheQuindi cancelliamo la riga ed eseguiamo l’app, vediamo che riusciamo a spostarci da una view all’altra e che possiamo cliccareriusciamo a spostarci da una view all’altra e che possiamo cliccare sui controlli; verifichiamo che i controlli non siano coperti dallasui controlli; verifichiamo che i controlli non siano coperti dalla Tab Bar, nel caso andiamo nel file XIB corrispondente eTab Bar, nel caso andiamo nel file XIB corrispondente e risistemiamoli.risistemiamoli.

Se dal punto di vista dell’interfaccia abbiamo fatto dei passiSe dal punto di vista dell’interfaccia abbiamo fatto dei passi avanti, dal punto di vista funzionale le cose sembrano tornateavanti, dal punto di vista funzionale le cose sembrano tornate indietro, nel senso che i comandi del televisore non sembranoindietro, nel senso che i comandi del televisore non sembrano avere più l’effetto corretto. Questo perché in questo momentoavere più l’effetto corretto. Questo perché in questo momento abbiamo gli oggetti del livello View e gli oggetti del livelloabbiamo gli oggetti del livello View e gli oggetti del livello Control, ma non abbiamo gli oggetti del livello Model. AbbiamoControl, ma non abbiamo gli oggetti del livello Model. Abbiamo le classi (Televisore, Telecomando,…) ma non abbiamo creatole classi (Televisore, Telecomando,…) ma non abbiamo creato alcun oggetto di queste classi. In effetti la riga che abbiamoalcun oggetto di queste classi. In effetti la riga che abbiamo cancellato poco fa faceva esattamente questo, concancellato poco fa faceva esattamente questo, con [[Televisore [[Televisore alloc] init] noi creavamo un’istanza di Televisore e laalloc] init] noi creavamo un’istanza di Televisore e la assegnavamo alla property ilTelevisore di un TVController.assegnavamo alla property ilTelevisore di un TVController.

Dobbiamo fare una cosa analoga.Dobbiamo fare una cosa analoga.

In myTvAppDelegate.h aggiungiamoIn myTvAppDelegate.h aggiungiamo

#import "Telecomando.h"#import "Telecomando.h"#import "TCController.h"#import "TCController.h"in modo che siano conosciute ed utilizzabili.in modo che siano conosciute ed utilizzabili.

Nel metodo Nel metodo application:didFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions: subito subito

Page 156: Xcode4 Tutorial Completo

dopo la { scriviamodopo la { scriviamo

Televisore *tv1=[[Televisore alloc] init];Televisore *tv1=[[Televisore alloc] init];Telecomando *tc1=[[Telecomando alloc ] init];Telecomando *tc1=[[Telecomando alloc ] init];tc1.myTv=tv1;tc1.myTv=tv1;In questo modo abbiamo creato due oggetti tv1 e tc1 istanzeIn questo modo abbiamo creato due oggetti tv1 e tc1 istanze rispettivamente di Televisore e Telecomando. Inoltre abbiamorispettivamente di Televisore e Telecomando. Inoltre abbiamo agganciato il televisore tv1 al telecomando assegnandolo alla suaagganciato il televisore tv1 al telecomando assegnandolo alla sua property myTv.property myTv.

L’ultima cosa che ci resta da fare è assegnare questi due oggettiL’ultima cosa che ci resta da fare è assegnare questi due oggetti alle relative property dei due controller; il punto è proprioalle relative property dei due controller; il punto è proprio questo…come recuperiamo i due controller, visto che li abbiamoquesto…come recuperiamo i due controller, visto che li abbiamo creati nello XIB e non nel codice?creati nello XIB e non nel codice?

Il meccanismo è sempre lo stesso: abbiamo bisogno di far parlareIl meccanismo è sempre lo stesso: abbiamo bisogno di far parlare il codice che scriviamo con oggetti definiti in uno XIB quindiil codice che scriviamo con oggetti definiti in uno XIB quindi usiamo gli Outlet, così come facciamo per gli elementi grafici.usiamo gli Outlet, così come facciamo per gli elementi grafici.

Andiamo in myTvAppDelegate.h ed aggiungiamoAndiamo in myTvAppDelegate.h ed aggiungiamo

@property (nonatomic, retain) IBOutlet TCController@property (nonatomic, retain) IBOutlet TCController *myTCController;*myTCController;@property (nonatomic, retain) IBOutlet TVController@property (nonatomic, retain) IBOutlet TVController *myTVController;*myTVController;Come tutte le property per le quali usiamo i metodi di default, inCome tutte le property per le quali usiamo i metodi di default, in myTvAppDelegate.m, dopo la rigamyTvAppDelegate.m, dopo la riga

@implementation myTvAppDelegate@implementation myTvAppDelegateScriviamoScriviamo

@synthesize myTCController;@synthesize myTCController;@synthesize myTVController;@synthesize myTVController;vi ricordo che in questo modo XCode prepara automaticamente ivi ricordo che in questo modo XCode prepara automaticamente i metodi di accesso (getter/setter) alle property.metodi di accesso (getter/setter) alle property.

A questo punto, torniamo nel metodoA questo punto, torniamo nel metodo application:didFinishLaunchingWithOptions: application:didFinishLaunchingWithOptions: e, dopo la creazionee, dopo la creazione di tc1 e tv1 scriviamo:di tc1 e tv1 scriviamo:

Page 157: Xcode4 Tutorial Completo

myTCController.ilTelecomando=tc1;myTCController.ilTelecomando=tc1;myTVController.ilTelevisore=tv1;myTVController.ilTelevisore=tv1;cioè assegnamo alle due property dei controller gli oggetti appenacioè assegnamo alle due property dei controller gli oggetti appena creati.creati.

L’ultima cosa da fare è collegare i nostri due Outlet ai controllerL’ultima cosa da fare è collegare i nostri due Outlet ai controller nello XIB. Apriamo il file MainWindow.xib, clicchiamo con ilnello XIB. Apriamo il file MainWindow.xib, clicchiamo con il bottone destro sull’oggetto bottone destro sull’oggetto My Tv App DelegateMy Tv App Delegate e dal popUp che e dal popUp che appare collegate la property appare collegate la property myTCControllermyTCController al primo dei due al primo dei due controller dentro Tab Bar Controller e collegate controller dentro Tab Bar Controller e collegate myTVControllermyTVController al secondo.al secondo.

Eseguiamo l’applicazione ed osserviamo il comportamento sia conEseguiamo l’applicazione ed osserviamo il comportamento sia con i tasti del televisore sia con quelli del telecomando. Se guardiamo ii tasti del televisore sia con quelli del telecomando. Se guardiamo i messaggi a console vediamo che tutto funziona correttamentemessaggi a console vediamo che tutto funziona correttamente mentre l’effetto sulle interfacce non è corretto. I comandi datimentre l’effetto sulle interfacce non è corretto. I comandi dati tramite l’interfaccia del telecomando non influisconotramite l’interfaccia del telecomando non influiscono sull’interfaccia del televisore (es. se accendete dal telecomando losull’interfaccia del televisore (es. se accendete dal telecomando lo schermo resta nero e lo switch del televisore resta su Off)schermo resta nero e lo switch del televisore resta su Off) rendendo in breve inconsistenti le due interfacce; in effetti perchèrendendo in breve inconsistenti le due interfacce; in effetti perchè dovrebbe? Noi tramite l’interfaccia del telecomando parliamo solodovrebbe? Noi tramite l’interfaccia del telecomando parliamo solo con il Modello non c’è nessun motivo per cui si dovrebbecon il Modello non c’è nessun motivo per cui si dovrebbe aggiornare l’interfaccia del televisore.aggiornare l’interfaccia del televisore.

Una possibilità per risolvere questo problema è quella diUna possibilità per risolvere questo problema è quella di individuare il momento in cui si passa alla visualizzazione delindividuare il momento in cui si passa alla visualizzazione del televisore ed aggiornare in quel momento lo stato dell’interfacciatelevisore ed aggiornare in quel momento lo stato dell’interfaccia in maniera coerente con lo stato del Modello.in maniera coerente con lo stato del Modello.

In TVController.m aggiungiamoIn TVController.m aggiungiamo

- (void)viewWillAppear:(BOOL)animated {- (void)viewWillAppear:(BOOL)animated {NSLog(@"Eccomi....");NSLog(@"Eccomi....");}}non c’è bisogno di dichararlo nel .h perchè è un metodonon c’è bisogno di dichararlo nel .h perchè è un metodo predefinito per UIViewController, di cui TVController èpredefinito per UIViewController, di cui TVController è sottoclasse. Ora eseguiamo l’app e passiamo più volte dal tabsottoclasse. Ora eseguiamo l’app e passiamo più volte dal tab

Page 158: Xcode4 Tutorial Completo

telecomando al tab televisore ed osserviamo la console. Possiamotelecomando al tab televisore ed osserviamo la console. Possiamo vedere che ogni volta che si passa all’interfaccia del televisorevedere che ogni volta che si passa all’interfaccia del televisore viene eseguito il metodo (e in effetti è proprio il suo scopo)viene eseguito il metodo (e in effetti è proprio il suo scopo) possiamo quindi usarlo per l’aggiornamento dell’interfaccia.possiamo quindi usarlo per l’aggiornamento dell’interfaccia.

Iniziamo ad aggiornare lo stato del televisoreIniziamo ad aggiornare lo stato del televisore

- (void)viewWillAppear:(BOOL)animated {- (void)viewWillAppear:(BOOL)animated {if (ilTelevisore.acceso) {if (ilTelevisore.acceso) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];} else {} else {schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];}}}}cioè controlliamo se il televisore è acceso, nel qual casocioè controlliamo se il televisore è acceso, nel qual caso accendiamo lo schermo (grigio) altrimenti lo spegnamo (nero). accendiamo lo schermo (grigio) altrimenti lo spegnamo (nero). Eseguiamo e proviamo.Eseguiamo e proviamo.

Però non basta, dobbiamo anche aggiornare lo statoPerò non basta, dobbiamo anche aggiornare lo stato dell’interruttore, altrimenti potremmo trovarci con lo schermodell’interruttore, altrimenti potremmo trovarci con lo schermo acceso e l’interruttore su Off. Abbiamo quindi bisogno che ilacceso e l’interruttore su Off. Abbiamo quindi bisogno che il controller comunichi lo stato all’interfaccia, quindi abbiamocontroller comunichi lo stato all’interfaccia, quindi abbiamo bisogno di un Outlet verso l’interruttore.bisogno di un Outlet verso l’interruttore.

In TVController.h aggiungiamoIn TVController.h aggiungiamo

@property (nonatomic, retain) IBOutlet UISwitch *interruttore;@property (nonatomic, retain) IBOutlet UISwitch *interruttore;e come al solito per le property, in TVController.me come al solito per le property, in TVController.m

@synthesize interruttore;@synthesize interruttore;Andiamo ora a fare il collegamento dell’Outlet con l’interfaccia.Andiamo ora a fare il collegamento dell’Outlet con l’interfaccia. Andiamo in TelevisoreView.xib e con il bottone destro su File’sAndiamo in TelevisoreView.xib e con il bottone destro su File’s owner apriamo il popup; colleghiamo l’Outlet owner apriamo il popup; colleghiamo l’Outlet interruttoreinterruttore con con l’oggetto grafico Switch.l’oggetto grafico Switch.

Torniamo a viewWillAppear e modifichiamolo inTorniamo a viewWillAppear e modifichiamolo in

- (void)viewWillAppear:(BOOL)animated {- (void)viewWillAppear:(BOOL)animated {if (ilTelevisore.acceso) {if (ilTelevisore.acceso) {

Page 159: Xcode4 Tutorial Completo

schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];interruttore.on=TRUE;interruttore.on=TRUE;} else {} else {schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];interruttore.on=FALSE;interruttore.on=FALSE;}}}}cioè nel ramo del televisore acceso abbiamo assegnato il valorecioè nel ramo del televisore acceso abbiamo assegnato il valore TRUE (Vero) alla property TRUE (Vero) alla property onon di interruttore; mentre nel ramo di interruttore; mentre nel ramo spento assegnamo il valore FALSE (Falso). Lo stato di questaspento assegnamo il valore FALSE (Falso). Lo stato di questa property predefinita per UISwitch ne comanda la visualizzazione.property predefinita per UISwitch ne comanda la visualizzazione.

Ora abbiamo bisogno di accendere il corretto selettore del canaleOra abbiamo bisogno di accendere il corretto selettore del canale sintonizzato, anche qui abbiamo bisogno di un Outlet in quanto ilsintonizzato, anche qui abbiamo bisogno di un Outlet in quanto il controller deve comunicare con l’interfaccia. Aggiungiamocontroller deve comunicare con l’interfaccia. Aggiungiamo

@property (nonatomic, retain) IBOutlet UISegmentedControl@property (nonatomic, retain) IBOutlet UISegmentedControl *numCanale;*numCanale;e la relativa @synthesize nel .m; e facciamo il collegamento nele la relativa @synthesize nel .m; e facciamo il collegamento nel file XIB. Modifichiamo viewWillAppearfile XIB. Modifichiamo viewWillAppear

- (void)viewWillAppear:(BOOL)animated {- (void)viewWillAppear:(BOOL)animated {if (ilTelevisore.acceso) {if (ilTelevisore.acceso) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];interruttore.on=TRUE;interruttore.on=TRUE;} else {} else {schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];interruttore.on=FALSE;interruttore.on=FALSE;}}numCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzatnumCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzato intValue];o intValue];nomeCanale.text=ilTelevisore.nomeDelCanale;nomeCanale.text=ilTelevisore.nomeDelCanale;}}quello che facciamo è assegnare un valore alla propertyquello che facciamo è assegnare un valore alla property selectedSegmentIndexselectedSegmentIndex del segmented control con il numero del del segmented control con il numero del selettore da selezionare. Il valore che usiamo per attivare ilselettore da selezionare. Il valore che usiamo per attivare il

Page 160: Xcode4 Tutorial Completo

selettore è quello rappresentato dalla property selettore è quello rappresentato dalla property canaleSintonizzatocanaleSintonizzato di ilTelevisore.di ilTelevisore. Inoltre aggiorniamo la label con il nome delInoltre aggiorniamo la label con il nome del canale.canale.

Infine bisogna aggiornare l’interfaccia anche relativamente alInfine bisogna aggiornare l’interfaccia anche relativamente al livello del volume. Anche qui abbiamo bisogno di un Outlet versolivello del volume. Anche qui abbiamo bisogno di un Outlet verso lo slider del volume.lo slider del volume.

@property (nonatomic, retain) IBOutlet UISlider *volume;@property (nonatomic, retain) IBOutlet UISlider *volume;Definiamo anche la synthesize ed il collegamento nello XIB. Definiamo anche la synthesize ed il collegamento nello XIB. Modifichiamo il metodo viewWillAppearModifichiamo il metodo viewWillAppear

- (void)viewWillAppear:(BOOL)animated {- (void)viewWillAppear:(BOOL)animated {if (ilTelevisore.acceso) {if (ilTelevisore.acceso) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];interruttore.on=TRUE;interruttore.on=TRUE;} else {} else {schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];interruttore.on=FALSE;interruttore.on=FALSE;}}numCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzatnumCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzato intValue];o intValue];nomeCanale.text=ilTelevisore.nomeDelCanale;nomeCanale.text=ilTelevisore.nomeDelCanale;volume.value=[ilTelevisore.livelloVolume floatValue];volume.value=[ilTelevisore.livelloVolume floatValue];livelloVolume.text = [NSStringlivelloVolume.text = [NSString stringWithFormat:@"%f",volume.value];stringWithFormat:@"%f",volume.value];}}Abbiamo assegnato un valore alla property value dello slider cheAbbiamo assegnato un valore alla property value dello slider che ne identifica lo stato e la posizione e gli assegnamo il valore dellane identifica lo stato e la posizione e gli assegnamo il valore della property di ilTelevisore interpretato come numero razionaleproperty di ilTelevisore interpretato come numero razionale (floatValue) che è il tipo della property value; inoltre abbiamo(floatValue) che è il tipo della property value; inoltre abbiamo anche aggiornato la label con il livello del volume.anche aggiornato la label con il livello del volume.

Bene. Dovremmo aver finito, eseguiamo l’applicazione eBene. Dovremmo aver finito, eseguiamo l’applicazione e vediamone gli effetti usando entrambe le interfacce.vediamone gli effetti usando entrambe le interfacce.

Vi invito a ripercorrere con attenzione gli ultimi capitoli eVi invito a ripercorrere con attenzione gli ultimi capitoli e

Page 161: Xcode4 Tutorial Completo

assimilare bene i concetti espressi anche facendo delle prove.assimilare bene i concetti espressi anche facendo delle prove.

Vi allego il progetto nella versione finale di questo capitolo.Vi allego il progetto nella versione finale di questo capitolo.

17 – Il KVO17 – Il KVO

iOS, , Mac, , OSX, , SDK, , Tech Room

In questo capitolo faremo di nuovo un po’ di ottimizzazione deiIn questo capitolo faremo di nuovo un po’ di ottimizzazione dei concetti visti con l’introduzione di un nuovo pattern.concetti visti con l’introduzione di un nuovo pattern.

Rivediamo cosa abbiamo fatto nel capitolo precedente: abbiamoRivediamo cosa abbiamo fatto nel capitolo precedente: abbiamo introdotto un altro oggetto, un telecomando, che comunica, aintrodotto un altro oggetto, un telecomando, che comunica, a livello Model, con un oggetto televisore e a livello Control con unlivello Model, con un oggetto televisore e a livello Control con un controller. Il televisore a sua volta comunica con un controller e icontroller. Il televisore a sua volta comunica con un controller e i due controller comunicano ognuno con una propria vista. Indue controller comunicano ognuno con una propria vista. In pratica abbiamo realizzato una cosa del tipo.pratica abbiamo realizzato una cosa del tipo.

Page 162: Xcode4 Tutorial Completo

Se osserviamo la parte televisore vediamo che il controller parlaSe osserviamo la parte televisore vediamo che il controller parla con le istanze del Model solo con il meccanismo delle invocazionicon le istanze del Model solo con il meccanismo delle invocazioni dirette di metodi, in pratica accedendo alle property del modello.dirette di metodi, in pratica accedendo alle property del modello.

Quale problema ci siamo trovati ad affrontare, proprio a causa diQuale problema ci siamo trovati ad affrontare, proprio a causa di questo tipo di relazione? Il fatto che le variazioni che arrivano allequesto tipo di relazione? Il fatto che le variazioni che arrivano alle istanze di Televisore attraverso l’interfaccia del telecomandoistanze di Televisore attraverso l’interfaccia del telecomando devono avere effetto sull’interfaccia del televisore.devono avere effetto sull’interfaccia del televisore.

Page 163: Xcode4 Tutorial Completo

Avendo a disposizione l’unico meccanismo dell’interrogazione diAvendo a disposizione l’unico meccanismo dell’interrogazione di property lo abbiamo risolto allineando l’interfaccia con il modelloproperty lo abbiamo risolto allineando l’interfaccia con il modello nel metodo viewWillAppear accedendo alle property del televisorenel metodo viewWillAppear accedendo alle property del televisore controllato; ma questo modo, come potete immaginare può essercontrollato; ma questo modo, come potete immaginare può esser utilizzato in situazioni come questa dove abbiamo una sola classeutilizzato in situazioni come questa dove abbiamo una sola classe come modello e una sola vista, non appena la realtà si complica uncome modello e una sola vista, non appena la realtà si complica un po’ farebbe diventare in breve tempo il viewWillAppear troppopo’ farebbe diventare in breve tempo il viewWillAppear troppo pesante.pesante.

Come si può generalizzare questa situazione?Come si può generalizzare questa situazione?

Con il meccanismo delle Actions noi implementiamo unCon il meccanismo delle Actions noi implementiamo un ragionamento del tiporagionamento del tipo

quando succede qualcosa sull’interfaccia fai…quando succede qualcosa sull’interfaccia fai…

Page 164: Xcode4 Tutorial Completo

ma quando diversi eventi provenienti da diverse viste possonoma quando diversi eventi provenienti da diverse viste possono avere effetto sugli stessi elementi di interfaccia, questoavere effetto sugli stessi elementi di interfaccia, questo meccanismo diventa inefficiente e sarebbe più utile implementaremeccanismo diventa inefficiente e sarebbe più utile implementare un ragionamento del tipoun ragionamento del tipo

quando cambia un valore del modello fai…quando cambia un valore del modello fai…

cioè il controller si attiva alla variazione di valore di una propertycioè il controller si attiva alla variazione di valore di una property indipendentemente da chi ha causato quella variazione. E’indipendentemente da chi ha causato quella variazione. E’ importante comprendere la differenza tra questi due approcci.importante comprendere la differenza tra questi due approcci.

Per implementare questo tipo di interazione facciamo ricorso adPer implementare questo tipo di interazione facciamo ricorso ad un altro pattern (cioè un modello di interazione tra oggetti) il Key-un altro pattern (cioè un modello di interazione tra oggetti) il Key-Value Observing (KVO). Tramite questo pattern un oggetto vieneValue Observing (KVO). Tramite questo pattern un oggetto viene informato dal sistema quando una certa proprietà di un altroinformato dal sistema quando una certa proprietà di un altro oggetto cambia valore. Che è esattamente quello che vogliamo.oggetto cambia valore. Che è esattamente quello che vogliamo.

Questo pattern si può utilizzare ogni volta che si ritiene necessarioQuesto pattern si può utilizzare ogni volta che si ritiene necessario ma sicuramente trova nell’interazione tra Model e Control unama sicuramente trova nell’interazione tra Model e Control una delle sue applicazioni principali.delle sue applicazioni principali.

Approfondimento: Cercate nella documentazione Apple “Key-Approfondimento: Cercate nella documentazione Apple “Key-Value Observing Programming Guide” e leggete i primi 2 capitoliValue Observing Programming Guide” e leggete i primi 2 capitoli

Andiamo ad applicare il KVO in modo da capirne ilAndiamo ad applicare il KVO in modo da capirne il funzionamento.funzionamento.

Iniziamo a gestire l’evento accensione del televisore. Dal punto diIniziamo a gestire l’evento accensione del televisore. Dal punto di vista del KVO questo significa che il controller istanza divista del KVO questo significa che il controller istanza di TVController dovrà essere informato dal sistema quando cambia ilTVController dovrà essere informato dal sistema quando cambia il valore della property acceso dell’istanza di Televisore che stiamovalore della property acceso dell’istanza di Televisore che stiamo vedendo.vedendo.

Andiamo nel file myTvAppDelegate.m e, modifichiamo il metodoAndiamo nel file myTvAppDelegate.m e, modifichiamo il metodo

- (BOOL)application:(UIApplication *)application- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptionsdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions{{// Override point for customization after application launch.// Override point for customization after application launch.

Page 165: Xcode4 Tutorial Completo

Televisore *tv1=[[Televisore alloc] init];Televisore *tv1=[[Televisore alloc] init];Telecomando *tc1=[[Telecomando alloc ] init];Telecomando *tc1=[[Telecomando alloc ] init];tc1.myTv=tv1;tc1.myTv=tv1;myTCController.ilTelecomando=tc1;myTCController.ilTelecomando=tc1;myTVController.ilTelevisore=tv1;myTVController.ilTelevisore=tv1;[myTVController.ilTelevisore addObserver:myTVController[myTVController.ilTelevisore addObserver:myTVControllerforKeyPath:@"acceso"forKeyPath:@"acceso"options:NSKeyValueObservingOptionNewoptions:NSKeyValueObservingOptionNewcontext:nil];context:nil];[self.window makeKeyAndVisible];[self.window makeKeyAndVisible];return YES;return YES;}}abbiamo cioè aggiunto l’istruzioneabbiamo cioè aggiunto l’istruzione

[myTVController.ilTelevisore addObserver:myTVController[myTVController.ilTelevisore addObserver:myTVControllerforKeyPath:@"acceso"forKeyPath:@"acceso"options:NSKeyValueObservingOptionNewoptions:NSKeyValueObservingOptionNewcontext:nil];context:nil];è l’invocazione del metodo di istanzaè l’invocazione del metodo di istanza addObserver:forKeyPath:options:context:addObserver:forKeyPath:options:context:, questo è il metodo che, questo è il metodo che serve per dire al sistema che un oggetto è interessato alleserve per dire al sistema che un oggetto è interessato alle variazioni del valore di una certa property di un altro oggetto.variazioni del valore di una certa property di un altro oggetto.

In particolare noi stiamo dicendo aIn particolare noi stiamo dicendo a myTVController.ilTelevisoremyTVController.ilTelevisore,, l’istanza della classe Televisore che riceve il messaggio conl’istanza della classe Televisore che riceve il messaggio con l’invocazione del metodo, che l’oggetto l’invocazione del metodo, che l’oggetto myTvControllermyTvController,, parametro di addObserver, è interessato alle variazioni dellaparametro di addObserver, è interessato alle variazioni della property property accesoacceso, parametro di forKeyPath. Con il parametro di, parametro di forKeyPath. Con il parametro di options, options, NSKeyValueObservingOptionNewNSKeyValueObservingOptionNew, specifichiamo che al, specifichiamo che al momento della segnalazione della variazione ci interessa riceveremomento della segnalazione della variazione ci interessa ricevere il nuovo valore della property. Infine in context, possiamoil nuovo valore della property. Infine in context, possiamo specificare un oggetto che ci verrà passato anche esso al momentospecificare un oggetto che ci verrà passato anche esso al momento della segnalazione e chedella segnalazione e che può essere utile per gestire l’evento; nelpuò essere utile per gestire l’evento; nel nostro caso abbiamo indicato nil, che vuol dire nessun oggetto.nostro caso abbiamo indicato nil, che vuol dire nessun oggetto.

Approfondimento: Cercate nella documentazione Apple il metodoApprofondimento: Cercate nella documentazione Apple il metodo

Page 166: Xcode4 Tutorial Completo

addObserver:forKeyPath:options:context e analizzate i possibiliaddObserver:forKeyPath:options:context e analizzate i possibili valori per il parametro options.valori per il parametro options.

Bene, a questo punto abbiamo detto che il controller deve essereBene, a questo punto abbiamo detto che il controller deve essere informato delle variazioni della property; ma come farà ad essereinformato delle variazioni della property; ma come farà ad essere informato?informato?

La risposta a questa domanda è nell’implementazione del metodoLa risposta a questa domanda è nell’implementazione del metodo

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context(id)object change:(NSDictionary *)change context:(void *)context {{}}da parte dell’oggetto osservatore.da parte dell’oggetto osservatore.

Questo metodo sarà invocato automaticamente dal sistema inQuesto metodo sarà invocato automaticamente dal sistema in risposta alla variazione del valore della property per la quale cirisposta alla variazione del valore della property per la quale ci siamo registrati.siamo registrati.

Analizziamo i parametri di questo metodo, che saranno valorizzatiAnalizziamo i parametri di questo metodo, che saranno valorizzati automaticamente allaautomaticamente alla sua invocazione: sua invocazione:

keyPath: contiene il nome della property il cui cambio dikeyPath: contiene il nome della property il cui cambio di valore ha generato l’invocazione; è un parametro importantevalore ha generato l’invocazione; è un parametro importante soprattutto se volessimo osservare diverse proprietà;soprattutto se volessimo osservare diverse proprietà;

object: contiene l’oggetto la cui proprietà ha cambiato ilobject: contiene l’oggetto la cui proprietà ha cambiato il valore; è importante soprattutto se un osservatore èvalore; è importante soprattutto se un osservatore è interessato a diversi oggetti; immaginate di avere piùinteressato a diversi oggetti; immaginate di avere più televisori;televisori;

change: contiene alcune informazioni relative alchange: contiene alcune informazioni relative al cambiamento avvenuto;cambiamento avvenuto;

context: contiene altre eventuali informazioni aggiuntivecontext: contiene altre eventuali informazioni aggiuntive relative all’evento, corrisponde all’oggetto specificato inrelative all’evento, corrisponde all’oggetto specificato in context nel addObserver:forKeyPath:options:context: checontext nel addObserver:forKeyPath:options:context: che abbiamo usato per registrare il controller come osservatore;abbiamo usato per registrare il controller come osservatore;

Andiamo quindi ad implementare questo metodo scrivendoAndiamo quindi ad implementare questo metodo scrivendo

Page 167: Xcode4 Tutorial Completo

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context(id)object change:(NSDictionary *)change context:(void *)context {{}}nel file TVController.m.nel file TVController.m.

Scriviamoci dentro il codice per gestire l’evento acceso/spento.Scriviamoci dentro il codice per gestire l’evento acceso/spento. Come al solito dobbiamo capire se stiamo accendendo il televisoreCome al solito dobbiamo capire se stiamo accendendo il televisore (acceso vale True) o lo stiamo spegnendo (acceso vale False),(acceso vale True) o lo stiamo spegnendo (acceso vale False), come facciamo? Il parametro change ci viene in aiuto proprio income facciamo? Il parametro change ci viene in aiuto proprio in questa occasione. Un’istanza di NSDictionary è una struttura chequesta occasione. Un’istanza di NSDictionary è una struttura che può essere vista come un insieme di coppie chiave/valorepuò essere vista come un insieme di coppie chiave/valore

((chiave1 valore1), (chiave2 valore2), (chiave3 valore3),..)((chiave1 valore1), (chiave2 valore2), (chiave3 valore3),..)

che può essere interrogata attraverso una chiave per ottenerne ilche può essere interrogata attraverso una chiave per ottenerne il valore corrispondente.valore corrispondente.

Approfondimento: Cercate nella documentazione Apple il metodoApprofondimento: Cercate nella documentazione Apple il metodo observeValueForKeyPath:ofObject:change:context: e seguite ilobserveValueForKeyPath:ofObject:change:context: e seguite il link “Keys used by the change dictionary.” per trovare le chiavilink “Keys used by the change dictionary.” per trovare le chiavi usate nel dictionary associato change.usate nel dictionary associato change.

Quindi possiamo scrivere il nostro metodoQuindi possiamo scrivere il nostro metodo

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context(id)object change:(NSDictionary *)change context:(void *)context {{if ([[change valueForKey:@"new"] boolValue]) {if ([[change valueForKey:@"new"] boolValue]) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];interruttore.on=TRUE;interruttore.on=TRUE;} else {} else {schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];interruttore.on=FALSE;interruttore.on=FALSE;}}}}Abbiamo introdotto un’istruzione condizionale che distingue i dueAbbiamo introdotto un’istruzione condizionale che distingue i due casi se stiamo accendendo il televisore o se lo stiamo spegnendo.casi se stiamo accendendo il televisore o se lo stiamo spegnendo.

Page 168: Xcode4 Tutorial Completo

Quale condizione andiamo a controllare?Quale condizione andiamo a controllare?

[change valueForKey:@"new"][change valueForKey:@"new"]questa è l’invocazione del metodo valueForKey su change che èquesta è l’invocazione del metodo valueForKey su change che è un’istanza di NSDictonary. Il metodo valueForKey è un metodoun’istanza di NSDictonary. Il metodo valueForKey è un metodo standard per NSDictionary e restituisce il valore associato allastandard per NSDictionary e restituisce il valore associato alla chiave passata come argomento nell’istanza di dizionariochiave passata come argomento nell’istanza di dizionario ricevente. La chiave che stiamo usando per recuperare un valore èricevente. La chiave che stiamo usando per recuperare un valore è new, questa chiave restituisce il nuovo valore della property chenew, questa chiave restituisce il nuovo valore della property che ha generato l’invocazione del metodo. Per usarlo all’interno delha generato l’invocazione del metodo. Per usarlo all’interno del test dell’istruzione if ci facciamo restituire il dato come valoretest dell’istruzione if ci facciamo restituire il dato come valore booleano (Vero/Falso) tramite l’invocazione di boolValue.booleano (Vero/Falso) tramite l’invocazione di boolValue.

Quindi se la condizione dell’if restituisce True vuol dire che ilQuindi se la condizione dell’if restituisce True vuol dire che il nuovo valore di acceso è Vero e quindi dovremo gestirenuovo valore di acceso è Vero e quindi dovremo gestire l’accensione del televisore, altrimenti dovremo gestire lol’accensione del televisore, altrimenti dovremo gestire lo spegnimento.spegnimento. Le istruzioni nei blocchi del Vero e del Falso non le Le istruzioni nei blocchi del Vero e del Falso non le commento in quanto sono le stesse che avevamo inserito nelcommento in quanto sono le stesse che avevamo inserito nel viewWillAppear nel capitolo precedente.viewWillAppear nel capitolo precedente.

Se abbiamo inserito l’aggiornamento dell’interfaccia in questoSe abbiamo inserito l’aggiornamento dell’interfaccia in questo metodo dobbiamo toglierlo da tutte le altre parti nelle quali erametodo dobbiamo toglierlo da tutte le altre parti nelle quali era presente. In particolare in viewWillAppear cancelliamopresente. In particolare in viewWillAppear cancelliamo

if (ilTelevisore.acceso) {if (ilTelevisore.acceso) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];interruttore.on=TRUE;interruttore.on=TRUE;} else {} else {schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];interruttore.on=FALSE;interruttore.on=FALSE;}}nel metodo accendiSpegni togliamo le due righenel metodo accendiSpegni togliamo le due righe

schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];......schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];Proviamo ad eseguire il programma ed a passare più volte dal tabProviamo ad eseguire il programma ed a passare più volte dal tab

Page 169: Xcode4 Tutorial Completo

telecomando al tab televisore e viceversa accendendo e spegnendotelecomando al tab televisore e viceversa accendendo e spegnendo il televisore. Potete osservare che in genere le cose funzionano…il televisore. Potete osservare che in genere le cose funzionano…ma se fate questa sequenza:ma se fate questa sequenza:

eseguite l’applicazioneeseguite l’applicazione

dall’interfaccia del telecomando (senza prima essere andati adall’interfaccia del telecomando (senza prima essere andati a quella del televisore) cliccate sul pulsante On/Offquella del televisore) cliccate sul pulsante On/Off

andate sulla view del televisoreandate sulla view del televisore

vedete che non c’è stato l’effetto voluto (schermo grigio edvedete che non c’è stato l’effetto voluto (schermo grigio ed interruttore su On). Perchè?interruttore su On). Perchè?

Per rispondere a questa domanda analizziamo un po’ meglio laPer rispondere a questa domanda analizziamo un po’ meglio la natura del lavoro che facciamo nei file xib.natura del lavoro che facciamo nei file xib.

Quando lavoriamo su un file xib (utilizzando in tal modo laQuando lavoriamo su un file xib (utilizzando in tal modo la componente Interface Builder di XCode) noi disegniamo la nostracomponente Interface Builder di XCode) noi disegniamo la nostra interfaccia utente utilizzando delle classi di Cocoa (UIView,interfaccia utente utilizzando delle classi di Cocoa (UIView, UISwitch, UISlider, UILabel,…); ogni elemento di interfaccia cheUISwitch, UISlider, UILabel,…); ogni elemento di interfaccia che aggiungiamo diventa un’istanza della propria classe, cioè unaggiungiamo diventa un’istanza della propria classe, cioè un oggetto, ed è a questi oggetti che tramite il collegamento IBOutletoggetto, ed è a questi oggetti che tramite il collegamento IBOutlet noi inviamo i nostri messaggi (cambia il colore, cambia lo stato,noi inviamo i nostri messaggi (cambia il colore, cambia lo stato, cambia il testo,…) per modificare l’interfaccia.cambia il testo,…) per modificare l’interfaccia.

iOS per ottimizzare l’uso della memoria però crea effettivamenteiOS per ottimizzare l’uso della memoria però crea effettivamente questi oggetti solo il momento in cui ne ha bisogno, cioè soloquesti oggetti solo il momento in cui ne ha bisogno, cioè solo quando li dovrà visualizzare. Nel nostro caso solo quandoquando li dovrà visualizzare. Nel nostro caso solo quando passeremo al tab televisore, in quanto finché restiamo nel tabpasseremo al tab televisore, in quanto finché restiamo nel tab telecomando non ha bisogno di mostrare la view dello schermo nételecomando non ha bisogno di mostrare la view dello schermo né nessun altro dei controlli del televisore. Questa ottimizzazionenessun altro dei controlli del televisore. Questa ottimizzazione però si scontra con l’uso del KVO,però si scontra con l’uso del KVO, in quanto i messaggi che in quanto i messaggi che invieremo agli IBOutlet legati agli elementi di interfaccia delinvieremo agli IBOutlet legati agli elementi di interfaccia del televisore non arriveranno ad alcun oggetto fino a quando nontelevisore non arriveranno ad alcun oggetto fino a quando non saremo passati almeno una volta dal tab televisore.saremo passati almeno una volta dal tab televisore.

La soluzione al problema è quindi quella di forzare la creazioneLa soluzione al problema è quindi quella di forzare la creazione degli oggetti di interfaccia prima di cominciare a mandare idegli oggetti di interfaccia prima di cominciare a mandare i messaggi. Sono chiari il problema e la soluzione che andremo admessaggi. Sono chiari il problema e la soluzione che andremo ad

Page 170: Xcode4 Tutorial Completo

implementare?implementare?

Andiamo nel metodoAndiamo nel metodo application:didFinishLaunchingWithOptions: diapplication:didFinishLaunchingWithOptions: di myTvAppDelegate, e prima dimyTvAppDelegate, e prima di

[myTVController.ilTelevisore addObserver:myTVController[myTVController.ilTelevisore addObserver:myTVControllerforKeyPath:@"acceso"forKeyPath:@"acceso"options:NSKeyValueObservingOptionNewoptions:NSKeyValueObservingOptionNewcontext:nil];context:nil];scriviamoscriviamo

[[NSBundle mainBundle] loadNibNamed:@"TelevisoreView"[[NSBundle mainBundle] loadNibNamed:@"TelevisoreView"owner:myTVControllerowner:myTVControlleroptions:nil];options:nil];NSBundle è una classe che fornisce diverse funzionalità perNSBundle è una classe che fornisce diverse funzionalità per operare sulla struttura dell’applicazione (vi rimando allaoperare sulla struttura dell’applicazione (vi rimando alla documentazione per il suo approfondimento), in particolare mettedocumentazione per il suo approfondimento), in particolare mette a disposizione il metodo loadNibNamed che serve proprio a crearea disposizione il metodo loadNibNamed che serve proprio a creare gli oggetti definiti in un particolare file xib (senza visualizzarli); ilgli oggetti definiti in un particolare file xib (senza visualizzarli); il nome del file gli viene passato come argomento insiemenome del file gli viene passato come argomento insieme all’oggetto che andrà a sostituire il segnaposto File’s owner cheall’oggetto che andrà a sostituire il segnaposto File’s owner che abbiamo già incontrato ogni volta che abbiamo creatoabbiamo già incontrato ogni volta che abbiamo creato un’interfaccia tramite IB.un’interfaccia tramite IB.

A questo punto eseguiamo l’applicazione e dovremmo avere ilA questo punto eseguiamo l’applicazione e dovremmo avere il comportamento corretto.comportamento corretto.

Bene, ora che il canale KVO è funzionante possiamo farci passareBene, ora che il canale KVO è funzionante possiamo farci passare le altre segnalazioni (c’è un ultimo aspetto da affrontare chele altre segnalazioni (c’è un ultimo aspetto da affrontare che vediamo immediatamente).vediamo immediatamente).

Iniziamo dal cambio del canale.Iniziamo dal cambio del canale.

Dobbiamo metterci in osservazione della propertyDobbiamo metterci in osservazione della property canaleSintonizzato. Nel metodo canaleSintonizzato. Nel metodo didFinishLaunchingWithOptionsdidFinishLaunchingWithOptions del file myTvAppDelegate.m aggiungiamodel file myTvAppDelegate.m aggiungiamo

[myTVController.ilTelevisore addObserver:myTVController[myTVController.ilTelevisore addObserver:myTVController

Page 171: Xcode4 Tutorial Completo

forKeyPath:@"canaleSintonizzato"forKeyPath:@"canaleSintonizzato"options:NSKeyValueObservingOptionNewoptions:NSKeyValueObservingOptionNewcontext:nil];context:nil];è la stessa istruzione della precedente registrazione diè la stessa istruzione della precedente registrazione di osservazione e quindi non la discutiamo, osserviamo solo cheosservazione e quindi non la discutiamo, osserviamo solo che abbiamo inserito la nuova property che ci interessa osservare,abbiamo inserito la nuova property che ci interessa osservare, canaleSintonizzato.canaleSintonizzato.

Passiamo alla gestione della segnalazione nel metodoPassiamo alla gestione della segnalazione nel metodo observeValueForKeyPath:ofObject:change: definito inobserveValueForKeyPath:ofObject:change: definito in TVController.m, che, come abbiamo detto e visto, viene invocatoTVController.m, che, come abbiamo detto e visto, viene invocato automaticamente a fronte delle variazione delle propertyautomaticamente a fronte delle variazione delle property osservate. Ora però il metodo può essere invocato in risposta alleosservate. Ora però il metodo può essere invocato in risposta alle variazioni di due property; è necessario quindi individuare qualevariazioni di due property; è necessario quindi individuare quale property ha cambiato valore per poter eseguire il corretto blocco diproperty ha cambiato valore per poter eseguire il corretto blocco di aggiornamento. Ricordiamo che tra gli argomenti che sono passatiaggiornamento. Ricordiamo che tra gli argomenti che sono passati al metodo c’è keyPath che è un’istanza di NSString che contieneal metodo c’è keyPath che è un’istanza di NSString che contiene esattamente il nome della property che ha generato l’invocazioneesattamente il nome della property che ha generato l’invocazione del metodo. Possiamo quindi scriveredel metodo. Possiamo quindi scrivere

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context(id)object change:(NSDictionary *)change context:(void *)context {{if ([keyPath isEqualToString:@"acceso"]) {if ([keyPath isEqualToString:@"acceso"]) {if ([[change valueForKey:@"new"] boolValue]) {if ([[change valueForKey:@"new"] boolValue]) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];interruttore.on=TRUE;interruttore.on=TRUE;} else {} else {schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];interruttore.on=FALSE;interruttore.on=FALSE;}}} else {} else {numCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzatnumCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzato intValue];o intValue];nomeCanale.text=ilTelevisore.nomeDelCanale;nomeCanale.text=ilTelevisore.nomeDelCanale;}}

Page 172: Xcode4 Tutorial Completo

}}Cioè la prima cosa che facciamo all’interno del metodo èCioè la prima cosa che facciamo all’interno del metodo è un’istruzione condizionale la cui condizione èun’istruzione condizionale la cui condizione è

[keyPath isEqualToString:@"acceso"][keyPath isEqualToString:@"acceso"]keyPath è un’istanza di NSString, isEqualToString è un metodokeyPath è un’istanza di NSString, isEqualToString è un metodo predefinito per la classe NSString che confronta due oggetti di tipopredefinito per la classe NSString che confronta due oggetti di tipo NSString e restituisce True (Vero) se le stringhe di caratteriNSString e restituisce True (Vero) se le stringhe di caratteri contenute sono uguali, altrimento restituisce False (Falso).contenute sono uguali, altrimento restituisce False (Falso). Attenzione, per confrontare due NSString, non si può usareAttenzione, per confrontare due NSString, non si può usare l’operatore di uguaglianza che abbiamo a volte incontrato inl’operatore di uguaglianza che abbiamo a volte incontrato in passato, ==, in quanto due istanze di NSString sono oggetti e nonpassato, ==, in quanto due istanze di NSString sono oggetti e non sequenze di caratteri. La differenza tra i due tipi di confronto èsequenze di caratteri. La differenza tra i due tipi di confronto è come se io avessi due foglietti di carta con scritto sopra la parolacome se io avessi due foglietti di carta con scritto sopra la parola “acceso”. Se confrontiamo i foglietti, questi saranno sicuramente“acceso”. Se confrontiamo i foglietti, questi saranno sicuramente diversi, potrebbero essere uno a righe e l’altro a quadretti, unodiversi, potrebbero essere uno a righe e l’altro a quadretti, uno rettangolare e l’altro triangolare, uno più grande e l’altro piùrettangolare e l’altro triangolare, uno più grande e l’altro più piccolo; ma se confronto quello che c’è scritto vedrò chepiccolo; ma se confronto quello che c’è scritto vedrò che l’informazione è la stessa: acceso.l’informazione è la stessa: acceso.

Il confronto ==, è analogo a confrontare i foglietti, il confrontoIl confronto ==, è analogo a confrontare i foglietti, il confronto isEqualToString è analogo a confrontare l’informazione contenuta.isEqualToString è analogo a confrontare l’informazione contenuta.

Quindi con il test di quella condizione verifichiamo se il metodo èQuindi con il test di quella condizione verifichiamo se il metodo è stato attivato dalla variazione della property acceso.stato attivato dalla variazione della property acceso.

Nel blocco corrispondente alla condizione “Vero” abbiamo leNel blocco corrispondente alla condizione “Vero” abbiamo le istruzioni per la gestione dell’accensione/spegnimento cheistruzioni per la gestione dell’accensione/spegnimento che avevamo già; mentre nel ramo corrispondente alla condizioneavevamo già; mentre nel ramo corrispondente alla condizione Falso, indicativo quindi del fatto che stiamo gestendo la propertyFalso, indicativo quindi del fatto che stiamo gestendo la property canaleSintonizzato (visto che non è la property acceso) abbiamo lecanaleSintonizzato (visto che non è la property acceso) abbiamo le due istruzioni di aggiornamento dell’interfaccia relativa al canaledue istruzioni di aggiornamento dell’interfaccia relativa al canale che avevamo inserito nel viewWillAppear e dal quale le togliamo.che avevamo inserito nel viewWillAppear e dal quale le togliamo. Inoltre dal metodo cambiaCanale ti TVController.m togliamoInoltre dal metodo cambiaCanale ti TVController.m togliamo l’istruzionel’istruzione

nomeCanale.text=ilTelevisore.nomeDelCanale;nomeCanale.text=ilTelevisore.nomeDelCanale;

Page 173: Xcode4 Tutorial Completo

Passiamo ora a gestire il cambiamento del volume.Passiamo ora a gestire il cambiamento del volume.

Registriamo il controller per ricever le variazioni della propertyRegistriamo il controller per ricever le variazioni della property livelloVolume, aggiungendolivelloVolume, aggiungendo

[myTVController.ilTelevisore addObserver:myTVController[myTVController.ilTelevisore addObserver:myTVControllerforKeyPath:@"livelloVolume"forKeyPath:@"livelloVolume"options:NSKeyValueObservingOptionNewoptions:NSKeyValueObservingOptionNewcontext:nil];context:nil];nel metodo nel metodo didFinishLaunchingWithOptionsdidFinishLaunchingWithOptions del file del file myTvAppDelegate.m. Andiamo a gestire la segnalazione nelmyTvAppDelegate.m. Andiamo a gestire la segnalazione nel metodo observeValueForKeyPath:ofObject:change: inmetodo observeValueForKeyPath:ofObject:change: in TVController.m.TVController.m.

Ora quando siamo nel ramo else dell’istruzione if più esternaOra quando siamo nel ramo else dell’istruzione if più esterna (quella con la condizione sul nome della property) sappiamo solo(quella con la condizione sul nome della property) sappiamo solo che non stiamo gestendo la property acceso; ma non sappiamo se èche non stiamo gestendo la property acceso; ma non sappiamo se è cambiato canaleSintonizzato o livelloVolume. Dobbiamo quindicambiato canaleSintonizzato o livelloVolume. Dobbiamo quindi aggiungere un ulteriore test sul nome della property.aggiungere un ulteriore test sul nome della property.

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context(id)object change:(NSDictionary *)change context:(void *)context {{if ([keyPath isEqualToString:@"acceso"]) {if ([keyPath isEqualToString:@"acceso"]) {if ([[change valueForKey:@"new"] boolValue]) {if ([[change valueForKey:@"new"] boolValue]) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];interruttore.on=TRUE;interruttore.on=TRUE;} else {} else {schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];interruttore.on=FALSE;interruttore.on=FALSE;}}} else {} else {if ([keyPath isEqualToString:@"canaleSintonizzato"]) {if ([keyPath isEqualToString:@"canaleSintonizzato"]) {numCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzatnumCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzato intValue];o intValue];nomeCanale.text=ilTelevisore.nomeDelCanale;nomeCanale.text=ilTelevisore.nomeDelCanale;} else {} else {

Page 174: Xcode4 Tutorial Completo

volume.value=[ilTelevisore.livelloVolume floatValue];volume.value=[ilTelevisore.livelloVolume floatValue];livelloVolume.text = [NSStringlivelloVolume.text = [NSString stringWithFormat:@"%f",volume.value];stringWithFormat:@"%f",volume.value];}}}}}}vi lascio l’analisi dell’istruzione if che abbiamo inserito, vi facciovi lascio l’analisi dell’istruzione if che abbiamo inserito, vi faccio solo osservare che abbiamo spostato qui le due istruzioni disolo osservare che abbiamo spostato qui le due istruzioni di aggiornamento del volume che avevamo in viewWillAppear che aaggiornamento del volume che avevamo in viewWillAppear che a questo punto è vuoto e si può cancellare. Eliminate infine dalquesto punto è vuoto e si può cancellare. Eliminate infine dal metodo regolaVolume di TVController.mmetodo regolaVolume di TVController.m

livelloVolume.text=[NSStringlivelloVolume.text=[NSString stringWithFormat:@"%f",sender.value];stringWithFormat:@"%f",sender.value];Eseguite ancora una volta l’applicazione e verificate ilEseguite ancora una volta l’applicazione e verificate il comportamento corretto.comportamento corretto.

Ripercorrete questo capitolo per consolidare il pattern KVO e gliRipercorrete questo capitolo per consolidare il pattern KVO e gli aspetti connessi.aspetti connessi.

18 – Un po’ di contenuti18 – Un po’ di contenuti

iOS, , Mac, , OSX, , SDK, , Tech Room

Proviamo ad estendere il nostro programma per dare un po’ diProviamo ad estendere il nostro programma per dare un po’ di colore alla nostra tv che per ora scrive solo il nome del canalecolore alla nostra tv che per ora scrive solo il nome del canale nello schermo. La prima cosa da fare prima di mettersi a scriverenello schermo. La prima cosa da fare prima di mettersi a scrivere il codice è definire cosa si vuole ottenere.il codice è definire cosa si vuole ottenere.

Decidiamo che il nostro televisore comincia a farci vedereDecidiamo che il nostro televisore comincia a farci vedere qualcosa: nei canali da 1 a 3 saranno mostrate delle immagini, nelqualcosa: nei canali da 1 a 3 saranno mostrate delle immagini, nel canale 4 sarà visualizzato un video e nel canale 5 un sito.canale 4 sarà visualizzato un video e nel canale 5 un sito.

Iniziamo con il procurarci tre immagini formato png; prendeteleIniziamo con il procurarci tre immagini formato png; prendetele dal vostro disco o scaricatele da Internet…in ogni caso prendete ledal vostro disco o scaricatele da Internet…in ogni caso prendete le

Page 175: Xcode4 Tutorial Completo

tre immagini e trascinatele dentro la cartella myTv di XCode.tre immagini e trascinatele dentro la cartella myTv di XCode. Accertatevi che sia attivo il flag Accertatevi che sia attivo il flag copy items into destination…copy items into destination… e e andate avanti.andate avanti.

Rinominate le tre immagini Onda1.png, Onda2.png e Onda3.png.Rinominate le tre immagini Onda1.png, Onda2.png e Onda3.png.

Ora per vedere le immagini in un’app è necessario ricorrere ad unOra per vedere le immagini in un’app è necessario ricorrere ad un oggetto (poteva essere diversamente?); in particolare è necessariaoggetto (poteva essere diversamente?); in particolare è necessaria un’istanza d UIImageView.un’istanza d UIImageView.

Selezioniamo il file TelevisoreView.xib in modo da aprire laSelezioniamo il file TelevisoreView.xib in modo da aprire la componente Interface Builder di XCode, cioè lo strumento con ilcomponente Interface Builder di XCode, cioè lo strumento con il quale disegniamo la nostra interfaccia.quale disegniamo la nostra interfaccia.

Come prima cosa spostiamo la label con il nome del canaleCome prima cosa spostiamo la label con il nome del canale nell’angolo in basso a sinistra in modo da non avere questa scrittanell’angolo in basso a sinistra in modo da non avere questa scritta al centro dei nostri contenuti, se trovate difficoltoso farlo perché loal centro dei nostri contenuti, se trovate difficoltoso farlo perché lo schermo nero vi impedisce di vedere la label, tramite l’Attributesschermo nero vi impedisce di vedere la label, tramite l’Attributes Inspector cambiate il colore della view e poi rimettetelo a Black.Inspector cambiate il colore della view e poi rimettetelo a Black.

Nella Library degli oggetti disponibili, la parte inferiore del frameNella Library degli oggetti disponibili, la parte inferiore del frame di destra, scegliamo Image View e trasciniamola dentro la Viewdi destra, scegliamo Image View e trasciniamola dentro la View schermo, nel pannello Objects a sinistra potete vedere la Imageschermo, nel pannello Objects a sinistra potete vedere la Image View all’interno della gerarchia della vista schermo. Spostiamo eView all’interno della gerarchia della vista schermo. Spostiamo e ridimensioniamo la Image View in modo che si sovrappongaridimensioniamo la Image View in modo che si sovrapponga esattamente alla view schermo; a questo punto dal pannelloesattamente alla view schermo; a questo punto dal pannello Objects (a sinistra) la trasciniamo fuori dalla gerarchia schermo eObjects (a sinistra) la trasciniamo fuori dalla gerarchia schermo e la mettiamo allo stesso livello della view esterna contenitore, allala mettiamo allo stesso livello della view esterna contenitore, alla fine avrete qualcosa di questo tipofine avrete qualcosa di questo tipo

Page 176: Xcode4 Tutorial Completo

Ora abbiamo quindi bisogno di un comportamento diversoOra abbiamo quindi bisogno di un comportamento diverso nell’interfaccia a seconda del canale selezionato.nell’interfaccia a seconda del canale selezionato.

Dove possiamo programmare questo comportamento? Direi nelDove possiamo programmare questo comportamento? Direi nel metodo che usiamo per ridisegnare l’interfaccia a secondametodo che usiamo per ridisegnare l’interfaccia a seconda dell’evento, cioè quello dove aggiorniamo la label con il nome deldell’evento, cioè quello dove aggiorniamo la label con il nome del canale canale observeValueForKeyPath:ofObject:change:observeValueForKeyPath:ofObject:change:..

Se guardate la documentazione di UIImageView vedete che haSe guardate la documentazione di UIImageView vedete che ha una property una property imageimage di tipo UIImage. Questa property si riferisce di tipo UIImage. Questa property si riferisce all’oggetto, istanza di UIImage, che contiene l’immagine che deveall’oggetto, istanza di UIImage, che contiene l’immagine che deve essere mostrata. Quindi il nostro codice dovrà assegnare il correttoessere mostrata. Quindi il nostro codice dovrà assegnare il corretto valore a questa property, vuol dire che abbiamo bisogno di unvalore a questa property, vuol dire che abbiamo bisogno di un legame dal codice all’interfaccia, cioè un IBOutlet verso lalegame dal codice all’interfaccia, cioè un IBOutlet verso la UIImageView. Aggiungiamo in TVController.hUIImageView. Aggiungiamo in TVController.h

@property (nonatomic, retain) IBOutlet UIImageView@property (nonatomic, retain) IBOutlet UIImageView *immagineCanale;*immagineCanale;ed in TVController.m la corrispondenteed in TVController.m la corrispondente

Page 177: Xcode4 Tutorial Completo

@synthesize immagineCanale;@synthesize immagineCanale;Colleghiamo nella componente Interface Builder il nostroColleghiamo nella componente Interface Builder il nostro IBOutlet all’oggetto di interfaccia. Selezioniamo il fileIBOutlet all’oggetto di interfaccia. Selezioniamo il file TelevisoreView.xib, e cliccando con il destro su File’s ownerTelevisoreView.xib, e cliccando con il destro su File’s owner colleghiamo l’outlet immagineCanale alla UIImageViewcolleghiamo l’outlet immagineCanale alla UIImageView trascinando il mouse con il sinistro premuto.trascinando il mouse con il sinistro premuto.

Vediamo ora come scrivere il codice del metodo e poi loVediamo ora come scrivere il codice del metodo e poi lo analizziamo.analizziamo.

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context(id)object change:(NSDictionary *)change context:(void *)context {{......if ([keyPath isEqualToString:@"canaleSintonizzato"]) {if ([keyPath isEqualToString:@"canaleSintonizzato"]) {numCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzatnumCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzato intValue];o intValue];nomeCanale.text=ilTelevisore.nomeDelCanale;nomeCanale.text=ilTelevisore.nomeDelCanale;switch ([ilTelevisore.canaleSintonizzato intValue]) {switch ([ilTelevisore.canaleSintonizzato intValue]) {case 0:case 0:case 1:case 1:case 2:case 2:immagineCanale.image=[UIImageimmagineCanale.image=[UIImage imageNamed:ilTelevisore.nomeDelCanale];imageNamed:ilTelevisore.nomeDelCanale];[schermo addSubview:immagineCanale];[schermo addSubview:immagineCanale];break;break;default:default:break;break;}}} else {} else {volume.value=[ilTelevisore.livelloVolume floatValue];volume.value=[ilTelevisore.livelloVolume floatValue];livelloVolume.text = [NSStringlivelloVolume.text = [NSString stringWithFormat:@"%f",volume.value];stringWithFormat:@"%f",volume.value];}}}}}}

Page 178: Xcode4 Tutorial Completo

Operiamo sul ramo if nel quale abbiamo verificato che l’eventoOperiamo sul ramo if nel quale abbiamo verificato che l’evento che stiamo gestendo è il cambio di canale (cioè è stato assegnatoche stiamo gestendo è il cambio di canale (cioè è stato assegnato un valore alla property canaleSintonizzato de ilTelevisore,un valore alla property canaleSintonizzato de ilTelevisore, ricordate il pattern KVO visto nel cap. precedente).ricordate il pattern KVO visto nel cap. precedente).

All’interno di questo ramo, dopo le prime due istruzioni cheAll’interno di questo ramo, dopo le prime due istruzioni che c’erano già e quindi non discutiamo, abbiamo aggiuntoc’erano già e quindi non discutiamo, abbiamo aggiunto un’istruzione un’istruzione switchswitch; l’abbiamo incontrata già qualche capitolo fa,; l’abbiamo incontrata già qualche capitolo fa, il suo comportamento è quello di valutare l’espressione trail suo comportamento è quello di valutare l’espressione tra parentesi tonda, che deve restituire un valore intero, ed in base alparentesi tonda, che deve restituire un valore intero, ed in base al risultato di questa valutazione esegue le istruzioni partendo dalrisultato di questa valutazione esegue le istruzioni partendo dal gruppo case relativo al valore risultante fino al primo breakgruppo case relativo al valore risultante fino al primo break incontrato. Il nostro switch èincontrato. Il nostro switch è

switch ([ilTelevisore.canaleSintonizzato intValue]) {switch ([ilTelevisore.canaleSintonizzato intValue]) {case 0:case 0:case 1:case 1:case 2:case 2:immagineCanale.image=[UIImageimmagineCanale.image=[UIImage imageNamed:ilTelevisore.nomeDelCanale];imageNamed:ilTelevisore.nomeDelCanale];[schermo addSubview:immagineCanale];[schermo addSubview:immagineCanale];break;break;default:default:break;break;}}L’espressione da valutare èL’espressione da valutare è [ilTelevisore.canaleSintonizzato [ilTelevisore.canaleSintonizzato intValue] che restituisce il valore intero (intValue) dell’oggettointValue] che restituisce il valore intero (intValue) dell’oggetto riferito dalla property canaleSintonizzato (che è unriferito dalla property canaleSintonizzato (che è un NSDecimalNumber) de ilTelevisore, cioè ci restituirà 0,1,2,3,4 aNSDecimalNumber) de ilTelevisore, cioè ci restituirà 0,1,2,3,4 a seconda del canale sintonizzato.seconda del canale sintonizzato.

Per come abbiamo deciso di visualizzare i contenuti, i primi 3Per come abbiamo deciso di visualizzare i contenuti, i primi 3 canali (0,1 e 2) mostrano le 3 foto, possiamo far sì che case 0, casecanali (0,1 e 2) mostrano le 3 foto, possiamo far sì che case 0, case 1 e case 2 eseguano la stessa sequenza di istruzioni scrivendo1 e case 2 eseguano la stessa sequenza di istruzioni scrivendo

case 0:case 0:case 1:case 1:

Page 179: Xcode4 Tutorial Completo

case 2:case 2:immagineCanale.image=[UIImageimmagineCanale.image=[UIImage imageNamed:ilTelevisore.nomeDelCanale];imageNamed:ilTelevisore.nomeDelCanale];[schermo addSubview:immagineCanale];[schermo addSubview:immagineCanale];break;break;questo vuol dire che se abbiamo 0, poiché cominciamo a scorrerequesto vuol dire che se abbiamo 0, poiché cominciamo a scorrere il codice da case 0 fino al primo break eseguiremo le istruzioniil codice da case 0 fino al primo break eseguiremo le istruzioni scritte dopo case 2:, lo stesso se otteniamo 1 e 2. Quindi in tutti escritte dopo case 2:, lo stesso se otteniamo 1 e 2. Quindi in tutti e tre i casi noi eseguiamotre i casi noi eseguiamo

immagineCanale.image=[UIImageimmagineCanale.image=[UIImage imageNamed:ilTelevisore.nomeDelCanale];imageNamed:ilTelevisore.nomeDelCanale];[schermo addSubview:immagineCanale];[schermo addSubview:immagineCanale];immagineCanale è il nostro IBOutlet che referenzia l’oggettoimmagineCanale è il nostro IBOutlet che referenzia l’oggetto UIImageView che abbiamo creato in Interface Builder. ComeUIImageView che abbiamo creato in Interface Builder. Come abbiamo detto sopra questa classe espone una property, abbiamo detto sopra questa classe espone una property, imageimage, alla, alla quale deve essere assegnata l’immagine da visualizzare nella view.quale deve essere assegnata l’immagine da visualizzare nella view. Noi stiamo assegnando il risultato di [UIImageNoi stiamo assegnando il risultato di [UIImage imageNamed:ilTelevisore.nomeDelCanale] questa è l’invocazioneimageNamed:ilTelevisore.nomeDelCanale] questa è l’invocazione di un metodo di classe, di un metodo di classe, imageNamedimageNamed, della classe UIImage., della classe UIImage. Questo metodo prendo in input il nome di un file immagineQuesto metodo prendo in input il nome di un file immagine presente nel progetto e restituisce un’istanza della classe UIImagepresente nel progetto e restituisce un’istanza della classe UIImage che lo rappresenta. Il parametro che noi passiamo in ingresso èche lo rappresenta. Il parametro che noi passiamo in ingresso è ilTelevisore.nomeDelCanale che è la property che abbiamoilTelevisore.nomeDelCanale che è la property che abbiamo dichiarato nella classe Televisore e che restituisce il nome deldichiarato nella classe Televisore e che restituisce il nome del canale Onda1, Onda2,…, in questo modo andremo a selezionare ilcanale Onda1, Onda2,…, in questo modo andremo a selezionare il nome corretto del file da mostrare.nome corretto del file da mostrare.

Approfondimento: Studiare sulla documentazione Apple la classeApprofondimento: Studiare sulla documentazione Apple la classe UIImage.UIImage.

La seconda istruzioneLa seconda istruzione

[schermo addSubview:immagineCanale];[schermo addSubview:immagineCanale];è l’invocazione del metodo addSubview sulla view schermoè l’invocazione del metodo addSubview sulla view schermo passando come argomento la nostra UIImageViewpassando come argomento la nostra UIImageView immagineCanale. La nostra immagineCanale viene aggiunta nellaimmagineCanale. La nostra immagineCanale viene aggiunta nella

Page 180: Xcode4 Tutorial Completo

gerarchia di viste come sotto vista dello schermo; è analogo agerarchia di viste come sotto vista dello schermo; è analogo a quello che si fa da Interface Builder spostando l’oggettoquello che si fa da Interface Builder spostando l’oggetto UIImageView dentro la gerarchia della view schermo. Poichè laUIImageView dentro la gerarchia della view schermo. Poichè la view schermo è visualizzata nell’interfaccia questa istruzione haview schermo è visualizzata nell’interfaccia questa istruzione ha l’effetto di mostrare anche la UIImageView immagineCanale el’effetto di mostrare anche la UIImageView immagineCanale e quindi l’immagine contenuta.quindi l’immagine contenuta.

Provate ad eseguire il programma e verificate che al cambio diProvate ad eseguire il programma e verificate che al cambio di canale siano mostrate le vostre immagini.canale siano mostrate le vostre immagini.

Bene, se tutto funziona, abbiamo messo in piedi il meccanismoBene, se tutto funziona, abbiamo messo in piedi il meccanismo centrale del cambio di immagine ma dobbiamo sistemare ancoracentrale del cambio di immagine ma dobbiamo sistemare ancora alcuni particolari prima di andare avanti.alcuni particolari prima di andare avanti.

Il primo lo osservate se provate ad andare sui canali 4 e 5; vedreteIl primo lo osservate se provate ad andare sui canali 4 e 5; vedrete che resta visualizzata l’ultima immagine mostrata, questo perchéche resta visualizzata l’ultima immagine mostrata, questo perché noi abbiamo sempre aggiunto la subview ma non l’abbiamo mainoi abbiamo sempre aggiunto la subview ma non l’abbiamo mai tolta; lo stesso effetto è presente quando si spegne il televisore,tolta; lo stesso effetto è presente quando si spegne il televisore, resta visualizzata l’ultima immagine. Quindi quello che dobbiamoresta visualizzata l’ultima immagine. Quindi quello che dobbiamo fare è che quando cambiamo canale o quando spegniamo ilfare è che quando cambiamo canale o quando spegniamo il televisore dobbiamo togliere la sottovista.televisore dobbiamo togliere la sottovista.

Nel metodo Nel metodo observeValueForKeyPath:ofObject:change:observeValueForKeyPath:ofObject:change: prima prima dell’istruzione switch che abbiamo inserito per gestire il cambiodell’istruzione switch che abbiamo inserito per gestire il cambio dei canali aggiungiamodei canali aggiungiamo

[immagineCanale removeFromSuperview];[immagineCanale removeFromSuperview];questa è l’invocazione del metodo removeFromSuperviewquesta è l’invocazione del metodo removeFromSuperview sull’oggetto immagineCanale; il suo effetto è quello di togliere lasull’oggetto immagineCanale; il suo effetto è quello di togliere la UIImageView dalla gerarchia di schermo e quindi non viene piùUIImageView dalla gerarchia di schermo e quindi non viene più visualizzata.visualizzata.

La stessa istruzione la aggiungiamo nel ramo dove gestiamo loLa stessa istruzione la aggiungiamo nel ramo dove gestiamo lo spegnimento del televisore, cioè prima dell’istruzionespegnimento del televisore, cioè prima dell’istruzione

schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];La versione attuale del metodo sarà quindiLa versione attuale del metodo sarà quindi

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:

Page 181: Xcode4 Tutorial Completo

(id)object change:(NSDictionary *)change context:(void *)context(id)object change:(NSDictionary *)change context:(void *)context {{if ([keyPath isEqualToString:@"acceso"]) {if ([keyPath isEqualToString:@"acceso"]) {if ([[change valueForKey:@"new"] boolValue]) {if ([[change valueForKey:@"new"] boolValue]) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];interruttore.on=TRUE;interruttore.on=TRUE;} else {} else {[immagineCanale removeFromSuperview];[immagineCanale removeFromSuperview];schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];interruttore.on=FALSE;interruttore.on=FALSE;}}} else {} else {if ([keyPath isEqualToString:@"canaleSintonizzato"]) {if ([keyPath isEqualToString:@"canaleSintonizzato"]) {numCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzatnumCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzato intValue];o intValue];nomeCanale.text=ilTelevisore.nomeDelCanale;nomeCanale.text=ilTelevisore.nomeDelCanale;[immagineCanale removeFromSuperview];[immagineCanale removeFromSuperview];switch ([ilTelevisore.canaleSintonizzato intValue]) {switch ([ilTelevisore.canaleSintonizzato intValue]) {case 0:case 0:case 1:case 1:case 2:case 2:immagineCanale.image=[UIImageimmagineCanale.image=[UIImage imageNamed:ilTelevisore.nomeDelCanale];imageNamed:ilTelevisore.nomeDelCanale];[schermo addSubview:immagineCanale];[schermo addSubview:immagineCanale];break;break;default:default:break;break;}}} else {} else {volume.value=[ilTelevisore.livelloVolume floatValue];volume.value=[ilTelevisore.livelloVolume floatValue];livelloVolume.text = [NSStringlivelloVolume.text = [NSString stringWithFormat:@"%f",volume.value];stringWithFormat:@"%f",volume.value];}}}}}}

Page 182: Xcode4 Tutorial Completo

L’altro particolare da sistemare è il fatto che quando accendete ilL’altro particolare da sistemare è il fatto che quando accendete il televisore lo schermo rimane grigio e non mostra nulla anche se iltelevisore lo schermo rimane grigio e non mostra nulla anche se il selettore del canale è su 1 e ci aspetteremmo di vedere la scrittaselettore del canale è su 1 e ci aspetteremmo di vedere la scritta Onda1 e l’immagine relativa al canale 1; questo avviene perché daOnda1 e l’immagine relativa al canale 1; questo avviene perché da nessuna parte nel codice abbiamo detto come aggiornare lanessuna parte nel codice abbiamo detto come aggiornare la visualizzazione al momento dell’accensione. Per risolvere questovisualizzazione al momento dell’accensione. Per risolvere questo problema la cosa migliore è utilizzare il codice che abbiamoproblema la cosa migliore è utilizzare il codice che abbiamo scritto finora ed attivare il KVO anche al momentoscritto finora ed attivare il KVO anche al momento dell’accensione. Il modo più semplice è forzare la scrittura deldell’accensione. Il modo più semplice è forzare la scrittura del valore nella property canaleSintonizzato in modo da attivarevalore nella property canaleSintonizzato in modo da attivare l’invocazione del metodol’invocazione del metodo observeValueForKeyPath:ofObject:change:, observeValueForKeyPath:ofObject:change:, quindi andiamo inquindi andiamo in Televisore.m e spostiamo l’istruzioneTelevisore.m e spostiamo l’istruzione

self.canaleSintonizzato=[NSDecimalNumber zero];self.canaleSintonizzato=[NSDecimalNumber zero];che inizializza la property canaleSintonizzato, dal metodo che inizializza la property canaleSintonizzato, dal metodo initinit al al metodo metodo accendiaccendi; in tal modo ogni volta che accendiamo il; in tal modo ogni volta che accendiamo il televisore aggiorniamo il valore della property e quinditelevisore aggiorniamo il valore della property e quindi scateniamo il metodo di osservazione.scateniamo il metodo di osservazione.

Passiamo ora a gestire il canale 4; nel quale vogliamo vedere unPassiamo ora a gestire il canale 4; nel quale vogliamo vedere un filmato.filmato.

Non è scopo di questo percorso coprire tutti gli aspetti relativi allaNon è scopo di questo percorso coprire tutti gli aspetti relativi alla riproduzione di contenuti multimediali, per il loro trattamentoriproduzione di contenuti multimediali, per il loro trattamento completo vi rimando alla documentazione relativa.completo vi rimando alla documentazione relativa.

Prima cosa procuratevi un filmato in formato m4v; rinominateloPrima cosa procuratevi un filmato in formato m4v; rinominatelo Onda4.m4v e trascinatelo dentro la cartella myTv di XCode,Onda4.m4v e trascinatelo dentro la cartella myTv di XCode, ricordatevi di attivare il flag Copy Items…ricordatevi di attivare il flag Copy Items…

La riproduzione di un filmato richiede l’utilizzo di un oggettoLa riproduzione di un filmato richiede l’utilizzo di un oggetto specializzato (analogamente all’uso di una UIImageView perspecializzato (analogamente all’uso di una UIImageView per mostrare un’immagine) della classe mostrare un’immagine) della classe MPMoviePlayerControllerMPMoviePlayerController..

Approfondimento: Studiare sulla documentazione Apple la classeApprofondimento: Studiare sulla documentazione Apple la classe MPMovieController.MPMovieController.

Page 183: Xcode4 Tutorial Completo

Un’istanza di questa classe ha una property view, che si riferisceUn’istanza di questa classe ha una property view, che si riferisce alla view nella quale viene riprodotto il filmato. L’indicazione delalla view nella quale viene riprodotto il filmato. L’indicazione del filmato da riprodurre è gestita da un’altra property, contentURL,filmato da riprodurre è gestita da un’altra property, contentURL, che si riferisce ad un’istanza di NSURL.che si riferisce ad un’istanza di NSURL.

Approfondimento: Studiare sulla documentazione Apple la classeApprofondimento: Studiare sulla documentazione Apple la classe NSURL.NSURL.

La classe MPMovieController è definita in un altro framework cheLa classe MPMovieController è definita in un altro framework che deve quindi essere aggiunto al progetto. Selezionate il progettodeve quindi essere aggiunto al progetto. Selezionate il progetto nell’alberatura di XCode, nel pannello di dettaglio selezionate ilnell’alberatura di XCode, nel pannello di dettaglio selezionate il target myTv e andate nel tab Build Phases. Aprite la sezione Linktarget myTv e andate nel tab Build Phases. Aprite la sezione Link Binary With Libraries e cliccate sul +. Nel pannello che si apreBinary With Libraries e cliccate sul +. Nel pannello che si apre selezionate il framework MediaPlayer.framework.selezionate il framework MediaPlayer.framework.

Oltre all’aggiunta del framework, nel quale sono definite le classi,Oltre all’aggiunta del framework, nel quale sono definite le classi, abbiamo bisogno anche di rendere noto al codice le dichiarazioniabbiamo bisogno anche di rendere noto al codice le dichiarazioni delle classi, quindi in TVController.h aggiungiamodelle classi, quindi in TVController.h aggiungiamo

#import <MediaPlayer/MediaPlayer.h>#import <MediaPlayer/MediaPlayer.h>insieme alle altre import.insieme alle altre import.

Ora andiamo a dichiarare le due variabili di istanza che ci servonoOra andiamo a dichiarare le due variabili di istanza che ci servono per gestire il player e il nome del filmato.per gestire il player e il nome del filmato.

Ancora in TVController.h, all’interno delle {} scriviamoAncora in TVController.h, all’interno delle {} scriviamo

NSURL *movieURL;NSURL *movieURL;MPMoviePlayerController *player;MPMoviePlayerController *player;cioè abbiamo dichiarato due variabili di istanza movieURL ecioè abbiamo dichiarato due variabili di istanza movieURL e player. Nella prima manterremo il nome del file con il filmato daplayer. Nella prima manterremo il nome del file con il filmato da riprodurre. Nella seconda avremo l’oggetto per la riproduzione delriprodurre. Nella seconda avremo l’oggetto per la riproduzione del video. Ora che le abbiamo dichiarate andiamo a definirle.video. Ora che le abbiamo dichiarate andiamo a definirle.

In TVController.m, nel metodoIn TVController.m, nel metodo bserveValueForKeyPath:ofObject:change: nello switch/case dibserveValueForKeyPath:ofObject:change: nello switch/case di gestione dei canali aggiungiamogestione dei canali aggiungiamo

case 3:case 3:

Page 184: Xcode4 Tutorial Completo

cioè il punto di ingresso per la gestione del canale 4.cioè il punto di ingresso per la gestione del canale 4.

La prima cosa che dobbiamo fare è creare un’istanza di NSURLLa prima cosa che dobbiamo fare è creare un’istanza di NSURL per il riferimento al nome de file video, aggiungiamo quindiper il riferimento al nome de file video, aggiungiamo quindi

movieURL = [NSURL fileURLWithPath:[[NSBundlemovieURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:ilTelevisore.nomeDelCanalemainBundle] pathForResource:ilTelevisore.nomeDelCanale ofType:@"m4v"]];ofType:@"m4v"]];stiamo assegnando alla variabile di istanza movieURL il risultatostiamo assegnando alla variabile di istanza movieURL il risultato dell’invocazione del metodo fileURLWithPath della classedell’invocazione del metodo fileURLWithPath della classe NSURL. Questo metodo crea un’istanza di NSURL che si riferisceNSURL. Questo metodo crea un’istanza di NSURL che si riferisce al file il cui percorso è passato come argomento. L’argomento cheal file il cui percorso è passato come argomento. L’argomento che passiamo èpassiamo è

[[NSBundle mainBundle][[NSBundle mainBundle] pathForResource:ilTelevisore.nomeDelCanale ofType:@"m4v"]pathForResource:ilTelevisore.nomeDelCanale ofType:@"m4v"]NSBundle, l’abbiamo già incontrata ed è una classe che fornisceNSBundle, l’abbiamo già incontrata ed è una classe che fornisce diverse funzionalità per operare sulla struttura dell’applicazionediverse funzionalità per operare sulla struttura dell’applicazione (vi rimando alla documentazione per il suo approfondimento),(vi rimando alla documentazione per il suo approfondimento), in in particolare il metodo che stiamo invocando pathForResource,particolare il metodo che stiamo invocando pathForResource, restituisce il percorso del file il cui nome gli è passato comerestituisce il percorso del file il cui nome gli è passato come argomento, ilTelevisore.nomeDelCanale che vi ricordo in questoargomento, ilTelevisore.nomeDelCanale che vi ricordo in questo momento vale “Onda4”, ed il cui tipo è m4v. Quindi in sostanzamomento vale “Onda4”, ed il cui tipo è m4v. Quindi in sostanza assegnamo alla variabile movieURL un’istanza di NSURL con ilassegnamo alla variabile movieURL un’istanza di NSURL con il riferimento al nostro filmato.riferimento al nostro filmato.

Quello che dobbiamo fare ora è creare l’oggetto player cheQuello che dobbiamo fare ora è creare l’oggetto player che abbiamo dichiarato come variabile di istanza. Aggiungiamo quindiabbiamo dichiarato come variabile di istanza. Aggiungiamo quindi

player =[[MPMoviePlayerController alloc]player =[[MPMoviePlayerController alloc] initWithContentURL:movieURL];initWithContentURL:movieURL];è la classica struttura alloc/init per la creazione di istanze con laè la classica struttura alloc/init per la creazione di istanze con la particolarità che l’init di questa classe prevede anche il passaggioparticolarità che l’init di questa classe prevede anche il passaggio dell’URL del video da riprodurre. Ora dobbiamo dire quanto èdell’URL del video da riprodurre. Ora dobbiamo dire quanto è grande la vista del player per la riproduzione, è l’operazionegrande la vista del player per la riproduzione, è l’operazione analoga a quello che abbiamo fatto da Interface Builder quandoanaloga a quello che abbiamo fatto da Interface Builder quando abbiamo ridimensionato la UIImageView per renderla grandeabbiamo ridimensionato la UIImageView per renderla grande

Page 185: Xcode4 Tutorial Completo

quantoquanto la view schermo. Aggiungiamo la view schermo. Aggiungiamo

player.view.frame=schermo.bounds;player.view.frame=schermo.bounds;Cioè assegnamo alla property frame della property view del playerCioè assegnamo alla property frame della property view del player le stesse dimensioni della property bounds della vista schermo.le stesse dimensioni della property bounds della vista schermo.

Approfondimento: Andate sulla documentazione di UIView eApprofondimento: Andate sulla documentazione di UIView e guardate la differenza tra la property frame e la property bounds.guardate la differenza tra la property frame e la property bounds.

A questo punto rendiamo visibile la view del player, comeA questo punto rendiamo visibile la view del player, come abbiamo fatto per la UIImageView, aggiungendola come sottovistaabbiamo fatto per la UIImageView, aggiungendola come sottovista dello schermo, aggiungiamo quindi l‘istruzionedello schermo, aggiungiamo quindi l‘istruzione

[schermo addSubview:player.view];[schermo addSubview:player.view];che aggiunge la view del player nella gerarchia della viewche aggiunge la view del player nella gerarchia della view schermo. Infine lanciamo la riproduzione del video aggiungendoschermo. Infine lanciamo la riproduzione del video aggiungendo l’istruzionel’istruzione

[player play];[player play];cioè invochiamo il metodo play sull’oggetto player che è istanzacioè invochiamo il metodo play sull’oggetto player che è istanza di MPMoviePlayerController. Il metodo play è predefinito perdi MPMoviePlayerController. Il metodo play è predefinito per questa classe ed ha l’effetto di riprodurre il filmato indicato dallaquesta classe ed ha l’effetto di riprodurre il filmato indicato dalla property contentURL.property contentURL.

Come abbiamo fatto per la view delle immagini; dobbiamoCome abbiamo fatto per la view delle immagini; dobbiamo togliere la view del player dalla gerarchia, quando non ne abbiamotogliere la view del player dalla gerarchia, quando non ne abbiamo più bisogno. Andiamo quindi prima dell’istruzione switch, dovepiù bisogno. Andiamo quindi prima dell’istruzione switch, dove abbiamo inserito l’istruzioneabbiamo inserito l’istruzione

[immagineCanale removeFromSuperview];[immagineCanale removeFromSuperview];ed aggiungiamoed aggiungiamo

[player.view removeFromSuperview];[player.view removeFromSuperview];La stessa istruzione l’aggiungiamo nel blocco dove gestiamoLa stessa istruzione l’aggiungiamo nel blocco dove gestiamo l’evento di spegnimento; cioè il ramo else della condizionel’evento di spegnimento; cioè il ramo else della condizione

if ([keyPath isEqualToString:@"acceso"])if ([keyPath isEqualToString:@"acceso"])La versione corrente del metodo diventaLa versione corrente del metodo diventa

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:

Page 186: Xcode4 Tutorial Completo

(id)object change:(NSDictionary *)change context:(void *)context(id)object change:(NSDictionary *)change context:(void *)context {{if ([keyPath isEqualToString:@"acceso"]) {if ([keyPath isEqualToString:@"acceso"]) {if ([[change valueForKey:@"new"] boolValue]) {if ([[change valueForKey:@"new"] boolValue]) {schermo.backgroundColor=[UIColor lightGrayColor];schermo.backgroundColor=[UIColor lightGrayColor];interruttore.on=TRUE;interruttore.on=TRUE;} else {} else {[immagineCanale removeFromSuperview];[immagineCanale removeFromSuperview];[player.view removeFromSuperview];[player.view removeFromSuperview];schermo.backgroundColor=[UIColor blackColor];schermo.backgroundColor=[UIColor blackColor];interruttore.on=FALSE;interruttore.on=FALSE;}}} else {} else {if ([keyPath isEqualToString:@"canaleSintonizzato"]) {if ([keyPath isEqualToString:@"canaleSintonizzato"]) {numCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzatnumCanale.selectedSegmentIndex=[ilTelevisore.canaleSintonizzato intValue];o intValue];nomeCanale.text=ilTelevisore.nomeDelCanale;nomeCanale.text=ilTelevisore.nomeDelCanale;[immagineCanale removeFromSuperview];[immagineCanale removeFromSuperview];[player.view removeFromSuperview];[player.view removeFromSuperview];switch ([ilTelevisore.canaleSintonizzato intValue]) {switch ([ilTelevisore.canaleSintonizzato intValue]) {case 0:case 0:case 1:case 1:case 2:case 2:immagineCanale.image=[UIImageimmagineCanale.image=[UIImage imageNamed:ilTelevisore.nomeDelCanale];imageNamed:ilTelevisore.nomeDelCanale];[schermo addSubview:immagineCanale];[schermo addSubview:immagineCanale];break;break;case 3:case 3:movieURL = [NSURL fileURLWithPath:[[NSBundlemovieURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:ilTelevisore.nomeDelCanalemainBundle] pathForResource:ilTelevisore.nomeDelCanale ofType:@"m4v"]];ofType:@"m4v"]];player =[[MPMoviePlayerController alloc]player =[[MPMoviePlayerController alloc] initWithContentURL:movieURL];initWithContentURL:movieURL];player.view.frame=schermo.bounds;player.view.frame=schermo.bounds;[schermo addSubview:player.view];[schermo addSubview:player.view];

Page 187: Xcode4 Tutorial Completo

[player play];[player play];break;break;default:default:break;break;}}} else {} else {volume.value=[ilTelevisore.livelloVolume floatValue];volume.value=[ilTelevisore.livelloVolume floatValue];livelloVolume.text = [NSStringlivelloVolume.text = [NSString stringWithFormat:@"%f",volume.value];stringWithFormat:@"%f",volume.value];}}}}}}Eseguiamo l’app e verifichiamo che i canali da 1 a 4 sianoEseguiamo l’app e verifichiamo che i canali da 1 a 4 siano correttamente visualizzati.correttamente visualizzati.

Passiamo ora a gestire la visualizzazione del canale 5, nel qualePassiamo ora a gestire la visualizzazione del canale 5, nel quale abbiamo deciso che vogliamo visualizzare un sito. Prima cosaabbiamo deciso che vogliamo visualizzare un sito. Prima cosa diciamo quale sito vogliamo visualizzare; andiamo indiciamo quale sito vogliamo visualizzare; andiamo in Televisore.m e nel metodo ricercaCanali, dove inizializziamo laTelevisore.m e nel metodo ricercaCanali, dove inizializziamo la property canaliMemorizzati con i valori “Onda1”,property canaliMemorizzati con i valori “Onda1”, “Onda2”,”Onda3”,… sostituiamo “Onda5” con ““Onda2”,”Onda3”,… sostituiamo “Onda5” con “http://www.apple.com”” o con un altro indirizzo a vostra scelta.o con un altro indirizzo a vostra scelta.

La visualizzazione di un sito web all’interno di un app è gestita daLa visualizzazione di un sito web all’interno di un app è gestita da una classe specifica UIWebView, così come per la visualizzazioneuna classe specifica UIWebView, così come per la visualizzazione di immagini si usa un UIImageView e per la visualizzazione di undi immagini si usa un UIImageView e per la visualizzazione di un video si usa un view inclusa in un MPMoviePlayerController.video si usa un view inclusa in un MPMoviePlayerController. Un’istanza di UIWebView sarà quindi una view che metteremoUn’istanza di UIWebView sarà quindi una view che metteremo come subview della view schermo e che sarà in grado dicome subview della view schermo e che sarà in grado di visualizzare un sito web.visualizzare un sito web.

Nei casi precedenti abbiamo visto due modalità diverse diNei casi precedenti abbiamo visto due modalità diverse di creazione delle view di gestione dei contenuti. Nel primo caso, lacreazione delle view di gestione dei contenuti. Nel primo caso, la visualizzazione di immagini, abbiamo creato la view da Interfacevisualizzazione di immagini, abbiamo creato la view da Interface Builder e da codice abbiamo solo gestito l’inserimento e laBuilder e da codice abbiamo solo gestito l’inserimento e la rimozione dalla gerarchia. Nel secondo caso invece abbiamorimozione dalla gerarchia. Nel secondo caso invece abbiamo

Page 188: Xcode4 Tutorial Completo

anche creato la view da codice, anche se implicitamente con laanche creato la view da codice, anche se implicitamente con la creazione di un controller. Queste sono due modalità che quandocreazione di un controller. Queste sono due modalità che quando sono entrambe percorribili sono entrambe corrette. Nel caso dellasono entrambe percorribili sono entrambe corrette. Nel caso della WebView utilizzeremo nuovamente la strada di creare tutto daWebView utilizzeremo nuovamente la strada di creare tutto da codice.codice.

Iniziamo con il dichiarare le due variabili di istanza che useremo.Iniziamo con il dichiarare le due variabili di istanza che useremo. In TVController.h dentro le {} aggiungiamoIn TVController.h dentro le {} aggiungiamo

NSURL *sitoURL;NSURL *sitoURL;UIWebView *myWebView;UIWebView *myWebView;La prima si riferirà ad un’istanza di NSURL che conterràLa prima si riferirà ad un’istanza di NSURL che conterrà l’indirizzo del sito da visualizzare, mentre la seconda conterràl’indirizzo del sito da visualizzare, mentre la seconda conterrà l’istanza di UIWebView responsabile della visualizzazione.l’istanza di UIWebView responsabile della visualizzazione.

Ora andiamo nel metodo observe… e all’interno dello switch nelOra andiamo nel metodo observe… e all’interno dello switch nel quale stiamo gestendo i cambi di canale, prima di default:quale stiamo gestendo i cambi di canale, prima di default: aggiungiamo il punto di ingresso per l’opzione 4.aggiungiamo il punto di ingresso per l’opzione 4.

case 4:case 4:La prima cosa che dobbiamo fare a questo punto è creare l’oggettoLa prima cosa che dobbiamo fare a questo punto è creare l’oggetto NSURL con l’indirizzo del sitoNSURL con l’indirizzo del sito

sitoURL=[NSURL URLWithString:ilTelevisore.nomeDelCanale];sitoURL=[NSURL URLWithString:ilTelevisore.nomeDelCanale];alla nostra variabile di istanza sitoURL assegnamo il risultatoalla nostra variabile di istanza sitoURL assegnamo il risultato dell’invocazione del metodo di classe URLWithString, questodell’invocazione del metodo di classe URLWithString, questo metodo restituisce un’istanza di NSURL che rappresentametodo restituisce un’istanza di NSURL che rappresenta un’indirzzo che gli viene passato in ingresso come oggettoun’indirzzo che gli viene passato in ingresso come oggetto NSString. Il nostro NSString è ilTelevisore.nomeDelCanale cheNSString. Il nostro NSString è ilTelevisore.nomeDelCanale che restituisce il valore @”http://…” che abbiamo scritto nel metodorestituisce il valore @”http://…” che abbiamo scritto nel metodo ricercaCanali del televisore.ricercaCanali del televisore.

Poi creiamo l’istanza della nostra web view aggiungendoPoi creiamo l’istanza della nostra web view aggiungendo

myWebView = [[UIWebView alloc] init];myWebView = [[UIWebView alloc] init];usiamo la coppia alloc/init per creare un’istanza di UIWebView edusiamo la coppia alloc/init per creare un’istanza di UIWebView ed assegnarla alla nostra variabile di istanza.assegnarla alla nostra variabile di istanza.

Ora configuriamo la nostra view in modo da darci una visione ilOra configuriamo la nostra view in modo da darci una visione il

Page 189: Xcode4 Tutorial Completo

più possibile completa del sito, anche se è piccola, aggiungendopiù possibile completa del sito, anche se è piccola, aggiungendo

myWebView.scalesPageToFit=YES;myWebView.scalesPageToFit=YES;la property scalesPageToFit ridimensiona la pagina in modo dala property scalesPageToFit ridimensiona la pagina in modo da visualizzarne la larghezza dentro la view.visualizzarne la larghezza dentro la view.

Diamo alla webView la stessa dimensione dello schermoDiamo alla webView la stessa dimensione dello schermo

myWebView.frame=schermo.bounds;myWebView.frame=schermo.bounds;Chiediamo alla webView di caricare il nostro sitoChiediamo alla webView di caricare il nostro sito

[myWebView loadRequest:[NSURLRequest[myWebView loadRequest:[NSURLRequest requestWithURL:sitoURL]];requestWithURL:sitoURL]];Il metodo loadRequest ha l’effetto di caricare nella webView cheIl metodo loadRequest ha l’effetto di caricare nella webView che lo esegue l’URL che gli viene passato come argomento.lo esegue l’URL che gli viene passato come argomento. L’argomento è passato come istanza di NSURLRequest, quindi èL’argomento è passato come istanza di NSURLRequest, quindi è necessario creare un’istanza di NSURLRequestnecessario creare un’istanza di NSURLRequest a partire dal a partire dal nostro oggetto NSURL, attraverso il metodo requestWithURL chenostro oggetto NSURL, attraverso il metodo requestWithURL che prende in ingresso il nostro oggetto NSURL sitoURL e restituisceprende in ingresso il nostro oggetto NSURL sitoURL e restituisce un oggetto di NSURLRequest che viene passato a loadRequest.un oggetto di NSURLRequest che viene passato a loadRequest.

Infine visualizziamo la webView inserendola nella gerarchia diInfine visualizziamo la webView inserendola nella gerarchia di schermoschermo

[schermo addSubview:myWebView];[schermo addSubview:myWebView];Anche in questo caso , come nei precedenti, dobbiamo togliere laAnche in questo caso , come nei precedenti, dobbiamo togliere la view dalla gerarchia conview dalla gerarchia con

[myWebView removeFromSuperview];[myWebView removeFromSuperview];prima dell’istruzione switch e nel ramo in cui gestiamo loprima dell’istruzione switch e nel ramo in cui gestiamo lo spegnimento del televisore.spegnimento del televisore.

Eseguiamo l’app e guardiamo i nostri canali.Eseguiamo l’app e guardiamo i nostri canali.

Prima di concludere facciamo un’ottimizzazione. Abbiamo vistoPrima di concludere facciamo un’ottimizzazione. Abbiamo visto che ogni volta che abbiamo aggiunto una nuova view ci siamoche ogni volta che abbiamo aggiunto una nuova view ci siamo sempre preoccupati di rimuoverla con le diverse istruzionisempre preoccupati di rimuoverla con le diverse istruzioni removeFromSuperview. Però abbiamo dovuto ripetere più volte laremoveFromSuperview. Però abbiamo dovuto ripetere più volte la stessa istruzione trattandosi di oggetti diversi. Vediamo comestessa istruzione trattandosi di oggetti diversi. Vediamo come

Page 190: Xcode4 Tutorial Completo

possiamo ottimizzare questo aspetto attraverso l’uso dei tag.possiamo ottimizzare questo aspetto attraverso l’uso dei tag.

Ogni oggetto in Objective-C ha un tag che è un numero intero cheOgni oggetto in Objective-C ha un tag che è un numero intero che gli può essere assegnato, vediamo com epossiamo usarlo pergli può essere assegnato, vediamo com epossiamo usarlo per ottimizzare il nostro codice. Quindi assegnamo alle nostre viewottimizzare il nostro codice. Quindi assegnamo alle nostre view sempre il tag 100.sempre il tag 100.

Andate in TelevisoreView.xib e selezionate l’oggetto Image View.Andate in TelevisoreView.xib e selezionate l’oggetto Image View. Nell’Attributes Inspector alla sezione View, scrivete 100 nelNell’Attributes Inspector alla sezione View, scrivete 100 nel campo tag.campo tag.

In TVController.m nel metodo observe…, dopo l’istruzioneIn TVController.m nel metodo observe…, dopo l’istruzione

player.view.frame=schermo.bounds;player.view.frame=schermo.bounds;aggiungeteaggiungete

player.view.tag=100;player.view.tag=100;Dopo l’istruzioneDopo l’istruzione

myWebView.scalesPageToFit=YES;myWebView.scalesPageToFit=YES;aggiungeteaggiungete

myWebView.tag=100;myWebView.tag=100;Bene, ora abbiamo le nostre tre view che hanno tutte il tag 100.Bene, ora abbiamo le nostre tre view che hanno tutte il tag 100. Quindi sostituiamo le tre istruzioniQuindi sostituiamo le tre istruzioni

[... removeFromSuperview][... removeFromSuperview]che abbiamo inserito prima dello switch e nel ramo diche abbiamo inserito prima dello switch e nel ramo di spegnimento conspegnimento con

[[schermo viewWithTag:100] removeFromSuperview];[[schermo viewWithTag:100] removeFromSuperview];Con [schermo viewWithTag:100] otteniamo la subview diCon [schermo viewWithTag:100] otteniamo la subview di schermo che ha tag 100; su di essa invochiamo ilschermo che ha tag 100; su di essa invochiamo il removeFromSuperview per rimuoverla dalla gerarchia, in questoremoveFromSuperview per rimuoverla dalla gerarchia, in questo modo possiamo fare tutto con un’unica istruzione anche se sonomodo possiamo fare tutto con un’unica istruzione anche se sono oggetti diversi.oggetti diversi.

Eseguiamo ancora una volta l’app per verificare che funzioni tutto.Eseguiamo ancora una volta l’app per verificare che funzioni tutto.

In questo capitolo abbiamo introdotto diversi oggetti che siIn questo capitolo abbiamo introdotto diversi oggetti che si

Page 191: Xcode4 Tutorial Completo

possono usare per visualizzare diversi tipi di contenuto,possono usare per visualizzare diversi tipi di contenuto, ovviamente non è possibile in un corso di queste dimensioniovviamente non è possibile in un corso di queste dimensioni trattare approfonditamente tutto il trattamento di contenutitrattare approfonditamente tutto il trattamento di contenuti multimediali e web, per cui vi rimando alla documentazionemultimediali e web, per cui vi rimando alla documentazione specifica per un’analisi più approfondita. Abbiamo però vistospecifica per un’analisi più approfondita. Abbiamo però visto come si gestisce la gerarchia di view in modo da cambiare icome si gestisce la gerarchia di view in modo da cambiare i contenuti mostrati in una interfaccia utente. Abbiamo visto le duecontenuti mostrati in una interfaccia utente. Abbiamo visto le due modalità di gestione delle gerarchie, cioè creando le view damodalità di gestione delle gerarchie, cioè creando le view da Interface Builder o creandole da codice ed abbiamo visto unInterface Builder o creandole da codice ed abbiamo visto un possibile utilizzo del tag delle viste.possibile utilizzo del tag delle viste.

In allegato potete scaricare il progetto aggiornato a questoIn allegato potete scaricare il progetto aggiornato a questo capitolo.capitolo.

19 – Aggiungiamo una TableView19 – Aggiungiamo una TableView

iOS, , Mac, , OSX, , SDK, , Tech Room

In questo capitolo iniziamo a parlare di uno degli elementi piùIn questo capitolo iniziamo a parlare di uno degli elementi più usati nelle app per iOS: la TableView. E’ la struttura di interfacciausati nelle app per iOS: la TableView. E’ la struttura di interfaccia che si usa per presentare i dati in forma di elenco; quella che usateche si usa per presentare i dati in forma di elenco; quella che usate quando accedete all’app Contatti o Impostazioni. La TableView siquando accedete all’app Contatti o Impostazioni. La TableView si può presentare in diversi modi e può anche essere personalizzata,può presentare in diversi modi e può anche essere personalizzata, per cui una volta che l’avremo usata nel nostro programma eper cui una volta che l’avremo usata nel nostro programma e quindi che vi avrò illustrato i concetti base, sarà necessario chequindi che vi avrò illustrato i concetti base, sarà necessario che guardiate la documentazione per approfondire tutte le opportunitàguardiate la documentazione per approfondire tutte le opportunità offerte da questo tipo di oggetto.offerte da questo tipo di oggetto.

Al nostro televisore con telecomando aggiungiamo unaAl nostro televisore con telecomando aggiungiamo una funzionalità di video-registratore.funzionalità di video-registratore.

Vogliamo avere la possibilità di programmare delle registrazioniVogliamo avere la possibilità di programmare delle registrazioni associate ad uno dei nostri canali. Ad ogni programmazioneassociate ad uno dei nostri canali. Ad ogni programmazione saranno poi associate le diverse registrazioni effettuate.saranno poi associate le diverse registrazioni effettuate.

Page 192: Xcode4 Tutorial Completo

Iniziamo con MVC.Iniziamo con MVC.

ModelModel

Quali informazioni vogliamo gestire?Quali informazioni vogliamo gestire?

Programmazione.Programmazione.

Ha le proprietàHa le proprietà

Stringa con il nome del canale da registrare;Stringa con il nome del canale da registrare;

Stringa con il nome del programma da registrare;Stringa con il nome del programma da registrare;

Elenco delle registrazioni associate a quellaElenco delle registrazioni associate a quella programmazioneprogrammazione

e le funzionie le funzioni

aggiungi una registrazione della programmazioneaggiungi una registrazione della programmazione

Elenco delle programmazioni Elenco delle programmazioni Con le tipiche operazioni diCon le tipiche operazioni di elenco: inserimento e cancellazione.elenco: inserimento e cancellazione.

RegistrazioneRegistrazione

Programmazione associataProgrammazione associata

Data/ora della registrazione.Data/ora della registrazione.

Scriviamo la classe per modellare una Scriviamo la classe per modellare una programmazioneprogrammazione..

Clicchiamo con il bottone destro sul gruppo myTv dell’albero diClicchiamo con il bottone destro sul gruppo myTv dell’albero di XCode e scegliamo New File. Nel gruppo di template CocoaXCode e scegliamo New File. Nel gruppo di template Cocoa Touch scegliamo Objective-C class. Nel pannello successivoTouch scegliamo Objective-C class. Nel pannello successivo scegliamo NSObject come superclasse, infine diamo il nome allascegliamo NSObject come superclasse, infine diamo il nome alla nostra classe chiamandola Programmazione.nostra classe chiamandola Programmazione.

Vengono creati i due file header (Programmazione.h) edVengono creati i due file header (Programmazione.h) ed implementation (Programmazione.m).implementation (Programmazione.m).

Selezioniamo il file Programmazione.h e dichiariamo le propertySelezioniamo il file Programmazione.h e dichiariamo le property che ci servono, inserendoche ci servono, inserendo

@property(nonatomic,retain) NSString *canaleDaRegistrare;@property(nonatomic,retain) NSString *canaleDaRegistrare;@property(nonatomic,retain) NSString *nomeDelProgramma;@property(nonatomic,retain) NSString *nomeDelProgramma;@property(nonatomic,retain) NSMutableArray@property(nonatomic,retain) NSMutableArray

Page 193: Xcode4 Tutorial Completo

*registrazioniAssociate;*registrazioniAssociate;Abbiamo la property Abbiamo la property canaleDaRegistrarecanaleDaRegistrare di tipo NSString, per di tipo NSString, per modellare la stringa con il nome del programma. La propertymodellare la stringa con il nome del programma. La property nomeDelProgrammanomeDelProgramma è una NSString per modellare la stringa con è una NSString per modellare la stringa con il nome del canale. La property il nome del canale. La property registrazioniAssociateregistrazioniAssociate è di tipo è di tipo NSMutableArray, la classe per gestire gli elenchi modificabili diNSMutableArray, la classe per gestire gli elenchi modificabili di oggetti.oggetti.

Approfondimento: Nella documentazione Apple studiate la classeApprofondimento: Nella documentazione Apple studiate la classe NSMutableArray.NSMutableArray.

Nel file implementation aggiungiamo innanzituttoNel file implementation aggiungiamo innanzitutto le synthesize le synthesize per le propertiesper le properties

@synthesize canaleDaRegistrare;@synthesize canaleDaRegistrare;@synthesize nomeDelProgramma;@synthesize nomeDelProgramma;@synthesize registrazioniAssociate;@synthesize registrazioniAssociate;Per quanto riguarda la modellazione dell’Per quanto riguarda la modellazione dell’elenco dielenco di programmazioniprogrammazioni utilizzeremo la classe predefinita utilizzeremo la classe predefinita NSMutableArray, per cui non abbiamo bisogno di creare nuoveNSMutableArray, per cui non abbiamo bisogno di creare nuove classi. Dobbiamo però dichiarare questo elenco all’interno delclassi. Dobbiamo però dichiarare questo elenco all’interno del nostro Televisore. Andiamo in Televisore.h ed aggiungiamonostro Televisore. Andiamo in Televisore.h ed aggiungiamo

@property(nonatomic,retain) NSMutableArray@property(nonatomic,retain) NSMutableArray *elencoProgrammazioni;*elencoProgrammazioni;nell’implementation scriviamo la relativa synthesize e nelnell’implementation scriviamo la relativa synthesize e nel metodoo init aggiungiamo l’istruzione per creare l’istanza dametodoo init aggiungiamo l’istruzione per creare l’istanza da associare.associare.

self.elencoProgrammazioni=[[NSMutableArray alloc] init];self.elencoProgrammazioni=[[NSMutableArray alloc] init];Infine modelliamo la classe per gestire le Infine modelliamo la classe per gestire le registrazioniregistrazioni..

Aggiungiamo quindi al nostro progetto una classe, sottoclasse diAggiungiamo quindi al nostro progetto una classe, sottoclasse di NSObject, che chiamiamo Registrazione, ottenendo i due fileNSObject, che chiamiamo Registrazione, ottenendo i due file header ed implementation.header ed implementation.

Nel file header (Registrazione.h) aggiungiamo le propertiesNel file header (Registrazione.h) aggiungiamo le properties

@property (nonatomic,retain) NSDate *dataOraRegistrazione;@property (nonatomic,retain) NSDate *dataOraRegistrazione;

Page 194: Xcode4 Tutorial Completo

@property (nonatomic,retain) Programmazione@property (nonatomic,retain) Programmazione *programmazioneAssociata;*programmazioneAssociata;Come abbiamo visto già altre volte, stiamo utilizzando una nuovaCome abbiamo visto già altre volte, stiamo utilizzando una nuova classe (Programmazione) e abbiamo quindi bisogno di farlaclasse (Programmazione) e abbiamo quindi bisogno di farla conoscere aggiungendoconoscere aggiungendo

#import "Programmazione.h"#import "Programmazione.h"Ora che abbiamo dichiarato le tre classi torniamo nel fileOra che abbiamo dichiarato le tre classi torniamo nel file Programmazione.h e aggiungiamo la dichiarazione del metodo daProgrammazione.h e aggiungiamo la dichiarazione del metodo da eseguire ogni volta che viene eseguita la programmazione (eeseguire ogni volta che viene eseguita la programmazione (e quindi si genera una nuova registrazione) e che aggiunge unquindi si genera una nuova registrazione) e che aggiunge un elemento nell’elenco delle registrazioni associato.elemento nell’elenco delle registrazioni associato.

-(void)addRegistrazioniAssociateObject:(Registrazione-(void)addRegistrazioniAssociateObject:(Registrazione *)laRegistrazione;*)laRegistrazione;Avremo quindi un metodo che prende in ingresso un oggetto diAvremo quindi un metodo che prende in ingresso un oggetto di tipo Registrazione e il suo compito sarà quello di inserirlotipo Registrazione e il suo compito sarà quello di inserirlo nell’elenco delle registrazioni associate alla programmazione.nell’elenco delle registrazioni associate alla programmazione. Come abbiamo visto già diverse volte per poter usare il nomeCome abbiamo visto già diverse volte per poter usare il nome Registrazione è necessario farlo conoscere al codice; quello cheRegistrazione è necessario farlo conoscere al codice; quello che facciamo di solito è inserirefacciamo di solito è inserire

#import "Registrazione.h"#import "Registrazione.h"Ma questa volta se osserviamo il file Registrazione.h vediamo cheMa questa volta se osserviamo il file Registrazione.h vediamo che lì abbiamo inserito l’import di Programmazione quindi selì abbiamo inserito l’import di Programmazione quindi se facessimo qui l’import di Registrazione creeremmo un ciclo difacessimo qui l’import di Registrazione creeremmo un ciclo di import che XCode non consente. Invece diimport che XCode non consente. Invece di

#import "Registrazione.h"#import "Registrazione.h"aggiungiamoaggiungiamo

@class Registrazione;@class Registrazione;con questa istruzione ci limitiamo a dire che useremo riferimenticon questa istruzione ci limitiamo a dire che useremo riferimenti alla classe Registrazione; ma, a differenza di quanto accede conalla classe Registrazione; ma, a differenza di quanto accede con l‘import, il codice non sa come è fatta la classe; non potremol‘import, il codice non sa come è fatta la classe; non potremo quindi far riferimento alle sue property o metodi.quindi far riferimento alle sue property o metodi.

Andiamo nell’implementation file (Programmazione.m) eAndiamo nell’implementation file (Programmazione.m) e

Page 195: Xcode4 Tutorial Completo

definiamo il metodo addRegistrazioniAssociateObject:. Quellodefiniamo il metodo addRegistrazioniAssociateObject:. Quello che dobbiamo fare in questo metodo è stabilireche dobbiamo fare in questo metodo è stabilire la relazione tra la relazione tra l’istanza di Programmazione che lo esegue e quella dil’istanza di Programmazione che lo esegue e quella di Registrazione che gli viene passata come parametro. AggiungiamoRegistrazione che gli viene passata come parametro. Aggiungiamo quindi nell’implementation filequindi nell’implementation file

- (void)addRegistrazioniAssociateObject:(Registrazione- (void)addRegistrazioniAssociateObject:(Registrazione *)laRegistrazione {*)laRegistrazione {[self.registrazioniAssociate addObject:laRegistrazione];[self.registrazioniAssociate addObject:laRegistrazione];laRegistrazione.programmazioneAssociata=self;laRegistrazione.programmazioneAssociata=self;}}sulla istanza di NSMutableArray referenziata dalla propertysulla istanza di NSMutableArray referenziata dalla property registrazioniAssociate invochiamo il metodo addObjectregistrazioniAssociate invochiamo il metodo addObject passandogli l’istanza laRegistrazione, il cui effetto è propriopassandogli l’istanza laRegistrazione, il cui effetto è proprio quello di aggiungere quella istanza all’elenco.quello di aggiungere quella istanza all’elenco.

Inoltre assegnamo alla property programmazioneAssociata diInoltre assegnamo alla property programmazioneAssociata di laRegistrazione l’istanza self. In questo modo abbiamo costruito ilaRegistrazione l’istanza self. In questo modo abbiamo costruito i due versi della relazione; da una programmazione possiamodue versi della relazione; da una programmazione possiamo accedere a tutte le sue registrazioni e da una registrazioneaccedere a tutte le sue registrazioni e da una registrazione possiamo accedere alla programmazione relativa. Ma per farepossiamo accedere alla programmazione relativa. Ma per fare questa operazione abbiamo bisogno di accedere ad un aproperty diquesta operazione abbiamo bisogno di accedere ad un aproperty di registrazione e siccome non l’abbiamo importata, ma l’abbiamoregistrazione e siccome non l’abbiamo importata, ma l’abbiamo solo dichiarata con @class, XCode ci segnala un errore. A questosolo dichiarata con @class, XCode ci segnala un errore. A questo punto andiamo in Programmazione.m ed aggiungiamopunto andiamo in Programmazione.m ed aggiungiamo

#import "Registrazione.h"#import "Registrazione.h"Riprendiamo l’istruzioneRiprendiamo l’istruzione

[self.registrazioniAssociate addObject:laRegistrazione];[self.registrazioniAssociate addObject:laRegistrazione];Abbiamo detto che questa istruzione aggiunge un oggetto ad unAbbiamo detto che questa istruzione aggiunge un oggetto ad un elenco referenziato da registrazioniAssociate. Ma noi non abbiamoelenco referenziato da registrazioniAssociate. Ma noi non abbiamo creato alcun elenco. E’ necessario quindi andare nel metodo init dicreato alcun elenco. E’ necessario quindi andare nel metodo init di Programmazione (nel file implementation), che, vi ricordo, è ilProgrammazione (nel file implementation), che, vi ricordo, è il metodo invocato ogni volta che viene creato un’istanza di unametodo invocato ogni volta che viene creato un’istanza di una classe, e modificarlo come segueclasse, e modificarlo come segue

Page 196: Xcode4 Tutorial Completo

- (id)init- (id)init{{self = [super init];self = [super init];if (self) {if (self) {// Initialization code here.// Initialization code here.self.registrazioniAssociate=[[NSMutableArray alloc] init];self.registrazioniAssociate=[[NSMutableArray alloc] init];}}return self;return self;}}cioè all’interno del blocco if (self) {} abbiamo aggiuntocioè all’interno del blocco if (self) {} abbiamo aggiunto l’istruzionel’istruzione

self.registrazioniAssociate=[[NSMutableArray alloc] init];self.registrazioniAssociate=[[NSMutableArray alloc] init];con la quale, con il pattern alloc/init, creiamo un’istanza dellacon la quale, con il pattern alloc/init, creiamo un’istanza della classe NSMutableArray e la assegnamo alla nostra property.classe NSMutableArray e la assegnamo alla nostra property.

ViewView

Dal punto di vista dell’Interfaccia Utente aggiungeremo un tabDal punto di vista dell’Interfaccia Utente aggiungeremo un tab alla TabBar per gestire le programmazioni ottenendo qualcosa delalla TabBar per gestire le programmazioni ottenendo qualcosa del tipotipo

Quando cliccheremo sul bottone + si aggiungerà una nuovaQuando cliccheremo sul bottone + si aggiungerà una nuova programmazione che caratterizzeremo attraverso la viewprogrammazione che caratterizzeremo attraverso la view

Page 197: Xcode4 Tutorial Completo

Quando selezioneremo una riga della TableView apriremo unaQuando selezioneremo una riga della TableView apriremo una view nella quale possiamo modificareview nella quale possiamo modificare le informazioni e, siccome le informazioni e, siccome non stiamo introducendo un timer, simulare l’avvio di unanon stiamo introducendo un timer, simulare l’avvio di una registrazione.registrazione.

Riepilogando avremo una navigazione, come questaRiepilogando avremo una navigazione, come questa

Page 198: Xcode4 Tutorial Completo

Apriamo il file MainWindow.xib. Nella sezione Objects del frameApriamo il file MainWindow.xib. Nella sezione Objects del frame di sinistra esplodiamo l’alberatura dell’oggetto Tab Bar Controller.di sinistra esplodiamo l’alberatura dell’oggetto Tab Bar Controller. Nella library degli oggetti disponibili, sezione inferiore del frameNella library degli oggetti disponibili, sezione inferiore del frame di destra prendete un oggetto Navigation Controller e trascinatelodi destra prendete un oggetto Navigation Controller e trascinatelo nell’alberatura del Tab Bar, fate attenzione che sia partenell’alberatura del Tab Bar, fate attenzione che sia parte dell’alberatura e non sia esterno. Selezionate il nuovo Tab Bardell’alberatura e non sia esterno. Selezionate il nuovo Tab Bar Item e nell’Attributes Inspector cambiate il titolo inItem e nell’Attributes Inspector cambiate il titolo in Programmazioni, oppure potete agire direttamente sull’elementoProgrammazioni, oppure potete agire direttamente sull’elemento grafico con un doppio click.grafico con un doppio click.

Approfondimento: nella documentazione Apple cercateApprofondimento: nella documentazione Apple cercate e studiate e studiate la classe UINavigationController.la classe UINavigationController.

Andiamo ora a creare lo xib con la TableView.Andiamo ora a creare lo xib con la TableView.

Cliccando con il bottone destro sul gruppo myTv di XCodeCliccando con il bottone destro sul gruppo myTv di XCode

Page 199: Xcode4 Tutorial Completo

scegliete New Filescegliete New File e nel gruppo User Interface della sezione iOs e nel gruppo User Interface della sezione iOs scegliete View, indicate iPhone come device target e date un nomescegliete View, indicate iPhone come device target e date un nome al file (ProgrammazioniTableView). Ora, con il nuovo file aperto,al file (ProgrammazioniTableView). Ora, con il nuovo file aperto, cancellate l’oggetto View nella sezione Objects del frame dicancellate l’oggetto View nella sezione Objects del frame di sinistra e trascinateci un oggetto Table View preso dalla Librarysinistra e trascinateci un oggetto Table View preso dalla Library del frame di destra.del frame di destra.

Creiamo ora la View per i dettagli della programmazione, neCreiamo ora la View per i dettagli della programmazione, ne creeremo una sola e gestiremo da codice la presenza o mento delcreeremo una sola e gestiremo da codice la presenza o mento del bottone Registra.bottone Registra.

Aggiungiamo al nostro progetto un nuovo xib di tipo View (comeAggiungiamo al nostro progetto un nuovo xib di tipo View (come abbiamo fatto sopra) e chiamiamoloabbiamo fatto sopra) e chiamiamolo DettagliProgrammazioneView.DettagliProgrammazioneView.

Prendiamo una Label dalla Library e posizioniamola sulla View.Prendiamo una Label dalla Library e posizioniamola sulla View. Con un doppio click sulla Label cambiamo il testo in “Nome delCon un doppio click sulla Label cambiamo il testo in “Nome del Programma”.Programma”.

Dalla Library prendiamo un TextField e posizioniamolo sotto laDalla Library prendiamo un TextField e posizioniamolo sotto la Label, e allarghiamone un po’ la dimensione.Label, e allarghiamone un po’ la dimensione.

Prendiamo un’altra Label, posizioniamola sotto il Text Field ePrendiamo un’altra Label, posizioniamola sotto il Text Field e scriviamoci Canale.scriviamoci Canale.

Prendiamo un Segmented Control e posizioniamolo sotto la Label.Prendiamo un Segmented Control e posizioniamolo sotto la Label. Nel Attributes Inspector indichiamo che ha 5 segmenti; clicchiamoNel Attributes Inspector indichiamo che ha 5 segmenti; clicchiamo su ogni segmento e scriviamoci i numeri da 1 a 5;su ogni segmento e scriviamoci i numeri da 1 a 5; ridimensionatelo opportunamente.ridimensionatelo opportunamente.

Prendiamo un Round Rect Button dalla Library e posizioniamoloPrendiamo un Round Rect Button dalla Library e posizioniamolo sulla View, con un doppio clicksulla View, con un doppio click scriviamoci Registra. scriviamoci Registra.

Alla fine dovreste avere qualcosa del tipoAlla fine dovreste avere qualcosa del tipo

Page 200: Xcode4 Tutorial Completo

ControllerController

Iniziamo a definire il controller della Table View.Iniziamo a definire il controller della Table View.

Una Table View è un’istanza della classe UITableView che esponeUna Table View è un’istanza della classe UITableView che espone tutti i metodi necessari per l’accesso alle informazioni presentitutti i metodi necessari per l’accesso alle informazioni presenti nella tabella. Una riga di una UITableView è costituita danella tabella. Una riga di una UITableView è costituita da un‘istanza di UITableViewCell; quindi per visualizzare delleun‘istanza di UITableViewCell; quindi per visualizzare delle informazioni in una riga di una UITableView sarà necessarioinformazioni in una riga di una UITableView sarà necessario istanziare un’oggetto di UITableViewCell.istanziare un’oggetto di UITableViewCell.

Per gestire una tabella, oltre ai metodi di UITableView, sonoPer gestire una tabella, oltre ai metodi di UITableView, sono necessari altri due set di metodi, uno per il popolamentonecessari altri due set di metodi, uno per il popolamento della della tableView (metodi di datasource) e uno per la gestione degli eventitableView (metodi di datasource) e uno per la gestione degli eventi su tableView (metodi di delegate). Sarà quindi necessariosu tableView (metodi di delegate). Sarà quindi necessario assegnare ad uno o più oggetti del nostro programma il ruolo diassegnare ad uno o più oggetti del nostro programma il ruolo di delegate e datasource per la tableview, questi oggettidelegate e datasource per la tableview, questi oggetti implementeranno i relativi metodi.implementeranno i relativi metodi.

Page 201: Xcode4 Tutorial Completo

Approfondimento: studiate sulla documentazione la classeApprofondimento: studiate sulla documentazione la classe UITableView ed i protocolli UITableViewDelegate eUITableView ed i protocolli UITableViewDelegate e UITableViewDatasource.UITableViewDatasource.

Creiamo la classe per il nostro controller. Aggiungiamo un file alCreiamo la classe per il nostro controller. Aggiungiamo un file al nostro progetto e scegliamo, nel gruppo Cocoa Touch, il tiponostro progetto e scegliamo, nel gruppo Cocoa Touch, il tipo UIViewController subclass. Nella finestra successiva scegliamoUIViewController subclass. Nella finestra successiva scegliamo UITableViewController come superclasse e chiamiamolaUITableViewController come superclasse e chiamiamola ProgrammazioniTableViewController.ProgrammazioniTableViewController.

Se scorriamo il file ProgrammazioniTableViewController.mSe scorriamo il file ProgrammazioniTableViewController.m vediamo che ci sono due sezioni #pragma una per i metodivediamo che ci sono due sezioni #pragma una per i metodi datasource e una per i metodi delegate, questo perché il templatedatasource e una per i metodi delegate, questo perché il template UITableViewController proposto da Apple prevede che la classeUITableViewController proposto da Apple prevede che la classe creata istanzi oggetti che avranno anche il ruolo di datasource ecreata istanzi oggetti che avranno anche il ruolo di datasource e delegate.delegate.

Il nostro controller, come abbiamo detto nell’introduzione alIl nostro controller, come abbiamo detto nell’introduzione al pattern MVC, dovrà collegare gli oggetti del Model agli oggetti dipattern MVC, dovrà collegare gli oggetti del Model agli oggetti di View. Come abbiamo fatto nei casi precedenti dovremo quindiView. Come abbiamo fatto nei casi precedenti dovremo quindi definire delle opportune properties per gestire questi legami.definire delle opportune properties per gestire questi legami.

Il collegamento verso View deve associare il controller allaIl collegamento verso View deve associare il controller alla tableview, la classe UITableViewController, di cui la nostra classetableview, la classe UITableViewController, di cui la nostra classe è sottoclasse, espone già una property (dichiarata come IBOutlet eè sottoclasse, espone già una property (dichiarata come IBOutlet e quindi collegabile ad oggetti di xib) chiamata quindi collegabile ad oggetti di xib) chiamata tableviewtableview adatta a adatta a questo scopo che viene ereditata.questo scopo che viene ereditata.

Il collegamento verso Model deve collegare il controller con leIl collegamento verso Model deve collegare il controller con le informazioni da visualizzare nella tableview, cioè, nel nostro caso,informazioni da visualizzare nella tableview, cioè, nel nostro caso, con l’elenco delle programmazioni che è gestito all’interno dellacon l’elenco delle programmazioni che è gestito all’interno della classe Televisore. Apriamo il fileclasse Televisore. Apriamo il file ProgrammazioniTableViewController.hProgrammazioniTableViewController.h e aggiungiamo all’interno e aggiungiamo all’interno della definizione della classedella definizione della classe

@property (nonatomic, retain) Televisore *ilTelevisore;@property (nonatomic, retain) Televisore *ilTelevisore;e nell’implementation file la relativa synthesize.e nell’implementation file la relativa synthesize.

Page 202: Xcode4 Tutorial Completo

Passiamo ad implementare i metodi di datasource, quelli chePassiamo ad implementare i metodi di datasource, quelli che servono a popolare correttamente la tableview.servono a popolare correttamente la tableview.

La prima informazione di cui il sistema ha bisogno è sapereLa prima informazione di cui il sistema ha bisogno è sapere quante sezioni ha la tableview. Il concetto di sezione è quello chequante sezioni ha la tableview. Il concetto di sezione è quello che potete vedere nell’app contatti nella quale i nomi sono raggruppatipotete vedere nell’app contatti nella quale i nomi sono raggruppati per lettera iniziale. Ogni iniziale individua una diversa sezioneper lettera iniziale. Ogni iniziale individua una diversa sezione della tableview.della tableview.

Nel nostro caso noi avremo un unico elenco di programmazioniNel nostro caso noi avremo un unico elenco di programmazioni non raggruppate quindi avremo una sola sezione. Andiamo nelnon raggruppate quindi avremo una sola sezione. Andiamo nel metodometodo

- (NSInteger)numberOfSectionsInTableView:(UITableView- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView*)tableViewche trovate nella sezione datasource e sostituiamoche trovate nella sezione datasource e sostituiamo

return 0;return 0;concon

return 1;return 1;Questo metodo viene invocato dal sistema quando ha bisogno diQuesto metodo viene invocato dal sistema quando ha bisogno di disegnare la tabella per sapere quante sezioni ha; noi stiamodisegnare la tabella per sapere quante sezioni ha; noi stiamo dicendo che avrà una sola sezione.dicendo che avrà una sola sezione.

Se è necessario (dipende dalla versione di XCode che stateSe è necessario (dipende dalla versione di XCode che state utilizzando) rimuovete la direttiva #warning che trovate all’inizioutilizzando) rimuovete la direttiva #warning che trovate all’inizio del metodo.del metodo.

Poi il sistema ha bisogno di sapere quante righe ci sono per ogniPoi il sistema ha bisogno di sapere quante righe ci sono per ogni sezione; lo fa invocando il metodosezione; lo fa invocando il metodo

- (NSInteger)tableView:(UITableView *)tableView- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)sectionnumberOfRowsInSection:(NSInteger)sectiona cui passa come argomento il numero della sezione e si aspettaa cui passa come argomento il numero della sezione e si aspetta come risultato il numero di righe della sezione. Quindi questocome risultato il numero di righe della sezione. Quindi questo metodo viene invocato tante volte quante sono le sezioni dellametodo viene invocato tante volte quante sono le sezioni della tabella risultato del metodo precedente.tabella risultato del metodo precedente.

Page 203: Xcode4 Tutorial Completo

Quanto righe avrà la nostra tabella? Deve scrivere una riga perQuanto righe avrà la nostra tabella? Deve scrivere una riga per ogni programmazione quindi saranno tante quante leogni programmazione quindi saranno tante quante le programmazioni, cioè tante quanti gli oggetti presenti nell’elencoprogrammazioni, cioè tante quanti gli oggetti presenti nell’elenco elencoProgrammazioni.elencoProgrammazioni.

Quindi sostituiamoQuindi sostituiamo

return 0;return 0;concon

return [self.ilTelevisore.elencoProgrammazioni count];return [self.ilTelevisore.elencoProgrammazioni count];cioè restituiamo il risultato dell’invocazione del metodo countcioè restituiamo il risultato dell’invocazione del metodo count sulla nostra istanza di NSMutableArray e questo metodosulla nostra istanza di NSMutableArray e questo metodo restituisce esattamente il numero di oggetti presenti.restituisce esattamente il numero di oggetti presenti.

Anche qui se è necessario rimuovete la direttiva #warning.Anche qui se è necessario rimuovete la direttiva #warning.

L’ultima informazione obbligatoria che il datasource deve fornireL’ultima informazione obbligatoria che il datasource deve fornire è quella relativa al contenuto di ogni cella; questo si ottiene conè quella relativa al contenuto di ogni cella; questo si ottiene con l’invocazione del metodol’invocazione del metodo

- (UITableViewCell *)tableView:(UITableView *)tableView- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPathcellForRowAtIndexPath:(NSIndexPath *)indexPathche usa come parametro un’istanza di NSIndexPath. Questa classeche usa come parametro un’istanza di NSIndexPath. Questa classe viene usata per modellare una coppia di numeri accedibiliviene usata per modellare una coppia di numeri accedibili attraverso le properties section e row, con le quali è possibileattraverso le properties section e row, con le quali è possibile individuare univocamente la cella per la quel bisogna fornire ilindividuare univocamente la cella per la quel bisogna fornire il contenuto.contenuto.

Se analizziamo questo metodo vediamo che c’è già una parte diSe analizziamo questo metodo vediamo che c’è già una parte di codice predisposto automaticamente, questa parte serve a crearecodice predisposto automaticamente, questa parte serve a creare l’istanza di UITableViewCell che sarà restituita e quindi usata perl’istanza di UITableViewCell che sarà restituita e quindi usata per costruire la tableview. Non entro nel merito di questa parte dicostruire la tableview. Non entro nel merito di questa parte di codice e vi rimando alla documentazione per capire appieno il suocodice e vi rimando alla documentazione per capire appieno il suo significato; l’unica considerazione che facciamo è relativasignificato; l’unica considerazione che facciamo è relativa all’argomento initWithStyle:UITableViewCellStyleDefault questoall’argomento initWithStyle:UITableViewCellStyleDefault questo parametro inizializza una cella con un tipo predefinito, questoparametro inizializza una cella con un tipo predefinito, questo indicato nel codice fornito è la classica cella con una riga di testo.indicato nel codice fornito è la classica cella con una riga di testo.

Page 204: Xcode4 Tutorial Completo

Ma noi abbiamo detto che vogliamo una riga per indicare ilMa noi abbiamo detto che vogliamo una riga per indicare il programma ed una riga per indicare il canale; cambiamo quindiprogramma ed una riga per indicare il canale; cambiamo quindi UITableViewCellStyleDefault con UITableViewCellStyleSubtitleUITableViewCellStyleDefault con UITableViewCellStyleSubtitle che costruisce una cella con due righe come vogliamo noi.che costruisce una cella con due righe come vogliamo noi.

Una volta che è stata costruita la cella dobbiamo riempirla con ilUna volta che è stata costruita la cella dobbiamo riempirla con il contenuto.contenuto.

Ad ogni riga della tabella corrisponderà un elemento dell’elencoAd ogni riga della tabella corrisponderà un elemento dell’elenco delle programmazioni, cioè la prima riga mostrerà le informazionidelle programmazioni, cioè la prima riga mostrerà le informazioni della prima programmazione, la seconda riga quelle della secondadella prima programmazione, la seconda riga quelle della seconda programmazione, e così via. Ricordando che all’interno di questoprogrammazione, e così via. Ricordando che all’interno di questo metodo è disponibile l’indexPath la cui property row indica ametodo è disponibile l’indexPath la cui property row indica a quale riga si riferisce la cella che dobbiamo restituire prima diquale riga si riferisce la cella che dobbiamo restituire prima di

return cell;return cell;aggiungiamoaggiungiamo

cell.textLabel.text=((Programmazione *)cell.textLabel.text=((Programmazione *) [self.ilTelevisore.elencoProgrammazioni[self.ilTelevisore.elencoProgrammazioni objectAtIndex:indexPath.row]).nomeDelProgramma;objectAtIndex:indexPath.row]).nomeDelProgramma;a sinistra abbiamo cell.textLabel.text. cell è l’istanza dia sinistra abbiamo cell.textLabel.text. cell è l’istanza di UITableViewCell che abbiamo appena creato all’inizio delUITableViewCell che abbiamo appena creato all’inizio del metodometodo e che restituiremo per costruire la tableview. Accediamo e che restituiremo per costruire la tableview. Accediamo alla property textLabel di questa istanza che è la UILabel chealla property textLabel di questa istanza che è la UILabel che mostra il testo principale della cella. Infine accediamo allamostra il testo principale della cella. Infine accediamo alla property text della UILabel per assegnare il testo da mostrare.property text della UILabel per assegnare il testo da mostrare.

ConCon

indexPath.rowindexPath.rowaccediamo alla property row di indexPath che indica il numero diaccediamo alla property row di indexPath che indica il numero di riga per la quale dobbiamo restituire la cella che stiamoriga per la quale dobbiamo restituire la cella che stiamo costruendo; concostruendo; con

[self.ilTelevisore.elencoProgrammazioni[self.ilTelevisore.elencoProgrammazioni objectAtIndex:indexPath.row]objectAtIndex:indexPath.row]invochiamo il metodo objectAtIndex sull’istanzainvochiamo il metodo objectAtIndex sull’istanza

Page 205: Xcode4 Tutorial Completo

elencoProgrammazioni passandogli l’argomento indexPath.row.elencoProgrammazioni passandogli l’argomento indexPath.row. Questo metodo restituisce l’oggetto presente nella posizioneQuesto metodo restituisce l’oggetto presente nella posizione indicata da indexPath.row nell’elenco elencoProgrammazioni,indicata da indexPath.row nell’elenco elencoProgrammazioni, questo oggetto sarà un’istanza della nostra classe Programmazioniquesto oggetto sarà un’istanza della nostra classe Programmazioni quindi possiamo accedere alla property nomeDelProgramma perquindi possiamo accedere alla property nomeDelProgramma per farci restituire la stringa da scrivere nella cella.farci restituire la stringa da scrivere nella cella.

[self.ilTelevisore.elencoProgrammazioni[self.ilTelevisore.elencoProgrammazioni objectAtIndex:indexPath.row].nomeDelProgrammaobjectAtIndex:indexPath.row].nomeDelProgrammaPer far si che XCode conosca la property nomeDelProgramma,Per far si che XCode conosca la property nomeDelProgramma, dobbiamo fare il type casting per indicare che l’oggetto restituito èdobbiamo fare il type casting per indicare che l’oggetto restituito è istanza di Programmazioneistanza di Programmazione

((Programmazione *)[self.ilTelevisore.elencoProgrammazioni((Programmazione *)[self.ilTelevisore.elencoProgrammazioni objectAtIndex:indexPath.row]).nomeDelProgrammaobjectAtIndex:indexPath.row]).nomeDelProgrammaCome abbiamo fatto più volte per far usare la classeCome abbiamo fatto più volte per far usare la classe Programmazione all’interno di questo file, andiamo in testa edProgrammazione all’interno di questo file, andiamo in testa ed aggiungiamoaggiungiamo

#import "Programmazione.h"#import "Programmazione.h"Quindi con questa istruzione assegnamo alla prima cella il nomeQuindi con questa istruzione assegnamo alla prima cella il nome del programma della prima programmazione, alla seconda il nomedel programma della prima programmazione, alla seconda il nome della seconda programmazione e così via.della seconda programmazione e così via.

Scriviamo anche la seconda riga della nostra cella, aggiungendoScriviamo anche la seconda riga della nostra cella, aggiungendo

cell.detailTextLabel.text=((Programmazione *)cell.detailTextLabel.text=((Programmazione *) [self.ilTelevisore.elencoProgrammazioni[self.ilTelevisore.elencoProgrammazioni objectAtIndex:indexPath.row]).canaleDaRegistrare;objectAtIndex:indexPath.row]).canaleDaRegistrare;qui accediamo alla property detailTextLabel che si riferisce allaqui accediamo alla property detailTextLabel che si riferisce alla seconda riga di testo della cella, la logica del termine a destra è laseconda riga di testo della cella, la logica del termine a destra è la stessa appena discussa.stessa appena discussa.

Bene, abbiamo definito i tre metodi base per le tabelle; abbiamoBene, abbiamo definito i tre metodi base per le tabelle; abbiamo detto quante sono le sezioni, quante righe ha ogni sezione e qual èdetto quante sono le sezioni, quante righe ha ogni sezione e qual è il contenuto di ogni riga.il contenuto di ogni riga.

Andiamo ora a collegare le nostre classi agli oggetti dei nostri xib.Andiamo ora a collegare le nostre classi agli oggetti dei nostri xib.

Page 206: Xcode4 Tutorial Completo

Apriamo il fileApriamo il file ProgrammazioniTableView.xib, selezioniamo il ProgrammazioniTableView.xib, selezioniamo il File’s Owner e nel Identity Inspector nel campo ClassFile’s Owner e nel Identity Inspector nel campo Class selezioniamo ProgrammazioniTableViewController. Con ilselezioniamo ProgrammazioniTableViewController. Con il bottone destro premuto colleghiamo la tableview con il File’sbottone destro premuto colleghiamo la tableview con il File’s owner selezionando datasource una voltaowner selezionando datasource una volta e delegate l’altra. Con il e delegate l’altra. Con il bottone estro premuto colleghiamo il File’s owner con la tablebottone estro premuto colleghiamo il File’s owner con la table view e scegliamo l’outlet view.view e scegliamo l’outlet view.

Apriamo il file MainWindow.xib, esplodiamo la gerarchia Tab BarApriamo il file MainWindow.xib, esplodiamo la gerarchia Tab Bar Controller ed al suo interno esplodiamo la gerarchia delController ed al suo interno esplodiamo la gerarchia del Navigation Controller. Selezioniamo il View Controller al suoNavigation Controller. Selezioniamo il View Controller al suo interno e nel Identity Inspectorinterno e nel Identity Inspector assegnamogli la classe assegnamogli la classe ProgrammazioniTableViewController. Nel Attributes Inspector nelProgrammazioniTableViewController. Nel Attributes Inspector nel campo NIB Name scriviamo ProgrammazioniTableView.campo NIB Name scriviamo ProgrammazioniTableView.

Completiamo i collegamenti, collegando il controller con l’istanzaCompletiamo i collegamenti, collegando il controller con l’istanza del televisore.del televisore.

Andiamo in myTvAppDelegate.h e dichiariamo una property perAndiamo in myTvAppDelegate.h e dichiariamo una property per collegare il controller al codicecollegare il controller al codice

@property (nonatomic, retain) IBOutlet@property (nonatomic, retain) IBOutlet ProgrammazioniTableViewController *myProgController;ProgrammazioniTableViewController *myProgController;Com al solito per poter usare la classeCom al solito per poter usare la classe ProgrammazioniTableViewControllerProgrammazioniTableViewController dobbiamo aggiungere la dobbiamo aggiungere la importimport

#import "ProgrammazioniTableViewController.h"#import "ProgrammazioniTableViewController.h"Non dimentichiamo di aggiungere la synthesizeNon dimentichiamo di aggiungere la synthesize nell’implementation file.nell’implementation file.

Colleghiamo ora la property appena creata all’oggetto nello xib.Colleghiamo ora la property appena creata all’oggetto nello xib. Apriamo il file MainWindow.xib, con il bottone destro premutoApriamo il file MainWindow.xib, con il bottone destro premuto colleghiamo l’oggetto My Tv App Delegate al View Controllercolleghiamo l’oggetto My Tv App Delegate al View Controller dentro al Navigation Controller e scegliamo l’Outletdentro al Navigation Controller e scegliamo l’Outlet myProgController.myProgController.

Infine, torniamo al file myTvAppDelegate.m ed andiamo alInfine, torniamo al file myTvAppDelegate.m ed andiamo al

Page 207: Xcode4 Tutorial Completo

metodometodo

(BOOL)application:(UIApplication *)application(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptionsdidFinishLaunchingWithOptions:(NSDictionary *)launchOptionsdopo l’istruzionedopo l’istruzione

myTVController.ilTelevisore=tv1;myTVController.ilTelevisore=tv1;aggiungiamoaggiungiamo

myProgController.ilTelevisore=tv1;myProgController.ilTelevisore=tv1;abbiamo assegnato alla property ilTelevisore del controller dellaabbiamo assegnato alla property ilTelevisore del controller della tableview l’istanza della classe Televisore che abbiamo appenatableview l’istanza della classe Televisore che abbiamo appena creato nel codice.creato nel codice.

Bene, abbiamo completato tutti i collegamenti, ripercorreteliBene, abbiamo completato tutti i collegamenti, ripercorreteli analizzandoli bene.analizzandoli bene.

Ora per provare il funzionamento dell nostro programma,Ora per provare il funzionamento dell nostro programma, popoliamo con dei dati la struttura dell’elenco dellepopoliamo con dei dati la struttura dell’elenco delle programmazioni.programmazioni.

Andiamo nel file ProgrammazioniTableViewController.m eAndiamo nel file ProgrammazioniTableViewController.m e andiamo nel metodo viewDidLoad. Questo metodo viene eseguitoandiamo nel metodo viewDidLoad. Questo metodo viene eseguito quando la view associata al controller viene caricata dal file xib.quando la view associata al controller viene caricata dal file xib.

Modifichiamolo cosìModifichiamolo così

- (void)viewDidLoad- (void)viewDidLoad{{[super viewDidLoad];[super viewDidLoad];Programmazione * programma=[[Programmazione alloc] init];Programmazione * programma=[[Programmazione alloc] init];programma.nomeDelProgramma=@"Film 1";programma.nomeDelProgramma=@"Film 1";programma.canaleDaRegistrare=@"Onda1";programma.canaleDaRegistrare=@"Onda1";[self.ilTelevisore.elencoProgrammazioni addObject:programma];[self.ilTelevisore.elencoProgrammazioni addObject:programma];programma=[[Programmazione alloc] init];programma=[[Programmazione alloc] init];programma.nomeDelProgramma=@"Documentario 1";programma.nomeDelProgramma=@"Documentario 1";programma.canaleDaRegistrare=@"Onda2";programma.canaleDaRegistrare=@"Onda2";[self.ilTelevisore.elencoProgrammazioni addObject:programma];[self.ilTelevisore.elencoProgrammazioni addObject:programma];programma=[[Programmazione alloc] init];programma=[[Programmazione alloc] init];

Page 208: Xcode4 Tutorial Completo

programma.nomeDelProgramma=@"Film 2";programma.nomeDelProgramma=@"Film 2";programma.canaleDaRegistrare=@"Onda3";programma.canaleDaRegistrare=@"Onda3";[self.ilTelevisore.elencoProgrammazioni addObject:programma];[self.ilTelevisore.elencoProgrammazioni addObject:programma];// Uncomment the following line to preserve selection between// Uncomment the following line to preserve selection between presentations.presentations.// self.clearsSelectionOnViewWillAppear = NO;// self.clearsSelectionOnViewWillAppear = NO;// Uncomment the following line to display an Edit button in the// Uncomment the following line to display an Edit button in the navigation bar for this view controller.navigation bar for this view controller.// self.navigationItem.rightBarButtonItem = self.editButtonItem;// self.navigationItem.rightBarButtonItem = self.editButtonItem;}}Abbiamo aggiunto tre blocchi di istruzioni.Abbiamo aggiunto tre blocchi di istruzioni.

programma=[[Programmazione alloc] init]programma=[[Programmazione alloc] init]creiamo un’istanza di Programmazione e la assegnamo allacreiamo un’istanza di Programmazione e la assegnamo alla variabile programma (viene dichiarata nella prima istruzionevariabile programma (viene dichiarata nella prima istruzione contestualmente all’assegnamento).contestualmente all’assegnamento).

programma.nomeDelProgramma=@"...";programma.nomeDelProgramma=@"...";programma.canaleDaRegistrare=@"...";programma.canaleDaRegistrare=@"...";assegnamo dei valori alle properties dell’istanza programma.assegnamo dei valori alle properties dell’istanza programma.

[self.ilTelevisore.elencoProgrammazioni addObject:programma];[self.ilTelevisore.elencoProgrammazioni addObject:programma];aggiungiamo l’oggetto creato all’elenco delle programmazioni.aggiungiamo l’oggetto creato all’elenco delle programmazioni.

Ora eseguiamo il programma e verifichiamo il correttoOra eseguiamo il programma e verifichiamo il corretto funzionamento del tab Programmazioni.funzionamento del tab Programmazioni.

Page 210: Xcode4 Tutorial Completo

Riprendiamo la gestione della nostra TableView vedendo comeRiprendiamo la gestione della nostra TableView vedendo come possiamo inserire e cancellare elementi ricordando sempre che lapossiamo inserire e cancellare elementi ricordando sempre che la TableView è solo la componente View di un Model costituito daTableView è solo la componente View di un Model costituito da elencoProgrammazioni che dovrà sempre essere allineato con laelencoProgrammazioni che dovrà sempre essere allineato con la visualizzazione.visualizzazione.

Rispetto a quanto abbiamo disegnato nel cap. precedente inveceRispetto a quanto abbiamo disegnato nel cap. precedente invece del bottone con ‘+’ che avremmo usato per aggiungere unadel bottone con ‘+’ che avremmo usato per aggiungere una programmazione useremo un bottone di edit che useremo sia perprogrammazione useremo un bottone di edit che useremo sia per aggiungere che per cancellare le programmazioni.aggiungere che per cancellare le programmazioni.

Andiamo nel file ProgrammazioniTableViewController.m e nelAndiamo nel file ProgrammazioniTableViewController.m e nel metodo viewDidLoad aggiungiamometodo viewDidLoad aggiungiamo

self.navigationItem.title=@"Programmazioni";self.navigationItem.title=@"Programmazioni";self.navigationItem.rightBarButtonItem=[self editButtonItem];self.navigationItem.rightBarButtonItem=[self editButtonItem];entrambe queste istruzioni accedono alla property navigationItementrambe queste istruzioni accedono alla property navigationItem che viene valorizzata automaticamente dal sistema quando unche viene valorizzata automaticamente dal sistema quando un UIView Controller è inserito nella gerarchia di un NavigationUIView Controller è inserito nella gerarchia di un Navigation Controller e contiene il riferimento alla barra di navigazione. ConController e contiene il riferimento alla barra di navigazione. Con la prima istruzione accediamo poi alla property title della barra ela prima istruzione accediamo poi alla property title della barra e le assegnamo @”Programmazioni”, questo è il titolo che saràle assegnamo @”Programmazioni”, questo è il titolo che sarà visualizzato nella barra. Con la seconda istruzione accediamo allavisualizzato nella barra. Con la seconda istruzione accediamo alla property rightBarButtonItem che si riferisce al bottone più a destraproperty rightBarButtonItem che si riferisce al bottone più a destra nella barra di navigazione e le assegnamo il risultatonella barra di navigazione e le assegnamo il risultato dell’invocazione [self editButtonItem]; questo è un metododell’invocazione [self editButtonItem]; questo è un metodo predefinito che restituisce un bottone che cambia stato (predefinito che restituisce un bottone che cambia stato (edit/doneedit/done)) e che attiva l’editing della tabella.e che attiva l’editing della tabella.

Proviamo ad eseguire e osserviamo il comportamento del bottoneProviamo ad eseguire e osserviamo il comportamento del bottone edit.edit.

Quello che accade èQuello che accade è

il bottone passa da il bottone passa da editedit a a donedone e viceversa e viceversa

in modalità edit a sinistra di ogni etichetta di cella appare unin modalità edit a sinistra di ogni etichetta di cella appare un

Page 211: Xcode4 Tutorial Completo

segno – in campo rossosegno – in campo rosso

cliccando il – a destra di ogni cella appare un bottone cliccando il – a destra di ogni cella appare un bottone deletedelete..

Fra un po‘ vedremo come gestire la cancellazione, intantoFra un po‘ vedremo come gestire la cancellazione, intanto riflettiamo su una cosa:riflettiamo su una cosa:

se questo è il modo per andare in edit sulla tabella come facciamose questo è il modo per andare in edit sulla tabella come facciamo a dire che vogliamo aggiungere una riga?a dire che vogliamo aggiungere una riga?

La soluzione che adottiamo è quella di usare una riga vuota comeLa soluzione che adottiamo è quella di usare una riga vuota come riga di inserimento, questo si tradurrà nell’interfaccia con il fattoriga di inserimento, questo si tradurrà nell’interfaccia con il fatto che in modalità edit apparirà un + in campo verde sulla prima rigache in modalità edit apparirà un + in campo verde sulla prima riga vuota della tabella.vuota della tabella.

La prima cosa da fare è dire al sistema che la tabella ha una riga inLa prima cosa da fare è dire al sistema che la tabella ha una riga in più rispetto a quelle dell’elenco, in modo da avere sempre una rigapiù rispetto a quelle dell’elenco, in modo da avere sempre una riga libera. Cambiamo il metodo che stituisce il numero di righe comelibera. Cambiamo il metodo che stituisce il numero di righe come seguesegue

- (NSInteger)tableView:(UITableView *)tableView- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)sectionnumberOfRowsInSection:(NSInteger)section{{ // Return the number of rows in the section. // Return the number of rows in the section. return [self.ilTelevisore.elencoProgrammazioni count]+1; return [self.ilTelevisore.elencoProgrammazioni count]+1;}}cioè abbiamo aggiunto 1 al valore restituito.cioè abbiamo aggiunto 1 al valore restituito.

Poi dobbiamo andare nel metodo Poi dobbiamo andare nel metodo cellForRowAtIndexPathcellForRowAtIndexPath per dire per dire come dovrà essere costruita anche l’ultima cella (cioè la primacome dovrà essere costruita anche l’ultima cella (cioè la prima vuota).vuota).

Racchiudiamo le istruzioni di personalizzazione delle celle con leRacchiudiamo le istruzioni di personalizzazione delle celle con le informazioni in un test di verifica se siamo nelle prime righe,informazioni in un test di verifica se siamo nelle prime righe, quelle per cui abbiamo un elemento corrispondente dell’elencoquelle per cui abbiamo un elemento corrispondente dell’elenco

if (indexPath.row<[self.ilTelevisore.elencoProgrammazioniif (indexPath.row<[self.ilTelevisore.elencoProgrammazioni count]) {count]) { cell.textLabel.text=((Programmazione *) cell.textLabel.text=((Programmazione *)[self.ilTelevisore.elencoProgrammazioni[self.ilTelevisore.elencoProgrammazioni

Page 212: Xcode4 Tutorial Completo

objectAtIndex:indexPath.row]).nomeDelProgramma;objectAtIndex:indexPath.row]).nomeDelProgramma; cell.detailTextLabel.text=((Programmazione *) cell.detailTextLabel.text=((Programmazione *)[self.ilTelevisore.elencoProgrammazioni[self.ilTelevisore.elencoProgrammazioni objectAtIndex:indexPath.row]).canaleDaRegistrare;objectAtIndex:indexPath.row]).canaleDaRegistrare;}}quindi solo le celle associate ad un elemento diquindi solo le celle associate ad un elemento di elencoProgrammazioni avranno qualcosa scritto dentro mentreelencoProgrammazioni avranno qualcosa scritto dentro mentre l’ultima cella resterà bianca.l’ultima cella resterà bianca.

Eseguiamo il programma e verifichiamo che la riga vuota siaEseguiamo il programma e verifichiamo che la riga vuota sia gestita correttamente, clicchiamo sul bottone edit e verifichiamogestita correttamente, clicchiamo sul bottone edit e verifichiamo che appaia anche nella riga bianca il segnale di edit.che appaia anche nella riga bianca il segnale di edit.

Osserviamo però che anche nella riga vuota appare il – in campoOsserviamo però che anche nella riga vuota appare il – in campo rosso mentre noi vogliamo il simbolo +. Per fare questo dobbiamorosso mentre noi vogliamo il simbolo +. Per fare questo dobbiamo implementare il metodo implementare il metodo --(UITableViewCellEditingStyle)tableView:(UITableView(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath*)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath*)indexPath questo è un metodo standard del delegate di questo è un metodo standard del delegate di UITableView che prende in ingresso un indice della tableView eUITableView che prende in ingresso un indice della tableView e restituisce lo stile di editing della cella nella posizione individuatarestituisce lo stile di editing della cella nella posizione individuata da quell’indice.da quell’indice.

Scriviamo quindi nel file ProgrammazioniTableViewController.mScriviamo quindi nel file ProgrammazioniTableViewController.m

-(UITableViewCellEditingStyle)tableView:(UITableView-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath*)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {*)indexPath { if (indexPath.row==[self.ilTelevisore.elencoProgrammazioni if (indexPath.row==[self.ilTelevisore.elencoProgrammazioni count]) {count]) { return UITableViewCellEditingStyleInsert; return UITableViewCellEditingStyleInsert; } else { } else { return UITableViewCellEditingStyleDelete; return UITableViewCellEditingStyleDelete; } }}}cioè controlliamo se è stato invocato per una delle celle concioè controlliamo se è stato invocato per una delle celle con associato un elemento dell’elenco, in questo caso restituiamo ilassociato un elemento dell’elenco, in questo caso restituiamo il

Page 213: Xcode4 Tutorial Completo

valore valore UITableViewCellEditingStyleDelete e sarà visualizzato ilUITableViewCellEditingStyleDelete e sarà visualizzato il -, altrimenti restituiamo UITableViewCellEditingStyleInsert e sarà-, altrimenti restituiamo UITableViewCellEditingStyleInsert e sarà visualizzato il +.visualizzato il +.

Eseguiamo l’app e verifichiamo la corretta visualizzazione deiEseguiamo l’app e verifichiamo la corretta visualizzazione dei bottoni.bottoni.

Bene, ora che abbiamo i due elementi di interfaccia (I pulsantiBene, ora che abbiamo i due elementi di interfaccia (I pulsanti delete sulle righe con dati e il pulsante + sulla riga vuota) cidelete sulle righe con dati e il pulsante + sulla riga vuota) ci chiediamo cosa accade quando ci clicchiamo sopra. Il sistemachiediamo cosa accade quando ci clicchiamo sopra. Il sistema invoca un metodo dell’oggetto delegato, in particolare invoca invoca un metodo dell’oggetto delegato, in particolare invoca -- (void)tableView:(UITableView *)tableView commitEditingStyle:(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath(NSIndexPath *)indexPath passandogli sia passandogli sia l’editingStylel’editingStyle, in modo, in modo da capire se dobbiamo cancellare o inserire, che l’indice della rigada capire se dobbiamo cancellare o inserire, che l’indice della riga cliccata.cliccata.

Andiamo ora a gestire i due eventi di cancellazione edAndiamo ora a gestire i due eventi di cancellazione ed inserimento.inserimento.

Scorrendo il file arriviamo al metodoScorrendo il file arriviamo al metodo

/// Override to support editing the table view./// Override to support editing the table view.- (void)tableView:(UITableView *)tableView commitEditingStyle:- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath(NSIndexPath *)indexPath{{ if (editingStyle == UITableViewCellEditingStyleDelete) { if (editingStyle == UITableViewCellEditingStyleDelete) { // Delete the row from the data source // Delete the row from the data source [tableView deleteRowsAtIndexPaths:[NSArray [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];withRowAnimation:UITableViewRowAnimationFade]; } } else if (editingStyle == UITableViewCellEditingStyleInsert) { else if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table viewthe array, and add a new row to the table view}}

Page 214: Xcode4 Tutorial Completo

che viene fornito automaticamente da XCode ma commentato.che viene fornito automaticamente da XCode ma commentato. Togliamo i simboli /* e */ che troviamo subito prima e subitoTogliamo i simboli /* e */ che troviamo subito prima e subito dopodopo il metodo e che, vi ricordo, servono per indicare alil metodo e che, vi ricordo, servono per indicare al compilatore che la parte di codice compresa è da ignorare incompilatore che la parte di codice compresa è da ignorare in quanto commento. In questo modo rendiamo operativo il metodoquanto commento. In questo modo rendiamo operativo il metodo all’interno del codice.all’interno del codice.

La struttura del metodo è semplice, si controlla se dobbiamoLa struttura del metodo è semplice, si controlla se dobbiamo cancellare l’elemento passato come parametro o se ne dobbiamocancellare l’elemento passato come parametro o se ne dobbiamo inserire uno nuovo e si esegue il blocco di codice corrispondente.inserire uno nuovo e si esegue il blocco di codice corrispondente. Nel blocco relativo alla cancellazione è già fornita l’istruzione perNel blocco relativo alla cancellazione è già fornita l’istruzione per cancellare la riga dalla tabella, deve invece essere implementata lacancellare la riga dalla tabella, deve invece essere implementata la cancellazione dell’elemento corrispondente nella struttura delcancellazione dell’elemento corrispondente nella struttura del Model (elencoProgrammazioni). Nel blocco insert invece è tuttoModel (elencoProgrammazioni). Nel blocco insert invece è tutto da sviluppare.da sviluppare.

Sappiamo dunque che avremo bisogno di inserire e cancellareSappiamo dunque che avremo bisogno di inserire e cancellare elementi dall’elenco delle programmazioni. Poiché questo elencoelementi dall’elenco delle programmazioni. Poiché questo elenco è esposto come property da ilTelevisore potremmo agireè esposto come property da ilTelevisore potremmo agire direttamente dal controller sull’elenco con i metodi tipici didirettamente dal controller sull’elenco con i metodi tipici di NSMutableArray, ma per pulizia di codice è preferibile scrivereNSMutableArray, ma per pulizia di codice è preferibile scrivere due metodi ad hoc nella classe Televisore, che gestisce l’elenco,due metodi ad hoc nella classe Televisore, che gestisce l’elenco, ed invocarli secondo le nostre necessità.ed invocarli secondo le nostre necessità.

In Televisore.h aggiungiamo la dichiarazione dei due metodiIn Televisore.h aggiungiamo la dichiarazione dei due metodi

-(void)cancellaProgrammazione:(NSInteger)posizione;-(void)cancellaProgrammazione:(NSInteger)posizione;-(void)inserisciProgrammazione:(Programmazione-(void)inserisciProgrammazione:(Programmazione *)nuovoProgramma;*)nuovoProgramma;Il primo riceve in ingresso un numero intero e cancelleràIl primo riceve in ingresso un numero intero e cancellerà l’elemento di elencoProgrammazioni in quella posizione.l’elemento di elencoProgrammazioni in quella posizione.

Il secondo riceve in ingresso un’istanza di Programmazione e laIl secondo riceve in ingresso un’istanza di Programmazione e la inserirà in elencoProgrammazione. Come abbiamo già incontratoinserirà in elencoProgrammazione. Come abbiamo già incontrato tante volte per usare la classe Programmazione ne dobbiamotante volte per usare la classe Programmazione ne dobbiamo importare il file header, aggiungiamo quindiimportare il file header, aggiungiamo quindi

#import "Programmazione.h"#import "Programmazione.h"

Page 215: Xcode4 Tutorial Completo

Andiamo in Televisore.m ed implementiamo i due metodi.Andiamo in Televisore.m ed implementiamo i due metodi.

-(void)cancellaProgrammazione:(NSInteger)posizione {-(void)cancellaProgrammazione:(NSInteger)posizione { [self.elencoProgrammazioni removeObjectAtIndex:posizione]; [self.elencoProgrammazioni removeObjectAtIndex:posizione];}}

-(void)inserisciProgrammazione:(Programmazione-(void)inserisciProgrammazione:(Programmazione *)nuovoProgramma {*)nuovoProgramma { [self.elencoProgrammazioni addObject:nuovoProgramma]; [self.elencoProgrammazioni addObject:nuovoProgramma];}}vi lascio come esercizio lo studio dei due metodi.vi lascio come esercizio lo studio dei due metodi.

Ora che abbiamo i nostri metodi di utilità, torniamo al metodo diOra che abbiamo i nostri metodi di utilità, torniamo al metodo di gestione degli eventi gestione degli eventi - (void)tableView:(UITableView *)tableView- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStylecommitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPathforRowAtIndexPath:(NSIndexPath *)indexPath ed ed implementiamo la cancellazioneimplementiamo la cancellazione

// Override to support editing the table view.// Override to support editing the table view.- (void)tableView:(UITableView *)tableView commitEditingStyle:- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath(NSIndexPath *)indexPath{{ if (editingStyle == UITableViewCellEditingStyleDelete) { if (editingStyle == UITableViewCellEditingStyleDelete) { // Delete the row from the data source // Delete the row from the data source [self.ilTelevisore cancellaProgrammazione:indexPath.row]; [self.ilTelevisore cancellaProgrammazione:indexPath.row]; [tableView deleteRowsAtIndexPaths:[NSArray [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];withRowAnimation:UITableViewRowAnimationFade]; } } else if (editingStyle == UITableViewCellEditingStyleInsert) { else if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table viewthe array, and add a new row to the table view } } }}Abbiamo due istruzioni:Abbiamo due istruzioni:

Page 216: Xcode4 Tutorial Completo

[self.ilTelevisore cancellaProgrammazione:indexPath.row];[self.ilTelevisore cancellaProgrammazione:indexPath.row];invoca il metodo che abbiamo appena definito nella classeinvoca il metodo che abbiamo appena definito nella classe Televisore al quale passiamo l’indice della riga selezionata cheTelevisore al quale passiamo l’indice della riga selezionata che corrisponde all’indice dell’elemento da cancellare dall’elenco.corrisponde all’indice dell’elemento da cancellare dall’elenco.

[tableView deleteRowsAtIndexPaths:[NSArray[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];withRowAnimation:UITableViewRowAnimationFade];è l’invocazione di un metodo predefinito di UITableView cheè l’invocazione di un metodo predefinito di UITableView che prende in ingresso un elenco di posizioni e le cancella dallaprende in ingresso un elenco di posizioni e le cancella dalla tableView con un effetto di animazione. L’elenco di posizioni datableView con un effetto di animazione. L’elenco di posizioni da cancellare che gli passiamo è costituito dalla sola posizionecancellare che gli passiamo è costituito dalla sola posizione selezionataselezionata

[NSArray arrayWithObject:indexPath][NSArray arrayWithObject:indexPath]Eseguiamo il programma e verifichiamo che la cancellazioneEseguiamo il programma e verifichiamo che la cancellazione avvenga correttamente.avvenga correttamente.

Passiamo a gestire l’inserimento.Passiamo a gestire l’inserimento.

La prima cosa che dobbiamo fare è allineare il Model, cioèLa prima cosa che dobbiamo fare è allineare il Model, cioè dobbiamo creare un’istanza di Programmazione ed aggiungerladobbiamo creare un’istanza di Programmazione ed aggiungerla all’elenco delle programmazioni.all’elenco delle programmazioni.

Nel blocco di gestione dell’evento di selezione del bottone +Nel blocco di gestione dell’evento di selezione del bottone +

else if (editingStyle == UITableViewCellEditingStyleInsert) { else if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table viewthe array, and add a new row to the table view } }aggiungiamoaggiungiamo

Programmazione *nuovoProgramma=[[Programmazione alloc]Programmazione *nuovoProgramma=[[Programmazione alloc] init];init];è il solito pattern di creazione delle istanze e quindi non miè il solito pattern di creazione delle istanze e quindi non mi dilungo.dilungo.

Aggiungiamo all’elenco del televisore la programmazione appenaAggiungiamo all’elenco del televisore la programmazione appena creata con l’istruzionecreata con l’istruzione

Page 217: Xcode4 Tutorial Completo

[self.ilTelevisore inserisciProgrammazione:nuovoProgramma];[self.ilTelevisore inserisciProgrammazione:nuovoProgramma];Ora dobbiamo dare le informazioni relative alla programmazioneOra dobbiamo dare le informazioni relative alla programmazione appena creata.appena creata.

Ricordiamo che abbiamo creato uno xib con l’interfaccia deiRicordiamo che abbiamo creato uno xib con l’interfaccia dei dettagli di una programmazione; quindi abbiamo il Model (ladettagli di una programmazione; quindi abbiamo il Model (la nuova istanza di Programmazione), nuova istanza di Programmazione), la View (lo xib conla View (lo xib con la vistala vista per iper i dettagli), ci manca il Controller.dettagli), ci manca il Controller.

Aggiungiamo al nostro progetto un nuovo file sottoclasse diAggiungiamo al nostro progetto un nuovo file sottoclasse di UIViewController (immagino che a questo punto sappiate comeUIViewController (immagino che a questo punto sappiate come fare) e chiamiamolo DettagliProgrammazioneViewController.fare) e chiamiamolo DettagliProgrammazioneViewController. Seguendo il Pattern MVC, colleghiamo questo controller con ilSeguendo il Pattern MVC, colleghiamo questo controller con il Model dichiarando all’interno del header le propertiesModel dichiarando all’interno del header le properties

@property (nonatomic,retain) Programmazione@property (nonatomic,retain) Programmazione *laProgrammazione;*laProgrammazione;@property (nonatomic,retain) Televisore *ilTelevisore;@property (nonatomic,retain) Televisore *ilTelevisore;e definiamone la synthesize nel implementation. Come al solito,e definiamone la synthesize nel implementation. Come al solito, per poter usare le classi Programmazione e Televisore dobbiamoper poter usare le classi Programmazione e Televisore dobbiamo fare la import in testa al file.fare la import in testa al file.

Per il collegamento con il livello View, osserviamo che noiPer il collegamento con il livello View, osserviamo che noi avremo bisogno di leggere il valore di un textfield, il nome delavremo bisogno di leggere il valore di un textfield, il nome del programma, lo stato di un segmented control ed infine dovremoprogramma, lo stato di un segmented control ed infine dovremo rispondere ad un bottone per effettuare la registrazione delrispondere ad un bottone per effettuare la registrazione del programma.programma.

Avremo quindi due outlet ed una action, il file header quindi saràAvremo quindi due outlet ed una action, il file header quindi sarà

@interface DettagliProgrammazioneViewController :@interface DettagliProgrammazioneViewController : UIViewControllerUIViewController{{ IBOutlet UITextField *nomeProgramma; IBOutlet UITextField *nomeProgramma; IBOutlet UISegmentedControl *numeroCanale; IBOutlet UISegmentedControl *numeroCanale;}}

@property (nonatomic,retain) Programmazione@property (nonatomic,retain) Programmazione

Page 218: Xcode4 Tutorial Completo

*laProgrammazione;*laProgrammazione;@property (nonatomic,retain) Televisore *ilTelevisore;@property (nonatomic,retain) Televisore *ilTelevisore;

-(IBAction)registra:(id)sender;-(IBAction)registra:(id)sender;

@end@endNel file implementation aggiungiamo la definizione del metodoNel file implementation aggiungiamo la definizione del metodo registra del quale scriveremo la logica più avanti.registra del quale scriveremo la logica più avanti.

-(IBAction)registra:(id)sender {-(IBAction)registra:(id)sender {}}Ora andiamo in Interface builder ed effettuiamo i collegamenti.Ora andiamo in Interface builder ed effettuiamo i collegamenti. Apriamo il file DettagliProgrammazioneView.xib, selezioniamo ilApriamo il file DettagliProgrammazioneView.xib, selezioniamo il File’s owner e, nell’Identity Inspector, assegnamogli la classeFile’s owner e, nell’Identity Inspector, assegnamogli la classe DettagliProgrammazioneViewController. Cliccate con il destro suDettagliProgrammazioneViewController. Cliccate con il destro su File’s owner per aprire il popup delle info e collegateFile’s owner per aprire il popup delle info e collegate opportunamente gli outlet ai controlli textfield eopportunamente gli outlet ai controlli textfield e segmentedControl e la action al bottone registra; collegate inoltresegmentedControl e la action al bottone registra; collegate inoltre la property view all’oggetto view.la property view all’oggetto view.

Prima di scrivere la logica di questo controller, inseriamolo nelPrima di scrivere la logica di questo controller, inseriamolo nel flusso di gestione degli eventi per verificarne il correttoflusso di gestione degli eventi per verificarne il corretto funzionamento. Torniamo nel metodofunzionamento. Torniamo nel metodo ProgrammazioniTableViewController e andiamo nel metodo ProgrammazioniTableViewController e andiamo nel metodo -- (void)tableView:(UITableView *)tableView commitEditingStyle:(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath(NSIndexPath *)indexPath dopo l’istruzione di creazione dopo l’istruzione di creazione dell’istanza di Programmazionedell’istanza di Programmazione

Programmazione *nuovoProgramma=[[Programmazione alloc]Programmazione *nuovoProgramma=[[Programmazione alloc] init];init];dichiariamo e creiamo un’istanza del nostro controller di dettagliodichiariamo e creiamo un’istanza del nostro controller di dettaglio associandolo al file xibassociandolo al file xib

DettagliProgrammazioneViewControllerDettagliProgrammazioneViewController *dController=[[DettagliProgrammazioneViewController alloc]*dController=[[DettagliProgrammazioneViewController alloc] initWithNibName:@"DettagliProgrammazioneView" bundle:nil];initWithNibName:@"DettagliProgrammazioneView" bundle:nil];

Page 219: Xcode4 Tutorial Completo

Abbiamo dichiarato la variabile dController che conterrà unAbbiamo dichiarato la variabile dController che conterrà un riferimento ad un’istanza diriferimento ad un’istanza di DettagliProgrammazioneViewController.DettagliProgrammazioneViewController. L’istanza viene creata L’istanza viene creata con l’invocazione di alloc/initWithNibName. Questo metodocon l’invocazione di alloc/initWithNibName. Questo metodo associa al controller il file xib. Poi aggiungiamoassocia al controller il file xib. Poi aggiungiamo

dController.laProgrammazione=nuovoProgramma;dController.laProgrammazione=nuovoProgramma;accediamo la property laProgrammazione del controller e leaccediamo la property laProgrammazione del controller e le assegnamo l’istanza che abbiamo creato sopra; in tal modoassegnamo l’istanza che abbiamo creato sopra; in tal modo passiamo un oggetto da un’istanza di una classe ad un’altra.passiamo un oggetto da un’istanza di una classe ad un’altra.

dController.ilTelevisore=self.ilTelevisore;dController.ilTelevisore=self.ilTelevisore;alla property ilTelevisore assegnamo la stessa istanza dealla property ilTelevisore assegnamo la stessa istanza de ilTelevisore del controller stesso.ilTelevisore del controller stesso.

Infine aggiungiamoInfine aggiungiamo

[self.navigationController pushViewController:dController[self.navigationController pushViewController:dController animated:YES];animated:YES];il metodo pushViewController, cui passiamo il nostroil metodo pushViewController, cui passiamo il nostro viewController, ha l’effetto di visualizzare la view del nuovo viewviewController, ha l’effetto di visualizzare la view del nuovo view controller e di mostrare nella navigation bar un bottone di ritornocontroller e di mostrare nella navigation bar un bottone di ritorno alla view precedente.alla view precedente.

Eseguiamo la nostra app e verifichiamo che la navigazioneEseguiamo la nostra app e verifichiamo che la navigazione funzioni.funzioni.

Proviamo a scrivere qualcosa nel campo di testo e osserviamo cheProviamo a scrivere qualcosa nel campo di testo e osserviamo che appena ci posizioniamo su di esso viene mostrataappena ci posizioniamo su di esso viene mostrata la tastiera con la la tastiera con la quale possiamo scrivere il nome del programma; dobbiamo peròquale possiamo scrivere il nome del programma; dobbiamo però gestire la sua scomparsa. Per fare questo è necessario individuaregestire la sua scomparsa. Per fare questo è necessario individuare un oggetto che assuma il ruolo di delegato per gli eventi sulun oggetto che assuma il ruolo di delegato per gli eventi sul textField e che si occuperà di rimuovere la tastiera quando saràtextField e che si occuperà di rimuovere la tastiera quando sarà premuto il tasto enter. Il nostro delegate sarà l’istanza dipremuto il tasto enter. Il nostro delegate sarà l’istanza di DettagliProgrammazioneViewController. Andiamo dunque nel suoDettagliProgrammazioneViewController. Andiamo dunque nel suo header e prima della { scriviamo <UITextFieldDelegate> in questoheader e prima della { scriviamo <UITextFieldDelegate> in questo modo diciamo che le istanza della nostra classe sono in grado dimodo diciamo che le istanza della nostra classe sono in grado di rispondere agli eventi delegati di un UiTextField. Andiamo ora nelrispondere agli eventi delegati di un UiTextField. Andiamo ora nel

Page 220: Xcode4 Tutorial Completo

implementation file per scrivere il codice con il quale gestiamoimplementation file per scrivere il codice con il quale gestiamo l’evento che ci interessa.l’evento che ci interessa.

Aggiungiamo il metodoAggiungiamo il metodo

-(BOOL)textFieldShouldReturn:(UITextField *)textField {-(BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; [textField resignFirstResponder]; return YES; return YES;}}questo metodo viene invocato dal sistema ogni volta che vienequesto metodo viene invocato dal sistema ogni volta che viene premuto il tasto return e deve restituire un booleanopremuto il tasto return e deve restituire un booleano True/FalseTrue/False (o(o Yes/No)Yes/No) per dire se l’evento deve essere gestito.per dire se l’evento deve essere gestito.

L’istruzione che abbiamo inserito prima del returnL’istruzione che abbiamo inserito prima del return

[textField resignFirstResponder];[textField resignFirstResponder];è l’invocazione di un metodo predefinito di UIResponder,è l’invocazione di un metodo predefinito di UIResponder, ereditato da UITextField , che ha proprio l’effetto di rimuovere laereditato da UITextField , che ha proprio l’effetto di rimuovere la tastiera.tastiera.

Approfondimento: studiare la classe UIResponder.Approfondimento: studiare la classe UIResponder.

Infine diciamo al textField chi è il suo delegato, andando inInfine diciamo al textField chi è il suo delegato, andando in DettagliProgrammazioneView.xib e dopo aver cliccato con ilDettagliProgrammazioneView.xib e dopo aver cliccato con il destro sul campo di testo colleghiamo l’outlet delegate con ildestro sul campo di testo colleghiamo l’outlet delegate con il File’s owner.File’s owner.

Eseguiamo per provare se funziona la dismissione della tastiera.Eseguiamo per provare se funziona la dismissione della tastiera.

Passiamo a gestire un altro evento e poi inseriremo la nostra logicaPassiamo a gestire un altro evento e poi inseriremo la nostra logica elaborativa. Vogliamo gestire la risposta alla selezione di una riga,elaborativa. Vogliamo gestire la risposta alla selezione di una riga, in questo caso si dovrà di nuovo aprire la vista di dettaglio con lein questo caso si dovrà di nuovo aprire la vista di dettaglio con le informazioni della programmazione selezionata.informazioni della programmazione selezionata.

Anche in risposta a questo evento viene invocato un metodoAnche in risposta a questo evento viene invocato un metodo apposito del delegato di UITableView all’interno del qualeapposito del delegato di UITableView all’interno del quale andremo a scrivere il nostro codice. Il metodo in questione è andremo a scrivere il nostro codice. Il metodo in questione è -- (void)tableView:(UITableView *)tableView(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPathdidSelectRowAtIndexPath:(NSIndexPath *)indexPath e lo trovate e lo trovate

Page 221: Xcode4 Tutorial Completo

nel file ProgrammazioniTableViewController.m, vi ricordo chenel file ProgrammazioniTableViewController.m, vi ricordo che questa classe è delegato per la tableView.questa classe è delegato per la tableView.

Sostituite il suo contenuto conSostituite il suo contenuto con

if (indexPath.row < [ilTelevisore.elencoProgrammazioni count]) { if (indexPath.row < [ilTelevisore.elencoProgrammazioni count]) { Programmazione Programmazione *nuovoProgramma=[ilTelevisore.elencoProgrammazioni*nuovoProgramma=[ilTelevisore.elencoProgrammazioni objectAtIndex:indexPath.row];objectAtIndex:indexPath.row]; DettagliProgrammazioneViewController DettagliProgrammazioneViewController *dController=[[DettagliProgrammazioneViewController alloc]*dController=[[DettagliProgrammazioneViewController alloc] initWithNibName:@"DettagliProgrammazioneView" bundle:nil];initWithNibName:@"DettagliProgrammazioneView" bundle:nil]; dController.laProgrammazione=nuovoProgramma; dController.laProgrammazione=nuovoProgramma; [self.navigationController pushViewController:dController [self.navigationController pushViewController:dController animated:YES];animated:YES]; } } Osserviamo che tutto il corpo del metodo è racchiuso all’internoOsserviamo che tutto il corpo del metodo è racchiuso all’interno dell’istruzionedell’istruzione

if (indexPath.row < [ilTelevisore.elencoProgrammazioni count]) {if (indexPath.row < [ilTelevisore.elencoProgrammazioni count]) {}}con questo controllo eseguiremo la navigazione solo per le cellecon questo controllo eseguiremo la navigazione solo per le celle con indice compreso nel limite dell’elencocon indice compreso nel limite dell’elenco elencoProgrammazioni. Per quanto riguarda il blocco, è lo stessoelencoProgrammazioni. Per quanto riguarda il blocco, è lo stesso pattern che abbiamo usato nel metodo di edit per navigare, l’unicapattern che abbiamo usato nel metodo di edit per navigare, l’unica differenza è nella prima istruzione, mentre nel metodo didifferenza è nella prima istruzione, mentre nel metodo di inserimento abbiamo creato una nuova istanza e l’abbiamo passatainserimento abbiamo creato una nuova istanza e l’abbiamo passata al controller qui abbiamo recuperato l’istanza che nell’elenco delleal controller qui abbiamo recuperato l’istanza che nell’elenco delle programmazioni occupa la posizione indicata dalla rigaprogrammazioni occupa la posizione indicata dalla riga selezionata.selezionata.

Eseguiamo il programma e verifichiamo che anche in risposta allaEseguiamo il programma e verifichiamo che anche in risposta alla selezione di una cella si apra la pagina dei dettagli.selezione di una cella si apra la pagina dei dettagli.

Bene ora che abbiamo costruito la struttura possiamo scrivere laBene ora che abbiamo costruito la struttura possiamo scrivere la nostra logica applicativa. Cosa vogliamo che succeda?nostra logica applicativa. Cosa vogliamo che succeda?

Quando si visualizza la vista di dettaglio deve esse allineata con iQuando si visualizza la vista di dettaglio deve esse allineata con i

Page 222: Xcode4 Tutorial Completo

dati della programmazione selezionata, quando si esce le eventualidati della programmazione selezionata, quando si esce le eventuali modifiche devono essere riportate sul modello e quindi sullamodifiche devono essere riportate sul modello e quindi sulla tabella con l'elenco delle programmazioni.tabella con l'elenco delle programmazioni.

Iniziamo con l’individuare i due punti del codice dove sono gestitiIniziamo con l’individuare i due punti del codice dove sono gestiti i due eventi di visualizazione e scomparsa della view. Questi sonoi due eventi di visualizazione e scomparsa della view. Questi sono il metodo il metodo viewWillAppearviewWillAppear ed il metodo ed il metodo viewWillDisappearviewWillDisappear del del controller dei dettagli. Questi due metodi sono invocaticontroller dei dettagli. Questi due metodi sono invocati automaticamente dal sistema in risposta ai due eventi di nostroautomaticamente dal sistema in risposta ai due eventi di nostro interesse, e quindi al loro interno noi scriveremo le istruzioni perinteresse, e quindi al loro interno noi scriveremo le istruzioni per allineare Modello e Interfaccia.allineare Modello e Interfaccia.

Nel file DettagliProgrammazioneViewController.m aggiungiamoNel file DettagliProgrammazioneViewController.m aggiungiamo

-(void)viewWillAppear:(BOOL)animated {-(void)viewWillAppear:(BOOL)animated { nomeProgramma.text=laProgrammazione.nomeDelProgramma; nomeProgramma.text=laProgrammazione.nomeDelProgramma;}}il metodo viewWillAppear, come già detto sopra, è eseguito ogniil metodo viewWillAppear, come già detto sopra, è eseguito ogni volta che viene visualizzata la view del controller. Quello chevolta che viene visualizzata la view del controller. Quello che abbiamo scritto significa che ogni voltaabbiamo scritto significa che ogni volta che viene mostrata la che viene mostrata la view noi assegnamo alla property text del UITextField il valoreview noi assegnamo alla property text del UITextField il valore della property nomeDelProgramma dell’istanza referenziata dadella property nomeDelProgramma dell’istanza referenziata da laProgrammazione. Ricordate che prima di attivare la transizionelaProgrammazione. Ricordate che prima di attivare la transizione delle vista con il pushViewController noi abbiamo assegnato undelle vista con il pushViewController noi abbiamo assegnato un oggetto a laProgrammazione.oggetto a laProgrammazione.

Eseguite l’applicazione ed osservate il comportamento quandoEseguite l’applicazione ed osservate il comportamento quando selezionate una riga con i dati già presenti. Ripercorreteselezionate una riga con i dati già presenti. Ripercorrete mentalmente il cammino delle informazioni.mentalmente il cammino delle informazioni.

Passiamo all’aggioramento del secondo elemento dell’interfaccia,Passiamo all’aggioramento del secondo elemento dell’interfaccia, cioè il segmentedControl con il numero del canale. Se osserviamocioè il segmentedControl con il numero del canale. Se osserviamo il nostro Model vediamo che la classe Programmazione non ha unil nostro Model vediamo che la classe Programmazione non ha un numero ma ha solo il nome del canale e mentre abbiamo un modonumero ma ha solo il nome del canale e mentre abbiamo un modo per recuperare il nome a partire dal numero (accedendo alper recuperare il nome a partire dal numero (accedendo al canaliMemorizzati di Televisore) non abbiamo nulla per ilcanaliMemorizzati di Televisore) non abbiamo nulla per il contrario; potremmo trovare diverse soluzioni ad es. scorrere icontrario; potremmo trovare diverse soluzioni ad es. scorrere i

Page 223: Xcode4 Tutorial Completo

canaliMemorizzati fino a trovare quello con quel nome ecanaliMemorizzati fino a trovare quello con quel nome e recuperare quindi la sua posizione e di conseguenza l’indice;recuperare quindi la sua posizione e di conseguenza l’indice; oppure potremmo costruirci direttamente un if-else che a secondaoppure potremmo costruirci direttamente un if-else che a seconda del nome del canale restituisce un indice, del nome del canale restituisce un indice, modo pessimo edmodo pessimo ed assolutamente da evitareassolutamente da evitare in quanto si scrive il codice sulla base dei in quanto si scrive il codice sulla base dei dati, con la conseguenza che se un giorno dovessimo cambiaredati, con la conseguenza che se un giorno dovessimo cambiare l’ordine dei canali dovremmo riscrive una parte del codice.l’ordine dei canali dovremmo riscrive una parte del codice. Siccome noi non abbiamo bisogno di complicarci la vita eSiccome noi non abbiamo bisogno di complicarci la vita e possiamo farlo, semplicemente cambiamo il Model, cioè a livellopossiamo farlo, semplicemente cambiamo il Model, cioè a livello del Model ci gestiamo il numero del canale invece che il suodel Model ci gestiamo il numero del canale invece che il suo nome.nome.

Nel file Programmazione.h cambiamo la classe diNel file Programmazione.h cambiamo la classe di canaleDaRegistrare da NSString acanaleDaRegistrare da NSString a NSNumber, questa classe NSNumber, questa classe l’abbiamo già usata altre volte sia direttamente che con la sual’abbiamo già usata altre volte sia direttamente che con la sua sottoclasse NSDecimalNumber. Allieniamo adesso a questasottoclasse NSDecimalNumber. Allieniamo adesso a questa modificamodifica ilil codice già scritto.codice già scritto.

Nel metodo viewDidLoad della classeNel metodo viewDidLoad della classe ProgrammazioniTableViewController cambiamo la parte diProgrammazioniTableViewController cambiamo la parte di inizializzazione con dati di prova come segueinizializzazione con dati di prova come segue

Programmazione *programma=[[Programmazione alloc] init];Programmazione *programma=[[Programmazione alloc] init];programma.nomeDelProgramma=@"Film 1";programma.nomeDelProgramma=@"Film 1";programma.canaleDaRegistrare=[NSNumber numberWithInt:0];programma.canaleDaRegistrare=[NSNumber numberWithInt:0];[self.ilTelevisore.elencoProgrammazioni addObject:programma];[self.ilTelevisore.elencoProgrammazioni addObject:programma];programma=[[Programmazione alloc] init];programma=[[Programmazione alloc] init];programma.nomeDelProgramma=@"Documentario 1";programma.nomeDelProgramma=@"Documentario 1";programma.canaleDaRegistrare=[NSNumber numberWithInt:1];programma.canaleDaRegistrare=[NSNumber numberWithInt:1];[self.ilTelevisore.elencoProgrammazioni addObject:programma];[self.ilTelevisore.elencoProgrammazioni addObject:programma];programma=[[Programmazione alloc] init];programma=[[Programmazione alloc] init];programma.nomeDelProgramma=@"Film 2";programma.nomeDelProgramma=@"Film 2";programma.canaleDaRegistrare=[NSNumber numberWithInt:2];programma.canaleDaRegistrare=[NSNumber numberWithInt:2];in pratica abbiamo cambiato gli assegnamenti alla propertyin pratica abbiamo cambiato gli assegnamenti alla property canaleDaRegistrare in maniera coerente al nuovo tipo di dato.canaleDaRegistrare in maniera coerente al nuovo tipo di dato.

A questo punto andiamo ad allineare al nuovo modello il metodoA questo punto andiamo ad allineare al nuovo modello il metodo

Page 224: Xcode4 Tutorial Completo

di creazione delle celle di creazione delle celle - (UITableViewCell *)tableView:- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath(NSIndexPath *)indexPath nel quale le istruzioni di costruzione nel quale le istruzioni di costruzione della cella diventanodella cella diventano

if (indexPath.row<[self.ilTelevisore.elencoProgrammazioniif (indexPath.row<[self.ilTelevisore.elencoProgrammazioni count]) {count]) { cell.textLabel.text=((Programmazione *) cell.textLabel.text=((Programmazione *)[self.ilTelevisore.elencoProgrammazioni[self.ilTelevisore.elencoProgrammazioni objectAtIndex:indexPath.row]).nomeDelProgramma;objectAtIndex:indexPath.row]).nomeDelProgramma; NSNumber *numCanale=((Programmazione *) NSNumber *numCanale=((Programmazione *)[self.ilTelevisore.elencoProgrammazioni[self.ilTelevisore.elencoProgrammazioni objectAtIndex:indexPath.row]).canaleDaRegistrare;objectAtIndex:indexPath.row]).canaleDaRegistrare; cell.detailTextLabel.text=[self.ilTelevisore.canaliMemorizzati cell.detailTextLabel.text=[self.ilTelevisore.canaliMemorizzati objectAtIndex:[numCanale integerValue]];objectAtIndex:[numCanale integerValue]];in sostanza prima del riempimento della label di dettaglio creiamoin sostanza prima del riempimento della label di dettaglio creiamo un’istanza di NSNumber valorizzata con il contenuto delun’istanza di NSNumber valorizzata con il contenuto del canaleDaRegistrare (è la stessa espressione della versionecanaleDaRegistrare (è la stessa espressione della versione precedente però questa volta restituisce un NSNumber con l’indiceprecedente però questa volta restituisce un NSNumber con l’indice del canale).del canale).

Per recuperare la NSString da assegnare alla label di dettaglio,Per recuperare la NSString da assegnare alla label di dettaglio, prima recuperiamo i valore interoprima recuperiamo i valore intero dell’indice del canale dell’indice del canale

[numCanale intValue][numCanale intValue]e con esso accediamo alla posizione dell’elencoe con esso accediamo alla posizione dell’elenco canaliMemorizzati per recuperarne il nome.canaliMemorizzati per recuperarne il nome.

Eseguiamo l’app e vediamo l’effetto andando subito sul tab delleEseguiamo l’app e vediamo l’effetto andando subito sul tab delle Programmazioni.Programmazioni.

Dovreste vedere una tabella nella quale le righe di dettaglio sonoDovreste vedere una tabella nella quale le righe di dettaglio sono vuote. Perchè? prendetevi qualche minuto di analisi prima divuote. Perchè? prendetevi qualche minuto di analisi prima di proseguire e provate a capire il motivo.proseguire e provate a capire il motivo.

Suggerimento: ripercorrete il ciclo di vita dell’elencoSuggerimento: ripercorrete il ciclo di vita dell’elenco canaliMemorizzati.canaliMemorizzati.

Page 225: Xcode4 Tutorial Completo

......

Se osservate la classe Televisore vedrete che l'elencoSe osservate la classe Televisore vedrete che l'elenco canaliMemorizzati viene inizializzato nel metodo accendi, quindicanaliMemorizzati viene inizializzato nel metodo accendi, quindi per il corretto funzionamento dell’app è necessario accendere ilper il corretto funzionamento dell’app è necessario accendere il televisore prima di andare nel tab Programmazioni.televisore prima di andare nel tab Programmazioni.

Eseguite di nuovo l’app e verificate che accendendo il televisore eEseguite di nuovo l’app e verificate che accendendo il televisore e poi andando nelle Programmazioni la visualizzazione sia corretta.poi andando nelle Programmazioni la visualizzazione sia corretta.

Facciamo un’altra prova per evidenziare un altro aspetto.Facciamo un’altra prova per evidenziare un altro aspetto.

Eseguite l’app e andate nel tab Programmazioni. Poichè nonEseguite l’app e andate nel tab Programmazioni. Poichè non abbiamo acceso il televisore mancaabbiamo acceso il televisore manca la riga dettagli. Ora andate nel la riga dettagli. Ora andate nel tab Televisore ed accendetelo. Se torniamo nel tabtab Televisore ed accendetelo. Se torniamo nel tab Programmazioni ci aspetteremmo di vedere la tabella completaProgrammazioni ci aspetteremmo di vedere la tabella completa anche della riga dettagli, invece no. Il motivo è che la tabella èanche della riga dettagli, invece no. Il motivo è che la tabella è stata creata la prima volta e non viene aggiornata. Quello che cistata creata la prima volta e non viene aggiornata. Quello che ci serve è che ogni volta che viene mostrata questa view la tabella siaserve è che ogni volta che viene mostrata questa view la tabella sia aggiornata. Come al solito individuiamo prima il metodo nel qualeaggiornata. Come al solito individuiamo prima il metodo nel quale gestire questo evento,gestire questo evento, il metodo che viene eseguito ogni volta che il metodo che viene eseguito ogni volta che una view viene mostrata è il viewWillAppear. Quindi andiamo neluna view viene mostrata è il viewWillAppear. Quindi andiamo nel metodo viewWillAppeardella classemetodo viewWillAppeardella classe ProgrammazioniTableViewController e aggiungiamo l’istruzioneProgrammazioniTableViewController e aggiungiamo l’istruzione

[self.tableView reloadData];[self.tableView reloadData];con la quale si invoca il metodo prefinito reloadData sulla tabellacon la quale si invoca il metodo prefinito reloadData sulla tabella referenziata dalla property tableView. L’effetto di questo metodo èreferenziata dalla property tableView. L’effetto di questo metodo è proprio quello di aggiornare la visualizzazione della tabella.proprio quello di aggiornare la visualizzazione della tabella.

Bene abbiamo cambiato il tipo di dato di canaleDaRegistrare edBene abbiamo cambiato il tipo di dato di canaleDaRegistrare ed abbiamo allineatoabbiamo allineato il codiceil codice a questa variazione. E' importantea questa variazione. E' importante anche vedere come si lavora sul nostro codice per apportare delleanche vedere come si lavora sul nostro codice per apportare delle modifiche mentre stiamo programmando.modifiche mentre stiamo programmando.

Torniamo ora al metodo viewWillAppear della classeTorniamo ora al metodo viewWillAppear della classe DettagliProgrammazioneViewController nel quale aggiorniamo laDettagliProgrammazioneViewController nel quale aggiorniamo la visualizzazione dei dettagli della programmazione.visualizzazione dei dettagli della programmazione.

Page 226: Xcode4 Tutorial Completo

AggiungiamoAggiungiamo

numeroCanale.selectedSegmentIndex=[laProgrammazione.canalenumeroCanale.selectedSegmentIndex=[laProgrammazione.canaleDaRegistrare intValue];DaRegistrare intValue];E’ un’istruzione che abbiamo già incontrato. Stiamo assegnandoE’ un’istruzione che abbiamo già incontrato. Stiamo assegnando alla property selectedSegmentIndex , che indica il pulsantealla property selectedSegmentIndex , che indica il pulsante selezionato del segmentedcontrol, il valore intero rappresentatoselezionato del segmentedcontrol, il valore intero rappresentato dall’istanza di NSNumber referenziata da canaleDaRegistrare.dall’istanza di NSNumber referenziata da canaleDaRegistrare.

Eseguiamo ancora una volta il programma e verifichiamo che tuttoEseguiamo ancora una volta il programma e verifichiamo che tutto funzioni correttamente.funzioni correttamente.

Passiamo ora a gestire l’aggiornamento delle informazioni unaPassiamo ora a gestire l’aggiornamento delle informazioni una volta che si esce dalla view dei dettagli. Il metodo che gestiscevolta che si esce dalla view dei dettagli. Il metodo che gestisce questo evento è viewWillDisappear, quindi in questo metodoquesto evento è viewWillDisappear, quindi in questo metodo scriveremo le istruzioni per aggiornare le property dellascriveremo le istruzioni per aggiornare le property della programmazione con i valori presenti sull’interfaccia.programmazione con i valori presenti sull’interfaccia.

Nel file DettagliProgrammazioneViewController aggiungete ilNel file DettagliProgrammazioneViewController aggiungete il metodometodo

-(void)viewWillDisappear:(BOOL)animated {-(void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [super viewWillDisappear:animated]; if (nomeProgramma.text==nil) { if (nomeProgramma.text==nil) { laProgrammazione.nomeDelProgramma=@""; laProgrammazione.nomeDelProgramma=@""; } else { } else { laProgrammazione.nomeDelProgramma=[NSString laProgrammazione.nomeDelProgramma=[NSString stringWithString:nomeProgramma.text];stringWithString:nomeProgramma.text]; } } laProgrammazione.canaleDaRegistrare=[NSNumber laProgrammazione.canaleDaRegistrare=[NSNumber numberWithInt:numeroCanale.selectedSegmentIndex];numberWithInt:numeroCanale.selectedSegmentIndex];}}La prima istruzioneLa prima istruzione

[super viewWillDisappear:animated];[super viewWillDisappear:animated];è l’invocazione dello stesso metodo nella superclasse, è buonaè l’invocazione dello stesso metodo nella superclasse, è buona regola di programmazione inserire questa invocazione anche seregola di programmazione inserire questa invocazione anche se può non avere effetti specifici.può non avere effetti specifici.

Page 227: Xcode4 Tutorial Completo

Poi controlliamo se abbiamo inserito un nome nel campo delPoi controlliamo se abbiamo inserito un nome nel campo del programma e, se non lo abbiamo fatto gli assegnamo la stringaprogramma e, se non lo abbiamo fatto gli assegnamo la stringa vuota (@””) altrimenti convuota (@””) altrimenti con

laProgrammazione.nomeDelProgramma=[NSStringlaProgrammazione.nomeDelProgramma=[NSString stringWithString:nomeProgramma.text];stringWithString:nomeProgramma.text];assegnamo alla property nomeDelProgramma il contenuto delassegnamo alla property nomeDelProgramma il contenuto del campo di testo nomeProgramma. Il valore che assegnamo è un’campo di testo nomeProgramma. Il valore che assegnamo è un’ istanza di NSString creata a partire dal contenuto del campo.istanza di NSString creata a partire dal contenuto del campo.

Infine conInfine con

laProgrammazione.canaleDaRegistrare=[NSNumberlaProgrammazione.canaleDaRegistrare=[NSNumber numberWithInt:numeroCanale.selectedSegmentIndex];numberWithInt:numeroCanale.selectedSegmentIndex];assegnamo alla property canaleDaRegistrare l’indice del tastoassegnamo alla property canaleDaRegistrare l’indice del tasto selezionato nel segmentedcontrol. In maniera coerente con il tiposelezionato nel segmentedcontrol. In maniera coerente con il tipo di dato il valore intero dell’indice viene passato ad un costruttoredi dato il valore intero dell’indice viene passato ad un costruttore di istanza per NSNumber ed il risultato assegnato alla property.di istanza per NSNumber ed il risultato assegnato alla property.

Eseguiamo il programma e proviamo a modificare i valori diEseguiamo il programma e proviamo a modificare i valori di dettaglio verificando l’effetto delle modifiche.dettaglio verificando l’effetto delle modifiche.

21 – Aggiungiamo le registrazioni21 – Aggiungiamo le registrazioni

iOS, , Mac, , OSX, , SDK, , Tech Room

Riprendiamo lo sviluppo della nostra funzione di registrazione.Riprendiamo lo sviluppo della nostra funzione di registrazione. Osserviamo che il bottone per avviare le registrazioni dovrebbeOsserviamo che il bottone per avviare le registrazioni dovrebbe apparire solo quando apriamo la view da una programmazione eapparire solo quando apriamo la view da una programmazione e non quando ci arriviamo creandone una nuova. Per fare questo,non quando ci arriviamo creandone una nuova. Per fare questo, agiremo sulla proprietà del bottone che lo rende visibile o no.agiremo sulla proprietà del bottone che lo rende visibile o no.

Quindi quello che dobbiamo fare è programmare qualcosa del tipoQuindi quello che dobbiamo fare è programmare qualcosa del tipo

Page 228: Xcode4 Tutorial Completo

se sto mostrando i dettagli di una programmazione esistente se sto mostrando i dettagli di una programmazione esistente visualizza il bottone ‘registra’ altrimenti visualizza il bottone ‘registra’ altrimenti nascondo il bottone ‘registra’nascondo il bottone ‘registra’

La prima cosa che dobbiamo decidere è dove implementare questaLa prima cosa che dobbiamo decidere è dove implementare questa sequenza; ovviamente il punto più corretto è il momento disequenza; ovviamente il punto più corretto è il momento di visualizzazione della view, quindi nel metodo viewWillAppear.visualizzazione della view, quindi nel metodo viewWillAppear.

Ora dobbiamo decidere come implementare la condizione ‘Ora dobbiamo decidere come implementare la condizione ‘stosto mostrando i dettagli di una programmazione esistente’ ,mostrando i dettagli di una programmazione esistente’ , possiamo possiamo riflettere sul fatto che se siamo nel caso di una nuovariflettere sul fatto che se siamo nel caso di una nuova programmazione le sue proprietà non sono ancora valorizzate,programmazione le sue proprietà non sono ancora valorizzate, quindi possiamo verificare questo aspetto su una delle proprietàquindi possiamo verificare questo aspetto su una delle proprietà della programmazione per distinguere in quale caso ci troviamo.della programmazione per distinguere in quale caso ci troviamo.

Per capire meglio quello che sto dicendo fate questa prova:Per capire meglio quello che sto dicendo fate questa prova: inserite nel metodo viewWillAppear diinserite nel metodo viewWillAppear di DettagliProgrammazioneViewController l’istruzioneDettagliProgrammazioneViewController l’istruzione

NSLog(@"-----%@-NSLog(@"-----%@-%@",laProgrammazione.nomeDelProgramma,laProgrammazione.%@",laProgrammazione.nomeDelProgramma,laProgrammazione.canaleDaRegistrare);canaleDaRegistrare);l’istruzione NSLog l’abbiamo vista fin troppe volte quindi non vel’istruzione NSLog l’abbiamo vista fin troppe volte quindi non ve la illustro. Provate ad eseguire il programma ed osservate ila illustro. Provate ad eseguire il programma ed osservate i messaggi a console quando visualizzate i dettagli di unamessaggi a console quando visualizzate i dettagli di una programmazione già presente o quando ne create una nuova.programmazione già presente o quando ne create una nuova.

Sostituiamo quindi l’istruzione NSLog che abbiamo appenaSostituiamo quindi l’istruzione NSLog che abbiamo appena inserito coninserito con

if (laProgrammazione.nomeDelProgramma==nil) {if (laProgrammazione.nomeDelProgramma==nil) {} else {} else {}}con la quale andiamo a verificare se il nomeDelProgrammacon la quale andiamo a verificare se il nomeDelProgramma contiene qualcosa oppure no per discriminare tra i due percorsi dicontiene qualcosa oppure no per discriminare tra i due percorsi di attivazione della view.attivazione della view.

Una volta che abbiamo capito come implementare la condizioneUna volta che abbiamo capito come implementare la condizione

Page 229: Xcode4 Tutorial Completo

passiamo ad analizzare le azioni nei due blocchi. In entrambe ipassiamo ad analizzare le azioni nei due blocchi. In entrambe i casi dobbiamo dire qualcosa al bottone, abbiamo quindi bisognocasi dobbiamo dire qualcosa al bottone, abbiamo quindi bisogno di un canale dal controller verso il bottone, cioè abbiamo bisognodi un canale dal controller verso il bottone, cioè abbiamo bisogno di un Outlet.di un Outlet.

Nel file DettagliProgrammazioneViewController.h aggiungiamoNel file DettagliProgrammazioneViewController.h aggiungiamo alle variabili di istanza della classealle variabili di istanza della classe

IBOutlet UIButton *avviaRegistrazione;IBOutlet UIButton *avviaRegistrazione;e nel file DettagliProgrammazioneView.xib colleghiamo l’outlet ale nel file DettagliProgrammazioneView.xib colleghiamo l’outlet al bottone cliccando con il destro su File’s owner e trascinando ilbottone cliccando con il destro su File’s owner e trascinando il mouse dall’outlet al bottone.mouse dall’outlet al bottone.

A questo punto possiamo tornare nel metodo viewWillAppear diA questo punto possiamo tornare nel metodo viewWillAppear di DettagliProgrammazioneViewController per implementare laDettagliProgrammazioneViewController per implementare la logica per mostrare e nascondere il bottone.logica per mostrare e nascondere il bottone.

Nel ramo True dell’if che abbiamo inserito sopra stiamo gestendoNel ramo True dell’if che abbiamo inserito sopra stiamo gestendo la condizione di nomeDelProgramma==nil, cioè siamo nellala condizione di nomeDelProgramma==nil, cioè siamo nella situazione di una nuova programmazione e vogliamo nascondere ilsituazione di una nuova programmazione e vogliamo nascondere il bottone; quindi all’interno di questo blocco scriviamobottone; quindi all’interno di questo blocco scriviamo

avviaRegistrazione.hidden=TRUE;avviaRegistrazione.hidden=TRUE;cioè assegnamo True alla property hidden del bottone referenziatocioè assegnamo True alla property hidden del bottone referenziato dal nostro outlet. l’effetto di questo assegnamento è quello didal nostro outlet. l’effetto di questo assegnamento è quello di nascondere il bottone nell’interfaccia.nascondere il bottone nell’interfaccia.

In maniera analoga nel ramo False, e quindi nel blocco dopo else,In maniera analoga nel ramo False, e quindi nel blocco dopo else, scriviamoscriviamo

avviaRegistrazione.hidden=FALSE;avviaRegistrazione.hidden=FALSE;che ha l’effetto contrario.che ha l’effetto contrario.

Ora siamo pronti per implementare la logica della registrazione.Ora siamo pronti per implementare la logica della registrazione. Come vi dicevo noi non registreremo nulla ma l’unica cosa cheCome vi dicevo noi non registreremo nulla ma l’unica cosa che faremo sarà quella di creare oggetti di Registrazione ed associarlifaremo sarà quella di creare oggetti di Registrazione ed associarli alla corrispondente programmazione.alla corrispondente programmazione.

Andiamo quindi nel metodo Andiamo quindi nel metodo registra,registra, del file del file

Page 230: Xcode4 Tutorial Completo

DettagliProgrammazioneViewController.m, che vi ricordo essereDettagliProgrammazioneViewController.m, che vi ricordo essere quello associato come action al bottone quello associato come action al bottone registraregistra dell’interfaccia. dell’interfaccia.

La prima cosa che dobbiamo fare è creare un’istanza della classeLa prima cosa che dobbiamo fare è creare un’istanza della classe Registrazione, abbiamo quindi bisogno della import della classeRegistrazione, abbiamo quindi bisogno della import della classe nel file DettagliProgrammazioneViewController.h.nel file DettagliProgrammazioneViewController.h.

Nel metodo registra scriviamoNel metodo registra scriviamo

Registrazione *nuovaRegistrazione=[[Registrazione alloc] init];Registrazione *nuovaRegistrazione=[[Registrazione alloc] init];per creare una nuova istanza di Registrazione.per creare una nuova istanza di Registrazione.

Ora scriviamoOra scriviamo

nuovaRegistrazione.dataOraRegistrazione=[NSDate date];nuovaRegistrazione.dataOraRegistrazione=[NSDate date];in questo modo assegnamo alla property dataOraRegistrazionein questo modo assegnamo alla property dataOraRegistrazione un’istanza di NSDate che rappresenta la data e l’ora attuali.un’istanza di NSDate che rappresenta la data e l’ora attuali.

Infine conInfine con

[self.laProgrammazione[self.laProgrammazione addRegistrazioniAssociateObject:nuovaRegistrazione];addRegistrazioniAssociateObject:nuovaRegistrazione];aggiungiamo l’istanza di registrazione che abbiamo appena creatoaggiungiamo l’istanza di registrazione che abbiamo appena creato alla programmazione selezionata.alla programmazione selezionata.

Eseguite l’applicazione e verificate che non ci siano errori.Eseguite l’applicazione e verificate che non ci siano errori.

Finora, per quanto riguarda Registrazione, abbiamoFinora, per quanto riguarda Registrazione, abbiamo sostanzialmente lavorato a livello Model, cioè abbiamo creatosostanzialmente lavorato a livello Model, cioè abbiamo creato istanze e le abbiamo relazionate con altri oggetti. Ora dobbiamoistanze e le abbiamo relazionate con altri oggetti. Ora dobbiamo costruire il livello View per vedere gli elenchi di registrazionicostruire il livello View per vedere gli elenchi di registrazioni delle diverse programmazioni.delle diverse programmazioni.

Scegliamo di inserire un altro tab che mostra una view comeScegliamo di inserire un altro tab che mostra una view come quella in figuraquella in figura

Page 231: Xcode4 Tutorial Completo

La struttura della view si ottiene con una tableview di tipoLa struttura della view si ottiene con una tableview di tipo grouped nella quale avremo una sezione per ogni programmazionegrouped nella quale avremo una sezione per ogni programmazione all’interno della quale elencheremo le diverse registrazioni.all’interno della quale elencheremo le diverse registrazioni.

Creiamo il file con l’interfaccia aggiungendo al progetto un fileCreiamo il file con l’interfaccia aggiungendo al progetto un file XIB di tipo View e lo chiamiamoXIB di tipo View e lo chiamiamo ElencoRegistrazioni. ElencoRegistrazioni. Cancelliamo l’oggetto view dal gruppo ObjectsCancelliamo l’oggetto view dal gruppo Objects ed aggiungiamo ed aggiungiamo una TableView prendendola dalla Library nel frame di destra. Orauna TableView prendendola dalla Library nel frame di destra. Ora selezioniamo la tableview e nell’Attributes Inspector cambiamo loselezioniamo la tableview e nell’Attributes Inspector cambiamo lo style da style da PlainPlain a a GroupedGrouped..

Apriamo il file MainWindow.xib, esplodiamo la gerarchia del TabApriamo il file MainWindow.xib, esplodiamo la gerarchia del Tab Bar Controller nella sezione Objects del frame di sinistra edBar Controller nella sezione Objects del frame di sinistra ed aggiungiamo un TableViewController scelto dalla Library.aggiungiamo un TableViewController scelto dalla Library. Selezioniamo il Tab Bar Item all’interno del Table View ControllerSelezioniamo il Tab Bar Item all’interno del Table View Controller appena inserito e nell’Attributes Inspector cambiamo il Titleappena inserito e nell’Attributes Inspector cambiamo il Title scrivendoci scrivendoci RegistrazioniRegistrazioni. Apriamo la gerarchia del Table View. Apriamo la gerarchia del Table View Controller e cancelliamo l’oggetto view al suo interno. OraController e cancelliamo l’oggetto view al suo interno. Ora selezioniamo il Table View Controller e nell’Attributes Inspectorselezioniamo il Table View Controller e nell’Attributes Inspector scriviamo ElencoRegistrazioni nel campo NIB Name.scriviamo ElencoRegistrazioni nel campo NIB Name.

Page 232: Xcode4 Tutorial Completo

Ora andiamo a creare la classe del controller. Clicchiamo con ilOra andiamo a creare la classe del controller. Clicchiamo con il bottone destro sulla cartella myTv in XCode; scegliamo Add File ebottone destro sulla cartella myTv in XCode; scegliamo Add File e dal gruppo Cocoa Touch selezioniamo UIViewController subclass.dal gruppo Cocoa Touch selezioniamo UIViewController subclass. Gli diamo il nome ElencoRegistrazioniController e lo definiamoGli diamo il nome ElencoRegistrazioniController e lo definiamo sottoclasse di UITableViewController.sottoclasse di UITableViewController.

Prima di definire la nostra classe Controller assegnamola agliPrima di definire la nostra classe Controller assegnamola agli oggetti di interfaccia. Selezioniamo oggetti di interfaccia. Selezioniamo il file MainWindow.xib, il file MainWindow.xib, selezioniamo il Table View Controller delle registrazioniselezioniamo il Table View Controller delle registrazioni assegnamogli la classe ElencoRegistrazioniController nel campoassegnamogli la classe ElencoRegistrazioniController nel campo Class.Class. Ora apriamo il file ElencoRegistrazioni.xib, selezioniamo Ora apriamo il file ElencoRegistrazioni.xib, selezioniamo il File’s owner e nell’Identity Inspector assegnamogli la classeil File’s owner e nell’Identity Inspector assegnamogli la classe ElencoRegistrazioniController. Clicchiamo con il bottone destroElencoRegistrazioniController. Clicchiamo con il bottone destro sul File’s owner e leghiamo l’outlet view all’oggetto tableview.sul File’s owner e leghiamo l’outlet view all’oggetto tableview. Clicchiamo con il destro sulla tableview e colleghiamo ilClicchiamo con il destro sulla tableview e colleghiamo il datasource ed il delegate al File’s owner.datasource ed il delegate al File’s owner.

Eseguiamo l’applicazione per verificare che non ci siano errori.Eseguiamo l’applicazione per verificare che non ci siano errori.

Riprendiamo il nostro controller per scrivere la logica diRiprendiamo il nostro controller per scrivere la logica di collegamento tra Model e View. La prima considerazione è che ilcollegamento tra Model e View. La prima considerazione è che il nostro controller dovrà accedere all’elenco delle programmazioninostro controller dovrà accedere all’elenco delle programmazioni detenuto dal televisore; quindi abbiamo bisogno di una property didetenuto dal televisore; quindi abbiamo bisogno di una property di Televisore che si riferirà al televisore, quindi inTelevisore che si riferirà al televisore, quindi in ElencoRegistrazioniController.h aggiungiamoElencoRegistrazioniController.h aggiungiamo

@property (nonatomic,retain) Televisore *ilTelevisore;@property (nonatomic,retain) Televisore *ilTelevisore;ovviamente dobbiamo fare la import della classe Televisore e laovviamente dobbiamo fare la import della classe Televisore e la synthesize nell’implementation file.synthesize nell’implementation file.

Andiamo ad implementare i metodi di datasource per ilAndiamo ad implementare i metodi di datasource per il popolamento della tableview.popolamento della tableview.

La prima cosa da fare è dire al sistema da quante sezioni èLa prima cosa da fare è dire al sistema da quante sezioni è composta la nostra tabella, questo si facomposta la nostra tabella, questo si fa implementando il metodo implementando il metodo numberOfSectionsInTableView del datasource per fargli restituirenumberOfSectionsInTableView del datasource per fargli restituire il numero di sezioni. Scorriamo il fileil numero di sezioni. Scorriamo il file ElencoRegistrazioniController.m fino a trovare il metodo.ElencoRegistrazioniController.m fino a trovare il metodo.

Page 233: Xcode4 Tutorial Completo

Cancelliamo, se presente (dipende dalla versione di XCode), laCancelliamo, se presente (dipende dalla versione di XCode), la rigariga

#warning Potentially incomplete method implementation.#warning Potentially incomplete method implementation.Attualmente questo metodo restituisce 0. Qual è il numero diAttualmente questo metodo restituisce 0. Qual è il numero di sezioni che dobbiamo avere nella nostra tabella? Se abbiamo unasezioni che dobbiamo avere nella nostra tabella? Se abbiamo una sezione per ogni programmazione, allora sarà pari al numero disezione per ogni programmazione, allora sarà pari al numero di programmazioni; quindi sostituiamoprogrammazioni; quindi sostituiamo

return 0;return 0;concon

return [ilTelevisore.elencoProgrammazioni count];return [ilTelevisore.elencoProgrammazioni count];Cioè accediamo alla property elencoProgrammazioni diCioè accediamo alla property elencoProgrammazioni di ilTelevisore, che ci restituisce un NSMutableArray con leilTelevisore, che ci restituisce un NSMutableArray con le programmaziuoni, ed invochiamo il metodo count che è unprogrammaziuoni, ed invochiamo il metodo count che è un metodo predefinito per NSArray che restituisce il numero dimetodo predefinito per NSArray che restituisce il numero di elementi.elementi.

La seconda cosa è dire al sistema qual è il titolo di ogni sezione,La seconda cosa è dire al sistema qual è il titolo di ogni sezione, questo si fa implementando il metodoquesto si fa implementando il metodo

-(NSString *)tableView:(UITableView *)tableView-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {titleForHeaderInSection:(NSInteger)section {}}che prende in input il numero della sezione e restituisce unche prende in input il numero della sezione e restituisce un NSString con il titolo per quella sezione. Per noi il titolo è il nomeNSString con il titolo per quella sezione. Per noi il titolo è il nome del programma cioè il valore della property del programma cioè il valore della property nomeDelProgrammanomeDelProgramma della programmazione contenuta nella posizione della programmazione contenuta nella posizione sectionsection dell’elenco dell’elenco elencoProgrammazionielencoProgrammazioni di ilTelevisore. Quindi di ilTelevisore. Quindi aggiungiamo il metodoaggiungiamo il metodo

-(NSString *)tableView:(UITableView *)tableView-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {titleForHeaderInSection:(NSInteger)section {return ((Programmazione *)[ilTelevisore.elencoProgrammazionireturn ((Programmazione *)[ilTelevisore.elencoProgrammazioni objectAtIndex:section]).nomeDelProgramma;objectAtIndex:section]).nomeDelProgramma;}}abbiamo fatto il type casting a (Programmazione *) dell’elementoabbiamo fatto il type casting a (Programmazione *) dell’elemento

Page 234: Xcode4 Tutorial Completo

restituito dall’accesso all’array per poter accedere alla suarestituito dall’accesso all’array per poter accedere alla sua property nomeDelProgramma; per far questo è necessario fareproperty nomeDelProgramma; per far questo è necessario fare l’import di Programmazione.l’import di Programmazione.

Ora dobbiamo dire al sistema quante righe ha ogni sezione, perOra dobbiamo dire al sistema quante righe ha ogni sezione, per fare questo è necessario implementare il metodofare questo è necessario implementare il metodo

- (NSInteger)tableView:(UITableView *)tableView- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)sectionnumberOfRowsInSection:(NSInteger)section{{}}che riceve in ingresso il numero della sezione e restituisce ilche riceve in ingresso il numero della sezione e restituisce il numero di righe per quella sezione.numero di righe per quella sezione.

Se le nostre sezioni devono contenere tante righe quante sono leSe le nostre sezioni devono contenere tante righe quante sono le registrazioni, allora il valore da restituire sarà il numero diregistrazioni, allora il valore da restituire sarà il numero di elementi dell’array elementi dell’array registrazioniAssociateregistrazioniAssociate della programmazione della programmazione che si trova nella posizione section dell’arrayche si trova nella posizione section dell’array elencoProgrammazioni di ilTelevisore.elencoProgrammazioni di ilTelevisore. Quindi scorriamo il file Quindi scorriamo il file fino a trovare il metodo e cancelliamo, se presente (dipende dallafino a trovare il metodo e cancelliamo, se presente (dipende dalla versione di XCode), la rigaversione di XCode), la riga

#warning Potentially incomplete method implementation.#warning Potentially incomplete method implementation.Attualmente questo metodo restituisce 0, quindi sostituiamoAttualmente questo metodo restituisce 0, quindi sostituiamo

return 0;return 0;concon

Programmazione *tmpProgrammazione=((Programmazione *)Programmazione *tmpProgrammazione=((Programmazione *)[ilTelevisore.elencoProgrammazioni objectAtIndex:section]);[ilTelevisore.elencoProgrammazioni objectAtIndex:section]);return [tmpProgrammazione.registrazioniAssociate count];return [tmpProgrammazione.registrazioniAssociate count];Per comodità abbiamo spezzato l’istruzione in due; nella primaPer comodità abbiamo spezzato l’istruzione in due; nella prima recuperiamo l’oggettorecuperiamo l’oggetto nella posizione section dall’elenco delle nella posizione section dall’elenco delle programmazioni e poi restituiamo il numero di registrazioniprogrammazioni e poi restituiamo il numero di registrazioni associate.associate.

Passiamo al metodo con il quale costruire le celle per la tabella.Passiamo al metodo con il quale costruire le celle per la tabella. Nel metodoNel metodo

Page 235: Xcode4 Tutorial Completo

- (UITableViewCell *)tableView:(UITableView *)tableView- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPathcellForRowAtIndexPath:(NSIndexPath *)indexPathprima diprima di

return cell;return cell;aggiungiamoaggiungiamo

Programmazione *tmpProgrammazione=(Programmazione *)Programmazione *tmpProgrammazione=(Programmazione *)[ilTelevisore.elencoProgrammazioni[ilTelevisore.elencoProgrammazioni objectAtIndex:indexPath.section];objectAtIndex:indexPath.section];Registrazione *tmpRegistrazione=(Registrazione *)Registrazione *tmpRegistrazione=(Registrazione *)[tmpProgrammazione.registrazioniAssociate[tmpProgrammazione.registrazioniAssociate objectAtIndex:indexPath.row];objectAtIndex:indexPath.row];cell.textLabel.text=[tmpRegistrazione.dataOraRegistrazionecell.textLabel.text=[tmpRegistrazione.dataOraRegistrazione description];description];Vi lascio come esercizio l’analisi di queste istruzioni.Vi lascio come esercizio l’analisi di queste istruzioni.

Infine nel metodo viewWillAppear aggiungiamo l’istruzione perInfine nel metodo viewWillAppear aggiungiamo l’istruzione per ricaricare la tabella ogni volta che viene visualizzataricaricare la tabella ogni volta che viene visualizzata

[self.tableView reloadData];[self.tableView reloadData];La cosa che ci rimane da fare è assegnare l’istanza di TelevisoreLa cosa che ci rimane da fare è assegnare l’istanza di Televisore alla property, seguiamola stessa logica adottata per gli altrialla property, seguiamola stessa logica adottata per gli altri controller. Nel file myTvAppDelegate.h aggiungiamocontroller. Nel file myTvAppDelegate.h aggiungiamo

#import "ElencoRegistrazioniController.h"#import "ElencoRegistrazioniController.h"ee

@property (nonatomic, retain) IBOutlet@property (nonatomic, retain) IBOutlet ElencoRegistrazioniController *myRegistrazioniController;ElencoRegistrazioniController *myRegistrazioniController;abbiamo dichiarato una property quindi è necessaria la synthesize.abbiamo dichiarato una property quindi è necessaria la synthesize.

Nel file myTvAppDelegate..m andiamo nel metodoNel file myTvAppDelegate..m andiamo nel metodo didFinishLaunchingWithOptionsdidFinishLaunchingWithOptions e aggiungiamo e aggiungiamo

myRegistrazioniController.ilTelevisore=tv1;myRegistrazioniController.ilTelevisore=tv1;Infine andiamo ad agganciare l’outlet che abbiamo appena creato.Infine andiamo ad agganciare l’outlet che abbiamo appena creato. Apriamo il file MainWindow.xib, clicchiamo con il destroApriamo il file MainWindow.xib, clicchiamo con il destro sull’oggetto My Tv App Delegate e colleghiamo l’outletsull’oggetto My Tv App Delegate e colleghiamo l’outlet

Page 236: Xcode4 Tutorial Completo

myRegistrazioniController con il Table View ControllermyRegistrazioniController con il Table View Controller Registrazioni.Registrazioni.

Eseguite l’applicazione e verificate il corretto funzionamento.Eseguite l’applicazione e verificate il corretto funzionamento.

22 – Pre-conclusione22 – Pre-conclusione

iOS, , Mac, , OSX, , SDK, , Tech Room

Con il capitolo 21 abbiamo concluso il percorso introduttivo alleCon il capitolo 21 abbiamo concluso il percorso introduttivo alle basi del linguaggio Objective-C basi del linguaggio Objective-C e di Cocoa.e di Cocoa.

Abbiamo visto le istruzioni fondamentali del linguaggio (laAbbiamo visto le istruzioni fondamentali del linguaggio (la sequenza, l’istruzione condizionale, il ciclo, l’invocazione disequenza, l’istruzione condizionale, il ciclo, l’invocazione di messaggi). L’elemento base del linguaggio è l’oggetto che èmessaggi). L’elemento base del linguaggio è l’oggetto che è un’istanza di una classe che ne descrive le sue caratteristiche inun’istanza di una classe che ne descrive le sue caratteristiche in termini di proprietà e metodi.termini di proprietà e metodi. Un programma in Objective-C è Un programma in Objective-C è costituito da un insieme di oggetti che cooperano nellacostituito da un insieme di oggetti che cooperano nella realizzazione della logica applicativa tramite lo scambio direalizzazione della logica applicativa tramite lo scambio di messaggi che si traducono nell’invocazione di metodi dell’oggettomessaggi che si traducono nell’invocazione di metodi dell’oggetto ricevente. Le classi sono strutturabili in gerarchie all’interno dellericevente. Le classi sono strutturabili in gerarchie all’interno delle quali esiste il meccanismo di ereditarietà. Inoltre le classi (oquali esiste il meccanismo di ereditarietà. Inoltre le classi (o meglio gli oggetti) possono relazionarsi con il meccanismo dimeglio gli oggetti) possono relazionarsi con il meccanismo di composizione, cioè un oggetto è costituito da altri oggetti, uncomposizione, cioè un oggetto è costituito da altri oggetti, un televisore ha al suo interno un elenco di programmazioni etelevisore ha al suo interno un elenco di programmazioni e l’elenco è un oggetto e le programmazioni sono altri oggetti.l’elenco è un oggetto e le programmazioni sono altri oggetti.

Page 237: Xcode4 Tutorial Completo

Abbiamo visto che lo sviluppo di un programma deve seguireAbbiamo visto che lo sviluppo di un programma deve seguire determinati pattern; il più importante (in quanto costituisce ladeterminati pattern; il più importante (in quanto costituisce la struttura di base del codice) è sicuramente il pattern MVC chestruttura di base del codice) è sicuramente il pattern MVC che separa gli oggetti che rappresentano le informazioni (Model) daglisepara gli oggetti che rappresentano le informazioni (Model) dagli oggetti destinati ad interagire con l’Utente (View) collegandolioggetti destinati ad interagire con l’Utente (View) collegandoli attraverso un livello di oggetti nei quali viene implementata laattraverso un livello di oggetti nei quali viene implementata la logica elaborativa (Control).logica elaborativa (Control).

Abbiamo poi usato diverse classi (NSString, NSArray,Abbiamo poi usato diverse classi (NSString, NSArray, NSNumber) predefinite in Cocoa che è esattamente questo: unNSNumber) predefinite in Cocoa che è esattamente questo: un insieme (enorme) di classi predefinite, raggruppate in framework,insieme (enorme) di classi predefinite, raggruppate in framework, da utilizzare nelle nostre applicazioni. In realtà esistono dueda utilizzare nelle nostre applicazioni. In realtà esistono due framework leggermente diversi Cocoa per lo sviluppo di app perframework leggermente diversi Cocoa per lo sviluppo di app per Mac OSX e Cocoa Touch per lo sviluppo di app per iOS, maMac OSX e Cocoa Touch per lo sviluppo di app per iOS, ma l’importante è capire il concetto di framework.l’importante è capire il concetto di framework.

Di seguito vi riporto alcuni dei framework di Cocoa TouchDi seguito vi riporto alcuni dei framework di Cocoa Touch

Audio and VideoAudio and Video

Core AudioCore Audio

OpenALOpenAL

Media LibraryMedia Library

AV FoundationAV Foundation

Data ManagementData Management

Core DataCore Data

SQLiteSQLite

Graphics and AnimationGraphics and Animation

Core AnimationCore Animation

OpenGL ESOpenGL ES

Quartz 2DQuartz 2D

Networking and InternetNetworking and Internet

BonjourBonjour

WebKitWebKit

Page 238: Xcode4 Tutorial Completo

BSD SocketsBSD Sockets

User ApplicationsUser Applications

Address BookAddress Book

Core LocationCore Location

Map KitMap Kit

Store KitStore Kit

Come potete immaginare è impossibile conoscere tutte le classiCome potete immaginare è impossibile conoscere tutte le classi (proprietà e metodi) di tutti questi framework, per cui la cosa più(proprietà e metodi) di tutti questi framework, per cui la cosa più importante da imparare è la consultazione della documentazioneimportante da imparare è la consultazione della documentazione allegata ad XCode.allegata ad XCode.

L’ultimo sviluppo che faremo sarà proprio l’introduzione di unL’ultimo sviluppo che faremo sarà proprio l’introduzione di un altro di questi framework: Core Data. Come potete vederealtro di questi framework: Core Data. Come potete vedere dall’elenco è un framework per la gestione dei dati che useremodall’elenco è un framework per la gestione dei dati che useremo per introdurre la persistenza nella nostra applicazione.per introdurre la persistenza nella nostra applicazione.

23 – Persistenza23 – Persistenza

iOS, , Mac, , OSX, , SDK, , Tech Room

Se lanciamo la nostra applicazione, creiamo alcuneSe lanciamo la nostra applicazione, creiamo alcune programmazioni e registrazioni poi la chiudiamo (non mettendolaprogrammazioni e registrazioni poi la chiudiamo (non mettendola in background) e la lanciamo nuovamente, osserviamo che lein background) e la lanciamo nuovamente, osserviamo che le programmazioni e registrazioni create precedentemente non ciprogrammazioni e registrazioni create precedentemente non ci sono più. Introdurre la persistenzasono più. Introdurre la persistenza in un’applicazione serve in un’applicazione serve proprio a superare questo problema. Possiamo dire che laproprio a superare questo problema. Possiamo dire che la persistenza dei dati è la capacità di un’applicazione di utilizzare,persistenza dei dati è la capacità di un’applicazione di utilizzare, all’interno di una sua esecuzione, dati creati in esecuzioniall’interno di una sua esecuzione, dati creati in esecuzioni precedenti.precedenti.

Page 239: Xcode4 Tutorial Completo

Esistono diversi meccanismi per implementare la persistenza eEsistono diversi meccanismi per implementare la persistenza e tutti si fondano sulla creazione di file. Si possono usaretutti si fondano sulla creazione di file. Si possono usare direttamente i file piatti, con le operazioni tipiche di lettura edirettamente i file piatti, con le operazioni tipiche di lettura e scrittura (rif. NSFileManager); si possono usare file strutturati inscrittura (rif. NSFileManager); si possono usare file strutturati in XML, utilizzando un parser per il recupero delle informazioni (rif.XML, utilizzando un parser per il recupero delle informazioni (rif. property list e NSUserDefaults); si possono usare dei database cheproperty list e NSUserDefaults); si possono usare dei database che sono un modo per strutturare dei dati all’interno di uno o più filesono un modo per strutturare dei dati all’interno di uno o più file (rif. SQLite). In Cocoa è disponibile il framework Core Data che(rif. SQLite). In Cocoa è disponibile il framework Core Data che astrae un database SQLite per modellare le classi della nostraastrae un database SQLite per modellare le classi della nostra applicazione, in tal modo noi continueremo a programmare (conapplicazione, in tal modo noi continueremo a programmare (con piccolissime variazioni) come siamo abituati e CoreData sipiccolissime variazioni) come siamo abituati e CoreData si preoccuperà di rendere persistenti i nostri dati.preoccuperà di rendere persistenti i nostri dati.

Il framework Core Data è molto articolato e anche molto benIl framework Core Data è molto articolato e anche molto ben documentato per cui è assolutamente necessario che approfondiatedocumentato per cui è assolutamente necessario che approfondiate tutti i concetti che vedremo nella documentazione Apple.tutti i concetti che vedremo nella documentazione Apple.

Nella progettazione di un’applicazione si definisce prima delloNella progettazione di un’applicazione si definisce prima dello sviluppo quali sono le classi persistenti, cioè quelle i cui oggettisviluppo quali sono le classi persistenti, cioè quelle i cui oggetti vogliamo disponibili tra diverse esecuzioni dell’applicazione. Noivogliamo disponibili tra diverse esecuzioni dell’applicazione. Noi però partiamo da un’applicazione che abbiamo già sviluppatoperò partiamo da un’applicazione che abbiamo già sviluppato quindi procederemo con un percorso che ci porterà a sostituirequindi procederemo con un percorso che ci porterà a sostituire alcune classi con altre persistenti.alcune classi con altre persistenti.

Come prima cosa diciamo al sistema che useremo Core Data,Come prima cosa diciamo al sistema che useremo Core Data, dobbiamo quindi aggiungere il framework al progetto. Selezionatedobbiamo quindi aggiungere il framework al progetto. Selezionate l’icona del progetto, cioè la radice dell’albero in XCode; oral’icona del progetto, cioè la radice dell’albero in XCode; ora selezionate il target e nel tab Build Phases aprite la sezione Linkselezionate il target e nel tab Build Phases aprite la sezione Link Binary with Libraries. Cliccate sul + e nel pannello che vi vieneBinary with Libraries. Cliccate sul + e nel pannello che vi viene presentato scegliete Core Data ed aggiungetelo.presentato scegliete Core Data ed aggiungetelo.

Facendo click sulla cartella del progetto selezioniamo New File eFacendo click sulla cartella del progetto selezioniamo New File e scegliamo Data Model nel gruppo Core Data dei template iOS,scegliamo Data Model nel gruppo Core Data dei template iOS, chiamiamolo myTv e salviamo. Al progetto viene aggiunto un filechiamiamolo myTv e salviamo. Al progetto viene aggiunto un file myTv.xcdatamodeld, selezionatelo e, se necessario, scegliamo lamyTv.xcdatamodeld, selezionatelo e, se necessario, scegliamo la visualizzazione del diagramma grafico (una view bianca avisualizzazione del diagramma grafico (una view bianca a quadretti).quadretti).

Page 240: Xcode4 Tutorial Completo

Cliccate su Add Entity e appare un’entità nella view delCliccate su Add Entity e appare un’entità nella view del diagramma. Fate doppio click sul nome Entity e scrivetediagramma. Fate doppio click sul nome Entity e scrivete ProgrammazioneCD. Con l’entità selezionata, cliccate su AddProgrammazioneCD. Con l’entità selezionata, cliccate su Add Attribute, nel Data Model Inspector (aprite il pannello a destra)Attribute, nel Data Model Inspector (aprite il pannello a destra) cambiate il nome dell’attributo in canaleDaRegistrare e scegliete ilcambiate il nome dell’attributo in canaleDaRegistrare e scegliete il tipo Integer 16. Aggiungete un altro attributo chiamatotipo Integer 16. Aggiungete un altro attributo chiamato nomeDelProgramma di tipo String. Non aggiungete l’attributo connomeDelProgramma di tipo String. Non aggiungete l’attributo con l’elenco delle Registrazioni.l’elenco delle Registrazioni.

Ora nella view del diagramma aggiungete un’altra entityOra nella view del diagramma aggiungete un’altra entity chiamandola RegistrazioneCD. Aggiungete a questa entity unchiamandola RegistrazioneCD. Aggiungete a questa entity un attributo, chiamato dataOraRegistrazione di tipo Date.attributo, chiamato dataOraRegistrazione di tipo Date.

Dopo aver selezionato l’entità ProgrammazioneCD aggiungeteDopo aver selezionato l’entità ProgrammazioneCD aggiungete ora una relazione, nelle ultime versioni di XCode, mantenendoora una relazione, nelle ultime versioni di XCode, mantenendo premuto il mouse su Add Attribute si evidenziano altre operazionipremuto il mouse su Add Attribute si evidenziano altre operazioni tra cui Add Relationship che dovete scegliere. Nel Data Modeltra cui Add Relationship che dovete scegliere. Nel Data Model Inspector della relazione cambiate il nome inInspector della relazione cambiate il nome in registrazioniAssociate, nel campo Destination sceglieteregistrazioniAssociate, nel campo Destination scegliete RegistrazioneCD e attivate il flag To-Many Relationship, in questoRegistrazioneCD e attivate il flag To-Many Relationship, in questo modo stiamo dicendo che aduna programmazione potranno esseremodo stiamo dicendo che aduna programmazione potranno essere associate più registrazioni. Nel campo Delete Rule selezionateassociate più registrazioni. Nel campo Delete Rule selezionate Cascade, in questo modo diciamo che quando cancelliamo unaCascade, in questo modo diciamo che quando cancelliamo una programmazione dovranno essere cancellate anche tutte le sueprogrammazione dovranno essere cancellate anche tutte le sue registrazioni.registrazioni.

Ora selezionate l’entity RegistrazioneCD ed aggiungete unaOra selezionate l’entity RegistrazioneCD ed aggiungete una relazione chiamata programmazioneAssociata. Nel Data Modelrelazione chiamata programmazioneAssociata. Nel Data Model Inspector scegliete come Destination ProgrammazioneCD e nelInspector scegliete come Destination ProgrammazioneCD e nel campo Inverse scegliete la relazione registrazioniAssociate;campo Inverse scegliete la relazione registrazioniAssociate; verificate che il flag To-Many Relationship non sia attivato, unaverificate che il flag To-Many Relationship non sia attivato, una registrazione è relativa una sola programmazione.registrazione è relativa una sola programmazione.

Page 241: Xcode4 Tutorial Completo

Bene, abbiamo finito di definire il nostro modello dei datiBene, abbiamo finito di definire il nostro modello dei dati persistente. Vediamo ora come possiamo utilizzarlo all’interno delpersistente. Vediamo ora come possiamo utilizzarlo all’interno del nostro codice. Per poter utilizzare queste entità abbiamo bisognonostro codice. Per poter utilizzare queste entità abbiamo bisogno di…classi; selezioniamo le due entity e clicchiamo sul menudi…classi; selezioniamo le due entity e clicchiamo sul menu File>New File, nel pannello che si presenta sceglieteFile>New File, nel pannello che si presenta scegliete NSManagedObject subclass dal gruppo Core Data della sezioneNSManagedObject subclass dal gruppo Core Data della sezione iOS.iOS. Vengo automaticamente creati i file per due classi con lo Vengo automaticamente creati i file per due classi con lo stesso nome delle entity e se tornate sul modello dati e selezionatestesso nome delle entity e se tornate sul modello dati e selezionate una entity, nel Data Model Inspector troverete che il campo Classuna entity, nel Data Model Inspector troverete che il campo Class è stato valorizzato con il nome della classe opportuna.è stato valorizzato con il nome della classe opportuna.

Passiamo a sviluppare il nuovo codice. Iniziamo con la riflessionePassiamo a sviluppare il nuovo codice. Iniziamo con la riflessione che CoreData richiede la creazione di tre istanze standard cheche CoreData richiede la creazione di tre istanze standard che costituiscono quella che viene chiamata Core Data Stack; come hocostituiscono quella che viene chiamata Core Data Stack; come ho anticipato è necessario che approfondiate questi concetti nellaanticipato è necessario che approfondiate questi concetti nella documentazione Apple.documentazione Apple.

Nel file myTvAppDelegate.h aggiungete le tre propertyNel file myTvAppDelegate.h aggiungete le tre property

@property (nonatomic, retain, readonly)@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;NSManagedObjectContext *managedObjectContext;

Page 242: Xcode4 Tutorial Completo

@property (nonatomic, retain, readonly) NSManagedObjectModel@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;*managedObjectModel;@property (nonatomic, retain, readonly)@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;NSPersistentStoreCoordinator *persistentStoreCoordinator;per queste tre property inseriamo nel file myTvAppDelegate.mper queste tre property inseriamo nel file myTvAppDelegate.m

@synthesize managedObjectContext = __managedObjectContext;@synthesize managedObjectContext = __managedObjectContext;@synthesize managedObjectModel = __managedObjectModel;@synthesize managedObjectModel = __managedObjectModel;@synthesize persistentStoreCoordinator =@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;__persistentStoreCoordinator;Con questa sintassi per le synthesize indichiamo anche il nomeCon questa sintassi per le synthesize indichiamo anche il nome della variabile di istanza che viene implicitamente creata eddella variabile di istanza che viene implicitamente creata ed associata alla property; non è necessario, in quanto se non loassociata alla property; non è necessario, in quanto se non lo facessimo avremmo una variabile di istanza con lo stesso nomefacessimo avremmo una variabile di istanza con lo stesso nome della property, ma proprio per questo motivo è utile per poterdella property, ma proprio per questo motivo è utile per poter distinguere quando si parla dell’unadistinguere quando si parla dell’una e quando dell’altra. e quando dell’altra.

Andiamo ora a creare i nostri metodi getter ed aggiungiamoAndiamo ora a creare i nostri metodi getter ed aggiungiamo

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator- (NSPersistentStoreCoordinator *)persistentStoreCoordinator{{if (__persistentStoreCoordinator != nil)if (__persistentStoreCoordinator != nil){{return __persistentStoreCoordinator;return __persistentStoreCoordinator;}}NSURL *documentsDirectory=[[[NSFileManagerNSURL *documentsDirectory=[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectorydefaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];inDomains:NSUserDomainMask] lastObject];NSURL *storeURL = [documentsDirectoryNSURL *storeURL = [documentsDirectory URLByAppendingPathComponent:@"myTv.sqlite"];URLByAppendingPathComponent:@"myTv.sqlite"];NSError *error = nil;NSError *error = nil;__persistentStoreCoordinator = [[NSPersistentStoreCoordinator__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[selfalloc] initWithManagedObjectModel:[self managedObjectModel]];managedObjectModel]];if (![__persistentStoreCoordinatorif (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreTypeaddPersistentStoreWithType:NSSQLiteStoreType

Page 243: Xcode4 Tutorial Completo

configuration:nil URL:storeURL options:nil error:&error])configuration:nil URL:storeURL options:nil error:&error]){{NSLog(@"Unresolved error %@, %@", error, [error userInfo]);NSLog(@"Unresolved error %@, %@", error, [error userInfo]);abort();abort();}}return __persistentStoreCoordinator;return __persistentStoreCoordinator;}}Questo metodo crea il primo oggetto del Core Data Stack. LaQuesto metodo crea il primo oggetto del Core Data Stack. La prima cosa che fa è chiedersi se la variabile di istanza associata haprima cosa che fa è chiedersi se la variabile di istanza associata ha già un valore nel qual caso lo restituisce. Altrimenti si crea ungià un valore nel qual caso lo restituisce. Altrimenti si crea un riferimento di tipo NSURL ad un file chiamato myTv.sqlite che èriferimento di tipo NSURL ad un file chiamato myTv.sqlite che è il database SQLite che Core Data userà per gestire le nostreil database SQLite che Core Data userà per gestire le nostre informazioni. Il file sarà posizionato all’internoinformazioni. Il file sarà posizionato all’interno delladella documentsDirectory dell’applicazione. Con l’invocazionedocumentsDirectory dell’applicazione. Con l’invocazione

[__persistentStoreCoordinator[__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreTypeaddPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]configuration:nil URL:storeURL options:nil error:&error]il file sarà utilizzato per gestire il nostro modello dati.il file sarà utilizzato per gestire il nostro modello dati.

Passiamo al secondo getter aggiungendoPassiamo al secondo getter aggiungendo

- (NSManagedObjectModel *)managedObjectModel- (NSManagedObjectModel *)managedObjectModel{{if (__managedObjectModel != nil)if (__managedObjectModel != nil){{return __managedObjectModel;return __managedObjectModel;}}NSURL *modelURL = [[NSBundle mainBundle]NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"myTv" withExtension:@"momd"];URLForResource:@"myTv" withExtension:@"momd"];__managedObjectModel = [[NSManagedObjectModel alloc]__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];initWithContentsOfURL:modelURL];return __managedObjectModel;return __managedObjectModel;}}Questo getter in sostanza restituisce il riferimento al nostroQuesto getter in sostanza restituisce il riferimento al nostro modello dei dati che abbiamo disegnato nel filemodello dei dati che abbiamo disegnato nel file

Page 244: Xcode4 Tutorial Completo

myTv.xcdtamodeld.myTv.xcdtamodeld.

Infine, aggiungiamo il terzo getterInfine, aggiungiamo il terzo getter

- (NSManagedObjectContext *)managedObjectContext- (NSManagedObjectContext *)managedObjectContext{{if (__managedObjectContext != nil)if (__managedObjectContext != nil){{return __managedObjectContext;return __managedObjectContext;}}NSPersistentStoreCoordinator *coordinator = [selfNSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];persistentStoreCoordinator];if (coordinator != nil)if (coordinator != nil){{__managedObjectContext = [[NSManagedObjectContext alloc]__managedObjectContext = [[NSManagedObjectContext alloc] init];init];[__managedObjectContext[__managedObjectContext setPersistentStoreCoordinator:coordinator];setPersistentStoreCoordinator:coordinator];}}return __managedObjectContext;return __managedObjectContext;}}La struttura di questi metodi è standard per cui si possonoLa struttura di questi metodi è standard per cui si possono riutilizzare ogni volta che c’è bisogno di lavorare con Core Datariutilizzare ogni volta che c’è bisogno di lavorare con Core Data facendo solo attenzione ad aggiornare ogni volta il nome del filefacendo solo attenzione ad aggiornare ogni volta il nome del file SQLite nel getter del persistentStoreCoordinator e del modelloSQLite nel getter del persistentStoreCoordinator e del modello dati in quello di managedObjectModel. Inoltre tenete presente chedati in quello di managedObjectModel. Inoltre tenete presente che se quando create il vostro progetto attivate il flag CoreData questise quando create il vostro progetto attivate il flag CoreData questi metodi saranno creati automaticamente nel template.metodi saranno creati automaticamente nel template.

Iniziamo ora a scrivere la parte di codice che ci serve per utilizzareIniziamo ora a scrivere la parte di codice che ci serve per utilizzare le nuove classi. Prima cosa dobbiamo decidere dove intervenire, ille nuove classi. Prima cosa dobbiamo decidere dove intervenire, il modo a mio avviso più lineare è quello di interevnire nel momentomodo a mio avviso più lineare è quello di interevnire nel momento in cui si crea un oggetto Televisore, mentre ora crea un elenco diin cui si crea un oggetto Televisore, mentre ora crea un elenco di programmazioni vuoto, noi faremo in modo che vengano lette leprogrammazioni vuoto, noi faremo in modo che vengano lette le programmazioni archiviate e assegnate all’elenco.programmazioni archiviate e assegnate all’elenco.

Dichiariamo quindi, in Televisore.h, il metodoDichiariamo quindi, in Televisore.h, il metodo

Page 245: Xcode4 Tutorial Completo

-(void)leggiProgrammazioni;-(void)leggiProgrammazioni;Andiamo ora nell’implementation file a definirlo.Andiamo ora nell’implementation file a definirlo.

-(void)leggiProgrammazioni {-(void)leggiProgrammazioni {}}Abbiamo ora bisogno di indicare di quale entità vogliamoAbbiamo ora bisogno di indicare di quale entità vogliamo recuperare le istanze, questo si ottiene creando un’istanza direcuperare le istanze, questo si ottiene creando un’istanza di NSEntityDescriptionNSEntityDescription

-(void)leggiProgrammazioni {-(void)leggiProgrammazioni {NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSEntityDescription *entity = [NSEntityDescriptionNSEntityDescription *entity = [NSEntityDescription entityForName:@"ProgrammazioneCD"entityForName:@"ProgrammazioneCD" inManagedObjectContext:managedObjectContext];inManagedObjectContext:managedObjectContext];}}come vedete per creare questa istanza abbiamo bisogno di uncome vedete per creare questa istanza abbiamo bisogno di un riferimento al managedObjectContext; per cui andiamo inriferimento al managedObjectContext; per cui andiamo in Televisore.h e creiamo una property adeguataTelevisore.h e creiamo una property adeguata

@property(nonatomic,retain) NSManagedObjectContext@property(nonatomic,retain) NSManagedObjectContext *managedObjectContext;*managedObjectContext;in Televisore.m facciamo la synthesize. In seguito vedremo comein Televisore.m facciamo la synthesize. In seguito vedremo come valorizzarla al momento della creazione dell’istanza di Televisore.valorizzarla al momento della creazione dell’istanza di Televisore.

Ora torniamo al metodo leggiProgrammazioni ed associamoOra torniamo al metodo leggiProgrammazioni ed associamo l’entity alla fetchRequestl’entity alla fetchRequest

-(void)leggiProgrammazioni {-(void)leggiProgrammazioni {NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSEntityDescription *entity = [NSEntityDescriptionNSEntityDescription *entity = [NSEntityDescription entityForName:@"ProgrammazioneCD"entityForName:@"ProgrammazioneCD" inManagedObjectContext:managedObjectContext];inManagedObjectContext:managedObjectContext];[fetchRequest setEntity:entity];[fetchRequest setEntity:entity];}}Possiamo decidere di ordinare le nostre programmazioni in base alPossiamo decidere di ordinare le nostre programmazioni in base al numero del canale da registrare creando un NSSortDescriptornumero del canale da registrare creando un NSSortDescriptor

-(void)leggiProgrammazioni {-(void)leggiProgrammazioni {

Page 246: Xcode4 Tutorial Completo

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSEntityDescription *entity = [NSEntityDescriptionNSEntityDescription *entity = [NSEntityDescription entityForName:@"ProgrammazioneCD"entityForName:@"ProgrammazioneCD" inManagedObjectContext:managedObjectContext];inManagedObjectContext:managedObjectContext];[fetchRequest setEntity:entity];[fetchRequest setEntity:entity];NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"canaleDaRegistrare" ascending:YES];initWithKey:@"canaleDaRegistrare" ascending:YES];}}e, poiché potremmo avere piùe, poiché potremmo avere più criteri di ordinamento creiamo un criteri di ordinamento creiamo un array con tutti i sortDescriptorsarray con tutti i sortDescriptors

-(void)leggiProgrammazioni {-(void)leggiProgrammazioni {NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSEntityDescription *entity = [NSEntityDescriptionNSEntityDescription *entity = [NSEntityDescription entityForName:@"ProgrammazioneCD"entityForName:@"ProgrammazioneCD" inManagedObjectContext:managedObjectContext];inManagedObjectContext:managedObjectContext];[fetchRequest setEntity:entity];[fetchRequest setEntity:entity];NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"canaleDaRegistrare" ascending:YES];initWithKey:@"canaleDaRegistrare" ascending:YES];NSArray *sortDescriptors = [[NSArray alloc]NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];initWithObjects:sortDescriptor, nil];}}e lo associamo alla fetchRequeste lo associamo alla fetchRequest

-(void)leggiProgrammazioni {-(void)leggiProgrammazioni {NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSEntityDescription *entity = [NSEntityDescriptionNSEntityDescription *entity = [NSEntityDescription entityForName:@"ProgrammazioneCD"entityForName:@"ProgrammazioneCD" inManagedObjectContext:managedObjectContext];inManagedObjectContext:managedObjectContext];[fetchRequest setEntity:entity];[fetchRequest setEntity:entity];NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"canaleDaRegistrare" ascending:YES];initWithKey:@"canaleDaRegistrare" ascending:YES];NSArray *sortDescriptors = [[NSArray alloc]NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];initWithObjects:sortDescriptor, nil];[fetchRequest setSortDescriptors:sortDescriptors];[fetchRequest setSortDescriptors:sortDescriptors];}}

Page 247: Xcode4 Tutorial Completo

a questo punto possiamo eseguire la request e assegnarne ila questo punto possiamo eseguire la request e assegnarne il risultato a elencoProgrammazioni, questo si ottiene conrisultato a elencoProgrammazioni, questo si ottiene con l’invocazione del metodo executeFetchRequestl’invocazione del metodo executeFetchRequest

-(void)leggiProgrammazioni {-(void)leggiProgrammazioni {NSError *error;NSError *error;NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSEntityDescription *entity = [NSEntityDescriptionNSEntityDescription *entity = [NSEntityDescription entityForName:@"ProgrammazioneCD"entityForName:@"ProgrammazioneCD" inManagedObjectContext:managedObjectContext];inManagedObjectContext:managedObjectContext];[fetchRequest setEntity:entity];[fetchRequest setEntity:entity];NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"canaleDaRegistrare" ascending:YES];initWithKey:@"canaleDaRegistrare" ascending:YES];NSArray *sortDescriptors = [[NSArray alloc]NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];initWithObjects:sortDescriptor, nil];[fetchRequest setSortDescriptors:sortDescriptors];[fetchRequest setSortDescriptors:sortDescriptors];self.elencoProgrammazioni=[NSMutableArray arrayWithArray:self.elencoProgrammazioni=[NSMutableArray arrayWithArray:[managedObjectContext executeFetchRequest:fetchRequest[managedObjectContext executeFetchRequest:fetchRequest error:&error]];error:&error]];}}come vedete il metodo executeFetchRequest richiede uncome vedete il metodo executeFetchRequest richiede un parametro (error:) che sia un riferimento ad un’istanza di NSErrorparametro (error:) che sia un riferimento ad un’istanza di NSError che ho dichiarato in testa al metodo. Il risultato diche ho dichiarato in testa al metodo. Il risultato di executeFetchRequest è un NSArray ma poiché noi abbiamoexecuteFetchRequest è un NSArray ma poiché noi abbiamo bisogno di un NSMutableArray per la nostra propertybisogno di un NSMutableArray per la nostra property elencoProgrammazioni, ci costruiamo un’istanza di questa classe aelencoProgrammazioni, ci costruiamo un’istanza di questa classe a partire dal risultato ottenuto.partire dal risultato ottenuto.

A questo punto nel metodo init cancelliamoA questo punto nel metodo init cancelliamo

self.elencoProgrammazioni=[[NSMutableArray alloc] init];self.elencoProgrammazioni=[[NSMutableArray alloc] init];Dal punto di vista della struttura applicativa è rimasto aperto ilDal punto di vista della struttura applicativa è rimasto aperto il punto relativo alla valorizzazione della propertypunto relativo alla valorizzazione della property managedObjectContext. Chiediamoci chi conosce l’istanzamanagedObjectContext. Chiediamoci chi conosce l’istanza valorizzata di managedObjectContext; la risposta è l’appDelegatevalorizzata di managedObjectContext; la risposta è l’appDelegate nel quale abbiamo implementato lo stack CoreData. Manel quale abbiamo implementato lo stack CoreData. Ma

Page 248: Xcode4 Tutorial Completo

appDelegate conosce anche l’istanza di Televisore in quanto è luiappDelegate conosce anche l’istanza di Televisore in quanto è lui che la crea, la cosa quindi è semplice: nel metodoche la crea, la cosa quindi è semplice: nel metodo didFinishLaunchingWithOptions di myTvAppDelegate dopodidFinishLaunchingWithOptions di myTvAppDelegate dopo l’istruzionel’istruzione

Televisore *tv1=[[Televisore alloc] init];Televisore *tv1=[[Televisore alloc] init];con la quale creiamo l’istanza di Televisore aggiungiamocon la quale creiamo l’istanza di Televisore aggiungiamo

tv1.managedObjectContext=self.managedObjectContext;tv1.managedObjectContext=self.managedObjectContext;con la quale assegnamo alla property di tv1 ilcon la quale assegnamo alla property di tv1 il managedObjectContext dello stack CoreData; e aggiungiamomanagedObjectContext dello stack CoreData; e aggiungiamo ancheanche

[tv1 leggiProgrammazioni];[tv1 leggiProgrammazioni];con la quale invochiamo il metodo che abbiamo appena scritto percon la quale invochiamo il metodo che abbiamo appena scritto per popolare elencoProgrammazioni.popolare elencoProgrammazioni.

Passiamo al file ProgrammazioniTableViewController che è quelloPassiamo al file ProgrammazioniTableViewController che è quello nel quale stiamo gestendo le Programmazioni e che invece oranel quale stiamo gestendo le Programmazioni e che invece ora dovrà gestire ProgrammazioneCD.dovrà gestire ProgrammazioneCD.

Prima cosa aggiungiamo la import di ProgrammazioneCD.Prima cosa aggiungiamo la import di ProgrammazioneCD.

#import "ProgrammazioneCD.h"#import "ProgrammazioneCD.h"Nel metodo viewDidLoad cancelliamo le istruzioni che usavamoNel metodo viewDidLoad cancelliamo le istruzioni che usavamo per creare tre programmazioni iniziali.per creare tre programmazioni iniziali.

Programmazione *programma=[[Programmazione alloc] init];Programmazione *programma=[[Programmazione alloc] init];programma.nomeDelProgramma=@"Film 1";programma.nomeDelProgramma=@"Film 1";programma.canaleDaRegistrare=[[NSNumber numberWithInt:0]programma.canaleDaRegistrare=[[NSNumber numberWithInt:0] retain];retain];[self.ilTelevisore.elencoProgrammazioni addObject:programma];[self.ilTelevisore.elencoProgrammazioni addObject:programma];programma=[[Programmazione alloc] init];programma=[[Programmazione alloc] init];programma.nomeDelProgramma=@"Documentario 1";programma.nomeDelProgramma=@"Documentario 1";programma.canaleDaRegistrare=[NSNumber numberWithInt:1];programma.canaleDaRegistrare=[NSNumber numberWithInt:1];[self.ilTelevisore.elencoProgrammazioni addObject:programma];[self.ilTelevisore.elencoProgrammazioni addObject:programma];programma=[[Programmazione alloc] init];programma=[[Programmazione alloc] init];programma.nomeDelProgramma=@"Film 2";programma.nomeDelProgramma=@"Film 2";

Page 249: Xcode4 Tutorial Completo

programma.canaleDaRegistrare=[NSNumber numberWithInt:2];programma.canaleDaRegistrare=[NSNumber numberWithInt:2];[self.ilTelevisore.elencoProgrammazioni addObject:programma];[self.ilTelevisore.elencoProgrammazioni addObject:programma];Nel metodo cellForRowAtIndexPath nel quale creiamo le celleNel metodo cellForRowAtIndexPath nel quale creiamo le celle della tableview delle programmazioni sostituiamodella tableview delle programmazioni sostituiamo Programmazione con ProgrammazioneCD.Programmazione con ProgrammazioneCD.

Passiamo ora al metodo commitEditingStyle nel quale vienePassiamo ora al metodo commitEditingStyle nel quale viene creato un nuovo oggetto per al nostra programmazione ecreato un nuovo oggetto per al nostra programmazione e sostituiamosostituiamo

Programmazione *nuovoProgramma=[[Programmazione alloc]Programmazione *nuovoProgramma=[[Programmazione alloc] init];init];concon

ProgrammazioneCD *nuovoProgramma=[NSEntityDescriptionProgrammazioneCD *nuovoProgramma=[NSEntityDescriptioninsertNewObjectForEntityForName:@"ProgrammazioneCD"insertNewObjectForEntityForName:@"ProgrammazioneCD"inManagedObjectContext:managedObjectContext];inManagedObjectContext:managedObjectContext];Con questa istruzione creiamo una nuova istanza persistente dellaCon questa istruzione creiamo una nuova istanza persistente della classe ProgrammazioneCD. Come potete osservare XCode viclasse ProgrammazioneCD. Come potete osservare XCode vi segnala un errore perchè stiamo usando managedObjectContext esegnala un errore perchè stiamo usando managedObjectContext e non lo abbiamo dichiarato.non lo abbiamo dichiarato. Andiamo quindi nel file header e Andiamo quindi nel file header e dichiariamo la propertydichiariamo la property

@property (nonatomic, retain) NSManagedObjectContext@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;*managedObjectContext;e nel file implementation inseriamo la sua synthesize. In seguitoe nel file implementation inseriamo la sua synthesize. In seguito vedremo come valorizzarla correttamente.vedremo come valorizzarla correttamente.

Ora andiamo in Televisore.h ed osserviamo che il metodoOra andiamo in Televisore.h ed osserviamo che il metodo inserisciProgrammazione prende un argomento di tipoinserisciProgrammazione prende un argomento di tipo Programmazione, mentre ora noi gli stiamo passando un’istanza diProgrammazione, mentre ora noi gli stiamo passando un’istanza di ProgrammazioneCD, cambiamo quindi la sua intestazione sia nelProgrammazioneCD, cambiamo quindi la sua intestazione sia nel header che nell’implementation. Ricordiamo, come sempre, di fareheader che nell’implementation. Ricordiamo, come sempre, di fare la import della classe per poterla usare.la import della classe per poterla usare.

Continuiamo con l’allineamento alla nuova classe andando nel fileContinuiamo con l’allineamento alla nuova classe andando nel file DettagliProgrammazioneViewController.h e osserviamo che c’èDettagliProgrammazioneViewController.h e osserviamo che c’è una property laProgrammazione che si riferisce alla classeuna property laProgrammazione che si riferisce alla classe

Page 250: Xcode4 Tutorial Completo

Programmazione, anche qui cambiamo la classe inProgrammazione, anche qui cambiamo la classe in ProgrammazioneCD.ProgrammazioneCD.

Torniamo al file ProgrammazioniTableViewController.m (nonTorniamo al file ProgrammazioniTableViewController.m (non fatevi prendere dal mal di mare) e andiamo nel metodofatevi prendere dal mal di mare) e andiamo nel metodo didSelectRowAtIndexPath al cui interno stiamo creando unadidSelectRowAtIndexPath al cui interno stiamo creando una variabile (nuovoProgramma) che si riferisce a Programmazione,variabile (nuovoProgramma) che si riferisce a Programmazione, modifichiamo anche qui la classe.modifichiamo anche qui la classe.

Bene fermiamoci un attimo perchè dovremmo aver finito diBene fermiamoci un attimo perchè dovremmo aver finito di aggiornare la parte di creazione di una nuova programmazione,aggiornare la parte di creazione di una nuova programmazione, prima di eseguire il programma per vedere se questa parteprima di eseguire il programma per vedere se questa parte funziona ricordiamo che dobbiamo valorizzare la propertyfunziona ricordiamo che dobbiamo valorizzare la property managedObjectContext del nostro oggetto istanza dimanagedObjectContext del nostro oggetto istanza di ProgrammazioniTableViewController. Qual è il nostro oggetto? E’ProgrammazioniTableViewController. Qual è il nostro oggetto? E’ quello referenziato dal IBOutletquello referenziato dal IBOutlet myProgController di myProgController di myTvAppDelegate. Quindi andiamo in myTvAppDelegate.m e,myTvAppDelegate. Quindi andiamo in myTvAppDelegate.m e, nel metodo didFinishLaunchingWithOptions, dopo l’istruzionenel metodo didFinishLaunchingWithOptions, dopo l’istruzione

myProgController.ilTelevisore=tv1;myProgController.ilTelevisore=tv1;aggiungiamoaggiungiamo

myProgController.managedObjectContext=self.managedObjectComyProgController.managedObjectContext=self.managedObjectContext;ntext;Ora eseguite il programma e provate a creare nuoveOra eseguite il programma e provate a creare nuove programmazioni. Verificate che vengano correttamente create, poiprogrammazioni. Verificate che vengano correttamente create, poi uscite e lanciate nuovamente l’app, andate nel tab delleuscite e lanciate nuovamente l’app, andate nel tab delle programmazioni e…cosa trovate? Nulla. E la persistenza dov’è?programmazioni e…cosa trovate? Nulla. E la persistenza dov’è?

Per completarePer completare l’implementazione della persistenza è necessario l’implementazione della persistenza è necessario salvare le modifiche che sono state fatte. Il punto corretto per ilsalvare le modifiche che sono state fatte. Il punto corretto per il salvataggio dipende dalla logica dell’applicazione che statesalvataggio dipende dalla logica dell’applicazione che state scrivendo; nel nostro caso salveremo il momento in cui chiudiamoscrivendo; nel nostro caso salveremo il momento in cui chiudiamo l’app.l’app.

Nel file myTvAppDelegate.m il metodoNel file myTvAppDelegate.m il metodo applicationDidEnterBackground: viene eseguito quandoapplicationDidEnterBackground: viene eseguito quando l’applicazione va in background (ad es. premendo il tasto home);l’applicazione va in background (ad es. premendo il tasto home);

Page 251: Xcode4 Tutorial Completo

al suo interno iniziamo a scrivereal suo interno iniziamo a scrivere

- (void)applicationDidEnterBackground:(UIApplication- (void)applicationDidEnterBackground:(UIApplication *)application*)application{{if ([self.managedObjectContext hasChanges]) {if ([self.managedObjectContext hasChanges]) {}}}}cioè iniziamo con il chiederci se ci sono state modifiche ai dati,cioè iniziamo con il chiederci se ci sono state modifiche ai dati, cioè se sono stati creati, cancellati o modificati oggetti delle classicioè se sono stati creati, cancellati o modificati oggetti delle classi persistenti.persistenti.

AggiungiamoAggiungiamo

- (void)applicationDidEnterBackground:(UIApplication- (void)applicationDidEnterBackground:(UIApplication *)application*)application{{NSError *error = nil;NSError *error = nil;if ([self.managedObjectContext hasChanges])if ([self.managedObjectContext hasChanges])if (![self.managedObjectContext save:&error])if (![self.managedObjectContext save:&error]){{NSLog(@"Unresolved error %@, %@", error, [error userInfo]);NSLog(@"Unresolved error %@, %@", error, [error userInfo]);abort();abort();}}}}Cioè nel caso di cambiamenti invochiamo il metodo save: diCioè nel caso di cambiamenti invochiamo il metodo save: di managedObjectContext, che ha proprio lo scopo di salvare lemanagedObjectContext, che ha proprio lo scopo di salvare le modifiche. Se l’invocazione non va a buon fine viene stampato unmodifiche. Se l’invocazione non va a buon fine viene stampato un messaggio di errore.messaggio di errore.

Bene, ora eseguite nuovamente l’app creando delleBene, ora eseguite nuovamente l’app creando delle programmazioni, poi uscite e lanciatela di nuovo, a questo puntoprogrammazioni, poi uscite e lanciatela di nuovo, a questo punto andate a verificare se le programmazioni sono ancora presenti.andate a verificare se le programmazioni sono ancora presenti.

Passiamo a gestire la cancellazione di una programmazione.Passiamo a gestire la cancellazione di una programmazione.

In Televisore.m abbiamo il metodo cancellaProgrammazione: cheIn Televisore.m abbiamo il metodo cancellaProgrammazione: che riceve come argomento la posizione della programmazione dariceve come argomento la posizione della programmazione da

Page 252: Xcode4 Tutorial Completo

cancellare e, nella versione attuale, la cancella dall’elenco dellecancellare e, nella versione attuale, la cancella dall’elenco delle programmazioni. Prima della cancellazione daprogrammazioni. Prima della cancellazione da elencoProgrammazioni inseriamoelencoProgrammazioni inseriamo

[managedObjectContext deleteObject:[self.elencoProgrammazioni[managedObjectContext deleteObject:[self.elencoProgrammazioni objectAtIndex:posizione]];objectAtIndex:posizione]];cioè invochiamo il metodo deleteObject passandogli l’elemento dicioè invochiamo il metodo deleteObject passandogli l’elemento di elencoProgrammazioni che occupa la posizione passata comeelencoProgrammazioni che occupa la posizione passata come argomento. Provate ad eseguire l’app cancellando qualcheargomento. Provate ad eseguire l’app cancellando qualche programmazione e verificando che alla successiva esecuzione laprogrammazione e verificando che alla successiva esecuzione la programmazione non è più presente.programmazione non è più presente.

Bene, abbiamo completato la gestione delle programmazioni perBene, abbiamo completato la gestione delle programmazioni per renderle persistenti con la nuova classe, passiamo a rendererenderle persistenti con la nuova classe, passiamo a rendere persistenti le registrazioni.persistenti le registrazioni.

Iniziamo con l’individuare il punto nel quale si crea laIniziamo con l’individuare il punto nel quale si crea la registrazione. Se ricordate, noi creiamo una nuova registrazioneregistrazione. Se ricordate, noi creiamo una nuova registrazione quando clicchiamo sul bottone registra delle view dei dettagliquando clicchiamo sul bottone registra delle view dei dettagli della programmazione. Andiamo quindi indella programmazione. Andiamo quindi in DettagliProgrammazioneViewController.h ed inseriamo laDettagliProgrammazioneViewController.h ed inseriamo la

#import "RegistrazioneCD.h"#import "RegistrazioneCD.h"per usare la nuova classe persistente.per usare la nuova classe persistente.

Ora andiamo nell’implementation file e nel metodo registraOra andiamo nell’implementation file e nel metodo registra cancelliamocancelliamo

Registrazione *nuovaRegistrazione=[[Registrazione alloc] init];Registrazione *nuovaRegistrazione=[[Registrazione alloc] init];ed inseriamoed inseriamo

RegistrazioneCD *nuovaRegistrazione=[NSEntityDescriptionRegistrazioneCD *nuovaRegistrazione=[NSEntityDescriptioninsertNewObjectForEntityForName:@"RegistrazioneCD"insertNewObjectForEntityForName:@"RegistrazioneCD"inManagedObjectContext:managedObjectContext];inManagedObjectContext:managedObjectContext];cioè creiamo la nuova registrazione come istanza della classecioè creiamo la nuova registrazione come istanza della classe persistente. Come potete vedere, anche in questo caso abbiamopersistente. Come potete vedere, anche in questo caso abbiamo bisogno del managedObjectContext e quindi dichiariamo labisogno del managedObjectContext e quindi dichiariamo la property con la relativa synthesize. Per valorizzare questa propertyproperty con la relativa synthesize. Per valorizzare questa property

Page 253: Xcode4 Tutorial Completo

dobbiamo individuare il punto nel quale creiamo l’istanza didobbiamo individuare il punto nel quale creiamo l’istanza di DettagliProgrammazioneViewController; questo avviene in dueDettagliProgrammazioneViewController; questo avviene in due momenti, uno quando creiamo una nuova programmazione ed unomomenti, uno quando creiamo una nuova programmazione ed uno quando ne visualizziamo i dettagli di una già esistente. Ma ilquando ne visualizziamo i dettagli di una già esistente. Ma il bottone registra, e quindi la possibilità di creare una nuovabottone registra, e quindi la possibilità di creare una nuova registrazione, c’è solo nel secondo caso. Questo evento è quelloregistrazione, c’è solo nel secondo caso. Questo evento è quello corrispondente alla selezione di una riga della tableview dellecorrispondente alla selezione di una riga della tableview delle programmazioni e cioè l’invocazione del metodoprogrammazioni e cioè l’invocazione del metodo didSelectRowAtIndexPath dididSelectRowAtIndexPath di ProgrammazioniTableViewController.ProgrammazioniTableViewController. In questo metodo, dopo In questo metodo, dopo

dController.laProgrammazione=nuovoProgramma;dController.laProgrammazione=nuovoProgramma;aggiungiamoaggiungiamo

dController.managedObjectContext=self.managedObjectContext;dController.managedObjectContext=self.managedObjectContext;Continuiamo con la sostituzione delle classi.Continuiamo con la sostituzione delle classi.

Nel file ElencoRegistrazioniController.h aggiungiamoNel file ElencoRegistrazioniController.h aggiungiamo

#import "ProgrammazioneCD.h"#import "ProgrammazioneCD.h"#import "RegistrazioneCD.h"#import "RegistrazioneCD.h"Nell’implementation file, nei metodi titleForHeaderInSection,Nell’implementation file, nei metodi titleForHeaderInSection, numberOfRowsInSection, cellForRowAtIndexPath cambiamonumberOfRowsInSection, cellForRowAtIndexPath cambiamo Programmazione in ProgrammazioneCD.Programmazione in ProgrammazioneCD.

Nel metodo cellForRowAtIndexPath la nuova istruzione per laNel metodo cellForRowAtIndexPath la nuova istruzione per la creazione dell’istanza di registrazione ècreazione dell’istanza di registrazione è

RegistrazioneCD *tmpRegistrazione=(RegistrazioneCD *)RegistrazioneCD *tmpRegistrazione=(RegistrazioneCD *)[[tmpProgrammazione.registrazioniAssociate allObjects][[tmpProgrammazione.registrazioniAssociate allObjects] objectAtIndex:indexPath.row];objectAtIndex:indexPath.row];questo perchè coreData gestisce le relazioni tra entity con degliquesto perchè coreData gestisce le relazioni tra entity con degli NSSet mentre noi nella versione precedete usavamo degliNSSet mentre noi nella versione precedete usavamo degli NSArray sui quali si poteva usare il metodo objectAtIndex perNSArray sui quali si poteva usare il metodo objectAtIndex per accedere all’elemento di una determinata posizione. Ora èaccedere all’elemento di una determinata posizione. Ora è necessario prima invocare il metodo allObjects sul NSSet pernecessario prima invocare il metodo allObjects sul NSSet per ottenere un NSArray corrispondente.ottenere un NSArray corrispondente.

Page 254: Xcode4 Tutorial Completo

Per finire facciamo un po’ di pulizia.Per finire facciamo un po’ di pulizia.

Cancelliamo i file Programmazione.h/.m e Registrazione.h/.m cheCancelliamo i file Programmazione.h/.m e Registrazione.h/.m che erano relativi alle classi non persistenti, cancelliamo tutte leerano relativi alle classi non persistenti, cancelliamo tutte le import relative a queste due classi.import relative a queste due classi.

Eseguite nuovamente il programma e verificate che sia leEseguite nuovamente il programma e verificate che sia le programmazioni che le registrazioni siano persistenti.programmazioni che le registrazioni siano persistenti.