Una delle caratteristiche più in voga delle applicazioni per iPhone che fanno uso di tabelle è sicuramente il famoso “Pull down to refresh”. Questa funzione permette di ricaricare i dati semplicemente trascinando verso il basso la tabella stessa. Essa è utilizzata in moltissime applicazioni di successo, come Twitter, Facebook e nientepopodimeno che “Orologi di Classe PLUS” (piccola pubblicità occulta :P):
Implementare questa funzionalità non è così difficile come può sembrare, esiste infatti un’ottima libreria che mette a disposizione il codice già bello che pronto: si tratta di EGOTableViewPullrefresh, e noi lo utilizzeremo in questo tutorial.

1. Creiamo un progetto con una tabella
Per prima cosa iniziamo creando un nuovo progetto del tipo “Navigation-based Application”, che ci fornisce già la struttura a tabella che ci serve:

Definiamo quindi gli elementi che saranno visualizzati all’interno della nostra tabella: essi saranno contenuti in una semplice lista (NSMutableArray). Nel file “RootViewController.h” inseriamo la seguente definizione:
#import
@interface RootViewController : UITableViewController {
NSMutableArray *listaElementi;
}
@end
Spostiamoci nel file di implementazione “RootViewController.m” e nel metodo “viewDidLoad” inizializziamo la nostra lista:
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"Lista nomi";
listaElementi = [[NSMutableArray alloc] initWithObjects:@"Andrea",@"Marco",@"Luigi", nil];
}
Fatto ciò non ci resta che far visualizzare dalla tabella questi elementi. Modifichiamo, quindi, questi ultimi due metodi (riga 2 e riga 15):
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [listaElementi 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:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [listaElementi objectAtIndex:indexPath.row];
// Configure the cell.
return cell;
}
Penso che questi passaggi siano ormai chiari per la maggior parte di voi, quindi non mi soffermo sulle singole istruzioni modificate. Se avete dei dubbi a riguardo leggete i tutorial dedicati alle UITableView.
Se compiliamo ed eseguiamo l’applicazione in questo momento, tutto dovrebbe funzionare senza problemi:

2. Inseriamo la libreria EGOTableViewPullRefresh
Dobbiamo ora inserire la libreria che ci permetterà di aggiungere il “Pull down to refresh” alla nostra applicazione. Potete scaricare la libreria con un esempio dimostrativo alla pagina di Github del progetto, oppure, se preferite, solo la classe da questo link che vi mettiamo a disposizione.
Inseriamo nel nostro progetto le classi “PullToRefreshTableViewController” e “EGORefreshTableHeaderView”, che ci forniranno le funzionalità desiderate:

Inseriamo anche l’immagine della freccia che trovaviamo nel pacchetto appena scaricato (se è diversa da quella di default “blueArrow.png” dovremo modificare il file “EGORefreshTableHeaderView” altrimenti non verrà visualizzata):

Prima di passare al codice vero e proprio, è necessario inserire un framework all’interno del progetto: “QuartzCore.framework”. Se utilizzate già, come me, XCode 4, per inserie un framework dovrete andare nel progetto, quindi nel pannello “Build Phases”, in cui troverete “Link Binary With Binaries”, da cui potrete inserire il framework QuartzCore:

