t078-view-personalizzata-con-xcode-objective-c-01 Rieccoci con un breve tutorial! Oggi vogliamo rispondere ad una domanda che ci è stata fatta diverse volte sul nostro forum: “Come riuscire ad implementare nella nostra applicazione iPhone una subView personalizzata simile a questa mostrata in figura qui a lato?”

Come spesso accade, non c’è un solo modo per realizzare una view simile per aspetto e funzionalità a quella in foto, ciascun programmatore è libero di esprimersi come meglio crede e, a meno di profondi errori, non c’è un metodo giusto ed uno sbagliato.
Io personalmente amo generalizzare e mi piace scrivere codice, magari più prolisso, ma che può essere riutilizzato in altri contesti. Questo tutorial segue proprio la mia filosofia. Alla fine infatti ci ritroveremo con due file .h e .m che possono essere copiati ed usati in un nuovo progetto senza modificare una riga di codice.

A dire il vero questo codice non è “completamente” generalizzato, in quanto gli slider eseguono una funzione predefinita, in pratica possiamo riutilizzare sì questa classe, ma solo per modificare un colore, mentre se avessi generalizzato anche la funzione associata avremmo potuto utilizzarla davvero per qualsiasi cosa. (Magari potremo vederlo in un prossimo tutorial)

Creiamo un nuovo progetto

Iniziamo quindi con il creare un nuovo progetto, per seguire questo tutorial può andare bene anche un template di tipo “view based application”. Creato il nostro progetto aggiungiamo una nuova classe cliccando sul menù file -> new file e scegliendo l’icona di “objective-c Class”. Selezioniamo “UIview” dalla casella “Subclass of” e clicchiamo su next. Inseriamo un nome significativo e clicciamo su “finish”. Io in un volo di fantasia l’ho chiamata “editView”.

Questa che abbiamo creato è quindi una nuova classe che eredita dalla classe UIView. Se avete già letto qualcosa su OOP vi sarà già chiaro che il grosso del lavoro è già stato fatto, tutti i metodi e le proprietà utilizzabili su un’oggetto di tipo UIView sono già automaticamente utilizzabili anche sulla nostra classe, non ci resta che personalizzarli.

Importiamo e dichiariamo gli elementi necessari

Visualizziamo il file editView.h che abbiamo creato ed aggiungiamo in testa, la direttiva

1
#import <QuartzCore/QuartzCore.h>

Quartz è un framework che ci permette di disegnare sullo schermo oggetti che non sono facilmente rappresentabili con UIKit, come per esempio la nostra view dagli angoli arrotondati.
Purtoppo la sola direttiva non è sufficiente a far si che il framework venga importato, bisogna aggiungerelo al progetto cliccando con il tasto destro (non ditemi che il mac non ha il tasto destro, please!) sulla cartella “frameworks” e selezioniamo “add -> existing framework” e aggiungiamo quindi “QuartzCore”


t078-view-personalizzata-con-xcode-objective-c-02

Aggiungiamo quindi le proprietà che servono alla nostra view, io ho aggiunto queste:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface editView : UIView {
	UISlider *redSlider;
	UISlider *greenSlider;
	UISlider *blueSlider;
	UILabel *red_text;
	UILabel *green_text;
	UILabel *blue_text;
	UIView *target;
}
 
@property (nonatomic, retain) UIView *target;
@property (nonatomic, retain) UISlider *redSlider;
@property (nonatomic, retain) UISlider *greenSlider;
@property (nonatomic, retain) UISlider *blueSlider;

Gli slider serviranno per modificare il colore della view specificata, mentre le label mostreranno il valore dello slider.
E la view “target” cos’è? Spero si capisca leggendo il codice, perché non è facile spiegarlo a parole :) in breve la view “target” fa da segnaposto (come i parametri formali delle funzioni) e viene correttamente valorizzata od ogni utilizzo della view.

Passiamo all’implementazione

Nel file editView.m troviamo alcuni metodi già scritti per noi, Xcode li ha aggiunti per semplificarci la vita.
Cerchiamo e modificichiamo quindi il metodo:

1
- (id)initWithFrame:(CGRect)frame

per far si che venga generata la view per come la desieriamo.

Ho deciso di non attribuire una dimensione fissa alla view, questo complica le cose ma ci tornerà utile se vogliamo utilizzarla in un altro progetto in cui lo spazio a disposizione è diverso. Ho limitato però la dimensione ad almeno 200px x 200px e l’ho fatto con un metodo un pò strano, utilizzando questo codice:

