Sei sulla pagina 1di 17

02 FEBBRAIO 2016

iOS
CUSTOM VIEWS
TESINA/TUTORIAL DI: MATTIA PAGINI
CORSO DI SCIENZE E TECNOLOGIE INFORMATICHE
UNIVERSITÀ DI BOLOGNA
INTRODUZIONE
Prima di capire come poter costruire una custom view, dobbiamo innanzitutto capire
che cosa è una view e su cosa è disegnata a sua volta.
Possiamo dire che la view è l’elemento base che compone l’interfaccia delle varie
applicazioni che ci troviamo di fronte ogni giorno, è come un tassello rettangolare che
noi inseriamo all’interno del nostro schermo in grado di interagire con l’utente.
A capo della view troviamo la window e nella maggior parte dei casi ne esiste una
sola per ogni applicazione automaticamente creata e gestita dal sistema operativo
stesso, il quale coordina il cambio delle varie view quando delle azioni effettuate
dall’utente innescano i passaggi di schermata programmati dall’app.
Nota: Considerate anche che una view può ospitare a sua volta altre view, pensate ad
esempio a una view come una lista dove ogni elemento è a sua volta una view,
quest’ultime vengono quindi chiamate subviews per chiarire meglio che sono
inglobate all’interno di altre view.

Come potete vedere dall’immagine, esiste anche una gerarchia di come le view
vengono disegnate a schermo, ciò consente alle view più in alto nella gerarchia (quelle
più vicine alla window) di gestire e posizionare in modo preciso le subviews ad esse
collegate.

1
COMPITI DI UNA VIEW
Una view deve quindi soddisfare tutta una serie di requisiti che non sono sempre facili
da gestire, infatti alcuni dei compiti più importanti che deve svolgere sono:
1 – Gestire il comportamento in caso di richiesta di ridimensionamento da parte della
view genitore che la ospita (magari a seguito di una rotazione del dispositivo)
2 – Gestire le view figlie che ospita (dette subviews)
3 – Disegnare se stessa nell’area rettangolare a lei assegnata e posizionare le sue
subviews se presenti
4 – Gestire gli eventi ricevuti dal touchscreen

LE VIEW DI IOS
Aprendo Xcode e cercando nel pannello chiamato Object Library, possiamo vedere
tutte le view standard che sono già fornite dall’SDK, queste, che prendono anche il
nome di “oggetti” sono le più utilizzate dagli sviluppatori, poiché forniscono nella
maggior parte dei casi tutte le funzionalità di cui necessitano le varie applicazioni.

2
PERCHÉ LE CUSTOM VIEW ALLORA?
Se è vero che le view già fornite coprono il 90% dei casi, nel restante 10% dobbiamo
arrangiarci autonomamente per creare delle view molto particolari a livello grafico/
estetico, o se semplicemente vogliamo ottimizzare delle view per un particolare
compito all’interno della nostra applicazione.
E’ quello che hanno fatto molte delle applicazioni più famose in circolazione, basti
pensare a Facebook, WhatsApp, Skype ecc.. ecc..

Aprendo una qualsiasi delle applicazioni appena nominate e facendo attenzione


all’interfaccia che forniscono, capite subito che l’interfaccia che vi si presenta non è
possibile ricrearla (per la maggior parte) tramite le view fornite con l’SDK.
E’ quindi il momento di capire se vale la pena buttarsi nella costruzione di una custom
view per la nostra applicazione o se possiamo “emularne” le funzionalità e l’aspetto
con quelle fornite di default. 

Se dopo averci pensato arrivate alla conclusione che creare una custom view è
necessario sia in termini di user experience che di funzionalità, allora è il momento di
proseguire con la lettura.

3
STRUMENTI A DISPOSIZIONE
L’SDK di iOS ci mette a disposizione vari modi per creare le nostra grafica, i più
importanti sono:

1 – Metal, SpriteKIT (2D) , SceneKit (3D) : fanno parte di alcuni nuovi strumenti
aggiunti con iOS 8 e servono soprattutto per creare videogiochi in 2D e 3D, Metal è la
nuova API che tenta di sostituire OpenGL ES e permette di eseguire codice di basso
livello sfruttando maggiormente l’architettura dei chip Apple A7 e successivi.

2 – Core Graphics : è il framework iOS basato su QuartzCore, fornisce un layer


grafico che non permette gestione grafica di basso livello o il 3D, ma è il framework 2D
per creare custom view più potente a disposizione.
3 – UIKit : è un sottoinsieme delle funzionalità messe a disposizione da Core
Graphics, solitamente più che sufficienti per creare custom view di medio livello.

In questa tutorial ci occuperemo di creare la nostra view customizzata usando un


misto di UIKit e Core Graphics con un pizzico di Core Animation che ci aiuterà
nell’animare alcune parti della nostra view. Saremo aiutati anche da delle nuove
funzionalità presenti da XCode 6 che aumentano il supporto alle custom view
aggiungendo all’interface builder nuove istruzioni come @IBInspectable e
@IBDesignable illustrate nei dettagli nelle prossime pagine.

OBBIETTIVO
Ovviamente prima di iniziare dobbiamo sapere esattamente cosa vogliamo creare e
per questo tutorial costruiremo una view che simboleggi la progressione di qualcosa,
ad esempio la progressione in una form di registrazione o di una serie di passaggi tra
delle view, nel nostro caso per semplicità saranno 3 cerchi fissi e animabili.

Lo stato iniziale dovrebbe essere come quello in alto presente nell’immagine, lo stato
finale con i cerchi riempiti e connessi deve presentarsi come la parte sottostante.

4
Per rendere il tutorial più interessante aggiungeremo anche delle animazioni in alcune
parti della custom view.
Il progetto è interamente scaricabile al seguente link : https://bitbucket.org/soulcyber/
ios_circleprogressview dove potete trovare il repository git da clonare o navigare
PROGETTO
Iniziamo creando un nuovo progetto su Xcode partendo da una Single View
Application

Chiamiamo il progetto come preferite, selezioniamo swift come linguaggio di


programmazione e poi salviamo il tutto in una cartella vuota qualsiasi

5
Per prima cosa dobbiamo creare la nostra interfaccia:
Nota : io ho deciso di utilizzare Auto-Layout vista la sua grande flessibilità con
dispositivi diversi e DPI diversi ma siccome è fuori dallo scopo di questo tutorial se non
lo conoscete potete utilizzare qualsiasi tecnica di posizionamento preferiate.
Apriamo quindi lo storyboard generato (Main.storyboard) e selezionando una view
vuota dal pannello degli oggetti la aggiungiamo al centro del view controller, nel size
inspector della view impostiamo una larghezza di 240 e un’altezza di 70.
Prendiamo poi 2 bottoni e li posizioniamo sotto alla view appena inserita, uno
leggermente a sinistra e uno un po’ più a destra, impostiamo ad entrambi una
larghezza di 100 e un’altezza di 50 e modifichiamo il loro background con un colore a
piacere adattando di conseguenza il colore del testo e il padding.
Al bottone di destra cambiamo la label in “Avanti” e facciamo lo stesso con quello di
sinistra cambiandola con “Indietro”
Alla fine di tutto dovremmo avere qualcosa di molto simile alla schermata sottostante

6
Ora creiamo la classe che rappresenterà la nostra custom view, quindi clicchiamo con
il destro sul progetto, facciamo “New File” e selezioniamo iOS -> Source -> Cocoa
Touch Class.

Chiamiamo la nostra view CircleProgressView e come superclasse impostiamo


UIView.

7
Ora abbiamo la nostra classe e possiamo iniziare lo sviluppo del codice.
CODICE CUSTOM VIEW
Iniziamo dichiarando qualche variabile che ci sarà utile per disegnare la nostra view.

