Le UISCrollview fanno parte dell’UIKit framework e sono utilizzate dalla stessa Apple per la realizzazione di componenti avanzati quali le UIWebView e UITableView.
Come è chiaro dal nome, le UISCrollView sono una subclass diretta di UIView quindi in termini di OOP possiamo dire che è una UIView specializzata.
Ma quale sarà mai la specializzazione di una UIScrollView? Ovviamente quella di scrollare, di rendere cioè possibile la visualizzazione di un contenuto più grande della UIScrollView stessa.
L’incipit della refernce (link ) dice:
The UIScrollView class provides support for displaying content that is larger than the size of the application’s window. It enables users to scroll within that content by making swiping gestures, and to zoom in and back from portions of the content by making pinching gestures.
(La classe UISCrollview permette di visualizzare contenuti che sono più grandi della dimensione della finestra dell’applicazione. Permette all’utente di scrollare il contenuto tramite swipe e zoomare tramite pinch).
Non so perché Apple faccia quel riferimento all’application window, direi che si tratta di un’imprecisione, perché affinché si possa scrollare è necessario che il contenuto sia più grande della UIScrollview stessa indipendentemente dalla dimensione della uiwindow.
Come funzionano le UIScrollView?
Esamineremo il funzionamento della scrollview creando una piccola applicazione di esempio, apriamo quindi xcode e creiamo un nuovo progetto di tipo “single view application”. Io l’ho chiamato devappScrollview ed ho tolto il flag sia da “Use storyboard” e sia da “Use Automatic reference counting” (che ci posso fare, sono vecchia scuola 🙂 )
In questo articolo non staremo molto a guardare lo stile di programmazione, quindi andremo a creare l’interfaccia direttamente nel metodo viewDidLoad del primo UIViewController.
Identifichiamo quindi questo metodo e posizioniamoci subito dopo [super viewDidLoad];
Come tutte le UIView anche la UISCrollview ha la sua proprietà frame che identifica il rettangolo dove verrà visualizzata.
Allochiamo e inizializiamo una nuova UIScrollView con il codice:
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(10, 30, 300, 400)];
Aggiungiamola quindi alla view principale del viewcontroller e non dimentichiamo di rilasciarla:
[self.view addSubview:scroll];
[scroll release];
Compiliamo e osserviamo il risultato…

Sembra che non sia successo nulla, perché come tutte le uiview anche la scrollview di default si presenta con il colore trasparente.
Per renderci conto di dove è effettivamente posizionata la scrollview coloriamola di rosso inserendo questo codice subito dopo l’inizializzazione:
[scroll setBackgroundColor:[UIColor redColor]];
Adesso il simulatore mostra la posizione della scrollview:

La UIScrollview abbiamo detto che fa da contenitore, quindi proviamo ad aggiungere un’immagine al suo interno e vediamo cosa succede.
Ho scelto appositamente un’immagine con una dimensione un pò particolare 400 x 1222, l’idea è quella di permettere quindi di scrollare l’immagine in orizzontale per poterla visualizzare. Questa è l’immagine utilizzata (cliccateci sopra per vedere le dimensioni effettive e per scaricarla e usarla nel vostro progetto):

Aggiungiamo quindi questa immagine al progetto xcode (file->add file to “project”) e creiamo una nuova UIImageview da inserire dentro la UIScrollview. Per chiarezza riporto tutto il metodo viewdidload:
- (void)viewDidLoad
{
[super viewDidLoad];
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(10, 30, 300, 400)];
[scroll setBackgroundColor:[UIColor redColor]];
UIImageView *imageview = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1222, 400)];
[imageview setImage:[UIImage imageNamed:@"panorama_orizzonte_medio_big.jpg"]];
[scroll addSubview:imageview];
[imageview release];
[self.view addSubview:scroll];
[scroll release];
}
Il risultato è questo:

Però c’è un grosso problema…non scrolla!
Abilitiamo lo scroll
Affinché la UIScrollView sappia quanto e come scrollare è necessario impostare la proprietà contentSize di questo oggetto, facciamolo e ricompiliamo:
- (void)viewDidLoad
{
[super viewDidLoad];
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(10, 30, 300, 400)];
[scroll setBackgroundColor:[UIColor redColor]];
UIImageView *imageview = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1222, 400)];
[imageview setImage:[UIImage imageNamed:@"panorama_orizzonte_medio_big.jpg"]];
[scroll addSubview:imageview];
[imageview release];
[self.view addSubview:scroll];
[scroll release];
}
Anche se lo screenshot è piccolino si riesce a vedere la barra di scorrimento orizzonale, segno che siamo riusciti nel nostro compito.

La gestione dello zoom
La gestione dello zoom è leggermente diversa dalla logica che abbiamo visto adesso e usa il concetto di delegate. Quando l’utente fa pinch sulla UISCrollview questa invoca sul suo delegate il metodo:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView al quale il delegate risponde con una UIView.
Ci si potrebbe chiedere come mai la scrollview semplicemente non faccia in autonomia uno zoom sulla UIView visualizzata ed il motivo è che utilizzando il delegate si ha, ad esempio, la possibilità di fornire una view diversa per diversi livelli di zoom. Supponiamo di avere due versioni della foto del panorama, una a bassa risoluzione ed un’altra ad altissima risoluzione, per ovvie ragioni di memoria all’inizio carichiamo quella a bassa risoluzione, poi se l’utente inizia a fare zoom oltre un certo livello potremmo sostituire l’immagine a bassa risoluzione con una porzione dell’immagine ad alta. Un pò come succede quando si usa una mapview o strumenti analoghi.
Per abilitare lo zoom dobbiamo quindi modificare il viewcontroller per renderlo conforme al protocollo UIScrollViewDelegate, aggiungiamo inoltre una variabile di istanza di tipo UIImageview. Il file .h del nostro viewcontroller si presenterà quindi in questo modo:
#import
@interface ViewController : UIViewController <UIScrollViewDelegate> {
UIImageView *imageview;
}
@end
Nel metodo viewdidload impostiamo quindi il delegate della scrollview e utilizziamo la variabile di istanza piuttosto che la variabile locale:
- (void)viewDidLoad
{
[super viewDidLoad];
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(10, 30, 300, 400)];
[scroll setDelegate:self];
[scroll setBackgroundColor:[UIColor redColor]];
imageview = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1222, 400)];
[imageview setImage:[UIImage imageNamed:@"panorama_orizzonte_medio_big.jpg"]];
[scroll addSubview:imageview];
[scroll setContentSize:CGSizeMake(1222, 400)];
[self.view addSubview:scroll];
[scroll release];
}
notate che ho tolto anche il release della imageview perché voglio che sia valida anche all’uscita di questo metodo, però non dobbiamo generare memory leak, quindi aggiungiamo il medo dealloc in cui rilasciamo tutto.
-(void)dealloc {
[imageview release];
[super dealloc];
}
..ok ok lo so..se avessi abilitato ARC mi sarei risparmiato un pò di rogne 😉
Aggiungiamo il metodo per la gestione dello zoom limitandoci a ritornare l’oggetto imageview:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return imageview;
}
Se proviamo a compilare a questo punto, ci accorgiamo che lo zoom non funziona ancora… e questo ci porta ad esaminare due altre propery dell’oggetto UISCrollView: maximumZoomScale e minimumZoomScale. Se guardiamo la documentazione notiamo che per entrambe il valore di default è 1.0 il che significa che il contenuto non può essere né zoomato né rimpicciolito. Impostiamo rispettivamente questi valori a 2.0 e 0.5 e ricompiliamo il progetto: finalmente siamo riusciti ad attivare anche questa funzionalità.

Paging con UIScrollview
Un utilizzo molto comune delle UISCrollview è quello di creare browser di fotografie, visualizzabili tramite swipe.
Se avessimo memoria infinita il compito sarebbe piuttosto semplice, basterebbe creare tante UIImageView quante sono le immagini da visualizzare, posizionarle all’interno della scrollview ed avremmo già realizzato il nostro browser. Purtroppo ci tocca essere un tantino più attenti e cercare di risparmiare quanta più memoria possibile. Decidiamo quindi di mantenere in memoria soltanto 3 immagini, quella corrente, quella precedente e quella successiva, questo ci permetterà di occupare una quantità fissa di memoria, indipendente dal numero di foto che compongono la galleria.
Un’idea di quello che andremo a realizzare lo si vede in questa simulazione:

