Manuale IOS
-
Upload
roberto-onomoni -
Category
Documents
-
view
227 -
download
5
description
Transcript of Manuale IOS
I n d I c e
Prefazione
Introduzione
Xcode: una breve panoramica
Primi passi in Xcode
I template Creare un nuovo progetto Le proprietà del progetto Interface Builder Le novità di iOS 5
Capitolo 1 - “Hello World”
Creiamolastrutturagrafica Definiamoglielementieleazioni Scriviamo il codice necessario Nascondiamo la tastiera
Capitolo 2 - Uno sguardo ai componenti base
Creiamolastrutturagrafica Scriviamo il codice necessario
Capitolo 3 - Gestiamo immagini ed eseguiamo semplici animazioni
Creiamolastrutturagrafica Implementiamo il movimento del logo
Capitolo 4 - MultiBrowser: creiamo un browser
Creiamolastrutturagrafica Creiamo la seconda vista Scriviamo il codice per aprire la pagina desiderata Gestiamo il passaggio tra le due viste
7
9
9
13
1315161821
23
23252829
33
3336
39
3941
45
45485152
Capitolo 5 - UITableView: le tabelle
Parte1:Creiamoedefiniamolatabella Creiamo la struttura Scriviamo il codice necessario Parte2:Inseriamoalcunefunzionalità Permettiamolacancellazionediunariga La vista di dettaglio Parte 3: Implementiamo la ricerca Aggiungiamo il box di ricerca Modifichiamoimetodigiàesistenti Implementiamo la ricerca
Capitolo 6 - AccessContact: utilizziamo i contatti
Creiamolastrutturagrafica Scriviamo il codice necessario
Capitolo 7 - XML
Cosa è XML XMLnellaprogrammazioneperiOS Creiamolastrutturagrafica Scriviamo il codice necessario
Capitolo 8 - SQL
Creiamo il database Creiamolastrutturadell’applicazione Definiamolaclasse“Data” Mostriamo i valori del database Cancellazionediunelemento Inserimento di un nuovo elemento
Capitolo 9 - Creiamo un semplice lettore di feed RSS
Parte 1: Creiamo la base del lettore Creiamolastrutturadell’applicazione Definiamoimetodidelparser Visualizziamoglielementinellatabella Parte2:Miglioriamolagraficadell’applicazione Creiamolacellapersonalizzata Definiamolagraficadellacella Utilizziamolacella Parte3:Visualizziamol’articolocompleto
55
55555661616264646568
71
7173
79
79818183
87
88949699
102105
111
111111112115117117120123126
I n d I c e
Capitolo 10 - Realizziamo il nostro “Brushes”
Parte1:Creiamolatavolagrafica Creiamolastrutturagrafica Definiamoimetodinecessari Parte2:Inseriamoleimpostazioni Creiamolastrutturagrafica Scriviamo il codice necessario Parte 3: Inseriamo il salvataggio del disegno
Conclusioni
Bibliografia
131
131131134139139142147
151
153
7
P r e f a z I o n e
Prefazione
Probabilmentemoltidivoistarannoleggendoperlasecondavoltaquestaprefazio-ne. Questo ebook, infatti, è giunto alla seconda versione, dopo più di due anni dalla sua prima versione. Tante cose sono cambiate in questi due anni, sicuramente non mi aspettavo un successo così grande per questa piccola guida.“TutorialpraticiperiOSSDK”ènataperracchiuderealcunitutorialsullaprogram-mazioneperiOS,chehoscrittoepubblicatoprimasuBubiDevsepoisudevAPP.Comehogiàdettopiùvolte,questolibrononvuoleessereunmanualeesaurienteosostituirsialladocumentazioneufficialediApple(acuidovetesemprefarriferimen-to),masolounostrumentoperchièalleprimearmi,oppureperchivuoleimpararequalcosacheancoranonsa.Trovereteunaseriediguidepratiche,daseguirepassoperpasso,commentateespiegate,perconsentireatuttidiimparareecapirequellochevienefatto.Illibroviguideràprimainunaseriedisemplicitutorial,incuiverrannopresentatieanalizzatigliaspettieicomponentibasedellaprogrammazioneperiOS.Gliultimiduecapitoli,invece,vipermetterannodicrearedueapplicazionivereeproprie,inmodocheab-biate un quadro d’insieme generale.
Ringraziochiunqueleggaquestepagine,etuttiquellicheinpassatolehannogiàlette.Sperochequestolibropossainsegnarviqualcosadinuovo,edaiutarvinelvostroscopo,siaessolavorativooperpurapassione(comelamia).
Ringrazio lamiafidanzata,checontinuaasopportarmiesostenermi, seppurnonsappianientediprogrammazione!Probabilmenteancheleiavràimparatoqualcosaaforzadisentirelemiespiegazioni..
RingrazioilmiograndeamicoMatteo,checongrandepassioneededizionesiède-dicatoall’impaginazionediquestaversione,conferendogliunaspettodavveropro-fessionale.
RingrazioSteveJobs,chepurtropporecentementeciha lasciato,peravercreatoApple,qualcosachevaoltreunasempliceazienda.
E ricordatevi, solo essendo curiosi e affamati è possibile scoprire e creare qualcosa di nuovo e veramente innovativo.
“Siate affamati, siate folli.”
9
I n t r o d u z I o n e
I. Introduzione
Se avete acquistato e state leggendo questo ebook sicuramente termini come iPhone,AppStore,appealtriancoranonvisarannonuovi.Nonmidilungherò,quin-di,sull’impattochedispositivicomeiPhoneeiPadhannoavutonelsettoremobile,perchésonosottogliocchiditutti.
Sviluppareapplicazioniperquestidispositivièdiventataunapratica(espessoun’esi-genza)moltodiffusa:ragazzialleprimearmichevoglionoscoprirequestomondo,maancheaziendecheoperanodaanninelsettoreICTchesiaffaccianosuquestinuovi dispositivi sempre più presenti nella vita quotidiana. Tutti, insomma, parlano di iOSedellesuepotenzialità.Creareun’applicazione,venderlainAppStoreeotteneredeiricavieconomicièormaiallaportataditutti:bastanounalicenzadasviluppatore,unMaceunpo’distudio.Quindi,nulladicomplesso!
Piùavantivedremocos’èlalicenzadasviluppatoreecomesiottiene,perilmomentosappiatechenonènecessarioaverlaperiniziareasviluppareapplicazioniperiPhone.Quellodicuiavetebisogno,perora,èunMac(conOSXLion)eXcode.
Xcodeèilnomedell’ambientedisviluppocreatodaApplechepermettedirealizzareapplicazionisiaperdispositiviiOScheperMac.XcodeèdisponibilesulMacAppStore gratuitamente (lo trovate a questo indirizzo, http://itunes.apple.com/it/app/xcode/id448457090?mt=12):unavoltascaricatoeinstallatosieteprontiperiniziarelavostraavventuradaprogrammatori!
Xcode: una breve panoramicaQuando parliamo di Xcode ci riferiamo, solitamente, all’insieme dei tool di sviluppo cheApplemetteadisposizione.Dopol’installazione,troveretetutti iprogrammidicuiavetebisognonellacartella“/Developer/Applications”.Glistrumentisonodavveromoltiealcunisonocomplessi(tranquilli,inquestoebookutilizzeremosolol’editorXcode). PercompletezzaeccoviunabrevedescrizionedeiprogrammicheApplecifornisce:•Dashcode,èunutiletoolconcuipotremocrearepagineweb,widget(destinatiallaDashboarddiMacOSX)ealtro.
•Instruments,permettedianalizzareevalutareleprestazionidellenostreapplica-zioni(interminidiconsumodimemoria,utilizzodelprocessore,etc).Èdestinato,ovviamente, agli sviluppatori più esperti.
•Quartz Composer,èuntoolchepermettedicreareanimazioniedeffettigrafici,dautilizzarepoiinaltriprogrammi.
•Xcode,è l’ambientedisviluppoveroeproprio.Saràqui, infatti,cheandremoacreareinostriprogetti,scrivereilcodiceecompilareiltutto.Dallaversione4haintegratoalsuointernoInterfaceBuilder,un’ottimotoolchecipermettedicrearel’interfacciagraficadellenostreapplicazioni,iltuttoinmanierasempliceeveloce.
10
Da qui possiamo riaprire velocemente uno degli ultimi progetti a cui abbiamo lavorato, oppurecrearneunonuovo.Èanchepossibileaprireladocumentazione(cosachedo-vrete imparare ad abituarvi a fare) oppure andare direttamente al portale sviluppatori.
Fig. A: Schermata di benvenuto di Xcode
AnalizziamopiùneldettaglioXcode.All’aperturasipresentaquestaschermata:
11
I n t r o d u z I o n e
Quandoapriamounprogetto(nuovooesistente)Xcodesipresentacosì:
Comepotete vedere dall’immagine, ci sono diverse sezioni che lo compongono. Inaltotroviamola“Toolbar”,un’ampiabarrachecifornisceimmediatoaccessoadal-cunefunzioni.Cliccandosulpulsante“Run”potremocompilareedeseguireilnostroprogetto,cheverràavviatosulsimulatoreosuldevicecollegatoalnostrocomputer. Afiancotroviamounmenùatendinaincuipossiamoselezionareappuntodoveese-guire il progetto. Alcentroundisplay,simileaquellodiiTunes,ciforniscedelleinformazionichecam-bianoasecondadell’operazionechestiamoeseguendo:civerrannonotificatiglierrori,l’avanzamentodellacompilazioneedaltreinformazionicheimparereteaconoscere.Nella parte destra, invece, troviamo tre piccoli bottoni raggruppati con la dicitura “View”,checipermettonodimostrareonasconderelevariearee(nell’ordine:“Navi-gation Area”,“Editor Area”,“Utility Area”e“Debug Area”).
La“Navigation Area”cipermettedivedereilnostroprogettoetuttiifilechelocom-pongono.Troveremoleclassi,leimmaginiinserite,iframeworkutilizzati.Nellapartesuperiorediquestaareacisonoalcuneicone,checipermettonodiaccedereadaltresezioni.Grazieaquestepotremoeseguiredellericerchemirateall’internodelcodice(“Searchnavigator”),oppurevisualizzareglieventualierroriogliwarning risultantidallacompilazione(“Issuenavigator”).
“Editor Area”èlaparteprincipaledell’editor,incuipotremoscrivereilcodicedellenostreapplicazioni.Forniscedellefunzionifantastichecomel’auto-completamento,lacorrezioneautomaticadeglierrorietantoaltro.Tuttefunzionicheimpareretepianpianoadapprezzare.
Fig. B: Sezioni che compongono Xcode
12
“Utility Area” fornisceaccessoalla libreriadegli oggetti (indispensabilequandosilavora in Interface Builder). Permette inoltre di accedere velocemente alla documen-tazionediunoggetto,graziealcomodo“QuickHelp”cheèsempreinfunzione.
“Debug Area”è, infine,lasezionecheforseodieretequandovitrovereteadutiliz-zarla:quivengononotificatieventualierrorichemandanoinbloccol’applicazione,oppureimessaggilanciatidaparticolariistruzioni.Anchevoi,comunque,sareteingrado di far stampare dei messaggi in quell’area, vedremo poi come. In questa se-zioneèanchepossibile,infasedirun-time,vederelostatodellevariabiliolostackdellechiamateallefunzioni.
CisarebberoaltrimilleaspettidiXcodedaanalizzareediscutere,maprobabilmenteservirebbe un libro solo per quello. Per avere una panoramica completa su que-stostupendostrumento,viconsigliodiselezionare“Learn about using Xcode”nellaschermatainizialechesipresentaquandoapriteXcode.
13
P r I m I P a s s I I n X c o d e
II. Primi passi in XcodeDopoavervistobrevementecomeXcodeèstrutturato,èvenutoilmomentodiinizia-readutilizzarloevederecosaciproponeperrealizzareinostriprogetti.
I templateQuandoselezioniamo“CreateanewXcodeproject”civienepropostalaseguenteschermata:
PotetenotarecheXCodeciforniscevaritemplate,ovverodellestrutturegiàpronte,checipermetterannodicreareapplicazionisullostilediquellenative.Nondovremoperderetempo,quindi,perreplicarelostiledellealtreapplicazioniperchéèApplestessachecelimetteadisposizione.
Analizziamoora ivari templatechecivengono fornitiedosserviamo i treesempiriportati nella pagina seguente.
•Document-Based Application,generaun’applicazionelacuistrutturabasedeidatièundocumento.Essopuòesseresiaundocumentointernoall’applicazione,siaunaseriedidatiricavatidaiCloud(ilnuovosistemadicloudintrodottodaApplecon iOS 5).
Fig. C: Creazione di un nuovo progetto in Xcode
14
•Master-Detail Application,generaun’applicazionecompostadaunatabella,unabarradinavigazioneeunavistadidettaglio.Per farviunesempio,pensateallastrutturadi“Impostazioni”nelvostroiPhone:aveteunabarrainaltochevisualizzailtitolodellasezionecorrente,evipermette,tramiteunbottone,ditornareallase-zioneprecedente.Questabarravienechiamata“NavigationBar”.Èutilepercreareapplicazionicon tabelle, incuivogliamomostrareanche le informazionisuivarielementi.LostessotemplatepermetteanchedicrearetabelleappositeperiPad(le“SplitView”).
•OpenGL Game, questa è sicuramente la tipologia più complessa, in quanto si basa sullatecnologiaOpenGL,sfruttataprincipalmenteperrealizzarevideogiochioani-mazionigrafichecomplesse.Inquestolibrononanalizzeremoquestatipologiadiapplicazioni.
•Page-Based Application,fornisceunastruttura“apagine”,comeunasortadilibro,sfogliabile scorrendo il dito da destra verso sinistra.
•Single View Application,fornisceun’applicazionevuota,senzanessunaimplemen-tazioneparticolare.Questotemplateècompostounicamentedaunafinestracheviene richiamata all’avvio dell’applicazione, chiamata “ViewController”. Questosaràilpuntodipartenzaperquasituttiinostritutorial.
•Tabbed Application,fornisceun’applicazioneconla“tabbar”,ovverolabarraneracompostadapiùsezioni(adesempioquellachetrovatenell’applicazionenativa“Musica”).
•Utility Application,questatipologiaimplementaunmenùchevienerichiamatoruo-tandolaschermataprincipale.Ancheinquestocaso,potetetrovareunasimilitu-dineconl’applicazione“Meteo”:sepremetesulla“i”presenteafondopagina,laschermataruoteràevipermetteràdimodificareleimpostazionidell’applicazione.
•Empty Application, fornisce solo un delegato per la nostra applicazione, senzanessunelementografico.Èdestinatoagliutentipiùesperti,chevoglionocrearelalorostrutturapersonalizzata.
Fig. D: Master-Detail Application Fig. E: Tabbed Application Fig. F: Utility Application
15
P r I m I P a s s I I n X c o d e
Creare un nuovo progettoIn ogni tutorial che vedremo, sarà necessario creare unnuovoprogetto.Per fareciò,apriteXcodeedallaschermatadibenvenutoselezionate“CreateanewXco-de project” (oppure, in alternativa, selezionare “File -> New -> New Project...”). Siapriràlafinestraconlasceltadeltemplate,cheabbiamoappenaanalizzato. Sceglieteiltemplatedesiderato(generalmentesarà“SingleViewApplication”)eclic-catesu“Next”.
Nellaschermatasuccessivadobbiamodefinirealcuneproprietàdelprogetto.•Product Name,èilnomedelnostroprogettochesolitamentecoincideconilnomechedaremoall’applicazione(nonpreoccupatevi,ilnomedell’apppotretecambiarloin qualsiasi momento).
•Company Identifier,èunidentificativounivococheassegnateallavostraapplica-zione.Perconvenzionedeveavereunaformacomequesta“com.companyname.app_name”.Nelmiocasohoinserito“net.bubidevs”(il“.app_name”vieneaggiun-to in automatico da Xcode).
•Class Prefix,quipoteteimpostareunprefissocheverràantepostoatutteleclassichecreerete(peroranonènecessario).
•Device Family,quiscegliamoidispositiviacuièdestinatalanostraapplicazione.PotremosceglieredidestinarlasoloadiPhone(comefaremonoipertuttiituto-rial),soloadiPad,oppurecreareun’applicazioneuniversale,destinataquindiadentrambi i dispositivi.
•Use storyboard,spuntandoquestaopzioneutilizzeremounnuovosistemaperladefinizionedell’interfacciagrafica.EssendounanovitàdiiOS5,hopreferitononutilizzarlainquestolibro,preferendoil“vecchio”sistemaafile“.xib”.
•Use Automatic Reference Counting,spuntandoquestaopzionesfrutteremoilnuo-vosistemadigestionedellamemoria,introdottoanch’essoconiOS5.Piùavantivispiegheròmeglioquestanovità,cheutilizzeremopersemplificarelosviluppo.
• Include Unit Test, questa opzione vi creerà anche delle classi destinate al testdell’applicazione.Peroranonutilizzatela.
16
Impostate le proprietà di questo primo progetto come nello screen seguente:
Cliccateancorasu“Next”eselezionate,infine,ilpercorsoincuivoletesalvareilpro-getto,premendopoi“Create”.Avretecosìcreatoilvostroprimoprogetto!
Le proprietà del progettoUnaspettomoltoimportanteechespessocreaalcunedifficoltàachièalleprimearmi con Xcode sono le proprietà relative ad un progetto.
Appenaabbiamocreatoilnostroprogetto,civengonomostratelesueproprietà(chepossiamo visualizzare ogni volta che desideriamo semplicemente selezionando ilnome del progetto nella navigation area di Xcode).
Èbeneanalizzareecomprenderefindasubitoquesteproprietà,perchéspessosonofattorimolto importanti nello sviluppo di un’applicazione. Alcune di esse sono lestessechevisonostaterichiestequandoavetecreatounnuovoprogetto,mentrealtre sono nuove.
Fig. G: Proprietà del progetto
17
P r I m I P a s s I I n X c o d e
Nellaprimasezionetroviamoalcuneproprietàraggruppatenelgruppo“iOSApplica-tionTarget”,eccoleneldettaglio:•Identifier,èil“CompanyIdentifier”dicuiabbiamodiscussopocofa.
•Version e Build,indicanoilnumerodiversionedellavostraapp.Èunnumeroche,solitamente,iniziada1.0perlaprimaversione,esiincrementaconivariupdatecherilasciate(adesempio1.1,1.2o2.0).
•Devices,indicaidispositiviconcuilavostraapplicazioneècompatibile(comeprima).
•Deployment Target,quiandremoaselezionarelaversionediiOSconcuivogliamochesiacompatibilelanostraapplicazione.Questoènecessarioinquanto,conilrilasciodinuoveversionidelsistemaoperativo,vengonoaggiunteanchenuovefunzionial-l’SDK(lecosiddetteAPI).Quindi,un’applicazionescrittaecompilataperiOS5potreb-benonfunzionaresudispositiviconiOS4.2installato(spessoèproprioimpossibileinstallarlainquantol’AppStorecontrollaquestacompatibilitàinfasediinstallazione). Alcontrario,secompiliamol’applicazioneperlaversione4.0funzioneràsicura-mentesuiOS5(amenochenonutilizziatedelleAPIconalcunibug).Èimportante,quindi,scegliereconcuralaversionedautilizzare,pernonescluderepartedegliutentidall’utilizzodellanostraapplicazione.
Lasecondasezione,“iPhone/iPodDevelopmentInfo”permette,tralealtrecose,diselezionareleorientazionisupportatedallanostraapplicazione:portraitelandscape,specificando,inoltre,ilversodiorientamentosupportato.Inquestasezionesipossono impostareanche le icone (indimensionenormaleeadattealRetinaDisplay)elesplashscreen.Visegnalocheèanchepossibilecam-biarelafinestraprincipalechevienecaricataall’avviodell’applicazione:solitamenteèla“ViewController”che,almenoperora,viconsigliodinoncambiare.
Fig.H: Proprietà complete del primo progetto
18
Interface BuilderCon il nuovo Xcode 4 Interface Builder è un componente integrato in Xcode, e non è piùun’applicazioneesterna.Essocipermettedicrearegraficamentelenostreinter-facce,senzadoverricorrerealungherighedicodice.
Nota: alcuni “puristi” del codice sono contrari a questo approccio mediante IB, perché lo ritengono uno spreco di memoria. Sicuramente scrivere componenti grafici via codice fa risparmiare qualcosa in termini di prestazioni, però per applicazioni semplici o non troppo complesse non ritengo che questo sia un fattore da tener molto in considerazione. In fondo, se Apple ha sviluppato e insistito con questo strumento un motivo ci sarà...
TornandoaXcode,selezionate il file “ViewController.xib”cheverràaperto tramitel’editor di IB mostrandoci questi elementi:
Fig. I: Apertura di ViewController.xib in Interface Builder
Fig. L: Inspector
Nellapartesuperioredi“UtilityArea”troviamo“Inspector”,sicuramentelapartepiùimportante di Interface Builder.
19
P r I m I P a s s I I n X c o d e
Cipermettediimpostarequalsiasiproprietà,relativaadognioggetto(dallavistaalsingolobottone).Potremo,quindi,modificarel’aspettodeglioggetti,modificarneledimensioniecollegareleazioni.Insomma,potreteagiresumoltissimiaspetti(iltuttosenzascrivereunarigadicodice).Lesezionichelocompongonosonoleseguenti:•File,ciforniscedelleinformazionisulfilecheabbiamoaperto(adesempioilper-corso)epossiamoinserirenuovelocalizzazioni(ovveroaggiungerelatraduzioneper altre lingue).
•Quick help,visualizzaunabreveguidadelcomponentecheabbiamoselezionato,ricavandoleinformazionidirettamentedalladocumentazioneufficiale.
•Identity, in questo pannello sono presenti alcune voci relative al progetto e all’ac-cessibilità.Inquestoebooknonutilizzeremomaiquesteproprietà.
•Attributes, qui possiamo variare gli attributi generici dei nostri oggetti, ad esempio ilcolore,ladimensionedelfont,edaltreproprietàcheimpareremopocoallavolta.
•Size,questasezionepermette,comedicelaparolastessa,dimodificareledimen-sionidell’oggetto,edimodificarneanchelaposizioneel’ancoraggio(ovverodovedeveesserefissato:questosaràfondamentalequandosiimplementalarotazionedellaschermata).
•Connections,èunodeipannelliprincipali.Quiandremoacollegareleazioniaglioggetti.Cosasignifica?Ve lospiegoconunesempio.Supponiamodiavereunbottonenellanostravista,evogliamochequandovienepremutovengascritto“Ciao”inunacaselladitesto.Creeremo,quindi,un’azionechesioccuperàdiscri-vere“Ciao”.Dovremopoicollegarequestaazionealbottone,inmodochequandol’utente preme il bottone, viene eseguito il comando desiderato. Spesso questi collegamenti vengono fatti in maniera automatica da Xcode ma a volte dovremo essere noi a crearli.
Semprenella“UtilityArea”troviamola“Library”,ovverolalibreriachecontienetuttiglielementichepossiamoutilizzarepercrearelenostreinterfacce.
Fig. M: Library di U
tility Area
20
Scorrendola potrete notare moltissimi componenti, come bottoni, label di testo, vi-ste,barreemoltoaltro.Tuttiquestielementipossonoessereutilizzatinellenostreap-plicazioni.Perinserirlibastatrascinareuncomponentenellaschermatadell’applica-zioneecollocarloapropriopiacimento.Questoèveropertuttiicomponentitrannei“Controllers”,chepotretericonoscereperchéhannounosfondogiallo/arancione.Questisonoelementidiversi,chehannoilcompitodirappresentarealtrioggetti(adesempioclassiocontroller),manonelementigraficiveriepropri.
Nellapartesinistradell’editor(afiancodellavistachevieneproposta)c’èunpannellochemostraleiconedialcunielementi(Fig.N).
Cliccatesulpulsantinochetrovatenellasuapartebassaperespanderetalemenù(Fig.O).
Fig. O: Elementi di ViewController (2)Fig. N: Elementi di ViewController (1)
Quipotretevedere icomponentidelvostrofile .xib (ovvero il filedi interfaccia). Ilcomponenteprincipaleèil“File’sOwner”:letteralmenteessoèilproprietariodellanostra vista, ovvero è l’elemento che si occupadi gestirne tutti i comportamentirelativi. Tale classe, infatti, dovrà impostare il testonelle label,modificare il com-portamentodeglioggettiecosìvia.Seloselezionateevispostatepoinell’“IdentityInspector”vedretecheilproprietariodiquestavistaèlaclasse“ViewController”:èproprio essa a gestire il comportamento di questa vista.
Abbiamo così completato la panoramica sull’Interface Builder. Mi pare inutile, per ora,dilungarmiinaltriaspettichevisarannosicuramentepiùchiariefamiliariconunpo’dipratica.Inpochissimotempo,nonriusciretepiùafareamenodiInterfaceBuilder!
21
P r I m I P a s s I I n X c o d e
Le novità di iOS 5ComeognimajorreleasediiOS,Applehaintrodottopiùdi1.500nuovifunzioniperglisviluppatori.Capiretechecitarleedescriverle tutteèun’impresa impossibileecertamentenonutileperchièalleprimearmi.Lanovitàcheciinteressamaggiormente,echeanalizzeremoinquestolibro,èl’ARC.
L’ARC(AutomaticReleaseCounting)èunnuovometododigestionedellamemoria,cheintroducedellefunzionialivellodicompilazionepersemplificarnelagestione.Prima di iOS 5 quando si creavano oggetti ed elementi era necessario occuparsi anchedellalorogestione:quandononeranopiùnecessariandavanoeliminati,uti-lizzandofunzionicome“release”.Grazieall’ARCnonsaràpiùnecessariooccuparsidiciò,perchéverràfattoinautomaticodalcompilatore.Attenzione,nonsitrattadiungarbage-collectorcomepotetetrovareinJavaoinaltrilinguaggi,masonodellesemplificazionicheXcodeeilcompilatorefannopernoi.Ovviamenteapprofondire-mo questi aspetti man mano li troveremo nell’ebook.
OracheaveteavutounapanoramicasuXcodeesucomefunziona,ègiuntal’oradicrearelanostraprimaapplicazione!
23
c a P I t o l o 1H e l l o Wo r l d
1. “Hello World”
Comeda“tradizione”iniziamolanostraavventuranelmondodellaprogrammazioneperiPhonetramiteilclassicissimo“HelloWorld”.
Lanostraprimaapplicazionesaràdavveromoltosemplice:chiederemoall’utenteilsuonome(adesempioTizio)eallapressionediunbottonefaremoapparireilmes-saggio“CiaoTizio”.Nientedicomplicatoquindi,peròcercatedicapirebenetuttiipassaggi, in modo da non avere lacune su questi concetti davvero basilari.
Creiamo la struttura graficaIniziamocreandounnuovoprogettoditipo“SingleViewApplication”echiamiamo-lo“HelloWorld”.Nellafinestrasuccessivatogliamolaspuntaa“Usestoryboard”e“IncludeUnitTest”mentreassicuriamocichesiaspuntatalavoce“UseAutomaticReferenceCounting”esiaselezionato“iPhone”comedevice.
Apriamoquindiilfile“ViewController.xib”:dobbiamocrearelastrutturagrafica,chedeveesseresimileall’immagineriportatanellapaginasuccessiva(Fig.1.2).
Fig. 1.1: Creiamo il progetto “Hello World”
24
Come potete osservare, ci sono tre componenti principali:•una“UITextField”incuil’utentepotràinserireilproprionome;
•un“UIButton”,conlascritta“Saluta!”,chel’utentedovràpremereperfarapparireilsaluto;
•due“UILabel”,unaconlascritta“Inserisciiltuonome”(equestaservesoloperrenderepiùintuitivoilnostroprogramma),l’altrapostaacentroschermo(checon-tienelastringaLabel).Èimportanteimpostareledimensionidiquestaultimalabelinmodocheoccupituttalalarghezzadellavista,altrimentiavremoproblemiquan-do andremo a stampare il messaggio di saluto in seguito.
Tramite“AttributeInspector”possiamodefinirealcuneproprietàperquestioggetti.SelezioniamoadesempiolaUITextField,eimpostiamoiseguentivalori:•“Capitalize”: “Words” (ovveroquando l’utente iniziaadigitare ilproprionome laprimaletteravienescrittainmaiuscolo);
•“Corrections”:“No”(disabilitiamolacorrezioneautomaticaperquestocampo);
•Spuntiamol’opzione“Clear When Editing Begins”(cancelleràilcontenutogiàpre-sentenonappenal’utenteselezionailcampo).
Avostropiacerepoteteancheimpostarealtreproprietà,provatenealcuneperpren-deremeglioconfidenzacon ilprogramma.Potete,adesempio,modificare il fontdelle due label oppure il colore di sfondo della vista.
Primadiproseguire,diamoun’occhiataaciòchecomponequestofile(lotrovatenelpannello alla sinistra della vostra vista, riportato nella pagina successiva, Fig. 1.3).
Fig. 1.2: View
Controller di “H
ello World”
25
c a P I t o l o 1H e l l o Wo r l d
Potetenotaretreelementi,cheXCodehagiàdefinitopernoi.•“File’s Owner”,comedicelaparolastessaèilproprietariodelfile.È,insostanza,laclassechegestiscelanostravista,ovverochegestisceognisingoloaspetto.Nelnostrocasosaràlaclasse“ViewController”,dovepoiandremoascrivereilcodicenecessario.
• “First Responder”, è un oggetto che nella programmazione per iPhone non hamoltosenso.Èun“relitto”derivatodallaversionediCocoaperMac,quindipoteteanchedimenticarvicheesista.
• “View”, è la nostra vista, ovvero la schermata che abbiamodefinito. In questocasoneabbiamounasola,manullacivietadidefinirnesvariateerichiamarlepoianostropiacimento.All’internodell’oggetto“View”potetevedereglielementicheabbiamoinseritotramiteIB,chefannoorapartedellavista.
Definiamo gli elementi e le azioniOravediamocomeèpossibilecollegareglielementigraficicheabbiamoappenain-seritocondeglioggettinelcodice.Xcode4permettedifaretuttociòconunsistemadavvero semplice e veloce.
Nella“Toolbar”diXcodecliccatesulseguentepulsante(sitrattadell’“Assistanteditor”):
Fig. 1.3: Elementi di ViewController.xib
Fig. 1.4: Assistant Editor
26
L’editorverràdivisoindue:asinistravedremolanostrainterfacciagrafica,mentreadestralarelativaclassechelogestisce.Dovresteavere,quindi,unaschermatacomequesta:
Fig. 1.6: Creazione delle connessioni
Fig. 1.5: Utilizzo dell’Assistant Editor
Selaclassechevivienemostratanonèquellacorretta,tramitelabarrachetrovatesopra ilcodicepotretenavigaretra ivarifilechecompongonoilprogettoeaprirequellacorretta(chedeveessere“ViewController.h”)
CliccatesullaUITextFieldetenendopremutoilpulsante“ctrl”cliccate(tenendopre-muto)partendodallatextfield:vedretecomparireunarigablu,trascinatelafinoalfiledellaclasse.Seaveteeseguitocorrettamentequestaoperazioneviappariràunpop-up simile al seguente:
27
c a P I t o l o 1H e l l o Wo r l d
Questovipermetteràdicreareduetipidielementi(campo“Connection”):•“Outlet”:permettedicollegareglielementiinseritiinIBconoggettinelcodice;
•“Action”:questoservepercrearedelleazioni,chedevonoessereassociateapar-ticolari eventi.
Selezioniamo“Outlet”einseriamocomenome“fieldNome”.Ripetiamo lo stesso procedimento con la label destinata ad ospitare il messaggio e chiamiamola“labelSaluto”.
Dobbiamocompletareicollegamenticonun’azione:quandol’utentepremesulbot-tonevogliamochevengarichiamatoundeterminatometodo,cheavràilcompitodimostrare il messaggio nella label predisposta.
Ancoraunavoltaeseguiamolostessoprocedimentopartendodalbottone;allacom-parsadelpop-upselezioniamo“Action”cometipologiadellaconnessioneechia-miamolo“saluta”:
Fig. 1.7: Creazione dell’azione
Codice 1.1: File di interfaccia ViewController.h
Abbiamocompletatoleconnessioninecessarie!Seabbiamoeseguitotuttiipassag-gi correttamente avremo la classe così composta:
12345678910
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UITextField *fieldNome;@property (strong, nonatomic) IBOutlet UILabel *labelSaluto;
- (IBAction)saluta:(id)sender;
@end
28
Idueoggettigraficisonostatidichiaratitramitedue“property”cheprestoimparereteaconoscere.Perorasappiatecheessevipermettonodiaveredellevariabiliaccessibiliancheall’esternodellaclasse,inquandogeneranoautomaticamenteimetodigetteresetternecessari.Possiamoanchenotarecheentrambeledichiarazionisonoprece-dutedallaclausola“IBOutlet”,cheindicaadInterfaceBuildercheèpossibilecollegarequestometodoadunelementografico.InquestocasoilcollegamentoèstatogiàfattodaXcode.Dopoquesteduedichiarazioni,trovatel’intestazionedelmetodo“saluta:”,eancheinquestocasotrovateunaclausola”IBAction”:anchequestastaasignificarechel’azioneècollegataaduncomponentediInterfaceBuilder.Essa,infasedicom-pilazione,corrispondeadunclassico“void”,ovverounmetodosenzanessunritorno. Ilparametro“sender”èinvecel’identificativodell’oggettochehachiamatoilmetodo. Grazieaquestoparametropotretecollegarelostessometodoapiùoggetti,ecapirepoidachièstatoinvocatopropriograzieall’oggetto“sender”.
Scriviamo il codice necessarioDobbiamooraimplementareilmetodo“saluta:”,chesioccuperàdileggereilnomeinserito dall’utente e stampare il messaggio di benvenuto nella label predisposta. Spo-stiamocinelfile“ViewController.m”(possiamoanchedisattivarel’“Assistanteditor”).
Codice 1.2: Metodo “saluta:”
12345678910
1112
- (IBAction)saluta:(id)sender { // leggiamo il nome inserito dall’utente NSString *nome = self.fieldNome.text; if (nome.length == 0) { // l’utente non ha inserito nessun nome self.labelSaluto.text = @”Ciao Anonimo!”; } else { // salutiamo l’utente con il nome che ha inserito self.labelSaluto.text = [NSString stringWithFormat:@”Ciao%@”,nome]; }}
Nota: Il primo file che abbiamo modificato aveva estensione “.h”: questa estensione indica che si tratta di classi di intestazione, ovvero dove vengono definiti i metodi e i componenti necessari, ma non vi è alcuna implementazione dei metodi. I file con estensione “.m”, invece, contengono tutte le imple-mentazioni dei metodi, ovvero il codice delle varie funzioni. Questa suddivisione permette di avere un codice più ordinato e più leggibile.
Quando aprite il file “.m” noterete che Xcode ha già inserito del codice per noi.Troverete,adesempio,dueistruzioni“@synthesize”,chedevonoesserenecessaria-menteinseritequandosidichiaranodelle“@property”.Neimetodi“viewDidLoad”e“viewDidUnload”troveretedelleistruzionichevengonoeseguitequandolavistavienecaricataoppurechiusa.Neiprossimitutorialandremoamodificareanchetalimetodi.Peroradobbiamodefiniresoloilmetodo“saluta:”,chetroveretegiàdefinitoinfondoalla vostra classe. Inserite il seguente codice:
29
c a P I t o l o 1H e l l o Wo r l d
Analizziamoilcodicechedefinisceilnostrometodo“saluta:”.Intalemetodononfaccia-moaltrocheleggereilnomeinseritodall’utente(tramite“fieldNome.text”,checiresti-tuiscelastringacontenutanellaUITextField)eassegnarloallavariabile“nome”(riga3).Tramiteuncostrutto“if”(dallariga5alla11)andiamopoiacontrollaresetalestringaèvuota(equindil’utentehapremutoiltastosenzainserirenessunnome)oppuresevièun valore. Ovviamente inseriamo due messaggi di saluto diversi a seconda del caso.
Particolareattenzionemeritapartedell’istruzioneallariga10,edinparticolareque-staporzionedicodice:“@“Ciao %@”,nome”.•“@”,laprimachiocciolastaadindicarecheciòchesegueèunastringa.Lasintassièsemprelastessaepuòessereschematizzatanelseguentemodo:@“stringa_de-siderata”.Dovretesempreutilizzarequestoformatoquandoutilizzateunastringa.
•“%@”,quandotrovateun%inunastringa,significacheliciandràilvalorediunadeterminatavariabile.Nonacasodopo lastringa trovare lavariabile “nome”: ilcontenutoditalevariabilenomeandràinseritoalpostodi%@.Seavessimodo-vutoinserire ilvalorediunavariabileditipofloatavremmousatolasintassi%f.Vedremoparecchiesempineiprossimitutorial.
Abbiamo concluso! Cliccate sul bottone “Run”: se non avete commesso errorinell’inserire ilcodicesiavvierà l’iPhoneSimulatorepotretetestare ilvostroprimoprogramma!
Nascondiamo la tastieraProvatead inserireunnomeeapremere il tasto“Invio”della tastieracheapparesull’iPhone:noteretecheessanonsichiude.Sitrattadiunerrore?Larispostaèno.Ènormale,inquantononabbiamodefinitonessunaazionechenascondalatastiera.Persistemarequestoinconveniente,apriamoilfile“ViewController.h”emodifichia-mol’intestazionenelseguentemodo:
Codice 1.3: Nascondere la tastiera passaggio 1
1 @interface ViewController : UIViewController <UITextFieldDelegate>
Abbiamoaggiunto“<UITextFieldDelegate>”,ovverolanostraclassedeveimplemen-tare il delegato della classe UITextField. Parleremo più avanti di cosa siano i delegati, perorasappiatechesonodeicomportamenticomuniadelleclassidioggetti.Fatto ciò, andiamo nel file “ViewController.m” e inseriamo, in un qualsiasi punto,questo metodo:
Codice 1.4: Nascondere la tastiera passaggio 2
1234
- (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return YES;}
30
Questoèunmetododeldelegato“UITextField”.Essoviene richiamatoquandosipremeiltasto“Return”dellatastiera.L’istruzioneallariga2toglie latextfielddalprimorisponditore,equestocausalascomparsa della tastiera.
Manca,però,ancoraunapiccolacosadafare.Torniamoalfile“ViewController.xib”ecliccatesullaUITextFieldall’internodellavista.Apriteancheil”Connectionsinspec-tor”nell’UtilityAreaenoteretecomeprimoelemento“delegate”:
Fig. 1.8: Connections Inspector della text field
Clicchiamosulpallinoafiancodelnomeecolleghiamoloconil“File’sOwner”chetroviamoalla sinistradella vista. In pratica, abbiamodetto che il delegatodi taleoggettoègestitodallaclasse“ViewController”.Seaveteeseguitolaconnessioneinmaniera corretta avrete il seguente risultato:
Fig. 1.9: Connessione del delegato della text field
Siamoprontipertestarelanostraapplicazione!Cliccatesu“Run”eprovatelavostraprimaapplicazionepienamentefunzionante!
31
c a P I t o l o 1H e l l o Wo r l d
Fig. 1.10: App 1 “HelloWorld”
33
c a P I t o l o 2E l e m e n t i P r i n c i p a l i
2. Uno sguardo ai componenti baseInquestosecondocapitolodaremounosguardoadalcunicomponentibasilari,cheviritrovereteadusarespessonellevostreapplicazioni.Creeremounasemplicissimaapplicazione,incuiverràmostrataun’immagine,dicuipotremoregolarelatraspa-renza.Tramiteunsegmentedcontrolsaràpossibileanchecambiarel’immagine.Unbottone,infine,permetteràdiaprireunpop-upchemostreràilvalorecorrentedellatrasparenzadell’immagine.
Disuol’applicazionenonhaunagrandeutilità,peròcipermetteràdianalizzarealcu-nicomponentimoltoutilizzatinelleapplicazioniperiPhone.
Creiamo la struttura graficaCreiamounnuovoprogettodi tipo“SingleViewApplication”echiamiamolo“Ele-mentiPrincipali”.Comesempreassicuriamociche“Usestoryboard”e“IncludeUnitTest”sianodisattivati.Apriamoquindiilfile“ViewController.xib”einiziamoadefinirela nostra interfaccia, inserendo i seguenti componenti:
Fig. 2.1: Schem
a elementi da inserire
Dobbiamomodificareunpaiodiproprietàde-gli elementi. Iniziamo selezionando il segmented controle aprendo la sezione “Attributes Inspector”dell’Utility Area.
Cerchiamolaproprietà“Segments”eaumen-tiamolaa3:vogliamo,quindi,checisianotresezioninell’oggetto.
Fig. 2.2: Segm
ented Control
34
Clicchiamopoisulnomediognisezioneecambiamolo,inserendo“Prima”,“Secon-da”e“Terza”.Eccoilrisultato:
Fig. 2.3: Struttura grafica com
pleta
Oradobbiamosolomodificareivaloridelloslider.Selezioniamoloe,semprein“Attri-butes Inspector”,settiamoilvalore“Minimum”a0,mentreil“Maximum”a1.
Fig. 2.4: Modifica valori dello Slider
Questamodificacipermetteràdiutilizzareilvalorecontenutonellosliderperlatra-sparenzadell’immagine(cheaccettasolovaloricompresitra0e1),senzadoverfarenessunaoperazionediconversione.
Avostropiacimentopotetemodificareancheglialtrielementi;perinostriscopi,co-munque,nonsonorichiesteulteriorimodifiche.
35
c a P I t o l o 2E l e m e n t i P r i n c i p a l i
Colleghiamooraglielementicreatiallaclasse“ViewController”,propriocomeab-biamo fatto nel primo capitolo. Creiamo un outlet per l’image view chiamandola“viewImmagine”.Dobbiamocreareanchetreazioni(sempreeseguendoilcollega-mento e selezionando “Action” dal pop-up che appare): unaper lo slider (“cam-biaAlpha”),unaper ilsegmentedcontrol (“cambiaImmagine”)eunaper ilbottone(“visualizzaAlpha”).
Il codice della vostra classe dovrebbe essere questo:
Codice 2.1: File di interfaccia ViewController.h
123456789
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIImageView *immagine;
- (IBAction)cambiaAlpha:(id)sender;- (IBAction)cambiaImmagine:(id)sender;- (IBAction)visualizzaAlpha:(id)sender;
@end
Nota: Perché non abbiamo creato gli outlet anche per lo slider e per il segmented control? Semplice, perché utilizzeremo il parametro “sender” dei rispettivi metodi.
Possiamo ora passare alla scrittura del codice.
36
Scriviamo il codice necessarioPrimadipoterimplementareimetodi,abbiamobisognodi3immagini,cheverran-no caricate all’interno della nostra image view. Scegliete tre immagini e inseritele nel vostro progetto semplicemente trascinandole al suo interno. Vi apparirà una schermataincuidovreteassicurarvidispuntarel’opzione“Copy items into desti-nation group’s folder (if needed)”.Cliccatesu“Finish”evedretelevostreimmaginiappariretraifiledelprogetto:
Codice 2.2: Implementazione del “viewDidLoad”
1234567
- (void)viewDidLoad { [super viewDidLoad]; // settiamo l’immagine iniziale self.immagine.image = [UIImage imageNamed:@”immagine1.png”]; // impostiamo l’alpha iniziale self.immagine.alpha = 0.5;}
Fig. 2.5: File del progetto
Orapossiamoiniziareascrivereilcodicechegestiscelanostraapplicazione.Apria-moilfile“ViewController.m”einiziamomodificandoilmetodo“viewDidLoad”:
Questometodovienerichiamatoquandolavistaèstatainizializzataestaperapparire.Dobbiamoimpostareun’immagineinizialechevengamostratanellaimageview(riga4).Questaoperazioneviene fattaconuncomodometododellaclasseUIImage,chepermettedispecificareilnomedell’immaginechedeveesserecaricata(l’immagine,ovviamente, deve essere all’interno del progetto).
L’istruzione successiva (riga 6) imposta un valore iniziale della trasparenza (devecoincidereconilvaloreinizialedelloslider).
Oradobbiamodefinire iduemetodichesioccupanodicambiare l’immagineedicambiarnelatrasparenza.
37
Eccoli:
Codice 2.3: Metodi per la modifica dell’immagine e della sua trasparenza
Codice 2.4: Metodo per la visualizzazione dell’alpha
123456789101112
131415
1
2
- (IBAction)cambiaAlpha:(id)sender { // associamo il sender ad un elemento UISlider UISlider *sliderAlpha = (UISlider*)sender; // modifichiamo il valore alpha dell’immagine self.immagine.alpha = sliderAlpha.value;}
- (IBAction)cambiaImmagine:(id)sender { // associamo il sender ad un elemento UISegmentedControl UISegmentedControl *seg = (UISegmentedControl*)sender; // creiamo il nome dell’immagine da caricare NSString *nomeImmagine = [NSString stringWithFormat:@”immagine%i.png”,seg.selectedSegmentIndex+1]; // carichiamo l’immagine voluta dall’utente self.immagine.image = [UIImage imageNamed:nomeImmagine];}
- (IBAction)visualizzaAlpha:(id)sender { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@”Alpha” message:[NSString stringWithFormat:@”Il valore della trasparenza è: %f”,self.immagine.alpha] delegate:nil cancelButtonTitle:@”Ok” otherButtonTitles:nil, nil]; [alert show];}
Inentrambipotetevederecomevieneutilizzatoilparametro“sender”.Primavienefattouncastingversol’oggettochesappiamoessere(righe3e10),poipuòessereutilizzato.Ovviamentequestaoperazionepuòesserefattasenzacontrolliseunparti-colaremetodovienechiamatodaunsolooggetto(comenelnostrocaso).Seinvecepiùoggetti (magarididiversanatura) richiamassero lostessometodo,dovremmoeseguiredeicontrolliprimadipoterdefinireunnuovooggetto.
Ilmetodo“cambiaAlpha:”èdavverosemplice:vieneimpostatalatrasparenza(alpha)dell’immagineponendolaugualealvaloredelloslider(riga5).
Ilmetodo“cambiaImmagine:”,invece,haun’istruzioneinpiù:vienecreataunava-riabile“nomeImmagine”incuisicreailnomedell’immaginedacaricare.Essaèfattasemplicementeinserendoilvaloredell’indicedelsegmentedcontrolselezionatopiùuno(perchèilprimoelementohaindicezero,enonuno)all’internodellastringa“im-magine.png”,propriocomeabbiamovistonelprimocapitolo.Inquestomodovienecreataunastringachecoincidealnomedell’immaginedavisualizzare.Essa,infatti,vienepoicaricataconlastessaistruzioneutilizzatanel“viewDidLoad”(riga14).
Manca ora solo un ultimo metodo:
c a P I t o l o 2E l e m e n t i P r i n c i p a l i
38
Questovisualizzeràunaclassicaalertviewincuivienemostratounmessaggioconilvaloredell’alphadell’immagine.Comepoteteintuire,cisaràsolounbottoneperchiu-derla(ilcuinomeè“Ok”),mentreilmessaggiovienecreatocomegiàvistopiùvolte(danotarecheinquestocasoabbiamoutilizzato“%f”perinserireunvaloreditipofloatall’internodellastringa).L’istruzioneallariga2,infine,visualizzaaschermol’alertview.Prendetebeneconfidenzaconquestooggetto,inquantoèlamodalitàpiùcomodapercomunicarebreviinformazioniall’utente.
Abbiamocompletatoanchequestaapplicazione!Premete“Run”etestatelavostranuovaapplicazione!
Fig. 2.6a: App 2 “ElementiPrincipali” Fig. 2.6b: App 2 “ElementiPrincipali”
39
c a P I t o l o 3Tr a s h A p p
3. Gestiamo immagini ed eseguiamo semplici animazioni
Inquestoterzotutorialinizieremoaprendereconfidenzaconunaspettomoltoim-portante:lagestionedelleimmaginiecomel’utentepuòinteragireconesse.Vedre-mo,inoltre,comerealizzaresemplicementedelleanimazioniconquesticomponenti.Andremoacreareunfintocestino,incuipotremotrascinareun’immagineeripristi-narla tramite un apposito pulsante.
Creiamo la struttura graficaCreiamo un nuovo progetto di tipo “Single View Application” e chiamiamolo“TrashApp”.Comesempreassicuriamociche“Usestoryboard”e“IncludeUnitTest”siano disattivati. Primadifarequalsiasicosa,inseriamonelnostroprogettoleimmaginicheciservi-ranno.Ioutilizzeròleseguentiimmagini:dueperilcestino,eunaperildocumentoda“eliminare”(potetetrovarequesteimmagininelfilediprogettodell’esempio):
Fig. 3.1a: Windows icon.png Fig. 3.1b: TrashIconEmpty.png Fig. 3.1c: TrashIconFull.png
TrascinateleimmagininelprogettoinXCode,enelpop-upcheviappariràmettetelaspuntaa“Copy items into destination group’s folder (if needed)”(comeabbiamofatto nel precedente tutorial).Apriamoquindiilfile“ViewController.xib”einiziamoadefinirelanostrainterfaccia.Inseriamo, per prima cosa, gli elementi necessari: due componenti UIImageView e un UIButton.
Fig. 3.2: Interdaccia di “TrashApp”
40
Dobbiamooraconfigurareleproprietàdelledueimageview.
Selezioniamoquella inaltoeandiamoin“AttributesInspector”:nelmenùatendi-na“Image”selezioniamoilfile“windows icon.png”elovedremocomparireanchenell’applicazione.Selezioniamo“Center”in“Mode”espuntiamoinoltrelacasella“User Interaction Enabled”.
Avremo un pannello così impostato:
Fig. 3.4: Interdaccia di “TrashApp” m
odificata
Fig. 3.3: Proprietà delle im
age view
Insostanzaabbiamoselezionatoquel-ladeterminataimmaginechedevees-serevisualizzatanellaimageview,ab-biamoimpostatochel’immaginesiaalcentrodell’oggettoeche sia abilitatal’interazioneconl’utente.
Facciamo la stessa cosa per l’image view sottostante, selezionando comeimmagine“TrashIconEmpty.png”; “Center”in“Mode”masenzaspuntare“UserInteractionEnabled”.
Inseriamo all’interno del bottone la scritta“Ripristinalogo”.
Aggiustiamoledimensionielaposizio-nedelledueimmagini(inmodocheibordi delle image view coincidano con le immagini al loro interno) fino ad ave-re un risultato come quello proposto di seguito(figura3.4).
41
Dobbiamooracreareglielementinellaclasse“ViewController”,comeabbiamogiàfattoneiprecedenticapitoli.Creiamounoutletperledueimageview,chiamandolerispettivamente“imageLogo”e“imageCestino”.Dobbiamocreareancheun’azioneperilbottone(sempreeseguendoilcollegamentoeselezionando“Action”dalpop-upcheappare”),chechiameremo“ripristinaLogo”.
Lavostraclasse“ViewController.h”saràquindicosìdefinita:
Codice 3.1: File di interfaccia ViewController.h
12345678
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIImageView *imageLogo;@property (strong, nonatomic) IBOutlet UIImageView *imageCestino;
- (IBAction)ripristinaLogo:(id)sender;
@end
Siamoprontiperscrivereilcodiceveroeproprio!
Implementiamo il movimento del logoIniziamoinserendounavariabileeunmetodocheciservirannosuccessivamente.
Definiamolinelfile“ViewController.h”:
Codice 3.2: Definizione degli elementi necessari
123456789101112
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController { BOOL cancellato;}
@property (strong, nonatomic) IBOutlet UIImageView *imageLogo;@property (strong, nonatomic) IBOutlet UIImageView *imageCestino;
- (IBAction)ripristinaLogo:(id)sender;
@end
Allariga4abbiamodefinitounavariabilebooleana(ovveropuòassumeresoloduevalori:YESoNO),checiserviràpercontrollareseillogodiWindowsègiàstatoeli-minato.
Quindi, al valore YES corrisponderà il logo eliminato, a NO il logo sarà ancora visibile.
c a P I t o l o 3Tr a s h A p p
42
Orainiziamoadimplementareimetodinecessari. Apriamoilfile“TrashAppViewController.m”einseriamoilseguentecodice:
Codice 3.3: Implementazione dei vari metodi
1234567891011121314
1516171819202122
232425262728
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { // ricaviamo i tocchi fatti dall’utente UITouch *touch = [[event allTouches] anyObject]; // controlliamo che il tocco sia stato sull’oggetto del logo if ([touch view] == imageLogo) { imageLogo.center = [touch locationInView:self.view]; }}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { // controlliamo se il logo interseca il cestino if (CGRectIntersectsRect([imageLogo frame], [imageCestino frame])) { // cambiamo l’immagine del cestino imageCestino.image = [UIImage imageNamed:@”TrashIconFull.png”]; cancellato = YES; // eseguiamo l’animazione per far sparire il logo [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.5]; imageLogo.transform = CGAffineTransformMakeScale(.001, .001); [UIView commitAnimations]; } else { // il logo non interseca il cestino, riportiamolo alla po-sizione iniziale [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.5]; imageLogo.center = CGPointMake(158.0, 147.0); [UIView commitAnimations]; }}
Ilmetodo“touchesMoved:withEvent”èunmetodoereditatodallaclasseUIRespon-der,cheogniclasseeredita.Talemetodovienerichiamatoquandol’utenteiniziaunmovimento sulla vista. Perprimacosasalviamonellavariabile“touch”glieventichel’utentecompietoc-candounqualsiasioggettodellanostraapplicazione(riga3).Controlliamo,poi,chel’oggettotoccatocorrispondaa“imageLogo”(riga5),cioèallogodiWindows:sel’oggettoèproprioquello,teniamotracciadelcentrodell’ogget-to(chenelfrattempovienemossodall’utente)conlaposizionedelditosulloschermo(riga6).Ovvero,ècomesespostassimofisicamentel’oggettoconilnostroditoelomuovessimo su un piano.
Ilsecondometodo,“touchesEnded:withEvent:”,vienerichiamatoquandoilmovimentotermina,ovveroquandol’utenterilasciailditodalloschermodell’iPhone.Quidobbiamocontrollaresel’utentehatrascinatoillogonelcestino.Perfareciòcontrolliamoseilfra-medi“imageLogo”(ovveroillogodiWindows)intersecailframedelcestino(riga12).
43
Incasopositivocambiamol’immaginedelcestino(riga14),settiamolavariabile“can-cellato” aYES ed eseguiamo l’animazione per far sparire il cestino (dalla riga 16). Leanimazionivengonoeseguitetramitele“UIAnimation”,disponibiliinUIView.Ilme-todo“beginAnimation:context:”(riga17)èilpuntodiiniziodell’animazione,conclu-socon“commitAnimations”(riga20).All’internodiquestedueistruzionipossiamospecificarequalsiasicomportamentochedeveessereeseguitosuglioggetti.Nelno-strocasosemplicementetrasformiamol’oggettorendendolopiccolissimo(riga19),inmodochesia“nascosto”.Ilcontrollosull’intersezione,però,potrebbeancheavereesitonegativo(riga21):intalcasoeseguiamoun’altraanimazione,riportandoillogodiWindowsalsuostatoiniziale(riga25).
Cimancasolol’implementazionedelmetodo“ripristinaLogo:”,associatoallapres-sionedelbottone.L’intestazionedelmetodoègiàstatadefinitadaXcode(lotrove-reteprobabilmenteallafinedi“ViewController.m”).
Eccolasuaimplementazione:
Codice 3.4: Implementazione di “ripristinaLogo:”
12345
6789
10111213
- (IBAction)ripristinaLogo:(id)sender { // controlliamo se il logo è stato eliminato if (cancellato) { cancellato = NO; // riportiamo il logo in posizione iniziale e svuotiamo il cestino [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.5]; imageLogo.transform = CGAffineTransformIdentity; imageCestino.image = [UIImage imageNamed:@”TrashIconEmpty.png”]; imageLogo.center = CGPointMake(158.0, 147.0); [UIView commitAnimations]; }}
Inquestometodoperprimacosacontrolliamoseillogoèstatocancellato(riga3):seilcontrollodaesitopositivo,settiamolavariabile“cancellato”a“NO”(riga4).Fattociòeseguiamol’animazioneperripristinareillogoesvuotareilcestino.Taleanima-zioneèdeltuttosimileallaprecedente,maeseguesolamentel’azionecontraria!
Abbiamoconcluso!Cliccatesu“Run”etestatelavostranuovaapplicazione!
c a P I t o l o 3Tr a s h A p p
44
Fig. 4.5b: App 3 “TrashApp”Fig. 4.5a: App 3 “TrashApp”
45
c a P I t o l o 4M u l t i B ro w s e r
4. MultiBrowser: creiamo un browser
Inquestocapitolovedremoun’aspettofondamentalenellacreazionediun’applica-zione,ovvero lagestionedipiùviste. Intutte leapplicazioni, infatti,sonopresentidiverseviste (“finestre”,perusareun terminepiùgenerico), chevengonospessocreateconInterfaceBuilder(equindidiversifile“.xib”)datalasuacomodità.Utilizzeremo,inoltre,uncomponentemoltocomodoepotente:leUIWebView,ovverodeglioggettichepermettonodicaricaredellepagineweb.
Creeremounasortadibrowser,incuicisaràunavistaprincipalecondeibottonicherimanderanno a determinate pagine web. Tali pagine verranno mostrate su un’altra finestracheverràapertaappositamente.Èimportanteseguirebenetuttiipassaggi,inmodochepossiatereplicarelastessastrutturaanchenellevostreapplicazionipiùcomplesse,senzaerroriesenzaperderetempoprezioso.
Creiamo la struttura graficaCreiamounnuovoprogettoditipo“SingleViewApplication”echiamiamolo“Multi-Browser”.Comesempreassicuriamociche“Use storyboard”e“Include Unit Test”siano disattivati.
Iniziamocreandolasemplicestrutturadellaprimavista.Apriamoquindiilfile“ViewCon-troller.xib”einseriamoduebottoni:
Fig. 4.1: Pulsanti per “M
ultiBrosw
er”
46
Dobbiamooracollegareibottoniallaclassecreandoleazionicorrispondenti,comeabbiamo più volte fatto. Abbiamo tre strade diverse percorribili:•Crearedueazionidistinteperognibottone,maquestaèlasoluzionepeggiore.
•Definiredueelementi(outlet)eunasolaazione,identificandopoiilbottonechehainvocatol’azioneinbasealsender(comeabbiamofattoneltutorialprecedente).
•Oppurec’èunaterzatecnica,checonsistenell’utilizzarelaproprietà“tag”.Questaèun’attributo (omeglio,unaproprietà)chequalsiasioggettodiUIKitpossiede,edèsemplicementeunvalorenumericocheloidentifica.Talevaloreperdefaultèzero,maassegnandovaloridiversiperognielementosaremoingradodidistin-guerli in maniera univoca.
Selezioniamo il primobottone e apriamo l’“Attributes Inspector”. Tra i vari campipresentinoteremoquellorelativoa“Tag”.Modifichiamoneilvalorea1:
Fig. 4.2: Attributes Inspector dei bottoni
Quindiilbottone“BubiDevs”avràlaproprietàtagimpostataa1.Facciamolastessacosa con il secondo bottone, ma impostando il tag a 2.Fattociònondobbiamofaraltrochecreareun’azionecherispondaallapressionedeiduebottoni.Comealsolito,creiamoun’azionepartendodalprimobottone(sem-preeseguendoilcollegamentoeselezionando“Action”dalpop-upcheappare),chechiameremo“apriSito”.Lavostraclasse“ViewController.h”saràquindicosìdefinita:
Codice 4.1: File di interfaccia ViewController.h”
1234567
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
- (IBAction)apriSito:(id)sender;
@end
47
Oradobbiamocollegare lastessaazioneancheall’altropulsante.Semprenelfile“ViewControlle.xib”selezionate il “File’sOwner”espostatevinel“Connections In-spector”.Avreteunpannellocosìcomposto:
Fig. 4.3: Connections Inspector del File’s Owner
Fig. 4.4: Collegamento eseguito correttamente
Prendiamoilpallinochetrovateadestradellascritta“Button-BubiDevs”etrascinia-molosulsecondobottone,selezionandopoi“Touch Up Inside”nelmenùatendinachesipresenta.Se avete eseguito correttamente il passaggio avrete il seguente risultato:
Abbiamocosìcollegatolastessaazioneanchealsecondobottone.Questoèilpro-cedimentochedeveessereeseguitoquandosicreaun’azione (ounoggetto)viacodice e lo si vuole poi collegare con un oggetto creato in IB.
c a P I t o l o 4M u l t i B ro w s e r
48
Creiamo la seconda vistaPrima di proseguire nell’implementazione della prima vista, creiamo la seconda.EssaconterràunasempliceUIWebView,chevisualizzeràlapaginawebasecondadel bottone premuto dall’utente.InXcode,scegliamo“File->NewFile...”eciappariràlaseguenteschermata:
Fig. 4.5: Creiamo il file per la seconda vista
Scegliamo“UIViewController”cometipologiadiclasse,inseriamo“BrowserViewCon-troller”comenomeeassicuriamocichesiaspuntatal’opzione“With XIB for user in-terface”.Nellaschermatasuccessivapremiamo“Create”.Abbiamocosìcreatounanuovavista,comprensivasiadiinterfacciagraficachedirelativaclasse.Apriamoilfile“BrowserViewController.xib”permodificarel’interfacciagraficacomenell’immaginepropostanellapaginasuccessiva(Fig.4.6).
49
Ilcomponentewebviewèquellochevisualizzeràlapaginawebdesiderata,edoc-cupa la maggior parte della vista. Nella parte bassa, invece, abbiamo inserito un componente di tipo UIToolbar e tre bottoni UIBarButtonItem, separando gli ultimi duetramiteun“Flexible Space Bar Button Item”.Questoèunelementochepermet-tediavereibottoniaidueestremidellatoolbar,senzadovercioccuparediimpostaredelle posizioni assolute. Tale componente, inoltre,mantiene i bottoni agli estremianchesel’interfacciavieneruotata.Iprimiduebottoniciservirannopermuovercitralepaginevisitate(iclassici“Avanti”e“Indietro”),mentreilterzoserviràpertornareallaschermataprincipale.Iniziamoimplementandoleazioniperibottoni“Avanti”e“Indietro”.Inquestocasonondovremofarepraticamentenulla,sioccupadituttolaUIWebView.Selezionatelaespostatevinel“ConnectionsInspector”:
Fig. 4.6: Struttura di B
rowserV
iewC
ontroller.xib
Fig. 4.7: Connessioni della UIWebView
c a P I t o l o 4M u l t i B ro w s e r
50
Comevedetesonogiàpresentivarieazioni.Collegate“goBack”conilbottone“<“(ovveroquellopertornareallapaginaprecedente”,e“goForward”conilbottone“>”.
Fig. 4.8: Connessioni per i bottoni “avanti” e “indietro”
Fatto!Questeazioniverrannoimplementateautomaticamentedallawebview,senzadoverscrivereunarigadicodice!
Cisonoancheduealtreazioni,cheservonoperricaricarelapaginaoperfermarneil caricamento. Noi non le abbiamo implementate, ma se volete creare un browser ancorapiùcompletonullavivietadiutilizzarle.
Fatto ciòdobbiamodefinire nella classeunoggettoper laweb vieweun’azionecollegataalbottone “Home”.Comesempre facciamociòcollegandogli elementidell’interfaccia con la classe.
Createquindiunoutletperlawebviewechiamatela“webView”,eun’azioneasso-ciataalbottonechiamata“tornaAdHome”.Seaveteeseguitotuttocorrettamentelavostraclasse“BrowserViewController.h”saràcosìcomposta:
Codice 4.2: File di interfaccia BrowserViewController.h
123456789
#import <UIKit/UIKit.h>
@interface BrowserViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIWebView *webView;
- (IBAction)tornaAdHome:(id)sender;
@end
51
Scriviamo il codice per aprire la pagina desiderataPrimadiproseguire,dobbiamodefinireunavariabile (con la relativaproperty)checonterràl’indirizzowebdellapaginadavisualizzare.Questavariabileverràimpostatadalla prima vista in base al bottone premuto dall’utente. Nelfile“BrowserViewController.h”inseriamoleseguentidichiarazioni:
Codice 4.3: Stringa dell’indirizzo web
Codice 4.4: Caricamento della pagina web
123456789101112
123456789101112
#import <UIKit/UIKit.h>
@interface BrowserViewController : UIViewController { NSString *indirizzo;}
@property (strong, nonatomic) NSString *indirizzo;@property (strong, nonatomic) IBOutlet UIWebView *webView;
- (IBAction)tornaAdHome:(id)sender;
@end
@synthesize webView;@synthesize indirizzo;
- (void)viewWillAppear:(BOOL)animated{ // creiamo un oggetto NSURL con l’indirizzo della pagina da caricare NSURL *url = [NSURL URLWithString:indirizzo]; // creiamo una richiesta per la pagina NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // visualizziamo la richiesta (la pagina) nella web view [webView loadRequest:request];}
Semplicemente abbiamodefinito una variabile di tipo stringa (riga 4) e la relativaproperty(riga7).Dobbiamoorainserireilcodicechesioccupadicaricarelapaginawebselezionatadall’utenteemostrarlanellaUIWebView.Utilizziamounmetodochevienerichiamatoquando la vista sta per apparire. Ecco il codice da inserire:
Allariga2abbiamoinseritoil“synthesize”necessarioperlapropertycheabbiamoprecedentementedefinito.Ilmetodo“viewWillAppear:”vienerichiamatoquandolavistastaperesserevisualizzata:èlicheinizializziamoglielementinecessaripervi-sualizzarelapaginawebdesiderata.Davvero semplice e veloce.
c a P I t o l o 4M u l t i B ro w s e r
52
Gestiamo il passaggio tra le due visteOra ci manca solo la gestione del passaggio tra le due viste. Per prima cosa ci occu-piamo del codice necessario per passare dalla prima vista alla seconda. Dovremo leggere quale bottone è stato premuto (controllando la proprietà tag) epassare l’indirizzocorrettoallasecondavista.Apriamo il file “ViewController.m”eandiamoadefinireilmetodo“apriSito:”:
Codice 4.5: Passaggio dalla prima alla seconda vista
1234
5
67891011121314
- (IBAction)apriSito:(id)sender{ UIButton *bottone = (UIButton*)sender; BrowserViewController *viewBrowser = [[BrowserViewController alloc] initWithNibName:@”BrowserViewController” bundle:nil]; viewBrowser.modalTransitionStyle = UIModalTransitionStyle-FlipHorizontal; if (bottone.tag == 1) { viewBrowser.indirizzo = @”http://www.bubidevs.net”; } else if (bottone.tag == 2){ viewBrowser.indirizzo = @”http://www.google.it”; } [self presentModalViewController:viewBrowser animated:YES];}
PerprimacosaabbiamoconvertitoilsenderinunoggettoditipoUIButton(comeabbiamo già fatto nel precedente capitolo), per poterne leggere la proprietà tag.
Allariga4abbiamoinizializzatolavista,inserendoancheilnomedelfile“.xib”chedeve essere caricato.
L’istruzioneallariga5,invece,cipermettediimpostarel’animazionedelpassaggiodallaprimaallasecondavista.Cisonotreanimazionigiàpredisposteedisponibili:•UIModalTransitionStyleCoverVertical, il caricamento della nuova vista sarà vertica-le,dalbassoversol’alto;
•UIModalTransitionStyleFlipHorizontal,c’èlarotazioneflip-side,ovverocomeseve-nissemostratoilretrodellavista;
•UIModalTransitionStyleCrossDissolve,dissolvenzaincrociata.
Troviamopoiun“if”chesettalavariabile“indirizzo”(lapropertycheabbiamodefi-nito nella classe BrowserViewController) a seconda del bottone premuto dall’utente. Potetevederecheciòvienefattocontrollandolaproprietàtag(righe7e9),propriocome avevamo detto.
L’ultimaistruzione(riga13)permettedimostrarelasecondavista,rendendolacosìvisibile all’utente.
53
Dopoaverinseritoquestocodice,viverràsegnalatounerroreallariga4(eanchealle successive). Questo avviene perché non c’è nessun riferimento alla classeBrowserViewController. Per correggere questo problema dobbiamo eseguire l’im-portazionedi taleclasse:semplicemente inseritequestadefinizioneall’iniziodelfile“ViewController.m”(sololariga2,lealtresonogiàpresenti):
Codice 4.6: Importiamo la classe BrowserViewController
Codice 4.7: Tornare alla prima vista
1234
123
#import “ViewController.h”#import “BrowserViewController.h”
@implementation ViewController
- (IBAction)tornaAdHome:(id)sender { [self dismissModalViewControllerAnimated:YES];}
Abbiamo quasi concluso! Non ci resta che definire ilmetodo che ci permetta ditornareallaprimavista.Apriamoilfile“BrowserViewController.m”emodifichiamoilmetodo“tornaAdHome:”
Questaunicaistruzionecipermettediusciredallavistacorrenteetornareaquellacheeravisualizzataprecedentemente(nelnostrocasolavista“ViewController”).
Abbiamoconcluso!Clicchiamosu“Run”etestiamoilnostromultibrowser!
c a P I t o l o 4M u l t i B ro w s e r
54
Fig. 4.8b: App 4 “MultiBrowser”Fig. 4.8a: App 4 “MultiBrowser”
55
c a P I t o l o 5Ta b l e V i e w Tu t o r i a l
5. UITableView: le tabelle
Ilcomponentepiùutilizzatonellosviluppodiapplicazionièsicuramentequellochegestisce le tabelle, ovvero laUITableView. Inmoltissimeapplicazioni, infatti, essevengonoutilizzatepermostraredelleinformazioniall’utente,chepuòancheintera-gire con questi dati.Inquestocapitolovedremocomedefinireunatabella,comeinserirealsuointernodeivaloriedefiniredeicomportamenti(adesempiolacancellazionedellerighe),necessariperrenderlautilizzabiledall’utente.Questocapitolosaràsuddivisointreparti:•Creazioneedefinizionedellatabella.
•Inserimentodialcunefunzionalità.
•Implementazionedellaricerca.Analizzeremoquestepartiunaallavolta,passoperpasso,inmododacomprenderebeneognisingolopassaggiocheandremoarealizzare.
Parte 1: Creiamo e definiamo la tabella
Laprimapartediquestotutorialèdedicataallacreazionedellastrutturadell’applica-zione:nedefiniremolagrafica,lalistadeglielementidavisualizzareecomemostrarliall’interno della tabella. Il tutto sarà fatto con la solita semplicità, sfruttando tutte le ca-ratteristichecheXcodeel’SDKcimettonoadisposizione,semplificandoilnostrolavoro.
Creiamo la strutturaCreiamounnuovoprogettoditipo“MasterDetailApplication”echiamiamolo“Ta-bleViewTutorial”.Comesempreassicuriamociche“Usestoryboard”e“IncludeUnitTest”sianodisattivati.QuestotemplatediXcodeciforniscegiàunatabellaelarelativaclassechelogesti-sce(“MasterViewController”).Aprendoilfile“MasterViewController.xib”visualizzere-telatabellagiàdefinitapervoi:
Fig. 5.1: MasterViewController.xib
56
Unatabellahadueelementifondamentali:il“dataSource”eil“delegate”.Seesaminate leconnessionidellatabellanoteretecheentrambequesteproprietàsonocollegateallaclasse“MasterViewController”,chelegestisce.Macosasono?•IldataSource, come dice il nome stesso, è la sorgente dei dati, e deve essere col-legatoconlaclassecheconterràidaticheandrannopoivisualizzatinellatabella.
•Ildelegate,invece,indicachirispondealdelegatodellatabella.Comevedremopoiessoforniscedeimetodimoltoutili,chefacilitano,adesempio,lamodificadellatabella da parte dell’utente.
Quandocreateunatabelladoveteassicurarvicheentrambeleproprietàsianocolle-gate con la classe preposta alla gestione della tabella.
Scriviamo il codice necessarioIniziamoaprendoilfile“MasterViewController.h”,edefiniamoilseguentecomponente:
Codice 5.1: Definizione della lista per il data source
Codice 5.2: Implementazione del “viewDidLoad”
1234567891011
12345678
9
#import <UIKit/UIKit.h>
@class DetailViewController;
@interface MasterViewController : UITableViewController { NSMutableArray *lista;}
@property (strong, nonatomic) DetailViewController *detailViewController;
@end
- (void)viewDidLoad { [super viewDidLoad]; // settiamo il titolo della vista self.title = @”Prodotti Apple”; // elementi della tabella lista = [[NSMutableArray alloc] initWithObjects: @”iPho-ne”, @”iPod”,@”iPod Touch”,@”iPad”,@”iMac”, @”iBook”, @”MacBook”, @”MacBook Pro”, @”Mac Pro”, @”PowerBook”, nil]; }
Abbiamodichiaratounarrayallariga6,checonterràglielementicheandremopoiavisualizzarenellatabella.L’elementoèditipo“mutable”,inquantoavremolaneces-sitàdimodificarneilcontenuto(adesempioquandol’utenteeliminaunariga).Notate,inoltre,cheèpresenteancheunoggettodellaclasse“DetailViewController”,cheèfornitodaltemplatediXcode.Taleclassepuòessereutilizzatapervisualizzarei dettagli di un oggetto, come vedremo più avanti.Spostiamocioranelfile“MasterViewController.m”.Iniziamoinserendoalcuneistru-zionichedevonoessereeseguitealcaricamentodellaclasse:
57
Abbiamosemplicementedefinitoduecosemoltosemplici.Allariga5abbiamoimpo-statoiltitoloallanavigationbar(questoèpossibilesoloquandosilavoraconquestotipoditemplate,cheèdefinitopartendodaunnavigationcontroller),mentreallariga8abbiamoinizializzatol’arrayconalcunielementi,cheandrannopoivisualizzatinellanostra tabella.
Oradobbiamodefinireimetodichesioccupanodipopolarelatabella.Scorrendoilfile“MasterViewController.m”litroveretegià(Xcodeliinserisceautomaticamentequandosiutilizzaquestotipoditemplate),dobbiamosolocompletarli.Iniziamodefinendoiseguentiduemetodi:
Codice 5.3: Metodi per la definizione della tabella
123456
78
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1;}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [lista count];}
Ilprimocheincontriamoè“numberOfSectionsInTableView:”.Seprovateamodificarequestovalore,vedreteche (quandoeseguirete l’applicazione) la listadeglioggettisaràripetutapiùvolte.Provarepercredere!Acosaserve?Nelcasoincuivogliatecreareunatabellasuddivisainpiùsezioni,adesempiocomequelladell’applicazionenativa“Contatti”,chesuddivideglielementiinordinealfabetico.Nelnostrocaso,adesempio,avremmopotutocrearedellediversesezionipericomputerportatilieperquellifissi.
Ilsecondometodoè“tableView: numberOfRowsInSection:”esettailnumerodicelleinognisezione(nelnostroesempioavremounasolasezione).Questovaloredevecorrisponderealnumerodielementichevogliamoinserirenellatabella,quindiquellicontenutinell’oggetto“lista”.
Dobbiamooradefinireilmetodochesioccupadiimpostarelevariecellechecom-pongonolatabella.Nellapaginasuccessivatrovereteilmetodoinquestione(anchequesto lo trovate già nella vostra classe).
c a P I t o l o 5Ta b l e V i e w Tu t o r i a l
58
Questoè ilmetodo (fondamentaleedobbligatorio)chesioccupadi impostare inmaniera corretta le celle della tabella.
Allariga4troviamoladichiarazionediunacella,chevienepoiinizializzataallariga6.
Alla riga 10 settiamo il valore contenuto nella cella: esso deve essere letto dall’array, nellaposizioneugualeaquelladellariga(ricordatevichesialanumerazionedell’ar-raychedellecellepartedazero).
Inquestocasoabbiamoimpostatounacellamoltosemplice,checontienesolounvalorealsuointerno(impostatoappuntotramitelaproprietà“textLabel”).Questostiledicellaè“UITableViewCellStyleDefault”,comepossiamovederenell’i-struzioneallariga6.Ilrisultatosaràilseguente:
Codice 5.4: Definizione delle celle
1
234
56
789101112
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @”Cell”; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; }
// Configure the cell. cell.textLabel.text = [lista objectAtIndex:indexPath.row]; return cell;}
Fig. 5.2a: UITableViewCellStyleDefault
59
Sonodisponibili,però,anchealtri stilidi cellecheovviamentepoteteutilizzare inbaseallevostreesigenze.Eccoveli:
•UITableViewCellStyleSubtitle:
Fig. 5.2b: UITableViewCellStyleSubtitle
Fig. 5.2c.: UITableViewCellStyleValue1
Fig. 5.2d: UITableViewCellStyleValue2
•UITableViewCellStyleValue1:
•UITableViewCellStyleValue2,
In questi diversi stili di cella potete accedere al secondo campo di testa tramite la proprietà“detailTextLabel”,propriocomeavvieneper“textLabel”.Quindi,perotte-nere un risultato come quello mostrato dovrete inserire il seguente codice:
Codice 5.5: Utilizzo della proprietà “detailTextLabel”
123
// Configure the cell.cell.textLabel.text = [lista objectAtIndex:indexPath.row];cell.detailTextLabel.text = @”Apple”;
c a P I t o l o 5Ta b l e V i e w Tu t o r i a l
60
Sieteprontipertestarelavostratabella!
Cliccatesu“Run”econtrollatechetuttofunzioni!
Fig. 5.3a: App 5 Beta “TableViewTutorial”
61
Parte 2: Inseriamo alcune funzionalità
Abbiamofinoadoravistoqualisonoipassifondamentaliperdefinireunatabella.Inquestasecondaparteaggiungeremoalcunefunzioni,adesempiolapossibilitàdieliminareunadeterminatariga.Analizzeremo,inoltre,comevienegestitalaselezionee l’apertura della vista di dettaglio.
Permettiamo la cancellazione di una rigaLaprimacosadafareèinserireilbottonechepermettediaccedereallamodificadellatabella.Perfareciòcibastainserireunasempliceistruzioneenelmetodo“viewDidLoad”:
Codice 5.6: Permettiamo la cnacellazione di una riga
123
101112
- (void)viewDidLoad { [super viewDidLoad]; ... // permettiamo la modifica di una cella self.navigationItem.rightBarButtonItem = self.editButtonItem;}
Semplicementeassociamoalbottonedestrodellanavigationbarilbottonecheabi-litalamodificadell’elemento.Ancheinquestocasoètuttomoltosemplice,graziealle API fornite da Apple.
Seprovateadeseguirel’applicazionenoteretechecancellandounarigaessanonvienerimossadallatabella.Questoavvieneperchénonabbiamoancoraimplemen-tato il codice vero e proprio per eliminare un elemento della tabella. Scorreteilvostrofile“MasterViewController.m”ecercateilmetodo“tableView: com-mitEditingStyle: forRowAtIndexPath:”,chetroveretecommentato.Decommentateloeinseritel’istruzioneallariga5:
Codice 5.7: Cancellazione di un elemento
12
34567
89
1011
// Override to support editing the table view.- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // eliminiamo l’elemento dalla lista [lista removeObjectAtIndex:indexPath.row]; // eliminiamo la cella dalla tabella [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; } else if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view. } }
c a P I t o l o 5Ta b l e V i e w Tu t o r i a l
62
Come possiamo notare dalla struttura un po’ complicata, questo è un metodo del protocolloUITableView.Ilcostrutto“if”(riga3)controllaseiltipodiazioneeseguitasullatabellaèlacancellazionediunariga.Diretevoi,cos’altroposso fare?Peroraniente,perché lanostra tabellasupportasolol’eliminazionediunariga,mavolendosipotrebbeimplementareanchel’inseri-mentodiunanuovariga(edèilcontrolloallariga8),oppurealtreazioni.Èsemprequestometodochesioccupadigestiretutteleazionisullatabella.
Eccospiegatalanecessitàdiquestocontrollo.Tornandoalcodice,all’internodell’“if”possiamonotaredueistruzioni,cheeliminanol’elementosiadallalista(riga5)chedal-latabella(riga9).Inquestomodolacancellazionediunarigaèdavveroimplementata.
Codice 5.8: Metodo che gestisce la selezione di una cella
1234
567
89
10
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (!self.detailViewController) { self.detailViewController = [[DetailViewController alloc] initWithNibName:@”DetailViewController” bundle:nil]; } // passiamo l’oggetto selezionato self.detailViewController.detailItem = [lista objectAtIndex:indexPath.row]; // apriamo la vista [self.navigationController pushViewController:self.detail-ViewController animated:YES];}
Nota: Fate attenzione a rimuovere prima l’elemento dalla lista e poi dalla tabella: invertendo queste due istruzioni otterreste un crash della vostra applicazione!
La vista di dettaglioVisareteprobabilmentegiàaccorticheiltemplatechestiamoutilizzandoforniscegiàun’implementazioneperlavistadidettagliodiognicella.Essaèunavistagene-rica,chevieneapertaeconfigurataasecondadell’elementoselezionatodall’utente.Seprovateaselezionareunqualsiasielemento,infatti,verràapertalastessavistadidettaglio,cheperòmostrasemprelostessomessaggio.VediamooracomeèstataimplementatadaAppleecomemodificarlaperrenderlaadattaainostriscopi.
Quandol’utenteselezionaunacellavienerichiamatoilseguentemetodo(chetrovategiàdefinitonellaclasse“MasterViewController.m”):
Ilmetodoègiàfunzionante,dovetesolo inserire l’istruzioneallariga7,chepassal’oggetto selezionato (in pratica solo una stringa con il nomedel prodotto sceltodall’utente) alla vista di dettaglio.
63
Questometodoprima inizializza la vista di dettaglio (se essa non è ancora statainizializzata),poilavisualizza.Noteretechel’istruzionepervisualizzarelavistaèdi-versadaquellautilizzatanelcapitoloprecedente:questoperchéstiamolavorandoinunnavigationcontroller,chehauncomportamentodiversorispettoallastrutturaprecedente.L’istruzione,comunque,èanch’essasempliceeimmediata.
Praticamenteabbiamogiàconcluso!Grazieadunasolaistruzionelavistadidetta-gliomostreràalcentroilnomedell’elementoselezionatodall’utente.
Poteteovviamentemodificareavostropiacimentolaclasse“DetailViewController”,chegestisceappuntolavistadidettaglio.
Fig. 5.3c: App 5 Beta “TableViewTutorial”Fig. 5.3b: App 5 Beta “TableViewTutorial”
c a P I t o l o 5Ta b l e V i e w Tu t o r i a l
64
Parte 3: Implementiamo la ricerca
Nella terza e ultima parte di questo lungo tutorial dedicato alle tabelle, vedremocome aggiungere un box di ricerca. Il comportamento sarà del tutto simile a quello dellaricercapresentenell’applicazionenativa“Contatti”.Vedretechequestaoperazionesiriveleràmoltosemplice,infattiApplehalavoratomoltosuquestoaspettoconleultimeversionidelSDK.
Aggiungiamo il box di ricercaApriamoilfile“MasterViewController.xib”permodificarlo:dovremoinserirelanostrabarradiricerca.Dalla“Library”selezioniamouncomponente“SearchBarandSe-archDisplayController”,etrasciniamolosopral’elemento“TableView”,comemo-strato nella seguente immagine:
Fig. 5.4: Inseriamo la barra di ricerca “Search Bar and Search Display Controller”
65
Seaveteeseguitotuttocorrettamentelavostratabellamostreràoraancheunabarradi ricerca:
Fig. 5.5: Barra di ricerca
Abbiamoconclusolapartegrafica,possiamooradedicarcialcodice!
Modifichiamo i metodi già esistentiPrimadiimplementarelaricercaveraepropria,dobbiamofaredellepiccolemodifi-cheaimetodigiàpresentinelnostroprogetto.
Dobbiamo,perprimacosa,definireunanuovalista,checonterràglielementi“filtrati”,ovveroquellichecorrisponderannoaicriteridiricerca.
Apriamoilfile“MasterViewController.h”einseriamolaseguentedefinizione(riga7):
Codice 5.9: Lista per gli elementi filtrati
12345678910
1112
#import <UIKit/UIKit.h>
@class DetailViewController;
@interface MasterViewController : UITableViewController { NSMutableArray *lista; NSMutableArray *listaFiltrata;}
@property (strong, nonatomic) DetailViewController *detailViewCon-troller;
@end
c a P I t o l o 5Ta b l e V i e w Tu t o r i a l
66
Oradobbiamomodificare ilmetodo “viewDidLoad”, inmodoche inizializzi anchequesta nuova lista. Spostiamociin“MasterViewController.m”emodifichiamotalemetodo:
Codice 5.10: Inizializzazione della nuova lista
12345678
9101112131415
- (void)viewDidLoad { [super viewDidLoad]; // settiamo il titolo della vista self.title = @”Prodotti Apple”; // elementi della tabella lista = [[NSMutableArray alloc] initWithObjects: @”iPho-ne”, @”iPod”,@”iPod Touch”,@”iPad”,@”iMac”, @”iBook”, @”MacBook”, @”MacBook Pro”, @”Mac Pro”, @”PowerBook”, nil]; // elementi per la lista filtrata listaFiltrata = [[NSMutableArray alloc] initWithArray:lista]; // permettiamo la modifica di una cella self.navigationItem.rightBarButtonItem = self.editButtonItem;}
Semplicementeabbiamoaggiuntol’istruzioneallariga11,cheinizializzalalistafiltrataconglistessielementidi“lista”. Inizialmentequesta listasaràugualealla listadeglielementi,everràmodificatamanmanochel’utenteinserisceiltestodaricercare.Questa lista, ora, sarà il nuovo dataSource, infatti la tabella verrà popolata in base al contenutodellalistafiltrata,mentrel’altralistaserviràsoloperripristinarelatabellaalsuostatonormale(ovverosenzaricerca).
Modifichiamo, quindi, ilmetodo “tableView: numberOfRowsInSection:”, che deveadattarsi a quanto appena detto:
Codice 5.11: Modifica del metodo che imposta il numero di elementi della tabella
Codice 5.12: Modifica del metodo che definisce il contenuto delle celle
1
23
12
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [listaFiltrata count];}
// Configure the cell.cell.textLabel.text = [listaFiltrata objectAtIndex:indexPath.row];
Latabella,quindi,conterràunnumerodielementiparialladimensionedellalistafiltrata.Modifichiamoanchel’istruzionenelmetodo“tableView:cellForRowAtIndexPath:”:
67
E,diconseguenza,ancheilmetodo“tableView:didSelectRowAtIndexPath:”
Codice 5.14: Cancellazione degli elementi in “lista”
Codice 5.13: Modifica del metodo che gestisce la selezione di una cella
123456789
12
// eliminiamo l’elemento dalla lista originale- (void)rimuoviElemento:(NSString*)item{ for (int i=0; i<[lista count]; i++) { NSString *elemento = [lista objectAtIndex:i]; if ([elemento compare:item] == NSOrderedSame) { [lista removeObjectAtIndex:i]; } }}
// passiamo l’oggetto selezionatoself.detailViewController.detailItem = [listaFiltrata objectAtIndex:indexPath.row];
Codice 5.15: Modifica del metodo che risponde alle cancellazioni
123
45678
9
if (editingStyle == UITableViewCellEditingStyleDelete) { // rimuovo dalla lista originale [self rimuoviElemento:[listaFiltrata objectAtIndex:indexPath.row]]; // eliminiamo l’elemento dalla lista [listaFiltrata removeObjectAtIndex:indexPath.row]; // eliminiamo la cella dalla tabella [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];}
Dobbiamoandareancheamodificareilmetodo“tableView:commitEditingStyle:forRowAtIndexPath:”.Questohabisognodialcuneaccortezzeinpiù,inquantopuòprodurregrossiproblemisenonvieneimplementatoconattenzione.Comeabbiamovisto,talemetodorispondeallecancellazionichel’utenteesegue.Inquestocaso,l’utenteandràadeliminaredeglielementidi“listaFiltrata”:ciòpuòavveniresiaquan-dovieneeseguitaunaricerca,siaquandolatabellaèvisualizzatainmanieranorma-le.Avremobisogno,quindi,diunmetodosupplementare,chesioccupidipropagarelacancellazioneancheall’oggetto“lista”.Ilmetododainserireèilseguente:
Semplicementequestometodoricevecomeparametrol’elementochedeveesserecancellato e scorre l’intra lista per eliminare ogni sua occorrenza.Nonpossiamoragionaresugliindici(comeeravamoabituati)inquandolacancellazionepotrebbeancheavveniredaunaricerca,equindil’indicecheciverrebbepassatononcorri-sponderebbealrealeelementopresentein“lista”.Inserite,ovviamente,anchel’intestazionedelmetodonelfile“MasterViewController.h”.Orapossiamofinalmentemodificareilmetodo“tableView:commitEditingStyle:for-RowAtIndexPath:”(riportosololeistruzionichesubisconodellemodifiche):
c a P I t o l o 5Ta b l e V i e w Tu t o r i a l
68
Codice 5.16: Implementazione dell’algoritmo di ricerca
1
23456
78910
111213141516
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope{ // rimuoviamo tutti gli elementi dalla lista filtrata [listaFiltrata removeAllObjects]; // controlliamo tutti gli elementi della lista se corrispondono alla stringa di ricerca NSString *elemento; for (elemento in lista){ // eseguiamo il confronto NSComparisonResult result = [elemento compare:searchText options:NSCaseInsensitiveSearch range:NSMakeRange(0, [searchText length])]; // controlliamo se l’elemento corrisponde alla ricerca if (result == NSOrderedSame){ [listaFiltrata addObject:elemento]; } }}
Comevedeteprimadieliminarel’oggettoda“listaFiltrata”richiamiamoilmetodocheloeliminadall’oggetto“lista”(riga3).Fattociòl’elementovieneeliminatodallalistafiltrataepuòesserepoiaggiornataanchelatabella.Questemodifichesononecessarie,accertatevidieseguirletutteinmanieracorretta.
Implementiamo la ricercaÈarrivatoilmomentodiimplementarelaricercaveraepropria.Iniziamoinserendoall’internodi“MasterViewController.m”l’algoritmodiricercaveroeproprio:
Icommentipresentinelcodicepossonogiàdarviun’indicazionesucometaleme-todofunzioni,mavediamoloneldettaglio.Perprimacosavengonoeliminatituttiglielementipresentinella listafiltrata (riga4), incuiverranno inseriti inuovielementicorrispondentiallastringadiricerca.Conilcilco“for”(riga8)vengonoesaminatiunoadunoglielementidi“lista”,ecomparaticonlastringadiricerca(cheèl’elemento“searchText”).Sel’elementocorrispondeallastringadiricercaessovieneaggiuntoallalistafiltrata(riga13).Questoalgoritmoprevedechel’elementodevecoincidereconlastringadiricerca(ocomunquel’iniziodell’elementodevecoincidereconlaricerca).Seavetenecessitàdifferenti,dovretemodificarelacomparazionetraidueelementiinbaseallevostreesigenze.
Dobbiamoora inserireunmetodo relativoal searchdisplaycontroller.Essovienerichiamatoadognicarattereinseritodall’utentenellabarradiricerca,ecipermettediaverequell’effettodiricerca“indiretta”,ovveroconirisultatichevengonofiltratimanmanochel’utentedigitailtesto.
69
Ecco il metodo da inserire:
Codice 5.17: Metodo per eseguire la ricerca ad ogni carattere inserito
1
2
345
- (BOOL)searchDisplayController:(UISearchDisplayController *)control-ler shouldReloadTableForSearchString:(NSString *)searchString{ [self filterContentForSearchText:searchString scope: [[self.sear-chDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]]; // Return YES to cause the search result table view to be reloaded return YES;}
Questometodononfaaltrocherichiamare,ognivoltachevieneinseritouncarattere,l’algoritmodiricercacheabbiamoscrittoinprecedenza.
Mancaunultimometodo, cheviene invece invocatoquandosi annulla la ricerca(cliccandosulbottone“Cancel”osulla“x”nelboxdiricerca):
Codice 5.18: Annullare la ricerca
12345
- (void)searchBarCancelButtonClicked:(UISearchBar *)saearchBar { [listaFiltrata removeAllObjects]; [listaFiltrata addObjectsFromArray:lista]; [self.tableView reloadData];}
Quando un utente annulla una ricerca semplicemente ripristiniamo la tabella con gli elementidilista,chedevonoessereriassegnatiancheallalistafiltrata.
Abbiamoconcluso!Cliccatesu“Run”etestatelavostranuovaapplicazione!
c a P I t o l o 5Ta b l e V i e w Tu t o r i a l
70
Fig. 5.6b: App 5 “TableViewTutorial”Fig. 5.6a: App 5 “TableViewTutorial”
71
c a P I t o l o 6A c c e s s C o n t a c t
6. AccessContact: utilizziamo i contatti
In questo capitolo andremo ad implementare una funzione un po’ particolare, inquantoaccederemoall’applicazionenativa“Contatti”ericaveremole informazionidi un contatto.
Impareremoadutilizzareilframework“AddressBook”,chemetteadisposizioneleclassielefunzioninecessarieperinteragireconlarubricadiiOS.
Creiamo la struttura graficaCreiamounnuovoprogettodi tipo “SingleViewApplication”echiamiamolo “Ac-cessContact”.Comesempreassicuriamoci che “Use storyboard”e “IncludeUnitTest”sianodisattivati.
Iniziamo creando la struttura dell’unica vista che compone la nostra applicazione.Apriamoquindiilfile“ViewController.xib”edefiniamounastrutturacomelaseguente:
Fig. 6.1: Struttura applicazioni
Sonotutticomponenticheabbiamogiàutilizzato,quindinondovresteaverenessunproblema.C’èunaimageviewperl’immaginedelcontatto,6label(treincuisem-plicementec’èscrittoilnomedelcampo,ealtretrecheospiterannoilvaloreveroeproprio) e un bottone.
Unapiccolaattenzione lamerita la labelchedeveospitareglieventualinumeriditelefono del contatto.
Talelabeldevepotercontenerepiùrighe,quindidovetefareinmodocheoccupipiùspazio(comepoteteosservareinfigura6.2propostanellapaginasuccessiva).
72
Inoltrenell’“AttributesInspector”impostateilvaloredi“Lines”a4,inmodochetalelabelpossaospitarefinoaquattrolinee:
Fig. 6.2: Dimensione della label Telefono
Fig. 6.3: Attributes Inspector della lable
Dobbiamo ora collegare gli elementi creati alla classe. Collegate le tre label e la ima-geview,creandoirispettivioutlet.Createancheilcollegamentoconilbottone,defi-nendoun’azione“apriContatto”.Lavostraclasse,quindi,dovràesseredefinitacomenel codice proposto nella pagina successiva.
73
Conclusa la fasedidefinizionedell’interfacciapossiamopassareallascritturadelcodice.
Scriviamo il codice necessarioPrimadiprocedere,dobbiamoimportareilframework“AddressBook”all’internodelnostroprogetto.Per fareciòselezioniamo ilprogettoespostiamocinellasezione“Build Phases”:
Codice 6.1: File di interfaccia ViewController.h
123456789101112
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIImageView *immagine;@property (strong, nonatomic) IBOutlet UILabel *labelNome;@property (strong, nonatomic) IBOutlet UILabel *labelCognome;@property (strong, nonatomic) IBOutlet UILabel *labelTelefono;
- (IBAction)apriContatto:(id)sender;
@end
Fig. 6.4: Sezione “Build Phases” del progetto
Espandiamopoiilgruppo“Link Binary With Libraries”.Quivedremoiframeworkchesonogiàassociatialnostroprogetto(nelnostrocasocisonosolamentequelliessen-zialichetutteleapplicazionidevonoavereperfunzionarecorrettamente).
c a P I t o l o 6A c c e s s C o n t a c t
74
Clicchiamosulbottone“+”,siapriràunelencodituttiiframeworkdisponibili:
Fig. 6.5: Framework disponibili
Fig. 6.6: Framework aggiunti
Selezioniamo“AddressBook.framework”e“AddressBookUI.framework”eclicchia-mosu“Add”.Abbiamocosìaggiuntoquestidueframeworkalnostroprogetto:orapotremoutilizzarelefunzionicheessicimettonoadisposizione.
Possiamoorainiziareadefinireilcomportamentodellanostraapplicazione.Perprimacosadobbiamofarealcunemodificheall’intestazionedellaclasse“ViewController”:
Codice 6.2: Inserimento del delegato e delle classi necessarie
12345
#import <UIKit/UIKit.h>#import <AddressBook/AddressBook.h>#import <AddressBookUI/AddressBookUI.h>
@interface ViewController : UIViewController <ABPeoplePickerNaviga-tionControllerDelegate>
75
Abbiamoimportatoledueclassi(contenuteneiframeworkcheabbiamoinseritopocofa)checisarannonecessarieperutilizzareicomponentinecessari(righe2e3),inoltreabbiamo inserito anche il delegato “ABPeoplePickerNavigationControllerDelegate”.Essosarànecessarioperlarealizzazionedell’applicazionechevedremofrapoco.
Spostiamocinelfile“ViewController.m”einiziamoascrivereilcodice.Iniziamodalmetodo“apriContatto”,chetroveretegiàdefinito:
Codice 6.3: Implementazione dell’azione “apriContatto:”
123
45678
- (IBAction)apriContatto:(id)sender { //Crea il picker ABPeoplePickerNavigationController *picker = [[ABPeoplePicker-NavigationController alloc] init]; //Imposta il delegate del picker come vista principale picker.peoplePickerDelegate = self; //Visualizza il picker [self presentModalViewController:picker animated:YES];}
Analizziamoconcalmailcodiceappenaproposto.Perprimacosaabbiamodefinitouncomponente“ABPeoplePickerNavigationController” (riga3):questoè l’oggettochepermettediinteragireconicontattipresentineldispositivo.Verrà,infatti,apertaunanuovavista,lastessachevieneutilizzatenell’applicazione“Contatti”.Ingergoquestocomponentevienespessochiamato“picker”.Nell’istruzionesuccessiva(riga5)impostiamochesialaclassecorrentearisponderealdelegato.Vedremofrapocoqualisonoimetodidadefinireperimplementarecorrettamentetaledelegato.L’ultima istruzione (riga7)è lastessacheabbiamoutilizzatonellocapitolo4,e faapparire la vista del picker.
Dobbiamooradefinirealcunimetodi,necessariinquantolanostraclassesièpresailcompitodirisponderealdelegatodelpicker.Imetodicheimplementeremovengonorichiamatiquandol’utenteeseguedeterminateazioni,vedremoquali.
Iniziamoinserendoilseguentemetodo“peoplePickerNavigationController: shouldCon-tinueAfterSelectingPerson:”(codiceriportatonellapaginasuccessiva).
c a P I t o l o 6A c c e s s C o n t a c t
76
Questometodovienerichiamatoquandol’utenteselezionaunprecisocontatto.Sioccupa,comepotretenotare,dileggereidatidelcontattoedivisualizzarlinellelabelapposite.Analizziamoalcunedelleistruzioniprincipali.Allariga4leggiamoilnomedelcontatto,sfruttandolaproprietà“kABPersonFirstNa-meProperty”.Essavieneconvertitainstringaeassegnatapoiallalabel.Lostessoprocedimentoèfattoperilcognome.Ovviamentepotetericavareanchealtreinfor-mazionidelcontatto,adesempioilsoprannome,iltitolo,etc.Potetetrovarel’elencocompletodelleproprietàmesseadisposizionedallaclassenelladocumentazioneufficiale.Allariga8ricaviamoinumeriditelefonodelcontatto(lostessoprocedimen-toavremmopotutofarlocongliindirizzimail).Essivengonolettieinseritiinunarray(riga9),inquantopossonoesserediversinumeri.Prima di impostare la label dobbiamo eseguire un controllo: se nessun numero è pre-sente(equindil’arrayhadimensionepariazero)nonpossiamosettarenessunvalorenellalabel,quindicilimitiamoadinseriredeisemplicitrattini(riga13).
Codice 6.4: Metodo per rispondere alla selezione di un contatto
12
34
56
78
9
101112131415161718
192021222324252627282930
- (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigation-Controller *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person { // ricaviamo e impostiamo il nome labelNome.text = (__bridge NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty); // ricaviamo e impostiamo il nome labelCognome.text = (__bridge NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty); // ricaviamo i numeri di telefono ABMultiValueRef multi = ABRecordCopyValue(person, kABPer-sonPhoneProperty); NSArray *numeri = (__bridge NSArray*)ABMultiValueCopyArrayOfAllValues(multi); if ([numeri count] == 0) { // nessun numero presente per il contatto labelTelefono.text = @”---”; } else { labelTelefono.text = @””; // stampiamo tutti i numeri di telefono for (int i=0; i < [numeri count]; i++) { labelTelefono.text = [NSString stringWithFor-mat:@”%@ \n%@”,labelTelefono.text,[numeri objectAtIndex:i]]; } } // ricaviamo e visualizziamo l’immagine del contatto NSData *imageData = (__bridge NSData*)ABPersonCopyImageData(person); immagine.image = [UIImage imageWithData:imageData]; //Rimuove il controller “Contatti” [self dismissModalViewControllerAnimated:YES]; return NO;}
77
Sesonopresentideinumeri,invece,lileggiamotramiteunciclofor(riga17)eliinse-riamo tutti nella label apposita.Infine,l’ultimocampocheandiamoaleggereèl’immaginedelcontatto:essavienelettacomeNSData,cheèunparticolareoggettoper larappresentazionedeidati.VienepoiconvertitainUIImageeassegnataallaimageview(riga24).Inquestocasonondobbiamoeseguirenessuncontrollo,perchèsenonèpresentenessunaimma-gineavremounvalore“null”allavariabilechenoncreaproblemiall’applicazione.L’istruzioneallariga27,infine,rimuoveilpickerdallavista,maanchequestaèun’i-struzionegiàvista.
Abbiamopraticamente scritto laparte fondamentaledellanostraapplicazione!Cirestano solo due veloci metodi da inserire:
Codice 6.5: Altri metodi del delegato del picker
1
2345
678
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{ return NO;}
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker { // facciamo tornare il controller alla vista principale [self dismissModalViewControllerAnimated:YES];}
Ilprimoèuncomportamentochevogliamodefinire:vogliamochequandol’utenteselezionauncontattononvenganoapertiidettaglidelcontattostesso,mailcon-trolloritorniallanostraapplicazione.Se,invece,voletepermettereall’utentedisele-zionaresolounparticolarecampodelcontattodoveteinserire“YES”comevalorediritorno per questo metodo.
Ilsecondometodo,invece,vienerichiamatoquandol’utenteescedalpickersenzaeffettuarenessunascelta,ovveropremendoiltasto“Cancel”.Ovviamentenondo-vremoeseguirenessunaapplicazione,masemplicementerimuovereilpicker.
Abbiamoconcluso!Cliccatesu“Run”etestatelavostranuovaapplicazione!
c a P I t o l o 6A c c e s s C o n t a c t
78
Fig. 6.7: App 6 “AccessContact”
79
c a P I t o l o 7X M L Tu t o r i a l
7. XML
Dopo aver visto la maggior parte dei componenti principali, vedremo nei prossi-miduecapitolicomeinteragireconidatidavisualizzarenellanostraapplicazione.Inizieremodaunatecnologiamoltoutilizzata,specialmenteinambitoweb:stiamoparlando di XML.
Vedremo,quindi,comeleggereunfileXML,dacuiricavaredelle informazionicheabbiamosalvato.Questaoperazioneverràeseguitainlocale(ovveroilfilexmlsaràall’internodelnostroprogetto),manullavietadiavereilfilecaricatosuunserverwebdacuiaccederetramitelanostraapplicazione.
Cosa è XMLXMLèunmetalinguaggiodimarkup(attenzione,nonèpropriamenteunlinguaggiodiprogrammazione),ovverounlinguaggiocheconsentediestendereocontrollareilcomportamento di altri linguaggi. Il linguaggio di markup più famoso è sicuramente l’HTML,chehamolteanalogieconXML.
XML è l’acronimo di eXtensible Markup Language, da cui possiamo capire la carat-teristicafondamentalediquestolinguaggio:cipermettedicrearetagpersonalizzati,inbaseallenostreesigenze.
Saràpiùsemplicecomprenderecomefunzionaquestolinguaggiomedianteunesempio:
XML 1: Esempio
<?xml version=”1.0” encoding=”ISO-8859-1”?><studenti> <studente> <matricola>000001</matricola> <cognome>Rossi</cognome> <nome>Paolo</nome> </studente> <studente> <matricola>000002</matricola> <cognome>Verdi</cognome> <nome>Francesco</nome> </studente></studenti>
LaprimarigadefiniscelaversionediXMLinusoelacodificautilizzata(secondolenormeISO).Dallasecondarigainpoi,invece,troviamodeitagpersonalizzati,chevanno a modellare dei dati a nostro piacimento.
Possiamovederecomeabbiamodefinitountaggenerale“studenti”,chevieneiniziatoallasecondarigaeconclusoall’ultima.Nelmezzotroviamo,invece,altritag,cheripor-tanoleinformazionichevogliamomemorizzare,perpoiutilizzarleanostropiacimento.
80
Ci sono alcune piccole regole da rispettare nella struttura XML:
1)Itagnonpossonoiniziareconnumeriocaratterispecialienonpossonocontenerespazi:
•corretti:<nome>,<cognome>,<dataDiNascita>•errati:<&soldi>,<12peso>,<annodinascita>
2)Itagdevonoesserebilanciati(ognitagapertodeveesserechiuso):•corretto:<nome>Andrea</nome>•errato:<nome>Andrea
3) I tag non devono contenere errori di annidamento.Ecco alcuni esempi errati:
XML 2: Esempi errati
XML 3: Esempio
<rubrica> <nome>Mario</nome> <cognome>Rossi</rubrica>
<rubrica> <nome>Mario</nome> <cognome>Rossi</rubrica></cognome>
<rubrica> <nome>Mario</nome> <cognome>Rossi</COGNOME></rubrica>
<studenti> <studente> <matricola value=”000001” /> <cognome value=”Rossi” /> <nome value=”Paolo” /> </studente> <studente> <matricola value=”000002” /> <cognome value=”Verdi” /> <nome value=”Francesco” /> </studente></studenti>
4)Sipossonoinseriredegliattributiall’internodeitag;lastrutturasaràquindilase-guente:<nometagattributo1=“valore1”attributo2=“valore2”>Valore</nometag>Inostrielementi,quindi,potrannoesserescrittianchenellaseguentemaniera:
81
Questastrutturaèdeltuttougualeaquellaprecedente.Notatecheinquestocasonon abbiamo usato il tag di chiusura,ma abbiamo inserito “/” all’interno del tagstessoproprioper indicarechequel tagnonhaelementodichiusura.Quellochecambierà sarà solamente il modo di leggere i valori via codice.
XML nella programmazione per iOSPer ora abbiamo fatto una panoramica generale su XML, presentando gli aspetti fondamentali di tale linguaggio. Ma come possiamo integrarlo con le nostre applica-zioni?L’oggettochesioccupadirecuperareidatidaunfileXMLvienedettoparser.
Esistonovaritipidiparser(diversiperlinguaggietecnologie),quellochenoiandre-moadutilizzare rientranellacategoriadeiSAX.Lacaratteristica fondamentalediquestoparserstanelfattocheprocessaidocumentilineaperlinea:datiacuisièaccedutoinprecedenzanonpossonoessererilettisenzalarielaborazionedell’interodocumento.Puòessereunosvantaggio,mailparserdisponibilenelSDKlavorainquesto modo.
Andremooraarealizzareun’applicazionemoltosemplice,chevisualizzeràsulloscher-moidatilettidalunfileXMLpresentenell’applicazionestessa.Nulladicomplesso,quindi,maèunbuonmodoperprendereunpo’diconfidenzaconXML.
Creiamo la struttura graficaCreiamounnuovoprogettoditipo“SingleViewApplication”echiamiamolo“XML-Tutorial”.Comesempreassicuriamociche“Usestoryboard”e“IncludeUnitTest”siano disattivati.
Iniziamocreandolastrutturadell’unicavistachecomponelanostraapplicazione.Apriamoquindiilfile“ViewController.xib”edefiniamounastrutturacomelaseguente:
Fig. 7.1: Struttura di “XMLTutorial”
c a P I t o l o 7X M L Tu t o r i a l
82
Semplicementec’èunbottoneeunatextview,componentechenonabbiamomaivisto.Essapermettedivisualizzaregrandiporzioniditesto,graziealloscrollgiàpresentenelcomponente.Permette, inoltre,dimodificare il testocontenutoalsuo interno,comeunasortadieditor.Questa,però,nonèunafunzionalitàchevogliamonellano-straapplicazione,quindiselezioniamotalecomponenteenell’”AttributesInspector”togliamolaspuntaallaproprietà“Editable”:
Fig. 7.2: Deselezione di “Editable”
Creiamo,ora,icollegamenticonlaclasse“ViewController”.Definiamounoutletperlatextviewechiamiamola“viewTesto”,eun’azioneassociataalbottoneconilnome“avviaParsing”.Laclasse“ViewController.h”saràquindicosìdefinita:
Codice 7.1: File di interfaccia ViewController.h
123456789
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UITextView *viewTesto;
- (IBAction)avviaParsing:(id)sender;
@end
83
Scriviamo il codice necessarioPrima di procedere con il codice necessario, dobbiamo inserire all’interno del pro-gettoilfilexmlconinostridati.Trascinateilfilexml(chepotetecreareex-novo,op-pure creare copiando il codice presentato nella prima parte di questo tutorial tutorial) all’internodelnostroprogetto,spuntandocomesemprel’opzione“Copy items into destination group’s folder (if needed)”.
Iniziamoinserendoundelegatonelladefinizionedellaclasse“ViewController.h”:
Codice 7.2: Delegato per il parser del file XML
Codice 7.3: DImplementazione dell’azione “avviaParsing:”
123456789
12345
6789
101112
131415
161718
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <NSXMLParserDelegate>
@property (strong, nonatomic) IBOutlet UITextView *viewTesto;
- (IBAction)avviaParsing:(id)sender;
@end
- (IBAction)avviaParsing:(id)sender { // azzeriamo il contenuto della text view [viewTesto setText:@””]; // definiamo il percorso del db NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@”dati.xml”]; // convertiamo il path del file in un NSURL NSURL *xmlURL = [NSURL fileURLWithPath:path]; // creiamo il parser NSXMLParser *parser = [[ NSXMLParser alloc] initWithContentsOfURL:xmlURL]; // settiamo come delegato la classe stessa parser.delegate = self; // effettuiamo il parsing del file e controlliamo se ci sono stati problemi if([parser parse] == NO){ // errori nel parsing UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@”Attenzione” message:@”Ci sono stati dei problemi nel par-sing del file” delegate:nil cancelButtonTitle:@”OK” otherButtonTitles: nil]; [alert show]; } }
Abbiamoinseritoladichiarazionedeldelegato“NSXMLParserDelegate”(riga3),checiforniràdeimetodiutiliperilparserdelfileXML.
Spostiamocinelfile“ViewController.m”edefiniamol’azione“avviaParsing:”
c a P I t o l o 7X M L Tu t o r i a l
84
Perprimacosavienedefinitoilpercorsodelfile(riga3),chesitrovanellacartelladell’applicazionestessa.Taleistruzionepotrebbesembrarecomplessa,maessanonfaaltrocherilevareilpercorsoincuisitroval’applicazione(siaessasuiPhoneSi-mulatorchesuiPhonefisico)eaggiungereatalepercorso“dati.xml”,cheèproprioilpercorsocompletodelnostrofileXML.SuccessivamentequestopercorsovieneconvertitoinunoggettoNSURL(riga5),inquandoènecessarioperchiamarepoiilmetodocheesegueilparsing.Allariga7inizializziamol’oggettochesideveoccuparedieseguireilparsing,acuivienesettatopoicomedelegatolaclassecorrente(riga9).Ciòsignificachesaràpropriolaclasse“ViewController”agestireimetodiinvocatidaldelegatoNSXMLParserDelegate.Allariga11,infine,vieneavviatoilparsingdelfile,dicuipoivienecontrollatol’esito:seessohaavutoesitonegativo,equindic’èstato un qualsiasi errore, viene mostrato all’utente un messaggi di errore.
Oradobbiamodefinireimetodichesioccupanodileggereilfilexml.Essisonometodideldelegatocheabbiamodefinito,quindidevonoesseretuttipre-sentinellanostraapplicazione.
Iniziamoinserendotalemetodo:
Codice 7.4: Definizione dell’azione “avviaParsing”
1
234
567
8910
111213
141516
1718
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)ele-mentName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict { if ([elementName isEqualToString:@”studenti”]){ [viewTesto setText:[[NSString alloc] initWithFormat:@”%@\nInizio studenti”,viewTesto.text]]; } else if([elementName isEqualToString:@”studente”]){ [viewTesto setText:[[NSString alloc] initWithFormat:@”%@\nNuovo studente”,viewTesto.text]]; } else if([elementName isEqualToString:@”matricola”]) { [viewTesto setText:[[NSString alloc] initWithFormat:@”%@\nMatricola: “,viewTesto.text]]; } else if([elementName isEqualToString:@”cognome”]) { [viewTesto setText:[[NSString alloc] initWithFormat:@”%@\nCognome: “,viewTesto.text]]; } else if([elementName isEqualToString:@”nome”]) { [viewTesto setText:[[NSString alloc] initWithFormat:@”%@\nNome: “,viewTesto.text]]; }}
Comepoteteosservare,visonounaseriedicontrolliif,chevannoacontrollarel’ele-mentocorrentecheèstatoindividuatodalparser.Inbaseall’elementovieneinseritaunastringaadeguatanellatextviewcheabbiamopredisposto.
85
Questoprocessoèpossibileperchéconosciamoapriorilastrutturadelfilexml:que-stoèquasisemprevero,inquantosarebbemoltopiùcomplicatoleggereunfilexmldi cui non conosciamo la struttura interna. Ilmetodovieneovviamente richiamatoognivoltache ilparser incontraunnuovoelemento, cioè l’apertura di un tag.
Percompletarel’applicazionemancanosoloduemetodi:
Codice 7.5: Altri metodi del delegato NSXMLParser
1
2
345
6
7
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { [viewTesto setText:[[NSString alloc] initWithFormat:@”%@%@”,viewTesto.text,string]];}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)ele-mentName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { [viewTesto setText:[[NSString alloc] initWithFormat:@”%@\nFine elemento: %@”,viewTesto.text,elementName]];}
Ilprimometodovienerichiamatoquandoilparserincontraunvaloreracchiusotraduetag(l’informazioneveraepropria).Nelnostrocasocilimitiamoainserirlanellatextview,peròpotrestefaredelleoperazionipiùomenocomplessesulleinformazioni cheleggetedalfile.
Ilsecondoeultimometodo,invece,vienerichiamatoquandoilparserincontrauntagdichiusura.Ancheinquestocasol’unicaazionechefaremosaràquellodiinserireuna stringa nella text view.
Abbiamoterminato!Cliccatesu“Run”etestateilvostroparserdidocumentixml!
c a P I t o l o 7X M L Tu t o r i a l
86
Fig. 7.3: App 7 “XMLTutorial”
87
c a P I t o l o 8S Q L Tu t o r i a l
8. SQL
NelcapitoloprecedenteabbiamovistocomeleggereunfileXMLefareilparsingdeidaticontenutialsuointerno.Nonè,però,l’unicapossibilitàperaccedereadunar-chiviodidati.LatecnologiamaggiormenteutilizzataèsicuramenteSQL,chesibasasudatabaserelazionali.InquestocapitolovedremocomesfruttareSQLiteperutiliz-zareundatabasedisponibileinlocale(quindisalvatoall’internodelnostroprogetto). Creeremo, quindi, una tabella in cui andremo ad inserire i valori letti dal nostro data-base,dopoavereseguitounaquerypredefinita.Vedremo,inoltre,comeeseguireleazionipiùcomuni:inserimentoecancellazionediunelemento.
DevopremetterechenonparleròdiSQLedatabaserelazionali,chedevonoesseregiàconosciutidachiaffrontaquestocapitolo.Èunasceltachepotrebbenonpiacereamoltidivoi,peròrichiederebbetroppotempoeunatrattazionechenonpuòesserefattainunsemplicetutorial.Dettociò,èpossibileseguirequestaguidaeconcludereconsuccessol’applicazione,anchesenzaconoscerenientediSQL,ovviamenteal-cunecaratteristicheealcunipassaggipotrebberorisultaredidifficilecomprensione.
In questa guida sentirete spesso parlare di SQLite, ma cosa è di preciso?SQLiteèunalibreriacheimplementaunDBMSSQL,permettendodicreareunda-tabaseall’internodiununicofile.Essoèmoltoindicatoperscopicomeilnostro,ov-verolacreazionediapplicazioniperdispositivimobili,chenonpossonopermettersidi avere un DMBS dedicato per la gestione delle basi di dati. Ovviamente fornisce un supportoparzialeadSQL,inquantomancadialcunecaratteristicheavanzate,cheperòdubitopossiateutilizzareall’internodiun’applicazione.SQLite,comunque,èmoltoveloceeleggero,perfettoquindiperapplicazionimobile.
Nota: molti utenti in passato mi hanno chiesto come utilizzare un database MySQL (risiedente su un server) all’interno delle applicazioni. Purtroppo non è disponibile nessun componente che permetta di fare ciò, quindi dovrete utilizzare un web service che faccia da collegamento tra la vostra applicazione e il database stesso.
88
Creiamo il databaseLaprimacosadafareècreareildatabasecheutilizzeremopoinellanostraappli-cazione.Per fareciòutilizzeremo“Navicat forSQLite”,unprogrammachepotetetrovare sul sito navicat.com.
Fig. 8.1: “Navicat for SQLite”
Fig. 8.2: New Connection
Apriteilprogrammaecliccatesu“Connection”.Siapriràunaschermataincuidovre-te inserire il nome del vostro database e il percorso in cui salvarlo:
89
Inseriteunnome(nelmiocasohoutilizzato“squadre”),selezionate“NewSQLite3”esceglieteunpercorsoincuisalvareilfile.Abbiamocosìcreatoilnostrodatabase,cheperòdobbiamoancoradefinire.Vogliamocreareunasemplicestruttura,incuipossiamoinserireilnomedialcunesquadreelarelativanazionalità.Fatedoppioclicsulnomedellavostraconnessioneesuccessivamentesu“main”.Quiavremodiverseazionidapotereffettuare,comevisualizzarelastrutturadeldboppureeseguiredelleinterrogazioni.Selezionate“Tables”efateclicsulbottone“+”chetrovatenellabarrainbasso:
Fig. 8.3: Sezione Tables di “Navicat for SQLite Lite”
Così facendo avremo la possibilità di definire una nuova tabella. Iniziamo inse-rendouncampodinome “id”, chesarà l’identificativounivocodiogni elemento. Selezionate“integer”cometipo,spuntatelacolonna“Key”el’opzione“Autoincre-ment”(Vedifig.8.4).
c a P I t o l o 8S Q L Tu t o r i a l
90
Inquestomodoavremouncamponumericochesiincrementeràautomaticamenteadogninuovoelementoinserito,eserviràcomeidentificativounivocodiognielemento.Perinserireunnuovocampocliccatesulbottone“AddField”chetrovatenellabarrasuperiore.Inserite,quindi,duenuovicampitestuali:“squadra”e“nazione”.Quando avrete terminato dovreste vedere il seguente risultato:
Fig. 8.4: Definizione del campo “id” della tabella
Fig. 8.5: Definizione dei campi “squadra” e “nazione”
91
Noncirestachecliccaresulbottone“Save”einserire“squadre”comenomedellanostratabella.Abbiamocosìcreatoilnostrodatabase!Oranellaschermata“Tables”dovrestevederelatabellaappenadefinita(eun’altratabellacheilprogrammacreainautomatico):
Fig. 8.6: Tabella “squadre definita correttamente
Noncirestacheinserirealcunivaloriall’internodelnostrodatabase,inmododanonavereun’applicazionevuota.Spostiamociin“Queries”eclicchiamoancorasul“+”nellabarra inferiore.Questavoltasiapriràunaschermatachepermetterà l’inseri-mento di qualsiasi genere di query.
Fig. 8.7: Inserimento di alcuni valori
c a P I t o l o 8S Q L Tu t o r i a l
92
Inseriamo le seguenti query:
Fig. 8.8: Esecuzione delle query
SQL 1: Query
INSERT INTO squadre (squadra, nazione) VALUES (‘Juventus’,’Italia’);INSERT INTO squadre (squadra, nazione) VALUES (‘Milan’,’Italia’);INSERT INTO squadre (squadra, nazione) VALUES (‘Chelsea’,’Inghilterra’);INSERT INTO squadre (squadra, nazione) VALUES (‘Real Madrid’,’Spagna’);INSERT INTO squadre (squadra, nazione) VALUES (‘Lione’,’Francia’);INSERT INTO squadre (squadra, nazione) VALUES (‘Porto’,’Portogallo’);
Epremiamopoisu“Run”pereseguirle.Seaveteeseguitotuttocorrettamentenonviverranno restituiti messaggi di errore:
Tornateallaschermatainizialeeselezionatelavostratabella.Conundoppioclicviverràapertaunaschermataincuivisualizzereteglielementipresentineldb,ovveroproprioquellicheabbiamoappenainserito(Vedifig.8.9).
93
Ilnostrodatabaseècosìprontoperessereutilizzatoall’internodell’applicazione!
Fig. 8.9: Database popolato
c a P I t o l o 8S Q L Tu t o r i a l
94
Creiamo la struttura dell’applicazioneCreiamounnuovoprogettoditipo“MasterDetailApplication”echiamiamolo“sql-Tutorial”.Comesempreassicuriamociche“Usestoryboard”e“IncludeUnitTest”siano disattivati. Come abbiamo visto nel capitolo 5, questo template ci fornisce un’implementazionedibaseperunatabella,incuinoiandremoadinserireivaloriletti dal database.
Dobbiamo,però,inserirealtreduecosemoltoimportanti:unaclassechesioccuperàdicomunicareconildatabase,elalibreriachepermettetalecomunicazione.Iniziamocreandolaclassecheserviràainostriscopi.Andiamoin“File->New->NewFile…”ecreiamouna“Objective-Cclass”,chiamandolasemplicemente“Data”:
Fig. 8.10: Creiamo una nuova classe
DobbiamoorainserireilframeworkcheforniscelefunzioniperutilizzareSQLite:ilpro-cedimento è lo stesso che abbiamo visto negli scorsi capitoli. Spostiamoci, quindi,in “BuildPhases”epoi in “LinkBinaryWithLibraries”eaggiungiamo il framework“libsqlite3.0.dylib”(comepotetevedereinfig8.11e8.12).
95
Infine,prendiamoilfile“squadre.sqlite”e trasciniamolo all’interno del nostro progetto, spuntando l’opzione “Copyitemsintodestinationgroup’sfolder(ifneeded)”comealsolito.
Abbiamocosìcompletatoladefinizionedellastrutturadellanostraapplicazione.
Se avete eseguito tutto in maniera cor-retta dovreste avere un progetto come quellopropostoinfigura8.13.
Fig. 8.11: Aggiunta della libreria libsqlite3.0.dylib
Fig. 8.13: Struttura del progetto
Fig. 8.12: Libreria aggiunta correttamente
c a P I t o l o 8S Q L Tu t o r i a l
96
Definiamo la classe “Data”Dobbiamooraimplementarelaclasse“Data”,sucuisibasagranpartedique-stocapitolo.Primadiiniziareascriverecodice,focalizziamol’attenzionesuciòchedeve fare questa classe.
Essa avrà il compito di:•creareunaconnessioneconildatabaselocale;•interrogarelabasedidatieseguendounaquerypredefinita,salvandoirisultatiinunarray;
•cancellareunelementodaldatabasesurichiestadell’utente;•inserireunnuovoelementoall’internodeldatabase.
Inizieremoadanalizzareiprimiduepunti,ovveroilcollegamentoaldatabaseel’esecu-zionediunasemplicequery,checirestituiràtuttiglielementipresentinellabasedidati.
Iniziamoaprendoilfile“Data.h”einserendoilseguentecodice:
Codice 8.1: Definizione del file di interfaccia Data.h
12345678910111213141516
#import <Foundation/Foundation.h>#import <sqlite3.h>
@interface Data : NSObject { NSString *pathDB; NSMutableArray *lista;}
@property (nonatomic, retain) NSMutableArray *lista;
- (void)caricaValori;- (void)cancellaSquadra:(NSMutableDictionary*)squadra;- (void)aggiungiSquadra:(NSMutableDictionary*)squadra;
@end
Allariga2abbiamoimportatolaclassenecessariaperutilizzareidatabaseSQLitein Cocoa. Ledefinizionisuccessivesonosemplici:abbiamodefinitounastringa (riga5)checonterràilpercorsodeldatabaseeunalista(riga7),cheinvececonterràtuttigliele-menti letti dal database. Diquestalistaabbiamodefinitoanchelaproperty, inquandopoivorremoleggerel’elementoanchealdifuoridellaclasse.Letreazioni(righe12,13e14),infine,implementarannoicomportamentipossibilisulnostrodatabase(letturavalori,cancellazioneeinserimentodiunnuovoelemento).
97
Spostiamocinelfile“Data.m”einiziamoadimplementarelaclasse:
Codice 8.2: Implementazione del metodo “init”
12345678910111213
1415161718
#import “Data.h”
static sqlite3 *database = nil;
@implementation Data
@synthesize lista;
- (id)init{ self = [super init]; if (self) { // definiamo il percorso del db pathDB = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@”squadre.sqlite”]; // leggiamo tutti gli elementi [self caricaValori]; } return self;}
Laprimaistruzioneparticolarecompareallariga3,edèladefinizionedell’oggettocheciserviràpercrearelaconnessioneconildatabase.Comeavreteormaiimparato,allariga7c’èla“synthesize”dellaproprietàlistacheabbiamodefinitonellaclassediinterfaccia.Allariga9vienedichiaratoilmetodo“init”,chevieneinvocatoquandosiinizializzaunnuovooggettoditipoData.Talemetodohaunastrutturachepotrebbesembrareunpo’strana:primarichiamailmetodo“init”dellasuperclasse(equindidellaclas-seNSObjectchevieneereditatadaqualsiasioggetto),poivienefattouncontrollosull’oggetto“self”.Sel’oggettoèstatoinizializzatoinmanieracorrettapossonoes-sereeseguitealtredefinizioni,propriocomeabbiamofattonoi.All’internodell’if,infatti,abbiamodefinitoilpercorsodelnostrodatabase(riga13),epoiabbiamorichiamatoilmetodochesioccuperàdicaricareivaloridaldatabase.L’istruzionecheleggeilpercorsodi“pathDB”èlastessacheabbiamovistonelpre-cedente capitolo.
Dobbiamoora implementare ilmetodo“caricaValori”,chedeve legge tuttigliele-menti dal database. Nella pagina successiva troverete il codice da inserire per la sua implementazione.
c a P I t o l o 8S Q L Tu t o r i a l
98
Analizziamoconcalmaquestometodo,chepotrebbenonesseresemplicepermoltidivoi.PerprimacosavienedefinitounoggettoditipoNSMutableArray(riga4),chesarà una lista temporanea in cui inseriremo uno alla volta gli elementi letti dal databa-se. Questa lista, poi, diventerà proprio la lista degli oggetti della nostra classe.Alla riga 6 viene aperta la connessione con il database: essa si trova all’interno di un costrutto“if”:setalecontrollodaesitopositivo,sipossonoelaborareidati,altrimentisipassaallafinedelmetodo,inquantononèpossibileinstaurarelaconnessione.Se la connessioneè stata creata, possiamocreare laquery cheandremopoi adeseguire (riga8).Alla riga11vieneeseguita talequery:anche inquestocaso,sel’esecuzionehaavutosuccesso,possiamoricavareivaloridesiderati,altrimentinonverràeseguitanessunaoperazione.
Codice 8.3: Implementazione del metodo “caricaValori”
123456
78
91011
12
1314
15
16
17
18
1920212223242526
// Carica i valori dal database passato come parametro- (void)caricaValori { // lista temporanea NSMutableArray *listaTemp = [[NSMutableArray alloc] init]; if (sqlite3_open([pathDB UTF8String], &database) == SQLITE_OK) { // query che ricava i valori const char *sql = “SELECT id, squadra, nazione FROM squadre”; sqlite3_stmt *select_statement; if(sqlite3_prepare_v2(database, sql, -1, &select_state-ment, NULL) == SQLITE_OK) { while(sqlite3_step(select_statement) == SQLITE_ROW) { // ricaviamo i valori letti dalla query NSString *idSquadra = [NSString stringWithUTF8String:(char *)sqlite3_column_text(select_statement, 0)]; NSString *nome = [NSString stringWithUTF8String:(char *)sqlite3_column_text(select_statement, 1)]; NSString *nazione = [NSString stringWithUTF8String:(char *)sqlite3_column_text(select_statement, 2)]; // inseriamo tutti i valori letti in un uni-co oggetto NSDictionary *dictionary = [[NSMutable-Dictionary alloc] initWithObjectsAndKeys:idSquadra, @”id”, nome, @”squadra”, nazione, @”nazione”, nil]; [listaTemp addObject:dictionary]; } } self.lista = listaTemp; sqlite3_finalize(select_statement); } sqlite3_close(database);}
99
Il ciclo while (riga 12) ci permetterà di scorrere tutti i risultati della nostra query(chepotrebberoessere,ovviamente,piùdiuno), finoal termine.Vengono ricava-ti i varicampi,chevengonomemorizzati invariabili testuali (adesempio riga14). Essi, poi, vengono inseriti in un oggetto di tipo “NSMutableDictionary” (riga 18). Inquestoparticolareoggettoognielementoèassociatoadunachiave,ilcherendemol-to comodo e veloce inserire e recuperare oggetti. Nelnostrocaso,inseriamoitrecampilettiassociandoadognunounachiavediversa:“id”,“squadra”e“nazione”.Successivamentevedremocomerecuperareivalorideside-rati da questo tipo di oggetto.L’elementodizionariovienepoiinseritonellalistatemporanea(riga19).Finitoilciclo,talelistavieneassociataallalistadellaclasse,checonterràquindituttiivalorilettidal database. Fattociòvienechiusalaconnessioneconildb.
Il procedimento potrebbe sembrarvi un po’ complicato, ma in linea generale è sem-prelostessodautilizzare.Ilmioconsiglioèquellodifareunpo’dipraticaperassi-milare bene i concetti.
Perorafermiamociquiconladefinizionedellaclasse“Data”,eandiamoacomple-tarel’applicazione.
Mostriamo i valori del databasePervisualizzareivalorilettidaldatabaseabbiamosceltounatabella,cheabbiamogiàampiamentedescrittonelcapitolo5.Nonmisoffermerò,quindi,sugliaspettigià visti.
Iniziamoaprendoilfile“MasterViewController.h”einseriamolaseguentedefinizione:
Codice 8.4: Definizione del file di interfaccia MasterViewController.h
12345678910111213
#import <UIKit/UIKit.h>#import “Data.h”
@class DetailViewController;
@interface MasterViewController : UITableViewController { Data *dataList;}
@property (strong, nonatomic) DetailViewController *detailViewCon-troller;
@end
SemplicementeabbiamodefinitounoggettoditipoData,cheabbiamochiamato“da-taList”(riga7).Questooggettosarànecessarioperinteragireconildatabaseelegger-neisuoivalori.Ricordatevidiinserireanchel’importazioneditaleclasse(riga2).
c a P I t o l o 8S Q L Tu t o r i a l
100
Spostiamocioranelfile“MasterViewController.m”einseriamoilseguentecodicenelmetodo“viewDidLoad”:
Codice 8.6: Implementazione dei metodi per definire il contenuto della tabella
Codice 8.5: Implementazione del metodo “viewDidLoad”
12345678
9101112
1314
15
16171819
20212223
123456
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return [dataList.lista count];}
// Customize the appearance of table view cells.- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *CellIdentifier = @”Cell”; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; cell.accessoryType = UITableViewCellAccessoryDisclosu-reIndicator; }
// Configure the cell. NSMutableDictionary *squadra = [dataList.lista objectAtIndex:indexPath.row]; cell.textLabel.text = [squadra objectForKey:@”squadra”]; cell.detailTextLabel.text = [squadra objectForKey:@”nazione”]; return cell;}
- (void)viewDidLoad { [super viewDidLoad]; // inizializziamo l’oggetto Data dataList = [[Data alloc] init];}
Vieneallocatoeinizializzatol’oggetto“dataList”.Lachiamataalsuometodo“init”provocherà l’esecuzionedelleazionicheabbiamovistopocofa: ladefinizionedelpercorsodeldatabaseelaletturadeglielementitramiteilmetodo“caricaValori”.
Dobbiamoimpostare iduemetodichecipermettonodidefinire ilcontenutodellatabella;eccoli:
101
Dovreste già conoscerli entrambi, se avete dei dubbi vi consiglio di rileggervi il ca-pitolo5.Notatecheentrambiimetodiutilizzanol’oggetto“lista”di“dataList”:infattiusando“dataList.lista”siaccedealla listacon tutti i valori lettidaldatabase,chepossono essere così mostrati nella tabella.L’unicopuntosucuimivogliosoffermareè l’utilizzodeldizionarioalla riga19. Inprecedenzaabbiamovistocomecreareunelementodizionario,qui,invece,vedetecomesiutilizza.Primavienericavatodallalistal’elementodavisualizzare(riga19),poivengonoutilizzatiisuoicampi.Per accedere, ad esempio, al nome della squadra basta utilizzare il metodo“objectForKey:”especificarelachiavedell’elementochesivuoleleggere,inquestocaso“squadra”.Così facendociverràrestituito ilnomedellasquadra, inmanieraveloceesemplice.Lostessoavvieneperlanazionalità,maavremmoanchepotutofarloperl’id,inquandotuttiglielementisonopresentineldizionario.
Abbiamoconclusoquestaparte!Cliccate“Run”econtrollatecheglielementisianovisualizzaticorrettamente.
Fig. 8.14a: App 8 Beta “SQLTutorial”
c a P I t o l o 8S Q L Tu t o r i a l
102
Cancellazione di un elementoOracheabbiamodefinitoilcomportamentobasilaredellanostraapplicazione,possia-moaggiungereleduefunzionimancanti.Iniziamodallacancellazionediunelemento.L’ideaepartedelfunzionamentoèlastessacheabbiamovistonelcapitolo5,maque-stavoltadobbiamoeliminarel’elementononsolodallalista,maanchedaldatabase.Ciservirà,quindi,unmetodospecificocheeseguataleoperazione.Questometodoè“cancellaSquadra:”,dicuiavevamogiàinseritol’intestazionenellaclasseData.
Andiamoin“Data.m”edimplementiamotalemetodo:
Codice 8.7: Implementazione del metodo “cancellaSquadra:”
123
45
678910
1112131415161718192021
- (void)cancellaSquadra:(NSMutableDictionary*)squadra{ if (sqlite3_open([pathDB UTF8String], &database) == SQLITE_OK) { // query per la cancellazione di una squadra NSString *query = [NSString stringWithFormat:@”DELETE FROM squadre WHERE id=%@”,[squadra objectForKey:@”id”]]; const char *sql = [query UTF8String]; sqlite3_stmt *delete_statement; // eseguiamo la query if(sqlite3_prepare_v2(database, sql, -1, &delete_state-ment, NULL) == SQLITE_OK) { if(sqlite3_step(delete_statement) == SQLITE_DONE) { // tutto ok, ricarichiamo la lista [self caricaValori]; } } // chiudiamo il db sqlite3_finalize(delete_statement); } sqlite3_close(database);}
Potetevederecomepartedelcodicesiamoltosimileaquellopresentein“caricaValori”. Quellochecambiaè,ovviamente,laquerydaeseguire,chedeveessereunadelete(riga5).Inessa,infatti,specifichiamochedeveessereeliminatol’elementoilcuiidcorrispondeaquelloall’oggetto“squadra”.Sel’esecuzionedellaquerydaesitopositivo,dobbiamoricaricareivalorideldb,inmododaaverelalistadeglielementiaggiornata(riga14).Potremmo,inverità,limitarciadeliminarel’oggettodallalista,senzabisognodirica-ricare tutti i valori. Decidete voi quale sistema preferite. Sicuramente ricaricare tutti gli elementi dal database è più dispendioso in termini di risorseeditempo,perògarantiscedinonavereincongruenzetralalistaeivaloripresenti nel database.
103
Ilmetodoèquindipronto,noncirestacherichiamarloalmomentoopportuno.Perfareciòspostiamocinelfile“MasterViewController.”emodifichiamoilmetodocherispondeall’eliminazionediunelementonellatabella(comegiàvistonelcap.5):
Codice 8.8: Modifica del metodo per l’eliminazione di un elemento
1
23
45
67
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { [dataList cancellaSquadra:[dataList.lista objectAtIndex:indexPath.row]]; // Delete the row from the data source. [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; } }
Questometodol’abbiamogiàanalizzato,maquellochequestavoltacambiaèl’i-struzioneallariga3:richiamiamo,infatti,ilmetodo“cancellaSquadra:”cheabbiamoappenadefinito.Atalemetodopassiamol’elementochel’utentehasceltodicancel-lare.Essosaràquindirimossoanchedaldatabase.
Fatto!Testiamolanostraapplicazioneecontrolliamochelacancellazionefunzionicorrettamente.
c a P I t o l o 8S Q L Tu t o r i a l
104
Fig. 8.14c: App 8 Beta “SQLTutorial”Fig. 8.14b: App 8 Beta “SQLTutorial”
105
Inserimento di un nuovo elementoInquestaultimapartecidedichiamoadunafunzionalitàmoltoimportanteinappli-cazionicheutilizzanoundatabase:l’aggiuntadinuovielementi.Permetteremoall’u-tentediinserireunanuovasquadra,conlarelativanazionalità,cheverràpoiinseritaall’interno del database. Per fare ciò creeremo una vista apposita, in cui l’utentepotràinserireleinformazioninecessarie.
Iniziamodefinendo ilmetodochedovrà inserireall’internodeldatabase lanuovasquadra. Anche per esso avevamogià definito l’intestazione “aggiungiSquadra:”,eccol’implementazionedainserirein“Data.m”:
Codice 8.7: Implementazione del metodo “aggiungiSquadra:”
123
45
678910
1112131415161718192021
- (void)aggiungiSquadra:(NSMutableDictionary*)squadra{ if (sqlite3_open([pathDB UTF8String], &database) == SQLITE_OK) { // query per l’inserimento del nuovo giocatore NSString *query = [NSString stringWithFormat:@”INSERT INTO squadre (squadra, nazione) VALUES (‘%@’,’%@’)”,[squadra objectForKey:@”squadra”],[squadra objectForKey:@”nazione”]]; const char *sql = [query UTF8String]; sqlite3_stmt *insert_statement; // eseguiamo la query if(sqlite3_prepare_v2(database, sql, -1, &insert_state-ment, NULL) == SQLITE_OK) { if(sqlite3_step(insert_statement) == SQLITE_DONE) { // ricarichiamo la lista [self caricaValori]; } } // chiudiamo il db sqlite3_finalize(insert_statement); } sqlite3_close(database);}
Questometodoèpraticamenteugualeaquelloperlacancellazionediunelemento,l’unicadifferenzaèancoraunavoltalaquery(riga5).Essaèuna“insert”,incuidob-biamoinserireilnomedellasquadraelasuanazionalità.
Vediamooralarestantepartedaaggiungereall’applicazione.Perprimacosadobbiamodefinireunanuovavista,chechiameremo“AddSquadra-ViewController”.Dalmenù“File->New->NewFile...”escegliamo“UIViewControllersubclass”come tipologia. Inseriamo il nome “AddSquadraViewController”, assicuriamoci che la subclasssia“UIViewController”echel’opzione“WithXIBforUserInterface”siaspuntata.
c a P I t o l o 8S Q L Tu t o r i a l
106
Apriamoilfile“AddSquadraViewController.xib”eimpostiamolaseguentestrutturagrafica:
Fig. 8.15: Creiamo una nuova vista
Fig. 8.16: Struttura grafica
Oraeseguiamoleconnessioniallaclasse“AddSquadraViewController”.Definiamodueoutletperleduetextfield,eun’azioneperilbottone.
107
Eccocomelehodefiniteio:
Codice 8.8: File di interfaccia AddSquadraViewController.h
Codice 8.9: synthesize dell’elemento dataList
123456789101112
1234567
#import <UIKit/UIKit.h>#import “Data.h”
@interface AddSquadraViewController : UIViewController
@property (assign, nonatomic) Data *dataList;@property (strong, nonatomic) IBOutlet UITextField *fieldNome;@property (strong, nonatomic) IBOutlet UITextField *fieldNazione;
- (IBAction)addSquadra:(id)sender;
@end
#import “AddSquadraViewController.h”
@implementation AddSquadraViewController
@synthesize dataList;@synthesize fieldNome;@synthesize fieldNazione;
OltreaglielementidiInterfaceBuilder(righe7,8e10)hodefinitoancheunapropertyper“dataList”,checiserviràperfarriferimentoalnostrodatabase,inparticolareallalistadeglielementichegiàèstatadefinita.Notiamochel’argomentoè“assign”:questosignificachenonvienecreatounnuovoelemento(comeaccadeadesempioquandotroviamostrong),masifariferimentoallostesso.Proprioquellocheserveanoi.
Definitalastrutturagraficaedellaclasse,possiamopassareall’implementazionedelcodicenelfile“AddSquadraViewController.m”.Perprimacosainseritelasynthesizedellaproprietà“dataList”(riga5):
c a P I t o l o 8S Q L Tu t o r i a l
108
Implementiamo, poi, il metodo associato alla pressione del bottone:
Codice 8.10: Implementazione del metodo associato al bottone
Codice 8.11: Aggiunta del bottone per l’inserimento di una squadra
123
45
67891011
121314
12345678
910
- (IBAction)addSquadra:(id)sender { // controlliamo se i campi sono stati inseriti if (([fieldNome.text length] != 0)&& ([fieldNazione.text length] != 0)){ // creiamo un dizionario con i valori NSMutableDictionary *squadra = [[NSMutableDictionary al-loc] initWithObjectsAndKeys:fieldNome.text, @”squadra”, fieldNazio-ne.text, @”nazione”, nil]; // inseriamolo nel db [dataList aggiungiSquadra:squadra]; // torniamo alla lista [self.navigationController popViewControllerAnimated:YES]; } else { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@”Attenzione” message:@”Controlla di aver inserito tutti i campi necessari” delegate:nil cancelButtonTitle:@”OK” other-ButtonTitles: nil]; [alert show]; }}
- (void)viewDidLoad { [super viewDidLoad]; // inizializziamo l’oggetto Data dataList = [[Data alloc] init]; // bottone per aggiungere una nuova squadra UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(aggiungiSquadra:)]; self.navigationItem.rightBarButtonItem = addButton;}
Questaazionedeverichiamareilmetodo“aggiungiSquadra”dell’oggettoData,perin-serirelanuovasquadraneldatabase.Primadifareciò,però,vienecontrollatocheiduecampiditestononsianovuoti(riga3),perchènonvogliamoavereundatabasecondelleinconsistenze.Sevienesuperatoilcontrollo,creiamoundizionarioincuiinseriamoiva-loridigitatidall’utente(riga5),epassiamopoiquestooggettoall’elementodataList(riga7),cheinseriràneldatabaselanuovasquadra.Alcontrario,sel’utentenonhainseritotuttiidatinecessarivienemostratounmessaggiodiavvertimento(riga11).Abbiamoquasiconcluso.Noncirestachedefinireunbottonedainserirenellanaviga-tionbarpostasopralatabellainiziale,inmodochel’utentesappiasubitodovepremereperinserireunanuovasquadra.Torniamo,quindi,nelfile“MasterViewController.m”enelmetodo“viewDidLoad”inseriamolaseguentedefinizione(righe8e9):
109
AbbiamosemplicementedefinitounUIBarButtonItem,ovverounbottoneperlanavi-gationbar,utilizzandolostile“UIBarButtonSystemItemAdd”,chefornisceunostiledidefaultconun“+”comeimmagine.L’oggettocreatovienepoiassegnatoalbottonedidestradellanavigationbar,cheèunaproprietàgiàdisponibile.Albottoneèassociatoilmetodo“aggiungiSquadra:”,chevieneinvocatoquandosipremesullostesso.Eccoladefinizioneditalemetodo:
Codice 8.12: Implementazione del metodo “aggiungiSquadra:”
Codice 8.13: Modifica del metodo “viewWillAppear:”
12
34
5
123456
- (void)aggiungiSquadra:(id)sender{ AddSquadraViewController *addController = [[AddSquadra-ViewController alloc] initWithNibName:@”AddSquadraViewController” bundle:nil]; addController.dataList = dataList; [self.navigationController pushViewController:addController animated:YES];}
- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // ricarichiamo la tabella [self.tableView reloadData];}
Ormaidovrestecapirealvolocosafaquestometodo:semplicementeinizializzaunanuovavista“AddSquadraViewController”,glipassailrifermentodell’oggettodata-Listevienepoimostratasulloschermo.Nullachenonabbiategiàvisto.
Mancasolounpiccolissimoritocco.Nelmetodo“viewWillAppear:”inseritequestaistruzione(riga5):
Essapermettedi ricaricare la tabellaquandovienevisualizzata lavista.Questoènecessarioquandol’utentehainseritounanuovasquadraeritornaallalistaprinci-pale:grazieaquestaistruzionelatabellaverràricaricataeilnuovoinserimentosaràvisibile all’utente.
Abbiamoconcluso!Cliccatesu“Run”etestate lavostraapplicazionepienamentefunzionante!
c a P I t o l o 8S Q L Tu t o r i a l
110
Fig. 8.17b: App 8 “SQLTutorial”Fig. 8.17a: App 8 “SQLTutorial”
111
c a P I t o l o 9R S S R e a d e r
9. Creiamo un semplice lettore di feed RSS
LaprimaapplicazionecheandremoarealizzareèunsemplicemautilissimolettoredifeedRSS.Vedremocomeleggereidati(solitamentedegliarticoli)diunfeed,vi-sualizzandoliinunatabella.
Perrealizzarequestosemplice lettoredi feedRSSutilizzeremodiversiaspetticheabbiamogiàanalizzatoneiprecedenticapitoli,inparticolarelagestionedelletabelle(capitolo5)el’utilizzodiXML(capitolo8).Sealcuniaspettidiquestiduetutorialnonvisonodeltuttochiari,viconsigliodiriguardarviiduecapitoliinquestione,inmododanonaverprobleminellacreazionedellettoredifeedRSS.
Parte 1: Creiamo la base del lettore
Creiamo la struttura dell’applicazioneCreiamo un nuovo progetto di tipo “Master Detail Application” e chiamiamolo“RSSReader”.Comesempreassicuriamoci che “Use storyboard”e “IncludeUnitTest”sianodisattivati.Comeabbiamovistonelcapitolo5,questotemplateci for-nisceun’implementazionedibaseperunatabella, incuinoiandremoadinserireivalori letti dal feed RSS.
Iniziamodefinendonellaclasse“MasterViewController.h”glielementichesarannonecessariperlanostraapplicazione:
Codice 9.1: Definizione del file di interfaccia MasterViewController.h
12345
67891011121314151617181920
2122
#import <UIKit/UIKit.h>
@class DetailViewController;
@interface MasterViewController : UITableViewController <NSXMLParser-Delegate>{ // lista di tutti gli elementi letti dal feed NSMutableArray *listaElementi; // variabile temporanea per un singolo elemento NSMutableDictionary *itemCorrente; // variabili temporanee per i singoli campi NSMutableString *elementoCorrente; NSMutableString *titolo; NSMutableString *data; NSMutableString *link; NSMutableString *contenuto;}
- (void)parseRSSFeed:(NSString*)feed;
@property (strong, nonatomic) DetailViewController *detailViewCon-troller;
@end
112
L’oggettoallariga7conterràtuttiglielementilettidalfeedRSS,chesolitamentesononotizieoarticoli.Talelistasarà,comeormaiavretebenimparato,ildatasourcedellatabella.Glielementidichiaratidallariga11inpoiservirannoalparserperleggeretut-teleinformazionidiunarticoloesalvarleall’internodiundizionario(riga9).Diognielementodelfeed,quindi,leggeremotitolo,datadipubblicazione,linkall’articoloecontenutodell’articolostesso(chepuòessereanchesolounapreviewdellostesso).
Definiamo i metodi del parserSpostiamocinelfile“MasterViewController.m”incuiiniziamoadinserireimetodine-cessaripereseguireilparsingdiunfeedRSS.Iniziamodalmetodo“viewDidLoad”:
Codice 9.3: Implementazione del metodo “parseRSSFeed:”
Codice 9.2: Imlementazione del metodo “viewDidLoad”
12345
6789
10111213141516171819
12345678
- (void)parseRSSFeed:(NSString*)feed{ // inizializziamo la lista degli elementi letti dal feed listaElementi = [[NSMutableArray alloc] init]; // dobbiamo convertire la stringa “feed” in un elemento “NSURL” NSURL *feedURL = [NSURL URLWithString:feed]; // inizializziamo il nostro parser XML NSXMLParser *rssParser = [[NSXMLParser alloc] initWithContentsOfURL:feedURL]; // settiamo alcune proprietà [rssParser setDelegate:self]; [rssParser setShouldProcessNamespaces:NO]; [rssParser setShouldReportNamespacePrefixes:NO]; [rssParser setShouldResolveExternalEntities:NO]; // avviamo il parsing del feed RSS [rssParser parse];}
- (void)viewDidLoad { [super viewDidLoad]; // impostiamo l’indirizzo del feed RSS NSString *path = @”http://feeds.feedburner.com/TheBubiDevs”; // richiamiamo il metodo che avvierà il parser [self parseRSSFeed:path];}
Semplicementeabbiamodefinitounastringaconl’indirizzodelfeedRSSdaleggere,eabbiamopoirichiamatoilmetodochesioccuperàdiinizializzareedavviareilpar-ser,acuivienepassatocomeparametropropriol’indirizzodelfeedRSS.Ditalemetodoabbiamogiàdefinitol’intestazionein“MasterViewController.h”,ve-diamooralasuaimplementazione:
113
Perprimacosavieneinizializzatalalistachedovràospitareivarielementilettidalfeed (riga3).Successivamente,vieneconvertito l’indirizzoadunoggettoNSURL,comeabbiamogiàvistonelcapitolodedicatoadXML.Ancheladefinizionedell’og-gettorssParserègiàvista,mentrealcuneproprietàchevengonosettatesononuove.Esse sono necessarie per la corretta lettura di un feed RSS, quindi non mi dilungo tropponellalorospiegazione.Allariga18,infine,vieneavviatoilparsingdelfeed.
Comegiàsapete,ildelegatodell’oggettoNSXMLParserrichiedechevenganoimple-mentatialcunimetodiperilcorrettofunzionamentodelparser.Iniziamoinserendoilseguentemetodo:
Codice 9.4: Implementazione del metodo che rileva i nuovi elementi
1
23456789101112
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)ele-mentName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ elementiCorrente = [elementName copy]; if ([elementName isEqualToString:@”item”]) { // inizializza tutti gli elementi itemCorrente = [[NSMutableDictionary alloc] init]; titolo = [[NSMutableString alloc] init]; data = [[NSMutableString alloc] init]; contenuto = [[NSMutableString alloc] init]; link = [[NSMutableString alloc] init]; }}
Comeabbiamogiàvisto,essovienerichiamatoquandoilparserincontraunnuovoelementonelfilexml.Quandotaleelementocorrispondead“item”significachestaperiniziareunnuovoarticolo(ounanuovanotizia),quindidovrannoessereinizializ-zatetuttelevariabilicheconterrannopoiivaloriletti(adesempioiltitolo,link,etc).
Ilsecondometododainserireèquellochevienerichiamatoquandofiniscelalettu-radiunelementodapartedelparser,ovveroquandosiincontrauntagdichiusura (vedicodice10.5).
c a P I t o l o 9R S S R e a d e r
114
Inquestocasoivaricampilettivengonosalvatinell’oggettoitemCorrente,chesap-piamoessereundizionario.Adognicampo,quindi,vieneassociataancheunachia-ve, in modo da poter recuperare i singoli valori in modo semplice e veloce.L’oggettoitemCorrente,infine,vieneaggiuntoallalistachecontienetuttiglielementilettidalfeed(riga11).
Per completare il parser ci mancano solo due metodi:
Codice 9.5: Implementazione del metodo richiamato a fine lettura di un elemento
Codice 9.6: Implementazione dei metodi finali del parser
1
234
5678910111213
1
2345678910111213141516
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{ if ([elementName isEqualToString:@”item”]) { // inseriamo tutti i valori letti nell’elemento itemCor-rente [itemCorrente setObject:titolo forKey:@”titolo”]; [itemCorrente setObject:link forKey:@”link”]; [itemCorrente setObject:contenuto forKey:@”contenuto”]; [itemCorrente setObject:data forKey:@”data”]; // inseriamo l’elemento nella lista degli elementi [listaElementi addObject:[itemCorrente copy]]; }}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{; // salva i caratteri per l’elemento corrente if ([elementoCorrente isEqualToString:@”title”]){ [titolo appendString:string]; } else if ([elementoCorrente isEqualToString:@”link”]) { [link appendString:string]; } else if ([elementoCorrente isEqualToString:@”description”]) { [contenuto appendString:string]; } else if ([elementoCorrente isEqualToString:@”pubDate”]) { [data appendString:string]; }}
- (void) parserDidEndDocument:(NSXMLParser *)parser { [self.tableView reloadData];}
115
Ilprimometodovienerichiamatoognivoltacheunnuovocarattereèrilevatodalpar-ser.Asecondadell’elementochestiamoconsiderando,andremoainserireilnuovocarattere in coda a quelli già letti dello stesso elemento, in modo da ricostruire l’in-formazionecompleta.Adesempio,stiamoconsiderandol’elemento“title”.Vengonoletti,nell’ordine, iseguenticaratteri:“Dev”,“Tutorial”,“#2”.Unendoivaricaratterilettiricostruiremoiltitoloesattodelnostrofeed,ovvero“DevTutorial#2”.
Ilsecondometodo,invece,vienerichiamatosoloquandoilparsercompletalaletturadelfeedRSS:ilparserhacompletatolasuaeleborazione,quindilatabellapuòrica-ricare i propri dati, mostrando gli elementi letti dal feed.
Visualizziamo gli elementi nella tabellaNoncirestacheimpostarelatabellainmodochemostriivarielementicheabbiamoricavato dal feed RSS. Ormai dovreste essere pratici con le table view, quindi eccovi i vari metodi da impostare:
Codice 9.7: Impostazione della tabella
1
2345
678
910
11
12131415
161718
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [listaElementi count];}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @”Cell”; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; cell.accessoryType = UITableViewCellAccessoryDisclosu-reIndicator; }
// Configure the cell. NSMutableDictionary *elemento = [listaElementi objectAtIndex:indexPath.row]; cell.textLabel.text = [elemento objectForKey:@”titolo”]; return cell;}
Questimetodidovresteormaiconoscerlibenissimo.Essinonfannoaltrochepopo-larelatabellautilizzandoglielementicontenutiin“listaElementi”.
Abbiamoconcluso!Clicchiamosu“Run”econtrolliamochelanostraapplicazionefunzionicorrettamente!
c a P I t o l o 9R S S R e a d e r
116
Fig. 9.1: App 10 Beta “RSSReader”
117
Parte 2: Miglioriamo la grafica dell’applicazione
Visaretesubitoaccortichelavisualizzazionedeglielementinonèlamigliore:iltitolononvienemostratocompletamente,inoltrenonsarebbemalepotervisualizzarean-cheladatadell’articoloounapartedelcontenutocheabbiamoscaricatodalfeed.Possiamo risolvere questi problemi creando una nostra cella personalizzata, chepossaconteneretutteleinformazionidesiderate.Vedremo,quindi,comecreareunacellapersonalizzatainbaseallenostreesigenze,cheandràpoiacomporrelatabellagià presente.
Creiamo la cella personalizzataIlprocedimentoperlacreazionediunacellapersonalizzatanonècomplicato,peròmeritaattenzioneperchèrichiedediversipassaggidaeffettuare.Iniziamocreandounfilexibvuoto.Perfareciòandiamoin“File”->“New”->“NewFile…”enellascher-matacheappariràspostiamocinellasezione“UserInterface”:
Fig. 9.2: Creazione di una cella personalizzata
Selezioniamol’elemento“Empty”,proseguiamoeselezioniamo“iPhone”comede-vice,continuiamoeinseriamo“FeedCell”comenomeperilfile.Abbiamocosìcreatounfilexibvuotocheconterràpoilanostracellapersonalizzata.Primadidefinirelacomposizionegraficadiquestoelemento,dobbiamocreareunaclas-sechegestiscatalecella.Sempredalmenù“File->New->NewFile...”selezioniamoquestavoltalasezione“CocoaTouch”escegliamol’elemento“Objective-Cclass”.
c a P I t o l o 9R S S R e a d e r
118
Come nome inseriamo “FeedCell” e selezioniamo “UITableViewCell” nel menù“Subclassof”:
Fig. 9.3: Creazione della classe per la cella personalizzata
Abbiamocosìcreatoanchelaclassechedevegestire lanostracella. Iniziamoadimplementareproprioquestaclasse,definendoglielementinecessari.Apriamoilfile“FeedCell.h”einseriamoilseguentecodice:
Codice 9.8: Definizione del file di interfaccia FeedCell.h
12345678910111213
#import <UIKit/UIKit.h>
@interface FeedCell : UITableViewCell { UILabel *labelTitolo; UILabel *labelContenuto; UILabel *labelData;}
@property (nonatomic, strong) IBOutlet UILabel *labelTitolo;@property (nonatomic, strong) IBOutlet UILabel *labelContenuto;@property (nonatomic, strong) IBOutlet UILabel *labelData;
@end
119
Comevedreteabbiamosemplicementedefinitotre label,chedovrannocontenerelevarieinformazionideglielementilettidalfeed.Diognilabelèdefinitaancheunaproperty,cheovviamentenecessitadellarelativasynthesizenelfile“FeedCell.m”:
Codice 9.8: Inserimento delle synthesize
Codice 9.9: Modifica della classe MasterViewController
1234567
123456
78910111213141516171819202122232425
2627
#import “FeedCell.h”
@implementation FeedCell
@synthesize labelTitolo;@synthesize labelContenuto;@synthesize labelData;
#import <UIKit/UIKit.h>#import “FeedCell.h”
@class DetailViewController;
@interface MasterViewController : UITableViewController <NSXMLParser-Delegate>{ // lista di tutti gli elementi letti dal feed NSMutableArray *listaElementi; // variabile temporanea per un singolo elemento NSMutableDictionary *itemCorrente; // variabili temporanee per i singoli campi NSMutableString *elementoCorrente; NSMutableString *titolo; NSMutableString *data; NSMutableString *link; NSMutableString *contenuto; // cella personalizzata FeedCell *itemCell;}
- (void)parseRSSFeed:(NSString*)feed;
@property (strong, nonatomic) IBOutlet FeedCell *itemCell;@property (strong, nonatomic) DetailViewController *detailViewCon-troller;
@end
Primadidedicarcialladefinizionegraficadellacella,dobbiamoinserireun’ulterioredefinizioneviacodice.Questavoltadobbiamoaprireilfile“MasterViewController.h”emodificarlonelseguentemodo:
c a P I t o l o 9R S S R e a d e r
120
SemplicementeabbiamodefinitounelementodellaclasseFeedCell(riga19),checiserviràperpopolarelatabella.C’èanchelarelativaproperty(riga24)el’importdellaclassedefinitapocofa(riga2).Nelfile“MasterViewController.m”,ovviamente,dovre-teancheinseritelasynthesizedell’elemento“itemCell”appenadefinito.
Definiamo la grafica della cellaPossiamooradedicarcialladefinizionegraficadellacella.Apriamoilfile“FeedCell.xib”einseriamoalsuointernounelemento“TableViewCell”:
Fig. 9.4: Inserimento di “Table View Cell”
Questa sarà la vista della nostra cella, in cui inseriremo tutti gli elementi necessari. Primadiognialtramodifica,andiamonel“SizeInspector”emodifichiamol’altezzadellacellaalvaloredi130,inmododaaveresufficientespazioadisposizioneperinserire tutti i componenti:
Fig. 9.5: Altezza cella
121
Andiamo,poi, in“Attributes Inspector”enelcampo“Identifier” inseriamo ilnome“FeedCell”:
Fig. 9.6: Inserimento nom
e
Fig. 9.7: Struttura della cella
Fig. 9.8: Classe FeedCell
Ora inseriamo le varie label necessarie. Esse dovranno contenere, rispettivamente, il titolodellanotizia,ladatadipubblicazioneeunabrevepartedelcontenuto,unasor-ta di preview dell’articolo stesso. Disponetele all’interno della vostra cella secondo i vostrigustielevostrenecessità.Iohoimpostatolaseguentestruttura:
Lelabelperiltitoloeilcontenutopossonocontenereduelineeditesto,quindihoimpostatoanchelarelativaproprietà(comegiàvistonelcapitolo6).
NoncirestachecollegareivaricomponentiaglioggettidefinitinellaclasseFeedCell.Per fareciòselezioniamo la tableviewcelleandiamo in “Identity Inspector”.Nelcampo“Class”scegliamo“FeedCell”comeclasse:
c a P I t o l o 9R S S R e a d e r
122
Oraspostiamocinel“ConnectionInspector”ecolleghiamoglielementicheabbiamode-finitoinprecedenzaconlecorrettelabel.Nelmiocaso,quindi,ilrisultatosaràilseguente:
Fig. 9.9: Connessioni
Tuttipassaggisemplicicheabbiamogiàvistoneiprecedenticapitoli.Percompletareladefinizionediquestacelladobbiamocollegarel’elementoitemCellcheabbiamodefinitonellaclasseMasterViewController.Perfarciò,abbiamobiso-gnochetaleclassesiailFile’sOwnerdellacellapersonalizzata.Selezioniamo,quin-di,ilFile’sOwnerdellacella,spostiamociin“IdentityInspector”enelcampo“Class”scegliamo“MasterViewController”comeclasse:
Fig. 9.10: Classe MasterViiewController per il File’s Owner
Fattociò,noncirestacheandarenel“ConnectionsInspector”ecollegarel’oggetto“itemCell”conlanostratableviewcell:
Fig. 9.11: Collegam
ento
Siamofinalmenteprontiperutilizzarequestacellapersonalizzatanellanostraapplicazione!
123
Utilizziamo la cellaPrimadivedereilcodicenecessarioperutilizzarelanuovacella,dobbiamofareunapiccolissimamodificaallatabellaoriginale:dobbiamoaumentareladimensionedellecelle, altrimenti tutto il layout verrà sballato. Apriamo il file “MasterViewController.xib” e selezioniamo la tabella. Andiamo nel“SizeInspector”enelcampo“RowHeight”inseriamoilvalore130,proprioladimen-sionecheabbiamosceltoperlacellapersonalizzata:
Fig. 9.12: Modifica dell’altezza nella tabella
Possiamosalvareespostarcinelfile“MasterViewController.m”.L’unicometodochedovremomodificaresaràquellochesioccupadiimpostareilcontenutodellevariecelle,ovvero“tableView:cellForRowAtIndexPath:”.DovretemodificarlocomevedeteinCodice10.10propostonellapaginasuccessiva.
c a P I t o l o 9R S S R e a d e r
124
Diversamente dalle altre volte in cui abbiamo lavorato con le table view, questa volta vieneinizializzatounoggettoditipoFeedCell(riga4),chevienepoiassociatoall’og-gettoitemCell.Questofasichevengacaricatalanostranuovacellapersonalizzatadirettamentedalfilexibcheabbiamodefinito.Laconfigurazionedellacella,poi,èdavverosemplice:ognilabelvienerichiamatacomeuna normale proprietà dell’oggetto cell, e viene impostato il contenuto desiderato. Nelcasodellapreviewdell’articololimitiamoicaratteridavisualizzarea80,pernonoccupare troppa memoria inutilmente.L’oggetto data, volendo, potrebbe essere convertito in un formato a noi più familiare, adesempionellaformadd/MM/yyyy.
Abbiamoconcluso!Clicchiamosu“Run”etestiamoilnostrolettoredifeedmoltopiùprofessionale!
Codice 9.10: Modifica del metodo “UITableViewCell”
1
234
567
8
910111213
1415
16171819
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @”FeedCell”; FeedCell *cell = (FeedCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { // carichiamo il nib della cella e assegniamolo alla cella corrente [[NSBundle mainBundle] loadNibNamed:@”FeedCell” owner:self options:NULL]; cell = itemCell; }
// Configure the cell. NSMutableDictionary *elemento = [listaElementi objectAtIndex:indexPath.row]; cell.labelTitolo.text = [elemento objectForKey:@”titolo”]; cell.labelContenuto.text = [[elemento objectForKey:@”contenuto”] substringToIndex:80]; cell.labelData.text = [elemento objectForKey:@”data”];
return cell;}
125
Fig. 9.13: App 10 Beta “RSSReader”
c a P I t o l o 9R S S R e a d e r
126
Parte 3: Visualizziamo l’articolo completo
Per rendere davvero completo il nostro lettore di feed RSS ci manca una semplice funzione:vogliamochequandol’utenteselezionaunarticolo,essovengacaricatoall’internodiunanuovavista(ovviamenteverràcaricatalapaginawebrelativaall’ar-ticolo in questione).
Per fare ciò, iniziamomodificando il file “DetailViewController.xib”, che sappiamoesserelavistachevieneapertaquandol’utenteselezionaunarigadellatabella.
Cancelliamo la label presente e inseriamo al suo posto una web view:
Fig. 9.14: Inserimento della web view
Creiamo il collegamento (outlet) tra l’oggetto e la classe “DetailViewController.h”,come abbiamo già fatto numerose volte. Chiamiamoquestooggetto“webView”.
127
Ecco come deve essere la vostra classe:
Codice 9.11: File di interfaccia DetailViewController.h
Codice 9.12: Implementazione del metodo “configureView”
12345678
910
1234567
891011
#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController
@property (strong, nonatomic) id detailItem;
@property (strong, nonatomic) IBOutlet UIWebView *webView;@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLa-bel;
@end
- (void)configureView { // Update the user interface for the detail item.
if (self.detailItem) { NSURL *url = [NSURL URLWithString:self.detailItem]; // creiamo una richiesta per la pagina NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // visualizziamo la richiesta (la pagina) nella web view [self.webView loadRequest:request]; }}
Volendopoteteeliminarel’oggetto“detailDescription”,inquandononèpiùutilizzato(senonloeliminatenonc’ènessunproblema).
Oradobbiamospecificareilcodicechesioccuperàdimostrarel’articolocompletonellawebview,usando lostessoprocedimentocheabbiamovistonelcapitolo4. Nelfile“DetailViewController.m”inseriamo,quindi,questocodicenelmetodo“con-figureView”:
Leistruzionisonoesattamentelestessecheabbiamogiàvisto.L’elementocheutiliz-zeremoperpassareillinkdell’articoloè“detailItem”,cheègiàpresentenellaclasse,echeabbiamogiàconosciutonelcapitolo5.
L’ultimacosadafareèinserireproprioilcodicechesioccupadipassareillinkallavista di dettaglio.
Nelfile“MasterViewController.m”modifichiamoilmetodocomepropostoinCodice10.13 nella pagina successiva.
c a P I t o l o 9R S S R e a d e r
128
Anchequestometododovrestegiàconoscerlo.Dopoaver inizializzato la vistadidettaglio, ricaviamo il linkdell’articoloselezionatodall’utente (riga7), lo ripuliamodaeventualicaratterichenonfannopartedell’urlveroeproprio(adesempiospazi,tabulazioni,etc)elopassiamoinfineallavistadidettaglio,usandolaproprietà“de-tailItem”.Cosìfacendolavistacaricheràlapaginawebdesideratadall’utente.
Lanostraapplicazioneèdavveropronta!Possiamocliccaresu“Run”egoderci ilnostrolettoredifeedRSScompletodituttelefunzioni!
Codice 9.13: Passiamo il link dell’articolo da visualizzare
1
23
4567
89
10
11
12131415
16
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (!self.detailViewController) { self.detailViewController = [[DetailViewController al-loc] initWithNibName:@”DetailViewController” bundle:nil]; } // ricaviamo il link dell’elemento selezionato NSString *linkDaAprire = [[listaElementi objectAtIndex:indexPath.row] objectForKey: @”link”]; // ripuliamo il link da spazi, return e tabs linkDaAprire = [linkDaAprire stringByReplacingOccurrence-sOfString:@” “ withString:@””]; linkDaAprire = [linkDaAprire stringByReplacingOccurrencesOfString:@”\n” withString:@””]; linkDaAprire = [linkDaAprire stringByReplacingOccurrence-sOfString:@” “ withString:@””]; // passiamo il link alla vista di dettaglio self.detailViewController.detailItem = linkDaAprire; [self.navigationController pushViewController:self.detail-ViewController animated:YES];}
129
Fig. 9.15b: App 10 “RSSReader”Fig. 9.15a: App 10 “RSSReader”
c a P I t o l o 9R S S R e a d e r
130
131
c a P I t o l o 1 0M y B r u s h e s
10. Realizziamo il nostro “Brushes”
Quellochehopensatopervoiinquestocapitoloèunpo’particolare,peròèsicuramenteuntutorialmoltocarinoeconmoltecoseutili.SicuramentetuttivoiconoscereteBrushes,unprogramma,disponibilenell’AppStore,moltofamosoperdisegnaresulproprioiPho-nee iPodTouch.Hopensato,quindi,dispiegarvicomerealizzarneunotuttovostro! Realizzeremounavistacheconterràla“tavolagrafica”,ovverolaparteincuil’utentepotràdisegnare.Cisarà,poi,unasecondavistaincuiinseriremoleimpostazioni,perpermettereall’utentedicambiareilcoloreeladimensionedelpennello.Infinevedremocomefareinmodochel’utentepossasalvareipropridisegninelrullinofotograficodiiOS.
Parte 1: Creiamo la tavola grafica
Inquestaprimapartevedremocomefareinmodochel’utentepossa,muovendoilditosulloschermo,disegnareasuopiacimento.Perquestaapplicazionesfrutteremoiltemplate“UtilityApplication”,checipermettediaveregiàdueviste,inmododarisparmiare un po’ di lavoro.
Creiamo la struttura graficaCreiamounnuovoprogettoditipo“UtilityApplication”echiamiamolo“MyBrushes”.Comesempreassicuriamociche“Usestoryboard”e“IncludeUnitTest”sianodisattivati.Lastrutturacheabbiamoselezionatocimetteadisposizionedueviste:un“Main-ViewController”cheèquellacheappareinizialmente,eun“FlipsideViewController”,cheapparesevienepremuto ilpulsante“i” (info),conun’animazionegiàdefinita.Questedueviste,quindi,potremosfruttarlepercrearelanostraapplicazione:nella“MainView”creeremolazonaincuil’utentepotràdisegnare,mentrenella“Flipside-View”inseriremoleimpostazioni(quindiilcoloredelpennello,ladimensione,etc).Apriamoilfile“MainViewController.xib”,avremounavistacomequesta:
Fig. 10.1: MainV
iewC
ontroller.xib
132
Dobbiamomodificarneunpo’l’aspetto.Iniziamoscegliendocomecoloredisfon-doilbianco(lofatesemplicementedalpannello“AttributesInspector”).Cancellate,inoltre,ilbottone“i”cheèpresentenell’angolobassodestrodellavista.
Inserendosulfondodellavistauna“Toolbar”.Iohomessocomestiledellatoolbar“BlackTranslucent”,ovviamentevoipotetefarlacomepreferite.Inserite,inoltre,duebottoni“BarButtonItem”eun“FlexibleSpaceBarButtonItem”traidue.Ilprimobottonerinominateloin“Cancella”mentreilsecondoin“Impostazioni”.Ecco come si presenta per ora la vista:
Fig. 10.2: Struttura grafica m
odificataFig. 10.3: S
truttura grafica completa
Fattociò,dobbiamoinserireunaimageviewinmodocheoccupituttalavista:
133
Questa sarà l’area in cui l’utente potrà disegnare.
Dobbiamooracreare i collegamenti traquestioggetti e laclasse “MainViewCon-troller”.Comesempreapriamol’”AssistantEditor”ecreiamounoutletper la ima-geview,chiamandolo“viewDisegno”.Creiamoun’azioneperilbottone“Cancella”,chiamandola“cancellaDisegno”.Seaveteeseguitotuttocorrettamentelavostraclassesaràcosìdefinita:
Codice 10.1: File di interfaccia MainViewController.h
123
45678910
#import “FlipsideViewController.h”
@interface MainViewController : UIViewController <FlipsideViewCon-trollerDelegate>
@property (strong, nonatomic) IBOutlet UIImageView *viewDisegno;
- (IBAction)cancellaDisegno:(id)sender;- (IBAction)showInfo:(id)sender;
@end
Per ilbottone“Impostazioni”, invece, riutilizziamo l’azione“showInfo”cheeragiàstatadefinita.Percollegarlacon ilnostrobottonevibasteràutilizzare ilcerchiettochevedeteafiancodelnomedelmetodo:
Fig. 10.4: Collegamento bottone - metodo
c a P I t o l o 1 0M y B r u s h e s
134
Prendeteloecollegateloconilbottone,edilgiocoèfatto!Abbiamocosìconclusoladefinizionedell’interfacciagrafica,possiamodedicarciallascritturadelcodice.
Definiamo i metodi necessariApriamoilfile“MainViewController.h”,dobbiamodefinirealcunielementicheciser-viranno nel nostro programma. Ecco il codice da aggiungere:
Codice 10.2: Definizione degli elementi necessari
123
45678910111213141516
#import “FlipsideViewController.h”
@interface MainViewController : UIViewController <FlipsideViewCon-trollerDelegate> { // ultimo punto salvato CGPoint ultimoPunto; // parametri del pennello float dimensionePennello; UIColor *colorePennello;}
@property (strong, nonatomic) IBOutlet UIImageView *viewDisegno;
- (IBAction)cancellaDisegno:(id)sender;- (IBAction)showInfo:(id)sender;
@end
Analizziamoglielementicheabbiamoappenainserito.Allariga5abbiamodefinitounoggettoCGPoint,chenonèaltrocheuncontenitoredelleduecomponentidiunpunto: le coordinate x e y. Questo ci servirà per sapere il punto precedente a quello considerato,permettendocicosìditracciareunalinea.Allariga7e8definiamolecaratteristichedelpennello:dimensioneecolore.Ladimensioneèunavariabileditiporeale(float),mentreilcoloreèdefinitodaltipoUIColor,cheinizializzeremoconlostandardRGB.
Questesonoleunichedichiarazionidicuiabbiamobisogno.Salviamoquindiquestofileespostiamociin“MainViewController.m”.Leazionichedovremodefiniresarannodaimplementarein3metoditipicidellagestionedelmultitouch:
•touchesBegan,richiamatoquandosiiniziaunmovimento.Dovremoricavareilpun-toincuideveiniziareildisegno;
•touchesMoved,richiamatoquandounmovimentoèincorso.Ognivoltachevieneeseguitodevedisegnareunsegmentocolorato,inmodocheappaiaunalinearaf-figuranteildisegnodell’utente;
•touchesEnded,metodorichiamatoquandoilmovimentoèterminato.Inparticolareserve quando l’utente compie solo un singolo tap, disegnando così un semplice punto.
135
Iniziamo,quindi,ascrivereilcodicenecessario.Iniziamoconilmetodo“viewDidLoad”:
Codice 10.3: Definizione del metodo “viewDidLoad”
Codice 10.4: Definizione del metodo “touchesBegan:”
12345
6
12345
- (void)viewDidLoad { [super viewDidLoad]; // settimo i valori di default del pennello dimensionePennello = 5.0; colorePennello = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0];}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // ricaviamo l’ultimo punto toccato dall’utente UITouch *touch = [touches anyObject]; ultimoPunto = [touch locationInView:viewDisegno];}
Ilsuocompitoèmoltosempliceechiaro:vieneimpostataladimensionestandarddelpennelloa5.0(pixel)euncolore,inquestocasoilrosso.LadefinizionedelcoloreavvieneimpostandolecomponentiRGB,ovveroRed(rosso),Green(verde)eBlue(blu):variandolequantitàconunvalorecompresoda0.0a1.0otterremotuttiicoloriammessi da questo sistema. Il campo alpha, invece, rappresenta la trasparenza.Fatequalcheprovaperprendereconfidenzaconquestosistema!
Ilsecondometodocheandremoadefinireè“touchesBegan”.Lasuaimplementa-zioneèmoltosemplice,dobbiamosoloricavareilpuntodiiniziodelmovimento.Ecco il metodo da inserire:
Comevedeteèmoltosemplice:dall’oggettotouchsiricavailpunto(sempredefinitocomeCGPoint),chevieneassegnatoall’oggetto“ultimoPunto”(riga4).Èimportantespecificare“viewDisegno”comeparametrodellafunzione“locationIn-View”, altrimenti ricaverete tocchi relativi anche ad altre parti dello schermo, cheperòanoinoninteressano.
Passiamooraalladefinizionedelmetodopiù impegnativoe importante: “touche-sMoved”,chetrovereteinCodice11.5nellapaginasuccessiva.
c a P I t o l o 1 0M y B r u s h e s
136
Iniziamoadanalizzarepassopassoilcodicecheabbiamoappenascritto.Allariga7troviamoladefinizionediun“GraphicsContexts”.QuestoèunaspettocomplessodelCoreGraphics,mautilizzandoquestafunzioneloadattiamosempli-cementeallanostraUIImageView,cheinfattiglipassiamocomeparametro.Questocipermetteràdidisegnareall’internodellanostraimmagine.Conl’istruzioneseguen-te,infatti,definiamol’areaincuipotremodisegnare:essaavràlastessadimensionedell’immagine“viewDisegno”.Lerighe10e11siriferisconoalladefinizionedel“pennello”,concuil’utentedise-gneràsulloschermo.Prima,infatti,vieneimpostatalaforma(inquestocaso“kCGLi-neCapRound”èdiformacircolare,mapossiamoanchefarloquadratootriangolare,vibastaguardareladocumentazionepervedereglialtrivaloripossibili),poiladimen-sione,passandoovviamentelavariabile“dimensionePennello”,checontieneproprioilvaloredesiderato(riga11).Lerighe13e14servonoperimpostareilcolorealnostropennello.AbbiamovistoprimacheilcoloreèdefinitodaunelementoUIColor.PurtroppodobbiamoconvertirelevariecomponentiinCGFloat,el’unicomodochehotrovatoèquellochevedetenelcodice.
Codice 10.5: Definizione del metodo “touchesMoved:”
12345678
910
11
1213
14
151617
18
192021
22232425
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint puntoCorrente = [touch locationInView:viewDisegno]; // definiamo il contest grafico UIGraphicsBeginImageContext(viewDisegno.frame.size); [viewDisegno.image drawInRect:CGRectMake(0, 0, viewDisegno.frame.size.width, viewDisegno.frame.size.height)]; // settiamo la forma e la dimensione del pennello CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCa-pRound); CGContextSetLineWidth(UIGraphicsGetCurrentContext(), dimensio-nePennello); // convertiamo il colore e impostiamolo come colore del pennello const CGFloat *components = CGColorGetComponents([colorePennello CGColor]); CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), com-ponents[0], components[1], components[2], components[3]); // disegna il percorso CGContextBeginPath(UIGraphicsGetCurrentContext()); CGContextMoveToPoint(UIGraphicsGetCurrentContext(), ultimoPunto.x, ultimoPunto.y); CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), puntoCorrente.x, puntoCorrente.y); CGContextStrokePath(UIGraphicsGetCurrentContext()); // settiamo il disegno appena creato viewDisegno.image = UIGraphicsGetImageFromCurrentImageCon-text(); UIGraphicsEndImageContext(); ultimoPunto = puntoCorrente; }
137
Primaconvertiamol’elemento“colorePennello”inunarrayditipoCGFloat(riga13),poipassiamolevariecomponentiall’istruzionechesettailcoloredelpennello(riga14).Lerighedalla16alla19sonoquellechesioccupanodeldisegnoveroepropriodellalinea.Partendoda“ultimoPunto”finoa“puntoCorrente”vienedisegnatounalineachecomporràpoiiltrattovolutodall’utente.Lerighe21e22settanoildisegnocheèstatocreatonell’immagine“viewDisegno”,rendendola così visibile all’utente.Comevedetenonèpoicosìcomplicatoilcodice,peròbisognafareattenzioneanondimenticare nulla.
Mancapocoperterminarelaprimapartedelnostroprogramma!Dobbiamodefini-re ilmetodo“touchesEnded:”.Essodovràesattamentefare lestessecosechefailmetodo “touchesMoved:”. Potremmoanche non implementare questometodo,peròincasodisingolotapnonsuccederebbeniente,mentrenoivogliamodisegnareunsingolopunto.Ovviamentepotetepersonalizzarequestoaspetto,decidendovoil’azionedacompiere.Eccoilcodicedainseriresevoletecheadunsingolotapvengadisegnatounpunto:
Codice 10.6: Codice per disegnare un singolo punto
Codice 10.7: Implementazione del metodo “cancellaDisegno:”
1234
123
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { // disegniamo un solo punto [self touchesMoved:touches withEvent:event];}
- (IBAction)cancellaDisegno:(id)sender { viewDisegno.image = nil;}
Comevedetenonfacciamoaltrocherichiamareilmetodo“touchesMoved:”,davve-ro semplicissimo.
L’ultimacosadafareèl’implementazionedelmetodo“cancellaDisegno:”,chetrove-retegiàdefinitonellaclasse.Lasuaimplementazioneèmoltosemplice:percancel-laretuttociòchel’utentehadisegnatobastaporrea“nil”l’immagine:
Abbiamocompletatoquestaprimaparte!Clicchiamosu“Run”etestiamoilnostroBrushes!
c a P I t o l o 1 0M y B r u s h e s
138
Fig. 10.5: App 11 “MyBrushes”
139
Parte 2: Inseriamo le impostazioni
In questa seconda parte aggiungeremo la possibilità di cambiare la dimensione e il coloredelpennello,tramiteadun’appositavistadedicataalleimpostazioni.
Creiamo la struttura graficaIniziamoaprendoilfile“FilpsideViewController.xib”:quidovremoinserireicomponentiperpermettereall’utentedipoterselezionareuncoloreeladimensionedelpennello. Ricreate, quindi, una struttura come quella qui proposta:
Fig. 10.6: Struttura grafica im
postazioni
Comepotetevedere,ciservirannotresliderpercambiareilcoloredelpennello(piùunoperlatrasparenza,“alpha”),unaimageviewincuimostreremoilcolorecorrente,inmodochel’utentepossasubitovisualizzarequalecolorestaselezionando,unoslider e una label per la dimensione del pennello.
Dobbiamoancheimpostaredeivalorimassimieminimideivarislider.Selezioniamoilprimoslider(quelloperilrosso),spostiamociin“AttributesInspector”econtrolliamochecome“MinumumValue”siaimpostato“0”,mentrecome“MaximumValue”cisia“1”:
Fig. 10.7: Valori slider colore
c a P I t o l o 1 0M y B r u s h e s
140
Controllatecheancheirestantisliderdedicatialcoloreabbianoglistessivalorimas-simi e minimi. Per lo slider della dimensione, invece, dobbiamo impostare dei valori diversi.Iohoscelto1comeminimo,e25comemassimo.Ovviamentepotetevariarecome volete questi valori, magari effettuando delle prove.
Fig. 10.9: Azione cambiaColore
Fig. 10.8: Valori slider dimensione
Dobbiamooracreare icollegamenti traquestielementie laclasse“FlipsideView-Controller.h”,comeabbiamosemprefatto.Createglioutletdeiquattrosliderperilcolore, dell’image view, dello slider per la dimensione e della sua label. Le property appenadefinitedovrebberoesserecomeleseguenti:
Codice 10.8: Property definite
1234567
@property (strong, nonatomic) IBOutlet UISlider *sliderRosso;@property (strong, nonatomic) IBOutlet UISlider *sliderBlu;@property (strong, nonatomic) IBOutlet UISlider *sliderVerde;@property (strong, nonatomic) IBOutlet UISlider *sliderAlpha;@property (strong, nonatomic) IBOutlet UIImageView *viewColore;@property (strong, nonatomic) IBOutlet UILabel *labelDimensione;@property (strong, nonatomic) IBOutlet UISlider *sliderDimensione;
Ciservonoanchedueazioni:unachemodifichiilcolorenellaimageviewquandol’u-tentemuoveunodeglislider,l’altrocheaggiornilalabeldelladimensionedelpennello. Partendodalprimoslider(quelloperilcolorerosso)creiamoun’azionechiamandola“cambiaColore”eassociandolaall’evento“ValueChanged”:
141
Lastessaazionedeveancheesserecollegataaglialtrislider(blu,verdeealpha).Perfareciòutilizziamoilpallinoafiancodelnomedell’azione,comeabbiamovistonelparagrafoprecedente.Unavoltacollegateleazioniaivarislider,possiamocontrollarediaverese-guitoicollegamentiinmanieracorrettaspostandocinel“ConnectionsInspector”:
Fig. 10.10: Collegamenti
Completiamo creando anche un’azione collegata allo slider della dimensione delpennello, chiamando l’azione “cambiaDimensione”.Se avete eseguito tutti i pas-saggiinmanieracorrettalavostraclasse“FlipsideViewController”saràcosìdefinita:
Codice 10.9: File di interfaccia FlipsideViewController.h
123456
7891011
12131415161718192021222324
#import <UIKit/UIKit.h>
@class FlipsideViewController;
@protocol FlipsideViewControllerDelegate- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;@end
@interface FlipsideViewController : UIViewController
@property (weak, nonatomic) IBOutlet id <FlipsideViewControllerDele-gate> delegate;@property (strong, nonatomic) IBOutlet UISlider *sliderRosso;@property (strong, nonatomic) IBOutlet UISlider *sliderBlu;@property (strong, nonatomic) IBOutlet UISlider *sliderVerde;@property (strong, nonatomic) IBOutlet UISlider *sliderAlpha;@property (strong, nonatomic) IBOutlet UIImageView *viewColore;@property (strong, nonatomic) IBOutlet UILabel *labelDimensione;@property (strong, nonatomic) IBOutlet UISlider *sliderDimensione;
- (IBAction)done:(id)sender;- (IBAction)cambiaColore:(id)sender;- (IBAction)cambiaDimensione:(id)sender;
@end
c a P I t o l o 1 0M y B r u s h e s
142
Scriviamo il codice necessarioPrima di implementare il codice dei varimetodi, dobbiamo definire nel file “Flip-sideViewController.h”unpaiodielementicheciserviranno.Eccoledichiarazionidainserire:
Codice 10.10: Definizione degli elementi
123456
789101112131415
16171819202122232425262728293031
#import <UIKit/UIKit.h>
@class FlipsideViewController;
@protocol FlipsideViewControllerDelegate- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;@end
@interface FlipsideViewController : UIViewController { // proprietà del pennello UIColor *colorePennello; float dimensionePennello;}
@property (weak, nonatomic) IBOutlet id <FlipsideViewControllerDele-gate> delegate;@property (strong, nonatomic) IBOutlet UISlider *sliderRosso;@property (strong, nonatomic) IBOutlet UISlider *sliderBlu;@property (strong, nonatomic) IBOutlet UISlider *sliderVerde;@property (strong, nonatomic) IBOutlet UISlider *sliderAlpha;@property (strong, nonatomic) IBOutlet UIImageView *viewColore;@property (strong, nonatomic) IBOutlet UILabel *labelDimensione;@property (strong, nonatomic) IBOutlet UISlider *sliderDimensione;// proprietà del pennello@property (strong, nonatomic) UIColor *colorePennello;@property (nonatomic, assign) float dimensionePennello;
- (IBAction)done:(id)sender;- (IBAction)cambiaColore:(id)sender;- (IBAction)cambiaDimensione:(id)sender;
@end
Allerighe11e12abbiamodefinito leduecaratteristichedelpennellochevoglia-mopotermodificare: ilcoloree ladimensione.Perentrambeabbiamoanchede-finito le rispettive property (righe 24 e 25), in modo da poterle impostare anchedalla vista principale. Questi due valori li passeremo direttamente dalla “Main-View”,quindiè importantechecisiano ledueproprietà impostatecorrettamente. Passeremoiduevaloriinmodoche,all’aperturadelpannelloperlaloromodifica,l’u-tentetroviivaloricorrentichestautilizzando(quindicoloreedimensioneattuali).
Passiamonelfile“FlipsideViewController.m”einiziamoinserendoleduesynthesizedelleduepropertyappenadefinitecomeinCodice10.10nellapaginasuccessiva.
143
Iniziamodefinendoilmetodo“viewDidLoad”:
Codice 10.11: Definizione del metodo “viewDidLoad”
Codice 10.12: Definizione delle azioni collegate agli slider
1234
5678910
1112
123
45678
9
- (void)viewDidLoad { [super viewDidLoad]; // settiamo gli elementi con il valore attuale del pennello const CGFloat *components = CGColorGetComponents([colorePennello CGColor]); [self.sliderRosso setValue:components[0]]; [self.sliderVerde setValue:components[1]]; [self.sliderBlu setValue:components[2]]; [self.sliderAlpha setValue:components[3]]; [self.viewColore setBackgroundColor:colorePennello]; self.labelDimensione.text = [NSString stringWithFormat:@”%f”,dimensionePennello]; [self.sliderDimensione setValue:dimensionePennello];}
- (IBAction)cambiaColore:(id)sender { // impostiamo il colore nella image view [self.viewColore setBackgroundColor:[UIColor colorWithRed:self.sliderRosso.value green:self.sliderVerde.value blue:self.sliderBlu.value alpha:self.sliderAlpha.value]];}
- (IBAction)cambiaDimensione:(id)sender { // impostiamo la label con la dimensione del pennello self.labelDimensione.text = [NSString stringWithFormat:@”%f”,self.sliderDimensione.value];}
In questometodo inizializziamo i componenti grafici ai valori attuali del pennello.All’avviodell’applicazionetalivalorisarannorossoperilcoloree5.0perladimensione.
Questivalori,però,potrannoesserevariatidall’utente,quindiènecessariocheognivoltachevieneapertalavistadelleimpostazioniessisianoimpostaticorrettamente.Inparticolare,ricaviamodalcolorelevariecomponenti(comeabbiamogiàvistonellaprimapartedeltutorial),esettiamoilvaloredeglislider(righe5-8).
Allariga9impostiamoilcoloredellaimageview,checimostreràcosìilcoloredelnostro pennello. Leultimeduerighe,infine,servonoperricavareladimensionedelpennelloeinserirlanella label predisposta.
Dobbiamooradefinireledueazionicollegateaglislider.Eccoiduemetodi(davverosemplici) da implementare:
c a P I t o l o 1 0M y B r u s h e s
144
Ilprimometodo(“cambiaColore:”)nonfaaltrocheleggereivalorideglislidereset-tare lo sfondo dell’immagine con l’UIColor corrispondente. Il secondo metodo lavora nellostessomodo,solamentechevisualizzailvaloredellosliderdelladimensionedelpennello.Semplicivero?
Cimancasolounamodificaadunmetodogiàesistente.Dobbiamo,infatti,passareallaclasse“MainViewController” inuovivaloridelladimensioneedelcoloredelpennello.Modifichiamo,quindi,ilmetodo“done”nelseguentemodo:
Codice 10.13: Modifica del metodo “done:”
Codice 10.14: Modifica dell’intestazione del metodo
123
4
5
12
3
- (IBAction)done:(id)sender { dimensionePennello = self.sliderDimensione.value; colorePennello = [UIColor colorWithRed:self.sliderRosso.value green:self.sliderVerde.value blue:self.sliderBlu.value alpha:self.sliderAlpha.value]; [self.delegate flipsideViewControllerDidFinish:self nuovoColore:colorePennello nuovaDimensione:dimensionePennello];}
@protocol FlipsideViewControllerDelegate- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller nuovoColore:(UIColor*)colore nuovaDimensione:(float)di-mensione;@end
Abbiamo semplicemente letto il valore della dimensione e del colore del pennello, per poipassarloalmetodo“flipsideviewControllerDidFinish”,cheabbiamoopportunamen-temodificato(infattivedretechecisonodeinuoviparametricheglivengonopassati).Torniamoalfile“FlipsideViewController.h”emodifichiamoanche l’intestazionedelmetodochetroviamonell’iniziodellaclasse:
Abbiamoquasiterminato.Torniamoalfile“MainViewController.m”,dobbiamomodi-ficareduecose.Vogliamochechevengapassatoallavistadelleimpostazioniivaloridelpennello,e,viceversa,chevenganoaggiornatidopochel’utentelihavariati.
Iniziatemodificandoilmetodo“flipsideViewControllerDidFinish:”,chetrovategiànellavostra classe come vedete in Codice 10.15 nella pagina successiva.
145
Comepotetevedere,èstataperprimacosamodificata l’intestazionedelmetodo(proprioquellochevihoaccennatopocofa).Vengono,poi,aggiornatelecaratteristi-chedelpennello,inserendoilnuovocoloreelanuovadimensione.
Ilsecondometododamodificareè“showInfo:”,anch’essogiàdefinitodaXcode:
Codice 10.15: Modifica del metodo “flipsideViewControllerDidFinish:”
1
23
4
5678
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller nuovoColore:(UIColor*)colore nuovaDimensione:(float)di-mensione{ // aggiorniamo le proprietà del pennello const CGFloat *components = CGColorGetComponents([colore CGCo-lor]); colorePennello = [UIColor colorWithRed:components[0] green:components[1] blue:components[2] alpha:components[3]]; dimensionePennello = dimensione; // chiudiamo la vista delle impostazioni [self dismissModalViewControllerAnimated:YES];}
Codice 10.16: Modifica del metodo “showInfo:”
12
34
5678910
- (IBAction)showInfo:(id)sender { FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:@”FlipsideViewController” bundle:nil]; controller.delegate = self; controller.modalTransitionStyle = UIModalTransitionStyleFlipHo-rizontal; // passiamo i valori attuali del pennello controller.dimensionePennello = dimensionePennello; controller.colorePennello = colorePennello; // visualizziamo la vista delle impostazioni [self presentModalViewController:controller animated:YES];}
Aquestometodoaggiungiamosololerighe6e7,inmododapassareallavistadelleimpostazionileproprietàcorrentidelnostropennello(quellechevengonolettenelmetodo“viewDidLoad”cheabbiamoscrittoprima).
Abbiamoconclusoanchequestaparte!Cliccatesu“Run”etestatecheilvostropan-nellodelleimpostazionifunzionicorrettamente!
c a P I t o l o 1 0M y B r u s h e s
146
Fig. 10.11: App 11 Beta “MyBrushes”
147
Parte 3: Inseriamo il salvataggio del disegno
Vogliamooraaggiungereunafunzionemoltoimportante:ilsalvataggiodeldisegnocreato.Permetteremoall’utente,quindi,disalvareilsuodisegno,inmodochepossapoiritrovarlonelrullinofotografico.
Apriamoilfile“MainViewController.xib”,dobbiamoinserireunnuovobottonepercon-sentire all’utente di salvare il proprio disegno. Inseriamolo, come già fatto, nella toolbar:
Fig. 10.12: Inserimento bottone “S
alva”
Creiamoun’azioneperquestobottoneechiamiamola“salvaDisegno”.Eccocomedeveessereilfile“MainViewController.h”:
Codice 10.17: File di interfaccia MainViewController.h
123
4567891011121314151617
#import “FlipsideViewController.h”
@interface MainViewController : UIViewController <FlipsideViewCon-trollerDelegate> { // ultimo punto salvato CGPoint ultimoPunto; // parametri del pennello float dimensionePennello; UIColor *colorePennello;}
@property (strong, nonatomic) IBOutlet UIImageView *viewDisegno;
- (IBAction)cancellaDisegno:(id)sender;- (IBAction)showInfo:(id)sender;- (IBAction)salvaDisegno:(id)sender;
@end
c a P I t o l o 1 0M y B r u s h e s
148
Dobbiamo ora implementare questometodo “salvaDisegno:”, scrivendo il codicenecessario per salvare l’immagine nella galleria dell’utente. Eccolasuaimplementazione:
Codice 10.18: Codice per salvare l’immagine nella galleria
123
456
789101112131415161718
1920
- (IBAction)salvaDisegno:(id)sender { // salviamo l’immagine nel rullino UIImageWriteToSavedPhotosAlbum(viewDisegno.image, self, @selector(imageSavedToPhotosAlbum: didFinishSavingWithError: contextIn-fo:), nil);}
- (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { NSString *messaggio; NSString *titolo; // controlliamo se c’è stato un errore nel messaggio if (!error) { titolo = @”Salvataggio”; messaggio = @”Immagine salvata con successo”; } else { titolo = @”Errore”; messaggio = [error description]; } // visualizziamo un’alert mostrando l’esito del salvataggio UIAlertView *alert = [[UIAlertView alloc] initWithTitle:titolo message:messaggio delegate:nil cancelButtonTitle:@”OK” otherButtonTitles:nil]; [alert show]; }
Comepotetevedereilcodiceèmoltosemplice.Ilmetodosalvautilizzaunmetodochepermettedisalvarel’immaginenelrullinofotografico.Adesso,inoltre,èassocia-toilmetodo“imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:”,checontrollal’esitodelsalvataggiodeldisegno.Intalemetodo,infatti,nonvedreteistruzionichesioccupanodelsalvataggio,masolouncontrollose l’operazioneèandataabuonfine.Vieneinfatticontrollatalavariabile“error”(riga10),evieneim-postatounmessaggiodavisualizzarepoiall’utente,chedovràessereavvisatoseil salvataggio si è concluso correttamente oppure se ci sono stati degli errori. Tutto moltosemplicecomesempre!
Abbiamodavveroconcluso!Clicchiamosu“Run”egodiamociilnostropersonalis-simoMyBrushes!
149
Fig. 10.13a: App 11 “MyBrushes” Fig. 10.13b: App 11 “MyBrushes”
c a P I t o l o 1 0M y B r u s h e s
151
c o n c l u s I o n I
Conclusioni
Eora?Dopoaverlettoquestolibrononpotete,ovviamente,considerarvidegliespertiprogrammatori per dispositivi iOS. Avete avuto, comunque, un’ampia panoramica dicosasiaXcode,dicomesisviluppinodellesempliciapplicazioniperiPhone.Percontinuare la vostra avventura in questo mondo dovrete provare, studiare, navigare alla ricerca di nuovi spunti per imparare sempre qualcosa di nuovo. La rete offre davvero tanto sull’argomento, ovviamente il vostro primo riferimento deve sempre essereladocumentazioneufficialediApple.
Vi consiglio, inoltre, di visitare spesso devAPP, la community italiana più grande sullaprogrammazioneperiPhone.Litroverete,oltreame,davverotantepersonedispo-nibili e ben preparate, disposte sempre a darvi una mano. Insomma, tenete devAPP semprecomeprimositodiriferimento;)
Quanto a me, se questo libro vi è piaciuto e lo trovate interessante diffondete la voce, consigliatelo ai vostri amici.
Perqualsiasiinformazione,oppurepersegnalazionisullibroosullaprogrammazio-ne,oanchesoloper fareduechiacchiere,contattatemiutilizzando ilmodulochetrovatesuBubiDevs(http://www.bubidevs.net/contattami).
Allaprossima!
153
B I B l I o g r a f I a
Bibliografia
I miei tutorial spesso sono nati prendendo spunto da altre guide trovate su Internet, scrittedaaltrepersone.Miparecorretto,quindi,riportarediseguitoilinkdacuihopresol’ispirazioneperscriverealcunitutorial,chesonocomunquestatiriadattatieriscrittiingranparte.Ilgiustomerito,comunque,spettaancheailegittimiautori.
Capitolo 3:iPhoneDevelopmentCentral,“TrashTutorial”http://www.iphonedevcentral.org/tutorials.php?page=ViewTutorial&id=30&uid=68863015
Capitolo 6:iPhoneNoob,“AccesstheAddressBook”http://iphone.zcentric.com/2008/09/19/access-the-address-book
Capitolo 9:TheAppleBlog,“iPhoneSDKTutorial:BuildaSimpleRSSreaderfortheiPhone”http://theappleblog.com/2008/08/04/tutorial-build-a-simple-rss-reader-for-iphone
Capitolo 10:iPodTouchFansForum,“[Tutorial]Drawingtothescreen”http://www.ipodtouchfans.com/forums/showthread.php?t=132024
Tutorial pratici per iOS SDK
Scritto da Andrea Busi
Copyright © 2011 - BubiDevsemail:[email protected]
url: www.bubidevs.net
L’autorehacuratolaformascrittael’impaginazionediquestolibro,manonvienedatanessunagaranziaespressaoimplicitadialcuntipoenonsiassumealcunare-sponsabilità per eventuali errori o omissioni. Si declina ogni responsabilità per danni accidentalioconsequenzialiinrelazioneoderivantidall’usodelleinformazioniodeiprogrammi in esso contenute.
Nomiemarchicitatineltestosonogeneralmentedepositatioregistratidallerispet-tive case produttrici.
L’impaginazionegraficaèstatacuratadaMatteoSalvi.
Versione 2.0
Novembre 2011