Dai commenti possiamo già farci un’idea dell’utilizzo stesso della variabili, quindi
concentriamoci velocemente sulle cose specifiche di Core Graphics come
CAShapeLayer, che è una classe che ci permette di disegnare una curva di Bezier
cubica(1), ovvero una curva definita all’interno di 4 punti.
(1) ref: https://it.wikipedia.org/wiki/Curva_di_B%C3%A9zier#Curve_di_B.C3.A9zier_cubiche

Nel nostro caso le useremo per contenere le forme dei nostri cerchi e delle nostre
linee.
Abbiamo poi la variabile circleRadius per impostare la larghezza di ogni cerchio e le 3
variabili CircleCenter per salvare le coordinate necessarie a disegnare i 3 diversi
cerchi.
Le variabili fillColor e strokeColor hanno anche un didSet interno, il che significa che a
runtime quando quelle variabili verranno settate chiameranno la funzione
initialDrawing() che vedremo tra poco essere la nostra funzione di disegno.
Notiamo anche le annotazioni @IBDesignable sulla classe e @IBInspectable sulle
due variabili appena descritte, la prima ci servirà per poter avere l’anteprima della view
quando siamo nell’interface builder, la seconda invece ci permette di modificare i valori
di quelle variabili dal pannello degli attributi senza dover toccare nessuna riga di
codice.

8
Assegnando la nostra classe alla view che abbiamo aggiunto in precedenza sullo
storyboard, dovremmo poter vedere le due variabili modificabili nella schermata degli
attributi.

Ora è il momento di iniziare a disegnare i nostri oggetti che comporranno la custom


view, partiamo dicendo che ci sono 2 metodi per disegnare in una view:
1 – Usando il metodo drawRect(rect: CGRect), che è ereditato dalla classe UIView e
che possiamo sovrascrivere implementando le nostre logiche di disegno. Questo
metodo è chiamato automaticamente dal sistema ogni volta che la view necessita di
essere ridisegnata (ad esempio per un cambio di orientamento del dispositivo) ma il
programmatore può richiedere alla view di ri-disegnarsi chiamando il metodo
setNeedsDisplay()
2 – Usando il metodo init?(coder aDecoder: NSCoder) e init(frame: CGRect) che
sono i metodi chiamati per primi durante la creazione della view, possiamo quindi
sfruttarli per fare i calcoli statici poiché verranno eseguiti solo al primo caricamento
della view.
Visto che dobbiamo aggiungere anche delle animazioni, siamo costretti a disegnare i
nostri oggetti nei metodi init quindi procediamo a creare le nostre funzioni di disegno,
che saranno initialDrawing() e initializeStaticVariable(frame: CGRect), creiamo
anche l’override delle funzioni init inserendo al loro interno la chiamata ad entrambe le
nostre funzioni di disegno.

9
Come potete notare dall’immagine, abbiamo fatto l’override anche del metodo
prepareForInterfaceBuilder().
Questo perché l’annotazione @IBDesignable che abbiamo messo in precedenza sulla
classe non funziona se le chiamate di disegno vengono effettuate al di fuori del
metodo drawRect, quindi per ovviare alla cosa la UIView ci mette a disposizione
questo metodo che “forza” il rendering degli oggetti costruiti al suo interno.
Incollandoci le nostre 2 funzioni di disegno possiamo cosi vedere l’anteprima
nell’interface builder.
Nota: prepareForInterfaceBuilder() viene chiamata solamente durante la fase di
design dall’editor di XCode, non abbiamo quindi problemi di prestazioni sul dispositivo
poiché a runtime il metodo viene ignorato.

POSIZIONIAMO I CERCHI

I calcoli relativi al posizionamento e alle dimensioni degli oggetti li facciamo all’interno


della funzione initializeStaticVariable(frame: CGRect). 

Sfruttando la variabile frame della UIView passata dai metodi init (contenente le
dimensioni del rettangolo disegnabile) possiamo effettuare semplici calcoli per fare in
modo che i nostri oggetti siano tutti disegnate correttamente all’interno della custom
view.

