• Programmazione Android
  • CORSI ONLINE
  • Web Agency

Logo

Corsi di programmazione web e mobile online
Navigation
  • Home
  • CORSI ONLINE
  • Tutorial Pratici
  • GUIDE COMPLETE
    • Corso completo di C
    • Corso videogame con Cocos2d
    • Programmazione Cocoa Touch
  • Sezioni
    • Libri e manuali
    • Tips & Tricks
    • Risorse utili
    • Strumenti di Sviluppo
    • Materiale OpenSource
    • Framework
    • Guide Teoriche
    • Guide varie
    • Grafica e Design
    • iPad
    • News
    • Video Tutorial
    • Windows Phone
  • Pubblicità
  • About
    • Chi siamo
    • Pubblicazioni
    • Collabora
    • Sostieni devAPP

T#051 – Accedere a Flickr dalle nostre applicazioni iPhone (parte 4)

By IgnazioC | on 10 Giugno 2010 | 10 Comments
Senza categoria

Ed eccoci arrivati all’ultima puntata di questo lungo tutorial, vedremo oggi come realizzare una semplice interfaccia grafica per il nostro programma.

L’idea è quella di realizzare un’unica schermata con quattro pulsanti, due per navigare avanti e indietro nell’elenco delle foto, un pulsante per salvare la foto su nostro telefono, e un pulsante per avviare il download del file xml da internet.

Aggiungeremo inoltre: una label per visualizzare alcune informazioni, un controllo UIActivityview da visualizzare durante le attese, un controllo UIImageView per visualizzare l’immagine, un array dove memorizzare gli URL delle foto e un intero che memorizzi l’indice della foto che stiamo visualizzando.

Un bel po’ di roba, no?

Apriamo quindi il nostro caro vecchio progetto, e modifichiamo il file devFlickrViewController.h inserendo queste dichiarazioni:

@interface devFlickrViewController : UIViewController {
	UILabel		*currentPhotoNum;
	UIImageView	*imageView;
	NSArray		*urlArray;
	int			currentphoto;
	UIActivityIndicatorView *act;
}
@property (nonatomic,retain) IBOutlet UILabel		*currentPhotoNum;
@property (nonatomic,retain) IBOutlet UIImageView	*imageView;
@property (nonatomic,retain) IBOutlet UIActivityIndicatorView	*act;
- (IBAction)nextPhoto;
- (IBAction)prevPhoto;
- (IBAction)savePhoto;
- (IBAction)refreshPhotoList;

Creiamo l’interfaccia tramite Interface Builder, quello che vedete in questa foto è il massimo che il mio estro artistico sia riuscito a partorire:

Creazione dell inerfaccia con inteface builder

Non dimenticate di collegare correttamente gli IBOutlet e di scivere i @syntetize nel file .m (commentate se ci sono difficoltà con questa parte!)

Passiamo ora a scrivere il codice delle funzioni che abbiamo dichiarato, iniziamo da refreshPhotoList

- (IBAction)refreshPhotoList
{
	[act startAnimating];
	interestingness *inter = [[interestingness alloc] initWithApiKey:@"MY_APY_KEY_IS_PRIVATE"];
	[inter downloadXML];
	URLbuilder *urlBuilder = [[URLbuilder alloc] initWithXMLdata:[inter getinterestingnessXML]];
	[urlBuilder startParsing];
	urlArray = [[NSArray arrayWithArray:[urlBuilder getURLarray]] retain];

	[inter release];
	[urlBuilder release];
	[act stopAnimating];

}

Esaminiamo questo codice: per prima cosa visualizziamo la “rotellina”, poi istanziamo un oggetto di tipo interestigness e invochiamo su di esso il metodo downloadXML per far si che l’oggetto recuperi l’intero file xml dal sito di flickr.

Successivamente istanziamo un oggetto di tipo urlBuilder passando come parametro proprio il file xml appena scaricato e invochiamo su di esso il metodo startParsing.

Ricordiamo che l’oggetto urlBuilder ha un metdo getURLarray che restituisce l’array con gli URL delle foto, invochiamo quindi tale metodo e memorizziamo questo array nella variabilie urlArray.

A questo punto possiamo effettuare il release degli oggetti inter e urlBuilder. (nessun tifo calcistico 🙂 )

Eseguendo l’applicazione notiamo una cosa poco gradita, cliccando sul pulsante che avvia questo metodo l’interfaccia si blocca per qualche secondo durante la fase di download del file xml che come abbiamo visto nella puntata precedente è una chiamata sincrona, inoltre l’activityview (la rotellina) non viene proprio visualizzata.