3. Modifichiamo il codice necessario
È venuto il momento di implementare il codice necessario per far funzionare il nostro “Pull down to refresh”.
Iniziamo dal file “RootViewController.h”. Dobbiamo, prima di tutto, cambiare la superclasse della classe in questione (scusate il giro di parole). Modifichiamo il codice in questo modo:
#import
#import "PullToRefreshTableViewController.h"
@interface RootViewController : PullToRefreshTableViewController {
NSMutableArray *listaElementi;
}
@end
Alla riga 2 abbiamo importato la classe “PullToRefreshTableViewController” necessaria, mentre alla riga 4 abbiamo cambiato la superclasse di RootViewController, che in precedenza era UITableViewController. Questa modifica ci permetterà di riutilizzare il codice presente nella classe “PullToRefreshTableViewController”, grazie all’ereditarietà, un paradigma fondamentale della programmazione ad oggetti.
Passiamo al file “RootViewController.m”, in cui ci saranno più modifiche da fare. La prima è semplice ed è da inserire nel metodo “viewDidLoad” (riga 7):
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"Lista nomi";
// definiamo la lista degli oggetti
listaElementi = [[NSMutableArray alloc] initWithObjects:@"Andrea",@"Marco",@"Luigi", nil];
// settiamo la data dell'ultimo refresh (in questo caso è nil, non ci sono date precedenti)
[self.refreshHeaderView setLastRefreshDate:nil];
}
Semplicemente abbiamo settato la data dell’ultimo refresh, che appare mentre la tabella ricarica i dati. Ovviamente si può fare in modo di memorizzare l’ultimo refresh (ad esempio tramite le NSUserDefault) e di visualizzare all’avvio dell’applicazione.
Non ci resta che inserire due semplicissimi metodi, che si occupano di ricaricare il “dataSource” della nostra tabella, ovviamente quando l’utente ha attivato il ri-caricamento dei dati. I due metodi da inserire (sempre in “RootViewController.m”) sono i seguenti:
- (void)reloadTableViewDataSource{
// qui possiamo richiamare metodi specifici per ricaricare i dati
[listaElementi addObject:@"Fabrizio"];
[listaElementi addObject:@"Kevin"];
[super performSelector:@selector(dataSourceDidFinishLoadingNewData) withObject:nil afterDelay:3.0];
}
- (void)dataSourceDidFinishLoadingNewData{
// settiamo la data corrente come ultimo refresh
[refreshHeaderView setCurrentDate];
[super dataSourceDidFinishLoadingNewData];
[self.tableView reloadData];
}
Nel primo metodo possiamo richiamare dei metodi specifici per aggiornare il nostro dataSource (ad esempio scaricandoli da un feed RSS). Nel nostro esempio abbiamo banalmente inserito due elementi nella nostra lista, ed abbiamo richiamato il metodo “dataSourceDidFinishLoadingNewData”, che segnala il completo caricamento dei nuovi dati. Nel nostro esempio, questo ultimo metodo viene richiamato con 3 secondi di ritardo, solamente per fingere un caricamento all’utente (è quindi per puro scopo illustrativo).
Il secondo metodo, invece, setta la nuova data dell’ultimo refresh (che anche in questo caso potrebbe essere gestita e salvata con NSUserDefaults) e ricarica infine la tabella.
Molto semplice vero?
Ora possiamo compilare ed avviare la nostra applicazione, che funzionerà senza problemi!

Se avete problemi con il tutorial, questo è il nostro file di progetto (per XCode 4).