Modifichiamo il nostro progetto inserendo 3 variabili di istanza di tipo UIImageview e una variabile intera che memorizzerà il numero della pagina corrente:
@interface ViewController : UIViewController {
UIImageView *imageview1;
UIImageView *imageview2;
UIImageView *imageview3;
int currentPage;
}
@end
e non dimentichimoci del metodo dealloc:
-(void)dealloc {
[imageview1 release];
[imageview2 release];
[imageview3 release];
[super dealloc];
}
Dichiariamo quindi una costante globale in cui memorizziamo il numero di foto che compongono la galleria:
const int numImages = 5;
e passiamo infine a generare la scrollview sempre all’interno del metodo viewdidload.
In questo caso il contentview della scrollview sarà uguale alla larghezza di ogni singola immagine per il numero delle immagini che compongono la galleria.
UIScrollView* scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(10, 30, 400, 300)];
[scroll setDelegate:self];
[scroll setContentSize:CGSizeMake(300 * numImages, 400)];
Passiamo quindi alla generazione delle tre UIImageview, ciascuna 300px più a destra della precedente e ne impostiamo la proprietà tag così da poterle recuperare con un po’ di matematica:
imageview1 = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 300, 400)];
[imageview1 setImage:[UIImage imageNamed:@"tiger0.jpg"]];
[imageview1 setTag:1];
imageview2 = [[UIImageView alloc] initWithFrame:CGRectMake(300, 0, 300, 400)];
[imageview2 setTag:2];
imageview3 = [[UIImageView alloc] initWithFrame:CGRectMake(600, 0, 300, 400)];
[imageview3 setTag:3];
[scroll addSubview:imageview1];
[scroll addSubview:imageview2];
[scroll addSubview:imageview3];
L’idea è quella di spostare queste imageview durante l’operazione di scrolling così da simulare la presenza di un numero illimitato di imageview.
Per fare questo aggiungiamo nel nostro viewcontroller anche il metodo
-(void)scrollViewDidScroll:(UIScrollView*)scrollView che viene richiamato durante la fase di scrolling.
All’interno del metodo calcoliamo l’attuale posizione della scrollview:
const CGFloat currPos = scrollView.contentOffset.x;
da questo valore calcoliamo la pagina che è attualmente visualizzata:
const NSInteger selectedPage = lroundf(currPos / 300.0);
calcoliamo infine quale delle tre imageview è al centro dello schermo:
const NSInteger zone = 1 + (selectedPage % 3);
const NSInteger nextPage = selectedPage + 1;
const NSInteger prevPage = selectedPage - 1;
Passiamo quindi a configurare l’imageView successiva, verificando di non essere arrivati alla fine della nostra galleria. Recuperiamo l’imageview attraverso la sua proprietà tag e la spostiamo nella posizione successiva alla schermata attuale impostandone la proprietà image:
if (nextPage < numImages)
{
NSInteger nextViewTag = zone + 1;
if (nextViewTag == 4)
nextViewTag = 1;
UIImageView* nextView = (UIImageView*)[scrollView viewWithTag:nextViewTag];
nextView.frame = CGRectMake(nextPage * 300, 0, 300, 400);
NSString *str = [NSString stringWithFormat:@"tiger%d.jpg", nextPage];
UIImage* img = [UIImage imageNamed:str];
nextView.image = img;
}
Lo stesso lavoro va fatto con la imageview precedente:
if (prevPage >= 0)
{
NSInteger prevViewTag = zone - 1;
if (!prevViewTag)
prevViewTag = 3;
UIImageView* prevView = (UIImageView*)[scrollView viewWithTag:prevViewTag];
prevView.frame = (CGRect){.origin.x = prevPage * 300, .origin.y = 0.0f, .size = prevView.frame.size};
NSString *str = [NSString stringWithFormat:@"tiger%d.jpg", prevPage];
UIImage* img = [UIImage imageNamed:str];
prevView.image = img;
}
Eccoci al termine della nostra guida, compilate ed eseguite il programma e, se avete seguito tutto correttamente, dovreste godervi la vostra nuova galleria fotografica.
Se avete problemi con il progetto presentato, questo è il link per scaricare l’esempio completo.









