{"id":9171,"date":"2012-07-02T16:48:22","date_gmt":"2012-07-02T14:48:22","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=9171"},"modified":"2012-07-02T16:48:22","modified_gmt":"2012-07-02T14:48:22","slug":"masterdetail-the-right-way-impariamo-a-pensare-ad-oggetti","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/masterdetail-the-right-way-impariamo-a-pensare-ad-oggetti\/","title":{"rendered":"MasterDetail: the right way! Impariamo a pensare ad oggetti."},"content":{"rendered":"<p>Chiedo anticipatamente scusa se il titolo pu\u00f2 apparire presuntuoso, la programmazione non \u00e8 una scienza esatta, quindi non esiste una vita completamente esatta o una completamente errata, ma esistono delle varie sfumature di grigio in cui il programmatore si deve muovere decidendo di volta in volta se prediligere un&#8217;aspetto piuttosto che un&#8217;altro.<br \/>\nInsomma come sempre si tratta di trovare il giusto compromesso.<\/p>\n<p>Ma allora qual \u00e8 l&#8217;obbiettivo di questo articolo? Beh l&#8217;idea \u00e8 quella di rispondere a tutti gli utenti che sul nostro forum hanno chiesto aiuto nella realizzazione di un&#8217;applicazione master\/detail fornendo non soltanto una guida su come creare rapidamente questo tipo di applicazioni, ma anche come farlo ragionando in termini di OOP e rispettando il paradigma MVC.<!--more--><\/p>\n<p>L&#8217;applicazione che andremo a realizzare \u00e8 molto semplice, visualizzer\u00e0 in una tabella l&#8217;elenco dei clienti, cliccando su ciascuno di questi verr\u00e0 visualizzata la view con i dettagli e da questa view cliccando sull&#8217;avatar si potr\u00e0 visualizzare la foto ingrandita, mentre cliccando sull&#8217;indirizzo si accede alla mappa.<\/p>\n<p>Ecco il mockup dell&#8217;applicazione:<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/master-detail-the-right-way-01.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/master-detail-the-right-way-01.jpg\" alt=\"master-detail-the-right-way-01\" title=\"master-detail-the-right-way-01\" width=\"550\" height=\"487\" class=\"aligncenter size-full wp-image-9176\" \/><\/a><br \/>\n<\/center><\/p>\n<p>Per chi fosse interessato questo mockup \u00e8 stato realizzato con Balsamiq, un software a pagamento (anche se la demo \u00e8 perfettamente funzionante) che trovate a questo indirizzo: <a href=\"http:\/\/www.balsamiq.com\/\" target=\"_blank\">http:\/\/www.balsamiq.com\/<\/a><\/p>\n<p>Ok, abbiamo tutte le indicazioni che ci servono per realizzare la nostra app, quindi aprite XCode e iniziate a digitare&#8230;. FERMI TUTTI! La prima cosa da fare quando si parte con lo sviluppo di un&#8217;applicazione \u00e8 stare fermi e pensare cosa fare, poi quando crediamo di aver pensato abbastanza, ci fermiamo ancora e cerchiamo di capire se quello che abbiamo pensato \u00e8 giusto.<\/p>\n<p>Iniziamoci a chiedere in quanti viewcontroller dividiamo l&#8217;applicazione.<\/p>\n<p>La risposta potrebbe esere uno per la tabella, uno per i dettagli e&#8230;. e poi? Siamo sicuri che servano due viewcontroller per la foto e la mappa? non posso semplicemente sostituire una imageview con una mapview a seconda del tasto che ho premuto nella schermata precedente? Certo lo potrei fare&#8230; anzi potrei anche caricare tutto sullo stesso viewcontroller se volessi, perch\u00e9 non lo faccio? Ci sono dei pro\/contro in termini di prestazioni?<\/p>\n<p>Non ci sono n\u00e9 pro e n\u00e9 contro puramente tecnici, ma le performance di un&#8217;applicazione sono soltanto uno degli obbiettivi del programmatore, il cui compito \u00e8 quello di scrivere codice chiaro e manutenibile.<br \/>\nPer raggiungere l&#8217;obbiettivo della chiarezza e semplicit\u00e0 del codice non si pu\u00f2 prescindere dal suddividere quanto pi\u00f9 possibile compiti e responsabilit\u00e0.<\/p>\n<p>Se in un&#8217;azienda non ci fossero suddivisioni di ruoli e responsabilit\u00e0 a chi vi rivolgereste se aveste  qualche problema? Allo stesso modo in un&#8217;applicazione, se non ci sono netti confini su chi fa una cosa e chi ne fa un&#8217;altra, si crea un caos nel quale \u00e8 impossibile anche solo manutenere il codice.<br \/>\nQuando si parla di manutenibilit\u00e0 del codice spesso si tende a sottovalutare il problema, perch\u00e9 lo si pensa come qualcosa fissato in un ipotetico futuro lontano. Io dico che il problema della manutenibilit\u00e0 del codice \u00e8 presente in tutti i progetti che per essere completati richiedono pi\u00f9 di due ore, perch\u00e9 in un pomeriggio si pu\u00f2 scrivere tanto di quel codice che se non \u00e8 strutturato bene gi\u00e0 l&#8217;indomani mattina non riesci pi\u00f9 a prenderne le fila.<\/p>\n<p>Tornando quindi al numero dei viewcontroller la mia risposta \u00e8 cinque.<\/p>\n<p>Cinque? Si cinque:<\/p>\n<ul>\n<li>uno per la tabella,<\/li>\n<li>uno per i dettagli,<\/li>\n<li>uno per la foto,<\/li>\n<li>uno per la mappa,<\/li>\n<li>e uno per il navigationviewcontroller!<\/li>\n<\/ul>\n<p>Adesso possiamo iniziare a programmare?<\/p>\n<p>Non ancora, abbiamo visto i viewcontroller, ma non abbiamo parlato del model!<\/p>\n<p>Il modello \u00e8 forse l&#8217;aspetto pi\u00f9 sottovalutato tra tutti i nostri utenti pi\u00f9 &#8220;giovani&#8221; nel mondo della OOP.<\/p>\n<p>Alla base del concetto della programmazione OOP c&#8217;\u00e8 la volont\u00e0 di creare una rappresentazione della realt\u00e0 all&#8217;interno dei nostri programmi. Non parlo di realt\u00e0 aumentata, non fraintendetemi. Dico che uno degli scopi della programmazione OOP \u00e8 quello di renderla un&#8217;attivit\u00e0 pi\u00f9 simile a quello che il nostro cervello \u00e8 abituato a fare tutti i giorni: interagire e far interagire tra di loro gli oggetti.<\/p>\n<p>Chi conosce almeno un linguaggio non ad oggetti, come ad esempio il C, sa bene che non si ha quasi mai a che fare con variabili che rappresentano qualcosa di &#8220;tangibile&#8221; o comunque appartenenti alla relt\u00e0, ma si ha a che fare con variabili char, int e via cos\u00ec. In OOP e nello specifico con iOS, invece, hai per esempio l&#8217;oggetto UIButton che cerca di mappare all&#8217;interno di un programma il concetto di &#8220;pulsante&#8221;.<\/p>\n<p>Il nostro compito di programmatori \u00e8 muoverci in questo ambito, capire l&#8217;universo del problema che dobbiamo risolvere e creare i nostri oggetti che mappino le entit\u00e0 di questo universo.<\/p>\n<p>Nel nostro esempio \u00e8 presto detto, visto che parliamo di clienti sar\u00e0 opportuno creare una classe Cliente e sar\u00e0 questa il nostro modello.<\/p>\n<p>Adesso possiamo iniziare a programmare? Non ancora, abbiamo un&#8217;idea generale di quali classi aggiungere, ma prima di iniziare veramente a scrivere codice \u00e8 bene scendere in dettaglio ed esaminare propriet\u00e0 e metodi di ciascuna classe.<\/p>\n<h4>Iniziamo con la classe Cliente.<\/h4>\n<p>In questa applicazione il nostro concetto di cliente ha le seguenti caratteristiche:<\/p>\n<ul>\n<li>ha un nome<\/li>\n<li>ha un cognome<\/li>\n<li>ha un indirizzo<\/li>\n<li>ha un avatar<\/li>\n<li>ha una foto ad alta risoluzione<\/li>\n<\/ul>\n<p>Per quanto riguarda nome e cognome possiamo tranquillamente considerarle delle stringhe, l&#8217;avatar e la foto sono sicuramente due immagini, ma l&#8217;indirizzo?<\/p>\n<p>Se abbiamo un p\u00f2 di lungimiranza sappiamo che per visualizzare il pin sulla mappa non \u00e8 sufficiente avere un indirizzo, ma questo indirizzo deve essere geolocalizzato, in pratica servono le coordinate geografiche di latitudine e longitudine.<\/p>\n<p>Chi,secondo voi, dovrebbe occuparsi di fare questo lavoro all&#8217;interno de nostro programma?<\/p>\n<p>Possono esserci pi\u00f9 risposte, pi\u00f9 o meno valide ed \u00e8 bene, quando ci si trova davanti ad un problema come questo, confrontarsi con qualche altro sviluppatore per esaminare attentamente pro e contro di ogni singola soluzione.<\/p>\n<p>A me sono venute in mente un paio di soluzioni, alla fine ne ho scelta una, ma piuttosto che la pappa pronta voglio spiegarvi il ragionamento che mi ha portato a questa soluzione esaminando prima le varie possibilit\u00e0 che sono state scartate:<\/p>\n<ol>\n<li>Il lavoro di geocoding lo effettua la mappa alla quale si passa l&#8217;indirizzo.<br \/>\n<strong>pro:<\/strong> sicuramente la soluzione pi\u00f9 semplice che si possa pensare, non modifichiamo la classe Cliente e siamo in grado di visualizzare qualsiasi indirizzo.<br \/>\n<strong>contro:<\/strong> il contro pi\u00f9 evidente \u00e8 che ad ogni visualizzazione deve essere fatta una richiesta al server, si potrebbe ovviare con un meccanismo di cache, ma non sarebbe semplice da implementare.<\/li>\n<li>Modifichiamo la classe Cliente e inseriamo all&#8217;interno un metodo privato che effettua il geocoding, poi aggiungiamo due variabili di latitudine e longitudine e ogni volta che modifichiamo l&#8217;indirizzo del cliente effettuiamo il geocoding memorizzando le informazioni.<br \/>\n<strong>pro:<\/strong> sembra un metodo piuttosto ragionevole, possiamo memorizzare i dati di lat e lng, la mappa riceverebbe direttamente le coordinate.<br \/>\n<strong>contro:<\/strong> dal punto di vista dell&#8217;architettura la classe Cliente non ha proprio senso che includa del codice per la geolocalizzazione. Se oltre ai clienti dovessimo aggiungere anche i Fornitori dovremmo forse duplicare il codice? oppure fare una subclass solo perch\u00e9 mantengano la geolocalizzazione come metodo in comune? Se per ciascun cliente dovessi memorizzare pi\u00f9 di un indirizzo? Diciamo che domani voglio memorizzare un numero arbitrario di indirizzi per ciascun cliente&#8230; devo aggiungere quanti campi latitudine e longitudine per poterne tenere conto?<\/li>\n<li>A questo punto la soluzione che mi \u00e8 sembrata pi\u00f9 corretta:<br \/>\nCreo una specifica classe per l&#8217;indirizzo, che ha al suo interno la logica di geocoding e memorizzazione delle coordinate geografiche.<br \/>\n<strong>pro:<\/strong> posso avere pi\u00f9 indirizzi per ogni cliente, o anche altre classi che non siano clienti e tutto funziona, ottengo un sistema di cache<br \/>\n<strong>contro:<\/strong> non me ne vengono in mente \ud83d\ude09<\/li>\n<\/ol>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/master-detail-the-right-way-02.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/master-detail-the-right-way-02.png\" alt=\"master-detail-the-right-way-02\" title=\"master-detail-the-right-way-02\" width=\"470\" height=\"204\" class=\"aligncenter size-full wp-image-9177\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/master-detail-the-right-way-02.png 470w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/master-detail-the-right-way-02-300x130.png 300w\" sizes=\"auto, (max-width: 470px) 100vw, 470px\" \/><\/a><br \/>\n<\/center><\/p>\n<h4>Il Viewcontroller principale<\/h4>\n<p>Servir\u00e0 per mostrare l&#8217;elenco dei clienti, avr\u00e0 come variabile di istanza un&#8217;array di clienti, ma non sar\u00e0 accessibile pubblicamente perch\u00e9 il viewcontroller provveder\u00e0 autonomamente a inserire all&#8217;interno di questa lista tutti i clienti memorizzati.<\/p>\n<h4>Il viewcontroller di dettaglio<\/h4>\n<p>Chiediamoci che stato interno pu\u00f2 avere? E quali sono i suoi metodi che posso richiamare dall&#8217;esterno?<br \/>\nRagionando un p\u00f2 ci rendiamo conto che lo stato interno del viewcontroller cambia in funzione del cliente che deve essere visualizzato, quindi il viewcontroller avr\u00e0 un metodo che verr\u00e0 utilizzato per settare quale cliente visualizzare.<\/p>\n<h4>Mappa e foto<\/h4>\n<p>Per  i restanti due viewcontroller aggiungerei soltato una variabile di istanza di tipo UIImage e una di tipo NSString per memorizzare l&#8217;indirizzo da visualizzare.<\/p>\n<p>Ragionando in questi termini abbiamo ottenuto una  notevole seprazione dei ruoli, se volessimo ad esempio sostiture il viewcontroller delle mappe con uno pi\u00f9 complesso tutto il resto della nostra app resterebbe illeso!<\/p>\n<p>Adesso finalmente possiamo iniziare a programmare. Apriamo quindi XCode e selezioniamo il template &#8220;master detail&#8221;, specifichiamo applicazione solo per iPhone e chiamiamola &#8220;learnMasterDetail&#8221;. Abilitate il supporto ARC e delezionate l&#8217;opzione per utilizzare CoreData.<\/p>\n<p>Se non avete mai utilizzato questo template prendetevi qualche minuto per verificarne il funzionamento.<\/p>\n<p>Il progetto si presenta con due file AppDelegate.h e .m e due viewcontroller, il MasterViewController e DetailViewController.<\/p>\n<p>Iniziamo a creare le classi che costituiranno il nostro model, quindi file -> new -> file e scegliamo &#8220;Objective-C Class&#8221; chiamiamola Location e creiamola come subclass di NSObject.<\/p>\n<p>Aggiungiamo quindi nel file Location.h la dichiarazione delle nostre property, il file completo \u00e8 questo:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n#import <Foundation\/Foundation.h\r\n@interface Location : NSObject\r\n@property (nonatomic, strong) NSString *address;\r\n@property (nonatomic, assign) double lat;\r\n@property (nonatomic, assign) double lng;\r\n@end\r\n<\/pre>\n<p>Una volta dichiarate le property bisogna aggiungere il @syntetize, in questo caso ho deciso di segure la consuetudine che vuole che il nome della variabile di istanza sia precedura da un underscore, mentre il metodo ha il nome della property.<\/p>\n<p>Il file .m completo \u00e8 questo:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n#import \"Location.h\"\r\n@implementation Location\r\n@synthesize address = _address;\r\n@synthesize lng = _lng;\r\n@synthesize lat = _lat;\r\n@end\r\n<\/pre>\n<p>Se vi state chiedendo del perch\u00e9 di questa dichiarazione, diciamo brevemente che in questo modo \u00e8 chiaro che se scrivo _lng = 0 accedo direttamente alla variabile di istanza, mentre se scrivo self.lng accedo al metodo.<\/p>\n<p>Dobbiamo creare adesso la classe Cliente, seguiamo la stessa procedura fatta precedentemente e aggiungiamo i due file Cliante.h e Cliente.m.<\/p>\n<p>Importiamo il file Location.h e dichiariamo le property.<br \/>\nRiporto qui i file completi .h e .m<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n#import <Foundation\/Foundation.h>\r\n#import \"Location.h\"\r\n@interface Cliente : NSObject\r\n@property (nonatomic, strong) NSString *nome;\r\n@property (nonatomic, strong) NSString *cognome;\r\n@property (nonatomic, strong) Location *defaultLocation;\r\n@property (nonatomic, strong) UIImage *thumb;\r\n@property (nonatomic, strong) UIImage *fullImage;\r\n@end\r\n-----------------------------------------------------------------------------------------------------------------------\r\n#import \"Cliente.h\"\r\n@implementation Cliente\r\n@synthesize nome = _nome;\r\n@synthesize cognome = _cognome;\r\n@synthesize defaultLocation = _defaultLocation;\r\n@synthesize thumb = _thumb;\r\n@synthesize fullImage = _fullImage;\r\n@end\r\n<\/pre>\n<p>Ottimo, adesso che abbiamo le classi pi\u00f9 importanti del nostro model passiamo al primo viewcontroller, il MasterViewController.<\/p>\n<p>Selezioniamo quindi il file MasterViewController.h e aggiugiamo in alto:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n#import \"Cliente.h\"\r\n#import \"Location.h\"\r\n<\/pre>\n<p>Questa classe sembra non avere alcuna variabile di istanza, ma invece non \u00e8 cos\u00ec. Per una maggiore pulizia del codice gli ingegnieri Apple hanno posto la variabile di istanza all'interno di una category (se non sapete cos`\u00e8 c'\u00e8 un <a href=\"http:\/\/www.devapp.it\/wordpress\/t083-aggiungiamo-funzionalita-agli-oggetti-le-categorie.html\" target=\"_blank\">articolo su devAPP<\/a>) troviamo infatti una variabile dichiarata nel file .m ed in particolare in questo blocco:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface MasterViewController () {\r\n    NSMutableArray *_objects;\r\n}\r\n@end\r\n<\/pre>\n<p>Il termine _objects \u00e8 abbastanza generico e possiamo riciclarlo per la nostra app, infatti questo array conterr\u00e0 l'elenco dei clienti da visualizzare.<\/p>\n<p>Prima di continuare eliminiamo tutto ci\u00f2 che non ci serve da questo file, il quale contiene tutti i metodi per aggiungere delle righe a runtime, ma che a noi non servono.<\/p>\n<p>Quindi partendo dal metodo viewDidLoad:<\/p>\n<p>Lo riduciamo all'osso, con la sola chiamata alla superclass. Non l'ho tolto del tutto perch\u00e9 ci servir\u00e0 tra un attimo.<br \/>\nTogliamo del tutto il metodoo insertNewObject: e anche il metodo tableView:canEditRowAtIndexPath:<br \/>\nTogliamo anche il metodo:<br \/>\ntableView:commitEditingStyle:forRowAtIndexPath:<\/p>\n<p>Provate a compilare, se non avete eliminato nulla di fondamentale l'applicazione dovrebbe continuare a girare, con l'unica differenza che non sono pi\u00f9 presenti le funzionalit\u00e0 per modificare la tabella e per aggiungere una nuova riga.<\/p>\n<p>Dobbiamo ora creare i nostri clienti, quindi all'interno del metodo viewDidLoad li creiamo e li memorizziamo all'interno dell'array _objects.<br \/>\nUtilizziamo questo codice:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n _objects = [[NSMutableArray alloc] init];\r\n    for (int i = 0; i < 20; i++) {\r\n        Cliente *c = [[Cliente alloc] init];\r\n        c.nome = [NSString stringWithFormat:@\"Nome_%i\", i];\r\n        c.cognome = [NSString stringWithFormat:@\"Cognome_%i\", i];\r\n        c.thumb     = [UIImage imageNamed:@\"thumb_%i.png\"];\r\n        c.fullImage = [UIImage imageNamed:@\"fullsize_%i.png\"];\r\n       \r\n        Location *loc = [[Location alloc] init];\r\n        loc.address = @\"Milano, piazza duomo\";\r\n       \r\n        c.defaultLocation = loc;\r\n       \r\n        [_objects addObject:c];\r\n       \r\n    }\r\n<\/pre>\n<p>Come funziona questo codice dovrebbe essere abbastanza chiaro, in pratica vengono creati 20 clienti e per ciascuno vengono settate le propriet\u00e0 con una sequenza come Nome_1, Nome_2.<br \/>\nIn questo modo ci risparmiamo di digitare nomi casuali e abbiamo anche un certo controllo sugli errori.<\/p>\n<p>Le immagini sono anche quelle numerate progressivamente e se non avete voglia di trovare 40 immagini e non conoscete <em>imagemagik<\/em>, potete trovare quelle che ho generato io all'interno del progetto.<\/p>\n<p>Ciascun cliente \u00e8 cosi aggiunto all'elenco.<\/p>\n<p>Provate a compilare e dovreste ottenere questo risultato:<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/master-detail-the-right-way-03.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/master-detail-the-right-way-03.jpg\" alt=\"master-detail-the-right-way-03\" title=\"master-detail-the-right-way-03\" width=\"350\" height=\"681\" class=\"aligncenter size-full wp-image-9178\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/master-detail-the-right-way-03.jpg 350w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2012\/07\/master-detail-the-right-way-03-154x300.jpg 154w\" sizes=\"auto, (max-width: 350px) 100vw, 350px\" \/><\/a><br \/>\n<\/center><\/p>\n<p>Non \u00e8 proprio il massimo, ma \u00e8 gi\u00e0 un buon inizio.<\/p>\n<p>Modifichiamo quindi il metoto che si occupa di disegnare la cella:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath\r\n<\/pre>\n<p>Modifichiamolo per viasualizzare il nome e cognome del contatto:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath\r\n{\r\n    static NSString *CellIdentifier = @\"Cell\";\r\n   \r\n    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];\r\n    if (cell == nil) {\r\n        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];\r\n        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;\r\n   }\r\n    Cliente *object = [_objects objectAtIndex:indexPath.row];\r\n    cell.textLabel.text = [NSString stringWithFormat:@\"%@ %@\", object.cognome, object.nome];\r\n    return cell;\r\n}\r\n<\/pre>\n<p>Come \u00e8 evidente dal metodo abbiamo estratto un oggetto di tipo Cliente dall'array e le sue propriet\u00e0 di nome e cognome vengono visualizzate sulla cella.<\/p>\n<p>Potete provare a cliccare su un elemento, vi porter\u00e0 al DetailViewController, ma dobbiamo modificarlo affinch\u00e9 mostri i dettagli del cliente selezionato.<\/p>\n<h4>DetailViewController<\/h4>\n<p>Modifichiamo il DetailViewController importando le classi del model e cambiando il tipo di variabile.<\/p>\n<p>Il DetailViewController.h finito appare cos\u00ec:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n#import <UIKit\/UIKit.h>\r\n#import \"Cliente.h\"\r\n#import \"Location.h\"\r\n@interface DetailViewController : UIViewController\r\n@property (strong, nonatomic) Cliente *detailItem;\r\n@end\r\n<\/pre>\n<p>Creiamo in Interface Builder la grafica del viewcontroller e colleghiamo gli IBOutlet. Questo non dovrebbe causare molto difficolt\u00e0 se avete pi\u00f9 o meno capito come funzione XCode, in ogni caso questi sono i due file di classe, nel progetto trovate il file xib.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n#import <UIKit\/UIKit.h>\r\n#import \"Cliente.h\"\r\n#import \"Location.h\"\r\n@interface DetailViewController : UIViewController {\r\n   \r\n}\r\n@property (strong, nonatomic) Cliente *detailItem;\r\n@property (strong, nonatomic) IBOutlet UILabel *nome;\r\n@property (strong, nonatomic) IBOutlet UILabel *cognome;\r\n@property (strong, nonatomic) IBOutlet UIImageView *thumb;\r\n@property (strong, nonatomic) IBOutlet UILabel *indirizzo;\r\n@end\r\n----------------------------------------------------------------------------------------------------------------------------\r\n#import \"DetailViewController.h\"\r\n@interface DetailViewController ()\r\n- (void)configureView;\r\n@end\r\n@implementation DetailViewController\r\n@synthesize detailItem = _detailItem;\r\n@synthesize nome = _nome;\r\n@synthesize cognome = _cognome;\r\n@synthesize thumb = _thumb;\r\n@synthesize indirizzo = _indirizzo;\r\n#pragma mark - Managing the detail item\r\n- (void)setDetailItem:(id)newDetailItem\r\n{\r\n    if (_detailItem != newDetailItem) {\r\n        _detailItem = newDetailItem;\r\n       \r\n        \/\/ Update the view.\r\n        [self configureView];\r\n    }\r\n}\r\n- (void)configureView\r\n{\r\n    \/\/ Update the user interface for the detail item.\r\n    if (self.detailItem) {\r\n        self.nome.text = self.detailItem.nome;\r\n        self.cognome.text = self.detailItem.cognome;\r\n        self.indirizzo.text = self.detailItem.defaultLocation.address;\r\n        self.thumb.image = self.detailItem.thumb;\r\n    }\r\n}\r\n- (void)viewDidLoad\r\n{\r\n    [super viewDidLoad];\r\n        \/\/ Do any additional setup after loading the view, typically from a nib.\r\n    [self configureView];\r\n}\r\n- (void)viewDidUnload\r\n{\r\n    [self setNome:nil];\r\n    [self setCognome:nil];\r\n    [self setThumb:nil];\r\n    [self setIndirizzo:nil];\r\n    [super viewDidUnload];\r\n    \/\/ Release any retained subviews of the main view.\r\n}\r\n- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation\r\n{\r\n    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);\r\n}\r\n- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil\r\n{\r\n    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];\r\n    if (self) {\r\n        self.title = NSLocalizedString(@\"Detail\", @\"Detail\");\r\n    }\r\n    return self;\r\n}\r\n                                                        \r\n@end\r\n<\/pre>\n<p>Il punto focale di questa classe \u00e8 il setter della variabile detailItem.<\/p>\n<p>Quando viene impostato un nuovo detailItem la classe chiama un secondo metodo che si chiama configureView. In questo metodo si recuperano le propriet\u00e0 dell'oggetto passato e si mostrano sullo schermo.<\/p>\n<p>Come potete vedere non c'\u00e8 nessun riferimento al precedente viewController, n\u00e9 tantomeno il MasterViewcontroller decide cosa visualizzare sulle label del DetailViewcontroller!<br \/>\nIl tutto avviene in una maniera molto elegante.<\/p>\n<h4>Esercitazione a sorpresa \ud83d\ude1b<\/h4>\n<p>Adesso una piccola sopresa per chi ha letto fino a qui... la restante parte del progetto \u00e8 una piccola sfida per tutti voi! Provate, con la base e con le specifiche che ho fornito in questo articolo, ad aggiungere le due funzionalit\u00e0 mancanti:<\/p>\n<ul>\n<li>la visualizzazione della foto in grande<\/li>\n<li>e la visualizzazione sulla mappa<\/li>\n<\/ul>\n<p>Postate le vostre soluzioni sul forum e verranno commentate. Se preferite mantenere l'anonimato nessun problema, mandatemi il progetto con un messaggio privato, vi risponder\u00f2 sul forum senza citare il nome dell'autore!<\/p>\n<p>Buona programmazione!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Chiedo anticipatamente scusa se il titolo pu\u00f2 apparire presuntuoso, la programmazione non \u00e8 una scienza esatta, quindi&#8230;<\/p>\n","protected":false},"author":53,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[8],"tags":[1137,1140,18,1138,1139],"class_list":["post-9171","post","type-post","status-publish","format-standard","hentry","category-guide-varie","tag-master-detail-ios","tag-mvc-ios","tag-oop","tag-pensare-ad-oggetti","tag-progettare-applicazione-iphone"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9171","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/53"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=9171"}],"version-history":[{"count":16,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9171\/revisions"}],"predecessor-version":[{"id":9191,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/9171\/revisions\/9191"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=9171"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=9171"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=9171"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}