28 Responses to “T#090 – Implementiamo il “Pull down to refresh” nelle nostre tabelle!”
21 Marzo 2011
FraDomanda da 100000€ come faccio a implementarla in una UIWebView?
Grazie
21 Marzo 2011
Andrea Busiquesta libreria è scritta per lavorare con le UITableView, quindi adattarla alle UIWebView è quasi impossibile (comunque molto difficile).
Inoltre non ho mai visto una web view ricaricata con questo sistema, no?
21 Marzo 2011
Fra21 Marzo 2011
RagazzettoIn una parola : spettacolare !
Grazie Andrea 😉
21 Marzo 2011
Albertoandrea, mi appello a te.
dato che ikaroweb non risponde, ti chiedo un favore: è possibile realizzare un tutorial su la popolazione di table view da xml?
grazie infinite!
22 Marzo 2011
paolinofiiiiiiiiiiiigo! grazie!
22 Marzo 2011
Andrea Busine trovi già due sull’argomento:
1) Creare un lettore di feed RSS (che si basano su XML): http://www.devapp.it/wordpress/t043-creiamo-il-nostro-lettore-di-feed-rss-per-iphone.html
2) Leggere file XML: http://www.devapp.it/wordpress/t042-come-utilizzare-xml-nelle-nostre-applicazioni-iphone-sdk.html
23 Marzo 2011
ShevaE’ possibile implementarla su una tableview all’interno di una uiviewcontroller ?
24 Marzo 2011
Andrea Busipenso di si, non penso cambi qualcosa. Se provi facci sapere 😉
25 Marzo 2011
Shevaho provato ma non riesco a farlo funzionare .. mi spiego .. ho una uitableview come oggetto di una superclasse uiviewcontroller .. dal tuo codice invece che la superclasse è di tipo :
@interface RootViewController : PullToRefreshTableViewController
dove PullToRefreshTableViewController è una uitableviewcontroller …
non riesco quindi a capire cosa dovrei modificare per farlo funzionare …
riesci ad aiutarmi ?
26 Marzo 2011
LorenzoHo implementato tutto correttamente nel mio lettore feed RSS. Adesso nel metodo reloadTableViewDataSource ho tolto l’afterDelay, quindi non smette di caricare… Come posso far in modo che venga interrotto il caricamento una volta che ha caricato i dati ?
Ecco il codice:
– (void)reloadTableViewDataSource{
//Nuovo parsing XML per aggiornare il Feed
NSString *path = @”indirizzo feed rss”;
[self parseXMLFileAtURL:path];
//Aggiorno la tabella con i nuovi dati
[super performSelector:@selector(dataSourceDidFinishLoadingNewData) withObject:nil];
}
– (void)dataSourceDidFinishLoadingNewData{
// settiamo la data corrente come ultimo refresh
[refreshHeaderView setCurrentDate];
[super dataSourceDidFinishLoadingNewData];
[self.tableView reloadData];
}
27 Marzo 2011
Andrea Busicome stai facendo tu è errato. Il metodo “dataSourceDidFinishLoadingNewData” lo devi richiamare quano hai terminato il parsing del feed RSS. In questo caso lo devi quindi richiamare nel metodo “parserDidEndDocument” che dovresti aver già implementato.
Il codice dovrebbe quindi essere simile a questo (potrei anche sbagliarmi, non l’ho provato):
- (void)reloadTableViewDataSource{ //Nuovo parsing XML per aggiornare il Feed NSString *path = @”indirizzo feed rss”; [self parseXMLFileAtURL:path]; } - (void)dataSourceDidFinishLoadingNewData{ // settiamo la data corrente come ultimo refresh [refreshHeaderView setCurrentDate]; [super dataSourceDidFinishLoadingNewData]; [self.tableView reloadData]; } - (void) parserDidEndDocument:(NSXMLParser *)parser { [self dataSourceDidFinishLoadingNewData]; }28 Marzo 2011
Andrea Busimi sa che nel tuo caso non puoi estendere direttamente la classe “PullToRefreshTableViewController” come abbiamo fatto nel tutorial, ma devi proprio implementarla nella tua UIViewController. In pratica devi inserire tutti i metodi di “PullToRefreshTableViewController” nella tua classe (almeno, mi pare di aver capito così)
28 Marzo 2011
ShevaCiao Andrea .. adesso ci provo grazie .. ti faccio sapere
28 Marzo 2011
ShevaGrande Andrea .. ci sono riuscito .. grazie mille..
28 Marzo 2011
LorenzoGrazie mille per la risposta.
Ho provato, con le tue correzioni, però non smette di aggiornare… Questo è il codice:
– (void)reloadTableViewDataSource{
//Nuovo parsing XML per aggiornare il Feed
NSString *path = @”http://feeds.feedburner.com/AnastaciaTheItalianDreamNews”;
[self parseXMLFileAtURL:path];
}
– (void)dataSourceDidFinishLoadingNewData{
// settiamo la data corrente come ultimo refresh
[refreshHeaderView setCurrentDate];
[super dataSourceDidFinishLoadingNewData];
[self.tableView reloadData];
}
– (void) parserDidEndDocument:(NSXMLParser *)parser {
[self dataSourceDidFinishLoadingNewData];
}
30 Marzo 2011
Andrea BusiHai ragione, ho fatto delle prove, eccoti il codice funzionante:
- (void)reloadTableViewDataSource{ //Nuovo parsing XML per aggiornare il Feed NSString *path = @"http://feeds.feedburner.com/AnastaciaTheItalianDreamNews"; [super performSelector:@selector(parseXMLFileAtURL:) withObject:path]; } - (void)dataSourceDidFinishLoadingNewData{ // settiamo la data corrente come ultimo refresh [refreshHeaderView setCurrentDate]; [super dataSourceDidFinishLoadingNewData]; [self.tableView reloadData]; } - (void) parserDidEndDocument:(NSXMLParser *)parser { [super performSelector:@selector(dataSourceDidFinishLoadingNewData) withObject:nil afterDelay:0.0]; }Ora funziona 😉
3 Aprile 2011
LorenzoGrazie mille, adesso funziona 🙂
10 Aprile 2011
LorenzoCiao Andrea,
ho implementato con successo il PDTR, però quando aggiorno il mio lettore feed RSS la parte destra dell’header del PDTR appare bianca, come se non fosse centrato l’header stesso.
Hai qualche idea ?
11 Aprile 2011
LorenzoDopo alcune prove, sono riuscito a trovare il problema.
nella seguente porzione di codice:
-(IBAction)seeNews{
//Set Animations properties
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.50];
//Hook To MainView
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:self.navigationController.view cache:YES];
//Build New ViewController
//newsViewController = [[News alloc] initWithNibName:@”News” bundle:nil];
//Push to on Navigation Controller
[self.navigationController pushViewController:newsViewController animated:NO];
//Start Animation
[UIView commitAnimations];
}
ho commentato la seguente riga:
//newsViewController = [[News alloc] initWithNibName:@”News” bundle:nil];
Adesso l’header del Pull down to refresh viene allineato correttamente.
Nel .h mi sono definito il ViewController e nel .xib ho associato al ViewController il nib file News.
19 Agosto 2011
milonetCiao a tutti, complimenti come sempre per l’ottimo lavoro e per il sito… volevo chiedervi una cosa.. sto cercando di fare una fusione tra quello spiegato in questo tutorial e il tutorial che parla di come fare una tabella con dati presi da sqllite. qualcuno ci è già riuscito?
Ho fatto tutte le modifiche necessarie ma mi perdo qualcosa.. prima di postarvi inutili righe di codice,sapete dirmi se c’è qualcosa di già fatto da cui posso prendere spunto?
grazie mille!
19 Dicembre 2011
FrancescoHo bisogno del vostro aiuto, ho fatto questo lettore feed rss e ho aggiunto anche il codice per il pull down to refresh trovato qui, ma ogni volta che faccio il refresh si blocca, ora non ho qui l’errore ma mi rimanda al file main.m se non sbaglio, cmq qui di seguito il codice :
ROOTVIEWCONTROLLER.m
#import “RootViewController.h”
@implementation RootViewController
#pragma mark –
#pragma mark View lifecycle
– (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @”Forex Signals”;
rssItems = nil;
rss = nil;
self.tableView.backgroundColor = [UIColor clearColor];
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
[self.tableView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
self.tableView.tableHeaderView = [[TableHeaderView alloc] initWithText:@”Forex Signals”];
[self.refreshHeaderView setLastRefreshDate:nil];
}
– (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
– (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (rss==nil) {
rss = [[RSSLoader alloc] init];
rss.delegate = self;
[rss load];
}
}
– (void)reloadTableViewDataSource{
//Nuovo parsing XML per aggiornare il Feed
NSString *path = @”http://www.win4forex.com/?feed=rss2″;
[super performSelector:@selector(parseXMLFileAtURL:) withObject:path afterDelay:3.0];
}
– (void)dataSourceDidFinishLoadingNewData{
// settiamo la data corrente come ultimo refresh
[super dataSourceDidFinishLoadingNewData];
[self.tableView reloadData];
}
– (void) parserDidEndDocument:(NSXMLParser *)parser {
[super performSelector:@selector(dataSourceDidFinishLoadingNewData) withObject:nil afterDelay:0.0];
}
Grazie mille
19 Dicembre 2011
FrancescoEcco qui l’errore :
2011-12-19 13:09:42.994 Win4Forex[9399:1803] fetch rss
2011-12-19 13:09:43.561 Win4Forex[9399:1803] -[RootViewController updatedFeedTitle:]: unrecognized selector sent to instance 0x1aba60
2011-12-19 13:09:56.634 Win4Forex[9399:607] -[RootViewController parseXMLFileAtURL:]: unrecognized selector sent to instance 0x1aba60
2011-12-19 13:09:56.724 Win4Forex[9399:607] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[RootViewController parseXMLFileAtURL:]: unrecognized selector sent to instance 0x1aba60’
*** Call stack at first throw:
(
0 CoreFoundation 0x3170764f __exceptionPreprocess + 114
1 libobjc.A.dylib 0x35222c5d objc_exception_throw + 24
2 CoreFoundation 0x3170b1bf -[NSObject(NSObject) doesNotRecognizeSelector:] + 102
3 CoreFoundation 0x3170a649 ___forwarding___ + 508
4 CoreFoundation 0x31681180 _CF_forwarding_prep_0 + 48
5 CoreFoundation 0x31674f03 -[NSObject(NSObject) performSelector:withObject:] + 22
6 Win4Forex 0x00003aad -[RootViewController reloadTableViewDataSource] + 60
7 Win4Forex 0x0000b5a9 -[PullToRefreshTableViewController scrollViewDidEndDragging:willDecelerate:] + 156
8 UIKit 0x31810229 -[UIScrollView _endPanWithEvent:] + 2204
9 UIKit 0x3180dfb1 -[UIScrollView handlePan:] + 96
10 CoreFoundation 0x31674f03 -[NSObject(NSObject) performSelector:withObject:] + 22
11 UIKit 0x317fff93 -[UIGestureRecognizer _updateGestureWithEvent:] + 562
12 UIKit 0x317ffd57 -[UIGestureRecognizer _delayedUpdateGesture] + 22
13 UIKit 0x3176aafd _UIGestureRecognizerUpdateObserver + 436
14 UIKit 0x31784a7f _UIGestureRecognizerUpdateGesturesFromSendEvent + 22
15 UIKit 0x31784881 -[UIWindow _sendGesturesForEvent:] + 712
16 UIKit 0x317844ab -[UIWindow sendEvent:] + 66
17 UIKit 0x3176d313 -[UIApplication sendEvent:] + 298
18 UIKit 0x3176cc53 _UIApplicationHandleEvent + 5090
19 GraphicsServices 0x30ff0e77 PurpleEventCallback + 666
20 CoreFoundation 0x316dea97 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 26
21 CoreFoundation 0x316e083f __CFRunLoopDoSource1 + 166
22 CoreFoundation 0x316e160d __CFRunLoopRun + 520
23 CoreFoundation 0x31671ec3 CFRunLoopRunSpecific + 230
24 CoreFoundation 0x31671dcb CFRunLoopRunInMode + 58
25 GraphicsServices 0x30ff041f GSEventRunModal + 114
26 GraphicsServices 0x30ff04cb GSEventRun + 62
27 UIKit 0x31797d69 -[UIApplication _run] + 404
28 UIKit 0x31795807 UIApplicationMain + 670
29 Win4Forex 0x00002ccd main + 60
30 Win4Forex 0x00002c8c start + 40
)
terminate called after throwing an instance of ‘NSException’
(gdb)
23 Dicembre 2011
Andrea BusiCiao,
dal codice che hai postato l’errore sembra essere dovuto all’istruzione
Esegui la chiamata al metodo “parseXMLFileAtURL:” ma non c’è nessuna definizione del metodo in questione 😉
16 Gennaio 2012
stevePer le tabelle e’ abbastanza seplice ma chiedo se qualcuno ha una buona soluzione per le ScrollView 🙂
– per ora controlle nella scrollview se l’ultente sta scrollando e se ha superato una certa soglia verso il basso ma non e’ ottimale…
– secondo voi si puo’ inserire la scrollview in una tableview di una sola riga e sfruttare la tecnica qui descritta per il refresh pur mantenendo la scrollview e le sue funzionalita’ di scroll?
– ed il caso inverso… controllare se un utente fa scroll in verso opposto ed arriva a fine scroll view per caricare dati successivi?
30 Aprile 2012
matteoCome hai fatto ? me lo puoi spiegare ?
grazie
ciao
29 Agosto 2012
MambaPuoi semplicemente creare una UITableView con una sola cella di dimensione abbondante (puoi variarla via codice in base al numero di caratteri presenti nella pagina o roba del genere) e inserire una UIWebView dentro la cella 😉
20 Ottobre 2012
FedericoCiao ragazzi, ho effettuato tutte le istruzioni, ma alla fine mi viene un dubbio, in questa stringa
listaElementi = [[NSMutableArray alloc] initWithObjects:@"Andrea",@"Marco",@"Luigi", nil];dovrei inserire al posto dei nomi il link a un sito contenete i feed, devo cambiare qualcosa nel codice? Stessa cosa per[listaElementi addObject:@"Fabrizio"];Grazie[listaElementi addObject:@"Kevin"];