Risolveremo questo problema alla fine di questo tutorial, per il momento lasciamolo così.

Dopo aver cliccato sul pulsante refresh ci si aspetta che venga visualizzata la prima foto, creiamo quindi un metodo che chiameremo viewPhotoAtIndex:

- (IBAction)viewPhotoAtIndex:(int)position
{
	NSURL *url = [NSURL URLWithString:	[urlArray objectAtIndex:position]];
	NSData *data = [NSData dataWithContentsOfURL:url];
	UIImage *img = [[UIImage alloc] initWithData:data];
	[imageView setImage:img];
	currentphoto = position;
	currentPhotoNum.text = [NSString stringWithFormat:@"Foto %d su %d",position+1,[urlArray count]];
	[img release];
}

Il lavoro svolto da questo medoto dovrebbe essere chiaro, il metodo accetta un intero come parametro, preleva dall’array degli URL quello in tale posizione, utilizza il metodo dataWithContentsOfURL per effettuare il download della foto, e la visualizza.

Aggiorniamo il valore di currentphoto e visualizziamo nella label il testo “foto x su y”.

Ho utilizzato position+1 perchè all’utente il fatto che gli array siano indicizzati da 0 non interessa, e non sarebbe carino vedere la scritta “foto 0 su 100” per la prima foto e “foto 99 su 100” per l’ultima.

Aggiungiamo quindi al metodo precedente (refreshPhotoList), giusto prima della chiusura, la riga

[self viewPhotoAtIndex:0];

in modo da visualizare la prima foto.

Fatto questo implementare i due metodi nextPhoto e prevPhoto è molto semplice perchè sarà sufficiente scrivere:

- (IBAction)nextPhoto
{
if (currentphoto + 1  < [urlArray count]) {
            [self viewPhotoAtIndex:currentphoto + 1];
}
}

 - (IBAction)prevPhoto {
if (currentphoto - 1 >= 0 )
{
    [self viewPhotoAtIndex:--currentphoto];
}
}

Un semplice controllo ci impedisce di andare “out of range” sul nostro array.

Non ci resta che implementare la funzione per salvare l’immagine su nostro cellulare:

Il comando sarebbe semplice, perché basterebbe questo codice:

- (IBAction)savePhoto {
	UIImage *img = [imageView image];
	UIImageWriteToSavedPhotosAlbum( img, self ,nil, nil );
}

Però in questo caso non potremmo verificare se il salvataggio è andato o meno a buon fine, allora sfruttiamo il terzo parametro di questa funzione è un selector, ovvero è il nome di un’altra funzione che verrà automaticamente invocata quando il metodo avrà finito di salvare l’immagine.

Questo avviene perchè il salvataggio è ASINCRONO ovvero parte quando invochiamo questo metodo, ma poi lavora “per conto suo”, invocherà il metodo specificato nel selector per informarci dell’esito dell’operazione.

Secondo le indicazioni delle apple il metodo deve essere di questa forma.

- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
	NSString *message;
	NSString *title;
	if (!error)
	{
		title = @"Ok";
		message = @"foto salvata";

	}
	else
	{
		title = @"Error";
		message = [error description];

	}
	UIAlertView *alert = [[UIAlertView alloc]
						  initWithTitle:title
						  message:message
						  delegate:self
						  cancelButtonTitle:@"OK"
						  otherButtonTitles:nil];
	
;
; }

a questo punto la funzione per il salvataggio delle foto diventa:

- (IBAction)savePhoto {
	UIImage *img = [imageView image];
	UIImageWriteToSavedPhotosAlbum( img, self, @selector(image:didFinishSavingWithError:contextInfo:), nil );
}

A questo punto il programma sarebbe pure finito, cerchiamo quindi di capire bene il problema del blocco in fase di download del file xml.

Il problema è che il download del file xml è quella che si chiama in gergo una “chiamata bloccante” ovvero una funzione o un metodo che impedisce la normale esecuzione del programma fin quando non è terminato.

Per evitare che queste funzioni blocchino quindi tutto il programma bisogna farle eseguire in una sorta di “sotto-programma”.

Questi sotto-programmi si chiamano thread e il foundation framework mette a disposizione degli oggetti per crearli e manipolarli comodamente.

Qui ci starebbe la frase di rito: “non me ne vogliano i più esperti per questa eccessiva esemplificazione”, frase scritta perlopiù non per scusarsi ma per non dar l’impressione di essere degli inesperti del settore.

La sintassi per eseguire una funzione bloccante su un thread separato è questa:

[NSThread detachNewThreadSelector:@selector(NOME_FUNZIONE) toTarget:self withObject:nil];

Sostituiamo quindi il metodo refreshPhotoList con questi tre metodi:

- (IBAction)refreshPhotoList
{
	[act startAnimating];
	[NSThread detachNewThreadSelector:@selector(getXML) toTarget:self withObject:nil];
}

- (void)getXML
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	interestingness *inter = [[interestingness alloc] initWithApiKey:@"bd4890c606144764897cd145bd880d95"];
	[inter downloadXML];
	URLbuilder *urlBuilder = [[URLbuilder alloc] initWithXMLdata:[inter getinterestingnessXML]];
	[urlBuilder startParsing];
	urlArray = [[NSArray arrayWithArray:[urlBuilder getURLarray]] retain];

	[inter release];
	[urlBuilder release];

	[self performSelectorOnMainThread:@selector(getXMLterminated) withObject:nil waitUntilDone:NO];
	[pool drain];

}
- (void)getXMLterminated
{
	[self viewPhotoAtIndex:0];
	[act stopAnimating];
}

Analizziamoli insieme. Nel primo metodo, refreshPhotoList, adesso ci limitiamo a creare un thread separato in cui verrà eseguito il metodo getXML.

Il metodo getXML una volta invocato verrà eseguito su un thread separato quindi è necessario istanziare un nuovo oggetto di tipo NSAutoreleasePool che si occuperà della gestione della memoria.

Potete leggere questo articolo a tal proposito.

Però vogliamo essere avvisati quando il thread ha finito il suo compito, se non altro perchè dobbiamo far smettere di girare la rotellina!

utilizziamo quindi la funzione:

[self performSelectorOnMainThread:@selector(getXMLterminated) withObject:nil waitUntilDone:NO];

per eseguire il metodo getXMLterminated nel threadprincipale.

Quando questo metodo quando viene invocato è sintomo che il threadsecondario ha terminato il suo lavoro, quindi possiamo invocare i metodi necessari per visualizzare l’immagine.



Se avete problemi con il tutorial, questo è il nostro file di progetto.

NOTA: nel progetto di esempio allegato ho modificato le funzioni “prevPhoto” e “nextPhoto” per essere eseguite su thread separati.

Spero che questi tutorial siano stati utili, attendo i vostri commenti e le vostre critiche.

Share this story:
  • tweet

Tags: API FlickrflickrIgnazio CalòTutorial Pratici

Recent Posts

  • Parte il percorso programmatori iOS in Swift su devACADEMY.it

    20 Dicembre 2017 - 0 Comment
  • Android, crittografare dati velocemente con Encryption

    24 Settembre 2018 - 0 Comment
  • Sql2o, accesso immediato ai database tramite Java

    3 Settembre 2018 - 0 Comment
  • Okio, libreria per ottimizzare l’input/output in Java

    27 Agosto 2018 - 0 Comment

Related Posts

  • iSketch: la lavagna magica di Ignazio Calò disponibile GRATUITAMENTE in App Store

    29 Agosto 2011 - 2 Comments
  • 17. La gestione dei file in C

    22 Agosto 2011 - 4 Comments
  • Uno sguardo a Unit test: impariamo a prevenire ore e ore di debug durante lo sviluppo di Applicazioni iOS

    8 Agosto 2011 - 2 Comments

Author Description

