Alzi la mano chi di voi non ha mai avuto problemi durante lo sviluppo delle proprie applicazioni per iPhone con le UILabel, o meglio, con il testo settato al loro interno. Il vero problema per noi sviluppatori iPhone è lo spazio a disposizione e di conseguenza molte volte siamo costretti a “croppare” le nostre UILabel, schiacciarle o ancora ridurre il testo al loro interno per far in modo che quanto mostrato sia ben leggibile e con un font che non sia troppo piccolo. Insomma, un vero problema di non poco conto! L’altra notte (io non dormo mai :P) pensando a questo problema, mi è venuta la brillante idea: perchè non realizzare un’UILabel il cui testo scorre al suo interno? Non è poi una cattiva idea, no? Potrebbe tornarci utile per mostrare informazioni di sistema oppure informazioni che vogliamo sfruttare per attirare l’attenzione dell’utente. Sono sicuro che le idee per sfruttare questo tutorial certo non mancheranno, ma ora basta chiacchiere e partiamo con il nostro progetto
Come funziona la nostra animazione
Quello che andremo a creare in questo nuovo tutorial di programmazione iPhone è un effetto di scorrimento da destra verso sinistra per un testo mostrato all’interno di un’UILabel. Immaginate un testo molto più lungo dello spazio disponibile all’interno della label. Inizialmente vedremo solo una parte (l’inizio) della nostra stringa, il resto sarà quindi “nascosto”. Per ottenere la nostra animazione e visualizzare quindi tutto il testo, dovremo farlo scorrere e per questo sfrutteremo un timer che durante la sua esecuzione eliminerà in modo ciclico il primo carattere della stringa visualizzata, aggiungerà il carattere successivo (al momento non visibile) e ripresenterà la stringa ottenuta nella UILabel. Ai cicli successivi verranno rieseguite le stesse operazioni e l’effetto finale sarà proprio quello di uno scorrimento animato del testo. Ovviamente finite le lettere della nostra frase verranno ripescate le prime eliminate e la frase ricomincerà la sua corsa. (ok, è più facile a farsi che a dirsi).
Il nostro progetto XCode
Apriamo un nuovo progetto XCode e per prima cosa occupiamoci di dichiarare nel file .h un indice che tenga conto della lettera da cui dovrò partire per muovermi verso sinistra e un timer che useremo ad ogni step della nostra animazione per eliminare una lettera all’inizio e aggiungerne un’altra alla fine della nostra stringa da mostrare:
@interface T_097ViewController : UIViewController
{
int index; //indice per tenere conto della lettera da cui partiamo
NSTimer * timer;
}
- (void)targetMethod:(NSTimer *)timer;
@end
Fatto questo possiamo creare la nostra UILabel e il nostro Timer che di fatto andrà poi a fare tutto il lavoro per noi. Apriamo quindi il file di implementazione e modifichiamo il metodo “viewDidLoad” come segue:
- (void)viewDidLoad
{
[super viewDidLoad];
UIImageView * sfondo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"sfondot#097.png"]];
[self.view addSubview:sfondo];
[sfondo release];
//stringa che andrò a visualizzare sulla label
NSString * strTesto = [[NSString alloc] initWithString:@"Questo è un tutorial per devAPP scritto da Andrea Cappellotto"];
//la mia label, la possiamo personalizzare come vogliamo
UILabel * lblText = [[UILabel alloc] initWithFrame:CGRectMake(40, 100, 240, 40)];
lblText.font = [UIFont systemFontOfSize:18];
lblText.backgroundColor = [UIColor clearColor];
lblText.textColor = [UIColor whiteColor];
lblText.text = strTesto;
lblText.tag = 100; //imposto un tag per poi andarla a recuperare nel metodo chiamato dal timer per spostare la stringa
[self.view addSubview:lblText];
[lblText release];
//imposto l'indice ad 1
index = 1;
//creo un dizionario con il paramentro del testo che poi andrà a ripetersi
NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:strTesto,@"text", nil];
//imposto un timer
timer = [NSTimer scheduledTimerWithTimeInterval:.15 // intervallo di tempo in cui il testo viene spostato 0.15 secondi in esempio
target:self
selector:@selector(targetMethod:)
userInfo:dict
repeats:YES];
[strTesto release];
}
Nelle prime 3 righe non facciamo altro che aggiungere uno sfondo alla nostra applicazione, creiamo quindi la stringa che andremo a visualizzare e successivamente l’UILabel. Questa label la potremo personalizzare come meglio crediamo, non comprometterà in alcun modo il funzionamento della nostra animazione. Nel nostro esempio ho impostato il font a 18, tolto il colore di sfondo e impostato il colore del testo in bianco e, ovviamente, settato il testo che visualizzeremo al suo interno.
Ricordiamoci una cosa fondamentale, ovvero impostare un tag a questa label, poichè nel metodo richiamato dal timer andremo a recuperare la label proprio grazie a questo tag!!
Altra cosa importante è il dizionario che creiamo e che poi passeremo al timer. In questo modo avremo all’interno del metodo richiamato dal timer la nostra stringa e, se durante il corso dell’applicazione volessimo cambiare il testo mostrato nella label, occorrerà semplicemente modificare strTesto.
Nelle istruzioni successive creiamo il timer, il primo parametro riguarda il tempo di richiamo del metodo, potremo personalizzare a piacimento anche questo valore, abbiamo ancora il target, il metodo richamato, il dizionario passato come info e infine diciamo semplicemente di ripetere il metodo.
Vediamo ora il cuore del nostro progetto, ovvero il metodo che svolge il compito di modificare la stringa visualizzata:
-(void)targetMethod:(NSTimer *)timerM
{
//ottengo il dizionario delle info impostato sopra
NSMutableDictionary * dict = [timerM userInfo];
NSLog(@"%@, index: %i", dict, index);
//ricavo la label dal tag
UILabel * lbl1 = (UILabel *)[self.view viewWithTag:100];
//mi creo il range che voglio visualizzare, ogni volta tolgo una lettera dall'inizio quindi da index al numero di lettere che ha la mia stringa
NSRange range = NSMakeRange(index, [[dict objectForKey:@"text"] length]);
NSLog(@"range : %i, %i", range.location, range.length);
//creo la stringa che andrà ad aggiungersi alla fine per dare l'effetto di ritorno
NSString *strText = [NSString stringWithFormat:@" --- %@", [dict objectForKey:@"text"]];
// dalla stringa sopra mi ricavo le lettere che mancano per comporre una stringa di lunghezza uguale a quella originale
NSString *str2 = [NSString stringWithString:[strText substringWithRange:NSMakeRange(0, index)]];
//se l'indice è arrivato alla fine lo faccio ripartire da 1
if (index == [[dict objectForKey:@"text"] length])
{
index = 1;
}
//concateno la stringa originale con le lettere che devo aggiungere e ne prendo il range che mi serve
NSString * str = [[dict objectForKey:@"text"] stringByAppendingString:str2];
lbl1.text = [str substringWithRange:range];
index ++;
}
Come vedete niente di complicato, i commenti presenti nel codice vi aiuteranno a comprendere il tutto.
Tutto qui, e come per magia le nostra label avrà ottenuto un bellissimo effetto!!! Molto utile, oltre agli esempi citati ad inizio articolo, anche per mostrare in una barra le ultime news di una rivista o un sito.
Ultima cosa, non dimentichiamoci di fermare il timer quando la vista scompare, di ripristinarlo quando ricompare e, non meno importante, di rilasciarlo nel dealloc!! 😉
- (void)viewDidUnload
{
[super viewDidUnload];
[timer invalidate];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc
{
[super dealloc];
[timer release];
}
- (void)viewWillAppear:(BOOL)animated
{
if (![timer isValid])
{
[timer fire];
}
}
Bene con questo tutorial abbiamo terminato, se avete bisogno di chiarimenti o aiuti non esitate a postare le vostre richieste sul nostro forum qui su devAPP.it
Buona giornata e alla prossima!! 😉
Se avete problemi con il tutorial, questo è il nostro file di progetto.
17 Responses to “T#097 – Animiamo il testo all’interno di un’UILabel”
17 Giugno 2011
danielasei un genio !!!
17 Giugno 2011
AndreaNn esagerare..:):) grazie cmq… Penso sia il miglior compromesso tra prestazioni ed effetti grafici… Certo animare la label sarebbe stato più bello visivamente ma molto più dispendioso..
17 Giugno 2011
luigi.vivianOTTIMO…cercavo una cosa del genere!ottimo, grande!
Una domanda (forse dovrei conoscere la risposta) e due appunti:
domanda: perchè usi il “dizionario” e non un NSString semplice come parametro..
appunto 1: non far scorrere il testo nel caso in cui il testo sia breve (semplice semplice)
appunto 2: il testo scorre ma in maniera “discreta”, lettera per lettera, per fare un testo come quello dell’ “ultim’ora” dell’ App di TGCOM più fluido è più complicato? oppure nella tua frase “Certo animare la label sarebbe stato più bello visivamente ma molto più dispendioso” c’e’ già la risposta! grazie mille!
18 Giugno 2011
Ignaziocprovo a rispondere io:
0) dizionario perché il metodo per richiamare il timer richiede proprio un dizionario..ed a dirla tutta si potrebbe aggiungere un’altra chiave con il puntatore alla label, così lo stesso metodo può animare più label contemporaneamente
3) ottenuto il riferimento alla label è possibile verificare se la sua dimensione è sufficiente a contenere il testo, la classe nsstring ha un metodo apposta per stabilire la lunghezza di una stringa (in pixel) quando è rappresentata con un particolare font.
2) per realizzare qualcosa di più fluido dovresti avere due label lunghe a sufficienza per visualizzare il testo (che praticamente finirebbero fuori il margine dell’iphone) e muovere direttamente le label, così puoi muoverti anche di un pixel alla volta. te ne servirebbero due per l’effetto “carosello”.
18 Giugno 2011
Andrea Cappellottocerto, l’effeto che si otterebbe con una label è sicuramente migliore, diciamo che ho cercato di tenere un compromesso tra la comprensione del tutorial e la fluidità. Certamente possiamo animare due label per fargli fare la stessa cosa. Diciamo che l’idea che ho io di tutorial è quella di far vedere come fare una cosa, ma di sicuro ci può essere un modo migliore per farla e uno più bello per farla..;) (altrimenti chissà dove lavorerei ora) adesso che avete capito come funziona provate ad applicarlo ad una label e vediamo come viene…;)
20 Giugno 2011
_malza_Ottimo….ma una domanda, alla riga 24 nel viewdidload leggo:
NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:strTesto,@”text”, nil];
bello….ma andando a vedere nella iOS developer library alla classe NSMutableDictionary nei metodi come init vedo solo il initWithCapacity:, da dove prendi tu invece initWithObjectsAndKeys?? scusa ma sono agli inizi e stò cercando di capire il linguaggio…con scarsi risultati :s
20 Giugno 2011
Andrea Cappellottoperchè NSMutableDictionary eredita da NSDictionary e NSDictionary tra i metodi di init c’è il suddetto.. 😉
20 Giugno 2011
_malza_…grz…e tu utilizzi quello perchè vuoi inizializzarlo passandogli l’oggetto strTesto e la chiave “text” giusto?….ma quando utilizzo una classe/oggetto visto che per il principio dell’ereditarietà eredito appunto tutti i metodi fino a NSObject devo vedermi tutti i metodi della mia classe e di tutte le sottoclassi per capire quale è meglio utilizzare?
20 Giugno 2011
_malza_…oops…intendevo super-classi
20 Giugno 2011
Andrea Cappellottobeh, diciamo che impari a conoscerli piano piano… di soliti utilizzi sempre gli stessi e sono 3-4 per tipo… in questi casi la compilazione automatica ti da una mano ad intuire quale fa per te..
21 Giugno 2011
lucagreCiao,
ottimo template, solo una domanda, nel file di implementazione del metodo alla riga11 leggo:
NSRange range = NSMakeRange(index, [[dict objectForKey:@”text”] length]);
è corretto così o intendevi NSRange *range cioè che range è un puntatore ad un oggetto NSRange? Ho cercato sul sito developer.apple nelle librerie un pò di doc su NSRange e NSMakeRange ma non ci ho trovato molto (o meglio non ci ho capito molto :)) mi potresti chiarire meglio quella riga di codice?
Grazie ancora
21 Giugno 2011
Andrea Cappellottociao, allora…. la riga è giusta, questo perchè NSRange non è un oggetto ma una struttura definita così:
typedef struct _NSRange
{
NSUInteger location;
NSUInteger length;
} NSRange;
quindi creo una struttura in cui il primo elemento è l’indice iniziale mentre il secondo è una lunghezza… spero ti sia un po’ piu chiaro..;) casomai mi trovate nel forum 😀
21 Giugno 2011
lucagreok ora è più chiaro, grazie!
22 Giugno 2011
danielaCiao !
Se volessi utilizzare questo esempio di codice per animare una cella di sezione all’interno di una tableview ??
Praticamente per attirare l’attenzione e invoglire a fare “tap” ?:-)
24 Giugno 2011
Andrea Cappellottoceto… ti basta inserire la label nel contenuto della cella…;)
5 Luglio 2011
U.R.L..Ma quanta memoria consuma questo “trucchetto”??
5 Luglio 2011
Andrea Cappellottoin teoria niente di piu del normale inserimento di una label… ma non ho provato ad analizzarlo..