15 Responses to “UIScrollview – Guida completa all’uso”
7 Novembre 2011
IgnaziocPer qualsiasi problema discutiamone pure sul forum! 🙂
8 Novembre 2011
FrugghiSbaglio o non hai impostato contentSize nel codice? In ogni caso ottimo articolo molto interessante come sempre!
8 Novembre 2011
ignaziocveramente c’è 🙂
[scroll setContentSize:CGSizeMake(300 * numImages, 400)];
grazie per i complimenti
8 Novembre 2011
FrugghiForse il mio iPad è impazzito ma io nell’articolo non la vedo! (non ho scaricato il progetto)
8 Novembre 2011
ignaziocgiuro che c’è 🙂 nel box di codice immediatamente successivo al testo:
“In questo caso il contentview della scrollview sarà uguale alla larghezza di ogni singola immagine per il numero delle immagini che compongono la galleria.”
8 Novembre 2011
FrugghiPardon, io mi riferivo al pezzo di codice dopo: “Affinché la UIScrollView sappia quanto e come scrollare è necessario impostare la proprietà contentSize di questo oggetto, facciamolo e ricompiliamo”
8 Novembre 2011
ignazioc🙂 gulp! è saltata…grazie per la correzione!
8 Novembre 2011
Francescodomandina sulle scrollview a cui non riesco a trovar risposta…
nella mia app avrei la necessita di visualizzare piu uitextfield (ma anche uilabel e uibutton) di quante non ce ne stiano in una vista standard quindi pensavo di usare la scrollview.
è possibile da IB “disegnare” la scrollview con dimensioni maggiori a quelle standard e poi vederla tramite scroll sul device/simulatore? come? oppure devo per forza “disegnarla” tramite codice per usare la scrollview?
Grazie in anticipo per la risposta
9 Novembre 2011
ignaziocda IB non ho trovato l’opzione per impostare il contentsize però puoi disegnare le tue textfield selezionarle e poi dal menù cliccare su “embed in scrollview” con un pò di pazienza potresti riuscire..però considera che non vedi scrollare da interface builder..ma lo vedi solo a runtime.
10 Novembre 2011
SergioGuarda questo video dovrebbe fare al caso tuo XCode 4 Tutorial Creating A Scroll View
ciao
13 Novembre 2011
yassassinciao, se ho capito bene, in questo modo le immagini sono contenute all’interno dell’applicazione e questo ha due effetti collaterali:
– per aggiungere altre immagini devo rilasciare una nuova versione dell’app
– la dimensione dell’app dipende dal numero delle immagini incluse al suo interno, e questo mi limita nella quantità
come potrei fare ad avere una galleria simile che però pesca le immagini dal web?
grazie!!!
16 Novembre 2011
Ivanper rispondere a yassassin, il metodo che mi viene in mente è creare un bel xml con titolo e indirizzo web dell’immagine che vuoi visualizzare e poi con un parser puoi visualizzare o anche scaricare l’immagine. Così ogni volta che vuoi aggiungere un’immagine basta che vai a modificare l’xml e non l’app
26 Agosto 2012
Aldoanche sul forum ho aperto una discussione , sto cercando di zoomare tutta la view con dei bottoni al interno…..avete qualche idea come si fa ?
20 Ottobre 2012
Marc0xcomplimenti…spiegazione chiara ed esaustiva!!! 😉
30 Ottobre 2012
MarcoCiao,
premetto che sono alle prime armi 🙂
Ho seguito la prima parte del tutoria (fino al paging) ed ho un problema con lo zoom.
In pratica, simulando il pinch, mi fa lo zoom di tutta la scrollview ma non dell’immagine, che rimane delle stesse dimensioni.
Provo a spiegarmi meglio…
L’area in cui è presente l’immagine si “zooma” ma l’immagine resta uguale. La vedo muoversi, rimanendo presumibilmente agganciata all’angolo in alto a sinistra della scrollview. Dopodichè posso fare pan su un’area più grande (perché zoomata appunto) e vedere l’immagine, non zoomata, scrollando verso Su-Sinistra.
Cosa mi sto perdendo?
Grazie