1
2
3
4
- (id)initWithFrame:(CGRect)frame {
	assert(frame.size.width >= 200);
	assert(frame.size.height >= 200);
	[...]

Ovviamente l’ho fatto a solo scopo didattico, perché la funzione assert genera una eccezione sel il suo parametro è false, quindi se viene dichiarata una view con un lato minore di 200 pixel il programma va in crash e si chiude. La funzione quindi risulta utile solo in fase di debug, perchè nell’applicazione finale dovrà essere sostituita con una reale gestione dell’errore.

Il codice prosegue con:

1
if ((self = [super initWithFrame:frame])) {

Invochiamo il metodo initiWithFrame della superclasse e ci assicuriamo che non restituisca null.

1
2
3
4
5
self.layer.cornerRadius	        = kDefaultCornerRadius;
self.backgroundColor		= kDefaultRectColor;
self.alpha				= kDefaultAlphaValue;
[self.layer setBorderColor:[[UIColor whiteColor] CGColor]];
[self.layer setBorderWidth: 2.0];

Qui stiamo creando la view vera e propria, utilizzando dei metodi forniti da Quartz. I valori utilizzati come parametro sono stati definiti in testa al file tramite il costrutto #define.

1
2
3
#define kDefaultRectColor          [UIColor blackColor]
#define kDefaultCornerRadius      20.0
#define kDefaultAlphaValue	     .8

A questo punto bisogna iniziare a creare la view via codice, ma non possiamo utilizzare riferimenti statici per gli slider, perchè abbiamo detto che la dimensione della view è variabile. Considerando quindi il numero dei componenti che dobbiamo inserire definiamo un margine superiore e inferiore pari a circa il 5% della dimensione della view:

1
2
int spacey = frame.size.height * 0.05;
int spacex = frame.size.width * 0.05;

Mentre alla casella di testo assegnamo il 10%

1
int textSpace = frame.size.width * 0.1;

Non ci resta quindi che stabilire le 2 dimensioni dello slider, ricordando però gli spazi tra i componenti e il fatto che gli slider sono 3.

1
2
int sizex = frame.size.width - (3 * spacex) - textSpace;
int sizey = (frame.size.height - (2 * spacey) ) / 3;

Magari vi sarà tutto più chiaro con questa immagine:


t078-view-personalizzata-con-xcode-objective-c-03

Non ci resta quindi che iniziare ad allocare gli slider. Il codice è praticamente identico per ciascuno slide, cambia soltanto la posizione relativa nella view, esaminiamo quindi il codice per l’inserimento del primo slide:

1
redSlider = [[UISlider alloc] initWithFrame:CGRectMake(spacex,  spacey, sizex, sizey)];

Allochiamo lo slide.

1
redSlider.minimumValueImage = [UIImage imageNamed:@"red.png"];

Aggiungiamo una immagine, “red.png” come immagine sinistra dello slide.

1
[redSlider setMinimumTrackImage:[[UIImage imageNamed:@"redSlider.png"] stretchableImageWithLeftCapWidth:10.0 topCapHeight:0.0] forState:UIControlStateNormal];

Impostiamo l’immagine redSlider.png come immagine sinistra per la barra di tracking, impostando che i primi 10 pixel devono essere fissi e non stretchabili:

1
2
3
4
5
redSlider.minimumValue = 0;
redSlider.maximumValue = 1;
redSlider.value = 0.5;
Impostiamo i valori di minimo, massimo e valore attuale.
redSlider.continuous = TRUE;

Se impostata a TRUE questa proprietà fa si che vengano generati in continuazione eventi “UIControlEventValueChanged”, mentre se impostata a FALSE avviene solo quando lasciamo lo slider.

1
[redSlider addTarget:self action:@selector(sliderAction) forControlEvents:UIControlEventValueChanged];

Settiamo qual’è la funzione che deve essere eseguita quando lo slide manda il messaggio

1
2
UIControlEventValueChanged.
[self addSubview:redSlider];

Aggiungo lo slider alla view.

Molto semplice anche l’aggiunta delle UILabel per visualizzare il valore numerico corrente:

1
2
3
4
5
6
red_text = [[UILabel alloc] initWithFrame:CGRectMake(sizex +  2 * spacex, spacey, textSpace, sizey)];
red_text.textColor = [UIColor whiteColor];
red_text.backgroundColor = [UIColor clearColor];
red_text.font = [UIFont boldSystemFontOfSize:11];
red_text.text = @"0.5";
[self addSubview:red_text];

Il metodo che viene invocato ad ogni cambio di valore è il seguente:

1
2
3
4
5
6
7
- (void)sliderAction {
	red_text.text = [NSString stringWithFormat:@"%.02f",[redSlider value]];
	green_text.text = [NSString stringWithFormat:@"%.02f",[greenSlider value]];
	blue_text.text = [NSString stringWithFormat:@"%.02f",[blueSlider value]];
	[self changeColor:redSlider.value green:greenSlider.value blue:blueSlider.value];
 
}

aggiorna quindi i valori delle label ed invoca un metodo che si chiama changeColor.

1
2
3
- (void)changeColor:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue{
	target.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1];
}

Come vediamo qui viene utilizzata la view “target” nonostante non sia mai stata inizializzata e vengono utilizzati i valori prelevati dagli slider per cambiare il colore.

Questo è possibile perchè la view ci è stata passata come parametro, più precisamente, vediamo in conclusione come usare quanto appena creato, ovvero il codice di una view che vuole istanziare un oggetto di tipo editView:

1
2
3
editView *ed = [[editView alloc] initWithFrame:CGRectMake(50,140, 220, 200)];
[ed setTarget:square];
[self.view addSubview:ed];

Spero il tutorial vi sia utile, viste le diverse tematiche affrontate..

Vi lasciamo con il risultato finale.
Alla prossima!


t078-view-personalizzata-con-xcode-objective-c-01