{"id":3651,"date":"2010-06-10T09:30:55","date_gmt":"2010-06-10T07:30:55","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=3651"},"modified":"2010-11-05T16:36:42","modified_gmt":"2010-11-05T15:36:42","slug":"t051-accedere-a-flickr-dalle-nostre-applicazioni-iphone-parte-4","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/t051-accedere-a-flickr-dalle-nostre-applicazioni-iphone-parte-4\/","title":{"rendered":"T#051 &#8211; Accedere a Flickr dalle nostre applicazioni iPhone (parte 4)"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" class=\"alignleft\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/05\/logo_flickr.jpg\" alt=\"\" width=\"223\" height=\"111\" \/> Ed eccoci arrivati all&#8217;ultima puntata di questo lungo tutorial, vedremo oggi come realizzare una semplice interfaccia grafica per il nostro programma.<\/p>\n<p>L&#8217;idea \u00e8 quella di realizzare un&#8217;unica schermata con quattro pulsanti, due per navigare avanti e indietro nell&#8217;elenco delle foto, un pulsante per salvare la foto su nostro telefono, e un pulsante per avviare il download del file xml da internet.<!--more--><\/p>\n<p>Aggiungeremo inoltre: una label per visualizzare alcune informazioni, un controllo UIActivityview da visualizzare durante le attese, un controllo UIImageView per visualizzare l&#8217;immagine, un array dove memorizzare gli URL delle foto e un intero che memorizzi l&#8217;indice della foto che stiamo visualizzando.<\/p>\n<p>Un bel po&#8217; di roba, no?<\/p>\n<p>Apriamo quindi il nostro caro vecchio progetto, e modifichiamo il file <em>devFlickrViewController.h<\/em> inserendo queste dichiarazioni:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface devFlickrViewController : UIViewController {\r\n\tUILabel\t\t*currentPhotoNum;\r\n\tUIImageView\t*imageView;\r\n\tNSArray\t\t*urlArray;\r\n\tint\t\t\tcurrentphoto;\r\n\tUIActivityIndicatorView *act;\r\n}\r\n@property (nonatomic,retain) IBOutlet UILabel\t\t*currentPhotoNum;\r\n@property (nonatomic,retain) IBOutlet UIImageView\t*imageView;\r\n@property (nonatomic,retain) IBOutlet UIActivityIndicatorView\t*act;\r\n- (IBAction)nextPhoto;\r\n- (IBAction)prevPhoto;\r\n- (IBAction)savePhoto;\r\n- (IBAction)refreshPhotoList;\r\n<\/pre>\n<p>Creiamo l&#8217;interfaccia tramite Interface Builder, quello che vedete in questa foto \u00e8 il massimo che il mio estro artistico sia riuscito a partorire:<\/p>\n<p><center><\/p>\n<div id=\"attachment_3653\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/Screen-shot-2010-06-09-at-11.05.05-.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-3653\" class=\"size-medium wp-image-3653\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/Screen-shot-2010-06-09-at-11.05.05--300x176.png\" alt=\"\" width=\"300\" height=\"176\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/Screen-shot-2010-06-09-at-11.05.05--300x176.png 300w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/Screen-shot-2010-06-09-at-11.05.05--150x88.png 150w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/Screen-shot-2010-06-09-at-11.05.05-.png 933w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p id=\"caption-attachment-3653\" class=\"wp-caption-text\">Creazione dell inerfaccia con inteface builder<\/p>\n<\/div>\n<p><\/center><\/p>\n<p>Non dimenticate di collegare correttamente gli IBOutlet e di scivere i <em>@syntetize<\/em> nel file .m (commentate se ci sono difficolt\u00e0 con questa parte!)<\/p>\n<p>Passiamo ora a scrivere il codice delle funzioni che abbiamo dichiarato, iniziamo da <strong>refreshPhotoList<\/strong><\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (IBAction)refreshPhotoList\r\n{\r\n\t[act startAnimating];\r\n\tinterestingness *inter = [[interestingness alloc] initWithApiKey:@\"MY_APY_KEY_IS_PRIVATE\"];\r\n\t[inter downloadXML];\r\n\tURLbuilder *urlBuilder = [[URLbuilder alloc] initWithXMLdata:[inter getinterestingnessXML]];\r\n\t[urlBuilder startParsing];\r\n\turlArray = [[NSArray arrayWithArray:[urlBuilder getURLarray]] retain];\r\n\r\n\t[inter release];\r\n\t[urlBuilder release];\r\n\t[act stopAnimating];\r\n\r\n}\r\n<\/pre>\n<p>Esaminiamo questo codice: per prima cosa visualizziamo la &#8220;rotellina&#8221;, poi istanziamo un oggetto di tipo <em>interestigness<\/em> e invochiamo su di esso il metodo <em>downloadXML<\/em> per far si che l&#8217;oggetto recuperi l&#8217;intero file xml dal sito di flickr.<\/p>\n<p>Successivamente istanziamo un oggetto di tipo <em>urlBuilder<\/em> passando come parametro proprio il file xml appena scaricato e invochiamo su di esso il metodo <em>startParsing<\/em>.<\/p>\n<p>Ricordiamo che l&#8217;oggetto <em>urlBuilder<\/em> ha un metdo <em>getURLarray<\/em> che restituisce l&#8217;array con gli URL delle foto, invochiamo quindi tale metodo e memorizziamo questo array nella variabilie <em>urlArray<\/em>.<\/p>\n<p>A questo punto possiamo effettuare il release degli oggetti <em>inter<\/em> e <em>urlBuilder<\/em>. (nessun tifo calcistico \ud83d\ude42 )<\/p>\n<p>Eseguendo l&#8217;applicazione notiamo una cosa poco gradita, cliccando sul pulsante che avvia questo metodo l&#8217;interfaccia si blocca per qualche secondo durante la fase di download del file xml che come abbiamo visto nella puntata precedente \u00e8 una chiamata sincrona, inoltre l&#8217;activityview (la rotellina) non viene proprio visualizzata.<\/p>\n<p>Risolveremo questo problema alla fine di questo tutorial, per il momento lasciamolo cos\u00ec.<\/p>\n<p>Dopo aver cliccato sul pulsante refresh ci si aspetta che venga visualizzata la prima foto, creiamo quindi un metodo che chiameremo <em>viewPhotoAtIndex<\/em>:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (IBAction)viewPhotoAtIndex:(int)position\r\n{\r\n\tNSURL *url = [NSURL URLWithString:\t[urlArray objectAtIndex:position]];\r\n\tNSData *data = [NSData dataWithContentsOfURL:url];\r\n\tUIImage *img = [[UIImage alloc] initWithData:data];\r\n\t[imageView setImage:img];\r\n\tcurrentphoto = position;\r\n\tcurrentPhotoNum.text = [NSString stringWithFormat:@\"Foto %d su %d\",position+1,[urlArray count]];\r\n\t[img release];\r\n}\r\n<\/pre>\n<p>Il lavoro svolto da questo medoto dovrebbe essere chiaro, il metodo accetta un intero come parametro, preleva dall&#8217;array degli URL quello in tale posizione, utilizza il metodo <em>dataWithContentsOfURL<\/em> per effettuare il download della foto, e la visualizza.<\/p>\n<p>Aggiorniamo il valore di <em>currentphoto<\/em> e visualizziamo nella label il testo &#8220;foto x su y&#8221;.<\/p>\n<p>Ho utilizzato position+1 perch\u00e8 all&#8217;utente il fatto che gli array siano indicizzati da 0 non interessa, e non sarebbe carino vedere la scritta &#8220;foto 0 su 100&#8221; per la prima foto e &#8220;foto 99 su 100&#8221; per l&#8217;ultima.<\/p>\n<p>Aggiungiamo quindi al metodo precedente (<em>refreshPhotoList<\/em>), giusto prima della chiusura, la riga<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n[self viewPhotoAtIndex:0];\r\n<\/pre>\n<p>in modo da visualizare la prima foto.<\/p>\n<p>Fatto questo implementare i due metodi <em>nextPhoto<\/em> e <em>prevPhoto<\/em> \u00e8 molto semplice perch\u00e8 sar\u00e0 sufficiente scrivere:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (IBAction)nextPhoto\r\n{\r\nif (currentphoto + 1  &lt; [urlArray count]) {\r\n            [self viewPhotoAtIndex:currentphoto + 1];\r\n}\r\n}\r\n\r\n - (IBAction)prevPhoto {\r\nif (currentphoto - 1 &gt;= 0 )\r\n{\r\n    [self viewPhotoAtIndex:--currentphoto];\r\n}\r\n}\r\n<\/pre>\n<p>Un semplice controllo ci impedisce di andare &#8220;out of range&#8221; sul nostro array.<\/p>\n<p>Non ci resta che implementare la funzione per salvare l&#8217;immagine su nostro cellulare:<\/p>\n<p>Il comando sarebbe semplice, perch\u00e9 basterebbe questo codice:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (IBAction)savePhoto {\r\n\tUIImage *img = [imageView image];\r\n\tUIImageWriteToSavedPhotosAlbum( img, self ,nil, nil );\r\n}\r\n<\/pre>\n<p>Per\u00f2 in questo caso non potremmo verificare se il salvataggio \u00e8 andato o meno a buon fine, allora sfruttiamo il terzo parametro di questa funzione \u00e8 un selector, ovvero \u00e8 il nome di un&#8217;altra funzione che verr\u00e0 automaticamente invocata quando il metodo avr\u00e0 finito di salvare l&#8217;immagine.<\/p>\n<p>Questo avviene perch\u00e8 il salvataggio \u00e8 ASINCRONO ovvero parte quando invochiamo questo metodo, ma poi lavora &#8220;per conto suo&#8221;, invocher\u00e0 il metodo specificato nel selector per informarci dell&#8217;esito dell&#8217;operazione.<\/p>\n<p>Secondo le indicazioni delle apple il metodo deve essere di questa forma.<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo\r\n{\r\n\tNSString *message;\r\n\tNSString *title;\r\n\tif (!error)\r\n\t{\r\n\t\ttitle = @\"Ok\";\r\n\t\tmessage = @\"foto salvata\";\r\n\r\n\t}\r\n\telse\r\n\t{\r\n\t\ttitle = @\"Error\";\r\n\t\tmessage = [error description];\r\n\r\n\t}\r\n\tUIAlertView *alert = [[UIAlertView alloc]\r\n\t\t\t\t\t\t  initWithTitle:title\r\n\t\t\t\t\t\t  message:message\r\n\t\t\t\t\t\t  delegate:self\r\n\t\t\t\t\t\t  cancelButtonTitle:@\"OK\"\r\n\t\t\t\t\t\t  otherButtonTitles:nil];\r\n\t<div class=\"alert\"><button type=\"button\" class=\"close\">&#215;<\/button><div class=\"clear\"><\/div><\/div>;\r\n\t<div class=\"alert\"><button type=\"button\" class=\"close\">&#215;<\/button><div class=\"clear\"><\/div><\/div>;\r\n\r\n}\r\n<\/pre>\n<p>a questo punto la funzione per il salvataggio delle foto diventa:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (IBAction)savePhoto {\r\n\tUIImage *img = [imageView image];\r\n\tUIImageWriteToSavedPhotosAlbum( img, self, @selector(image:didFinishSavingWithError:contextInfo:), nil );\r\n}\r\n<\/pre>\n<p>A questo punto il programma sarebbe pure finito, cerchiamo quindi di capire bene il problema del blocco in fase di download del file xml.<\/p>\n<p>Il problema \u00e8 che il download del file xml \u00e8 quella che si chiama in gergo una &#8220;chiamata bloccante&#8221; ovvero una funzione o un metodo che impedisce la normale esecuzione del programma fin quando non \u00e8 terminato.<\/p>\n<p>Per evitare che queste funzioni blocchino quindi tutto il programma bisogna farle eseguire in una sorta di &#8220;sotto-programma&#8221;.<\/p>\n<p>Questi sotto-programmi si chiamano thread e il foundation framework mette a disposizione degli oggetti per crearli e manipolarli comodamente.<\/p>\n<p>Qui ci starebbe la frase di rito: &#8220;non me ne vogliano i pi\u00f9 esperti per questa eccessiva esemplificazione&#8221;, frase scritta perlopi\u00f9 non per scusarsi ma per non dar l&#8217;impressione di essere degli inesperti del settore.<\/p>\n<p>La sintassi per eseguire una funzione bloccante su un thread separato \u00e8 questa:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n[NSThread detachNewThreadSelector:@selector(NOME_FUNZIONE) toTarget:self withObject:nil];\r\n<\/pre>\n<p>Sostituiamo quindi il metodo refreshPhotoList con questi tre metodi:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (IBAction)refreshPhotoList\r\n{\r\n\t[act startAnimating];\r\n\t[NSThread detachNewThreadSelector:@selector(getXML) toTarget:self withObject:nil];\r\n}\r\n\r\n- (void)getXML\r\n{\r\n\tNSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];\r\n\tinterestingness *inter = [[interestingness alloc] initWithApiKey:@\"bd4890c606144764897cd145bd880d95\"];\r\n\t[inter downloadXML];\r\n\tURLbuilder *urlBuilder = [[URLbuilder alloc] initWithXMLdata:[inter getinterestingnessXML]];\r\n\t[urlBuilder startParsing];\r\n\turlArray = [[NSArray arrayWithArray:[urlBuilder getURLarray]] retain];\r\n\r\n\t[inter release];\r\n\t[urlBuilder release];\r\n\r\n\t[self performSelectorOnMainThread:@selector(getXMLterminated) withObject:nil waitUntilDone:NO];\r\n\t[pool drain];\r\n\r\n}\r\n- (void)getXMLterminated\r\n{\r\n\t[self viewPhotoAtIndex:0];\r\n\t[act stopAnimating];\r\n}\r\n<\/pre>\n<p>Analizziamoli insieme. Nel primo metodo, refreshPhotoList, adesso ci limitiamo a creare un thread separato in cui verr\u00e0 eseguito il metodo getXML.<\/p>\n<p>Il metodo getXML una volta invocato verr\u00e0 eseguito su un thread separato quindi \u00e8 necessario istanziare un nuovo oggetto di tipo NSAutoreleasePool che si occuper\u00e0 della gestione della memoria.<\/p>\n<p>Potete leggere <a href=\"http:\/\/www.devapp.it\/wordpress\/l008-%E2%80%93-objective-c-parte-iii.html\" target=\"_blank\">questo<\/a> articolo a tal proposito.<\/p>\n<p>Per\u00f2 vogliamo essere avvisati quando il thread ha finito il suo compito, se non altro perch\u00e8 dobbiamo far smettere di girare la rotellina!<\/p>\n<p>utilizziamo quindi la funzione:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n[self performSelectorOnMainThread:@selector(getXMLterminated) withObject:nil waitUntilDone:NO];\r\n<\/pre>\n<p>per eseguire il metodo getXMLterminated nel threadprincipale.<\/p>\n<p>Quando questo metodo quando viene invocato \u00e8 sintomo che il threadsecondario ha terminato il suo lavoro, quindi possiamo invocare i metodi necessari per visualizzare l&#8217;immagine.<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/flickr_screen-.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-3654\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/flickr_screen--157x300.png\" alt=\"\" width=\"157\" height=\"300\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/flickr_screen--157x300.png 157w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/flickr_screen--78x150.png 78w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/flickr_screen-.png 382w\" sizes=\"auto, (max-width: 157px) 100vw, 157px\" \/><\/a><br \/>\n<\/center><\/p>\n<p style=\"text-align: center;\"><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/t051-devFlickr-4a-puntata.zip\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/05\/download_icon.png\" alt=\"\" width=\"33\" height=\"40\" align=\"middle\" \/><\/a> Se avete problemi con il tutorial, <a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/06\/t051-devFlickr-4a-puntata.zip\">questo \u00e8 il nostro file di progetto.<\/a> <\/p>\n<p><strong>NOTA:<\/strong> <em>nel progetto di esempio allegato ho modificato le funzioni &#8220;prevPhoto&#8221; e &#8220;nextPhoto&#8221; per essere eseguite su thread separati.<\/em><\/p>\n<p>Spero che questi tutorial siano stati utili, attendo i vostri commenti e le vostre critiche.<\/p>\n<p style=\"text-align: center\"><a href=\"http:\/\/www.devapp.it\/wordpress\/supporto-applicazioni\/parole-vietate-di-ignazio-calo\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/02\/bannerIgnazioc.png\" alt=\"\" width=\"480\" height=\"100\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ed eccoci arrivati all&#8217;ultima puntata di questo lungo tutorial, vedremo oggi come realizzare una semplice interfaccia grafica&#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":[1],"tags":[240,238,241],"class_list":["post-3651","post","type-post","status-publish","format-standard","hentry","category-tutorial-pratici","tag-api-flickr","tag-flickr","tag-ignazio-calo"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/3651","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=3651"}],"version-history":[{"count":10,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/3651\/revisions"}],"predecessor-version":[{"id":5013,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/3651\/revisions\/5013"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=3651"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=3651"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=3651"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}