10 Responses to “T#051 – Accedere a Flickr dalle nostre applicazioni iPhone (parte 4)”

  1. 16 Luglio 2010

    pippo

    Il link per il download non funziona…

  2. 1 Settembre 2010

    Matteo

    Il link per scaricare il progetto non funziona 🙁

  3. 18 Ottobre 2010

    stefano

    Il link non funziona..

  4. 5 Novembre 2010

    Staff devAPP


    stefano:

    Il link non funziona..

    Aggiornato, scusate!! Ora dovrebbe andare ^^

  5. 9 Marzo 2011

    Luigi

    Ciao, complimenti per l’interessantissimo tutorial. Avrei qualche domanda da farti riguardo il progetto già pronto che ho scaricato:
    – se lo mando in esecuzione ottengo 3 warning:
    1) warning: class ‘URLbuilder’ does not implement the ‘NSXMLParserDelegate’ protocol
    2) warning: control reaches end of non-void function
    3) warning: ‘devFlickrViewController’ may not respond to ‘-viewPhotoAtIndex:’

    – volevo provare anzichè la lista delle foto più interessanti di flickr a visualizzare le foto di un gruppo. Ho trovato l’api flickr.groups.pools.getPhotos e provandola ho visto che come struttura per i risultati è la stessa. Ho provato quindi a cambiare la costante FLICKR_INTERESTIGNESS_GETLIST_URL mettendo l’url (funzionante) che mi restituisce l’xml delle foto che mi interessano. Pero’ l’applicazione si chiude. Ho cambiato anche la key in initWithApiKey, e cmq è compresa anche nella richiesta. Cosa ho sbagliato? Grazie ancora.

  6. 10 Marzo 2011

    ignazioc

    ciao Luigi:
    Per quel che riguarda i warning: io ne ottengo solo due:
    ‘devFlickrViewController’ may not respond to ‘viewPhotoAtIndex:’
    indica che il metodo viewPhotoAtIndex non è stato trovato nell’header della classe, basta quindi aggiungere:
    – (IBAction)viewPhotoAtIndex:(NSNumber *)position nel file devFlickrViewController.h

    e ottengo anche “2) warning: control reaches end of non-void function”
    in effetti deve essere qualche modifica al codice, quel metodo non restituisce nulla invece nel nome del metodo c’è scritto che restituisce un array..quindi può essere sostituito con un:
    – (void) startParsing (sia nel file .m sia nel file .h)

    per quanto riguarda l’ultimo warning (il tuo n.1) se non ricordo male c’era una differenza tra alcune versioni dell’sdk, ma cmq significa semplicemnte che la classe URLbuilser non esplicita la conformità a NSXMLParserDelegate, anche se poi lo utilizziamo proprio come delegate di un parser.
    per risolver il warning aggiungere nell’intestazione della classe.

  7. 10 Marzo 2011

    ignazioc

    Per quanto riguarda la modifica che hai fatto, mi risulta difficile capire il motivo, ma da quello che dici mi sembra che stai usando due volte l’apikey nella url REST. può essere?

  8. 14 Marzo 2011

    Luigi

    Grazie Ignazio, è proprio come dici tu. Poi me ne sono accorto da solo rivedendo meglio come era costruita la stringa. Per i warning appena posso controllo quello che mi hai scritto.
    Una richiesta allo staff: perchè sullo stesso tema non realizzate un tutorial su come visualizzare le foto di un album di facebook?

  9. 5 Novembre 2011

    spag

    per il download del xml in un altro thread non si potrebbe usare pure GCD? tipo dispatch_queue_t per creare l’operazione e dispatch_async per effettuarla in multithreading?
    Ovviamente entra in gioco l’uso dei blocks.

  10. 6 Novembre 2011

    Ignazioc

    Ovviamente si.
    CI sono diversi modi per eseguire operazioni in un thread secondario, GDC è uno di questi (e non era ancora stato introdotto quando ho scritto questo articolo)

Leave a Reply

Your email address will not be published. Required fields are marked *


*
*

Corso online di programmazione android e java

SEZIONI

  • Android
  • Comunicazioni
  • Contest
  • Corsi ed Eventi
  • Corso completo di C
  • Corso programmazione videogiochi
  • Framework
  • Grafica e Design
  • Guida rapida alla programmazione Cocoa Touch
  • Guide Teoriche
  • Guide varie
  • iPad
  • Le nostre applicazioni
  • Libri e manuali
  • Materiale OpenSource
  • News
  • Pillole di C++
  • Progetti completi
  • Risorse utili
  • Strumenti di Sviluppo
  • Swift
  • Tips & Tricks
  • Tutorial Pratici
  • Video Tutorial
  • Windows Phone

Siti Amici

  • Adrirobot
  • Allmobileworld
  • Apple Notizie
  • Apple Tribù
  • Avvocato360
  • Blog informatico 360°
  • bubi devs
  • fotogriPhone
  • GiovaTech
  • iApp-Mac
  • iOS Developer Program
  • iPodMania
  • MelaRumors
  • Meritocracy
  • SoloTablet
  • TecnoUser
  • Privacy & Cookie Policy
©2009-2018 devAPP - All Rights Reserved | Contattaci
devAPP.it è un progetto di DEVAPP S.R.L. - Web & Mobile Agency di Torino
Str. Volpiano, 54 - 10040 Leini (TO) - C.F. e P.IVA 11263180017 - REA TO1199665 - Cap. Soc. € 10.000,00 i.v.

devACADEMY.it

Vuoi imparare a programmare?

Iscriviti e accedi a TUTTI i corsi con un’unica iscrizione.
Oltre 70 corsi e migliaia di videolezioni online e in italiano a tua disposizione.

ISCRIVITI SUBITO