10
FUNZIONE DI DISEGNO PRINCIPALE
Ora dobbiamo iniziare il lavoro di disegno vero e proprio che verrà eseguito all’interno
della funzione initialDrawing() istanziando i CAShapeLayer definiti prima e creare i
cerchi veri e propri usando il metodo UIBezierPath(center: CGPoint, radius:
CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool) messo a
disposizione da UIKit.

In questa prima parte della funzione, ci occupiamo esclusivamente delle due linee di
congiunzione tra i 3 cerchi, una cosa da notare è il fatto di aver assegnato ad entrambi
gli shape layer, uno strokeEnd con valore 0.
Questo ci permette di rendere “invisibili” le linee fino a quando non saremo noi a
mostrarle nella view, l’analogia più immediata potrebbe essere l’opacità.

11
Nella seconda parte della funzione creiamo i cerchi e li assegniamo ai rispettivi
CAShapeLayer, impostiamo i vari parametri e possiamo notare che la proprietà
fillColor è impostata a un colore trasparente nel cerchio centrale e quello di destra,
questo perché come detto in precedenza, solo il cerchio più a sinistra sarà sempre
riempito col colore scelto mentre gli altri due saranno colorati in base alla selezione
dell’utente.
Ora se torniamo un attimo nello storyboard dovremmo poter vedere l’anteprima della
nostra custom view nell’interface builder.

Non male, ma adesso dobbiamo creare le varie animazioni che verranno eseguite alla
pressione dei bottoni avanti e indietro.
Visto che abbiamo solo 4 possibili stati della nostra view, per semplicità li inseriamo in
un enum che chiamiamo Step

Ora dobbiamo creare le due funzioni che verranno lanciate alla pressione dei bottoni
sull’interfaccia grafica del dispositivo.

12
In base al valore di currentState e alla funzione chiamata, esse chiameranno la
funzione animateStep(currentStep: Step) che dirà alla logica di animazione come
impostare le variabili e quali layer devono essere animati.

13
Questo sopra è il codice della funzione animateStep, in cui noi impostiamo una
CATransaction.begin() per l’animazione, ovvero tutto quello che viene inserito al suo
interno non viene eseguito fino a quando non incontra il metodo
CATransaction.commit().
All’interno grazie alla variabile currentStep possiamo sapere in che direzione deve
essere eseguita l’animazione e quindi animare da destra verso sinistra o da sinistra
verso destra con le proprietà fromValue e toValue. Altra cosa è la timingFunction
impostata nella modalità EasyOut (ma se ne possono scegliere molte altre).
Prima di chiudere la transaction, usiamo anche il metodo setCompletionBlock, che
verrà richiamato quando l’animazione è conclusa e servirà per richiamare la funzione
animateCircleFilling(currentStep: Step) che costruiremo tra poco per animare il
riempimento dei cerchi.
Infine con il metodo addAnimation aggiungiamo l’animazione al layer della linea che
deve essere animata e completiamo il tutto committando la transazione.

Ora costruiamo la funzione di animateCircleFilling(currentStep: Step) come


nell’immagine sottostante

Come vediamo il metodo è del tutto simile a quello precedente, solo che invece di
animare la proprietà strokeEnd delle linee, animiamo la proprietà fillColor dei cerchi.

14
A questo punto non ci rimane altro che collegare i nostri 2 bottoni inseriti inizialmente
nella view con le funzioni nextStep() e prevStep() che abbiamo costruito qualche
passo più su, per vedere il risultato.
Andiamo quindi nello storyboard e creiamo delle referenze nella classe
ViewController.swift che è quella creata automaticamente al View Controller dello
storyboard quando si crea un nuovo progetto. Colleghiamo i 2 bottoni chiamandoli
nextButton e prevButton mentre la nostra CircleProgressView la chiamiamo
stateView.

Alla fine dovremmo avere esattamente il codice rappresentato nell’immagine qui sotto

Ora se lanciate il progetto potete vedere il risultato nel simulatore e alla pressione dei
tasti Avanti e Indietro le linee di congiunzione e i vari cerchi si animeranno in base alla
posizione corrente e alla direzione.

15
FINE.
Mattia Pagini

16