Nei nostri programmi iPhone e iPad spesso utilizziamo le classi UIAlertView e UIActionSheet per mostrare all’utente messaggi, avvisi e notifiche sullo stato del programma. Queste due classi sono ottime quando vogliamo l’intervento dell’utente, per esempio per richiedere la conferma per la cancellazione di un dato o qualcosa del genere ma ci sono altri casi in cui vogliamo soltanto mostrargli un messaggio, e per questo mettiamo nell’alertView solo il pulsante “ok”. Ecco, in questi casi le alertView e gli actionSheet pussono risultare fastidiosi se i messaggi mostrati sono tanti.
Questo problema mi si è presentato in un progetto sviluppato per l’azienda dove lavoro e mentre mi chiedevo quale soluzione alternativa potessi utilizzare mi è venuto in mente growl, il sistema di notifiche che ho installato sul mio mac.
L’idea mi è sembrata pulita ed elegante, delle piccole view semitrasparenti da mostrare in un angolo della pagina e poi far scomparire in automatico…ok, si può fare.
Il risultato finale che dovremmo ottenere è questo:

se vi piace continuate la lettura di questo nostro articolo, se non vi piace continuate lo stesso 😛 e commentate.. 🙂
Prepariamo il nostro progetto
Per realizzare questo oggetto partiamo dal creare un nuovo file all’interno del nostro progetto, il file dovrà essere subClass di UIview e lo chiameremo “NotificationView” (.h e .m).
Per chi non sapesse come fare, vi ricordo che basta cliccare sul menù “file->new file” e scegliere “Objective-C class” dalla sezione “Cocoa Touch Class” avendo cura, però, che nella casella a discesa sia selezionato “subclass of UIView”.
Questa classe dovrà (al momento) avere almeno tre proprietà:
- un titolo,
- un messaggio
- e una piccola immagine da mostrare in alto a sinistra.
Iniziamo quindi a dichiarare questi oggetti all’interno del file header. In questa occasione ho utilizzato il costrutto @property invece dei più classici getter e setter, spero non vi dispiaccia!
Visto che vogliamo avere gli angoli arrotondati ci servirà importare anche quartz, quindi aggiungiamo il framework QuartzCore. I nostri due file dovrebbero apparire in questo modo:
//file: NotoficationView.h
#import
@interface NotificationView : UIView {
NSString *title;
NSString *message;
UIImage *icon;
}
@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSString *message;
@property (nonatomic, retain) UIImage *icon;
//file:NotificationView.m
#import "NotificationView.h"
@implementation NotificationView
@synthesize title, message,icon;
- (void)dealloc {
title = nil;
message = nil;
icon = nil;
[super dealloc];
}
Prepariamo il nostro nuovo oggetto personalizzato
Bene, iniziamo adesso ad occuparci di come dovremo istanziare questo oggetto nel nostro codice.. mica vorremmo ogni volta dover settare il titolo, il messaggio e l’icona in tre istruzioni diverse. Vogliamo un solo metodo che, anche se lungo 300 caratteri, ci permetta di fare tutto con una sola istruzione!
Aggiungiamo quindi un inizializzatore personalizzato, io in un eccesso di fantasia l’ho chiamato:
- (id)initWithTitleMessageImage:(NSString *)atitle message:(NSString *)amessage image:(UIImage *)animage
Per prima cosa all’interno di questo metodo dobbiamo richiamare un secondo costruttore, presente di default e sul quale non facciamo nessun override:
self = [self initWithFrame:CGRectMake(768, 55 , 300, 80)];
La view ha dimensioni 300×80, ma il suo angolo superiore sinistro ha coordinate (768,55) quindi attualmente è fuori dallo schermo.
Ottenuto il riferimento a “self” passiamo ad impostare le proprietà che avevamo dichiarato:
self.title = atitle;
self.message = amessage;
self.icon = animage;
Per le caratteristiche grafiche della notification ho preferito utilizzare dei #define nel file .h così che sia più facile eventualmente modificare questi valori. Ho scritto quindi in testa al file NotificationView.h:
#define kDefaultRectColor [UIColor blackColor]
#define kDefaultCornerRadius 10.0
#define kDefaultAlphaValue 0.7
#define kDefaultBorderWidth 2.0
così all’interno del metodo initWithTitleMessageImage ho settato l’aspetto della view tramite:
self.layer.cornerRadius = kDefaultCornerRadius;
self.backgroundColor = kDefaultRectColor;
self.alpha = kDefaultAlphaValue;
[self.layer setBorderColor:[[UIColor whiteColor] CGColor]];
[self.layer setBorderWidth:kDefaultBorderWidth];
Infine ho dichiarato le due label e l’oggetto UIImageView per mostrare le proprietà. Il metodo completo è questo:
- (id)initWithTitleMessageImage:(NSString *)atitle message:(NSString *)amessage image:(UIImage *)animage{
self = [self initWithFrame:CGRectMake(768, 55 , 300, 80)];
if (self) {
self.title = atitle;
self.message = amessage;
self.icon = animage;
self.layer.cornerRadius = kDefaultCornerRadius;
self.backgroundColor = kDefaultRectColor;
self.alpha = kDefaultAlphaValue;
[self.layer setBorderColor:[[UIColor whiteColor] CGColor]];
[self.layer setBorderWidth:kDefaultBorderWidth];
//Allocazione e posionzamento della label per il titolo.
UILabel *titleLabel;
titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 5, 250, 35)];
[titleLabel setFont:[UIFont boldSystemFontOfSize:18]];
[titleLabel setBackgroundColor:[UIColor clearColor]];
[titleLabel setTextColor:[UIColor whiteColor]];
[titleLabel setAdjustsFontSizeToFitWidth:TRUE];
[titleLabel setText:title];
[self addSubview:titleLabel];
//Allocazione e posionzamento della label per il messaggio.
UILabel *messageLabel;
messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 30, 250, 40)];
[messageLabel setFont:[UIFont systemFontOfSize:16]];
[messageLabel setBackgroundColor:[UIColor clearColor]];
[messageLabel setTextColor:[UIColor whiteColor]];
[messageLabel setAdjustsFontSizeToFitWidth:TRUE];
[messageLabel setText:message];
[self addSubview:messageLabel];
//Allocazione e posizionamento della imageview.
UIImageView *imageView;
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 32, 32)];
[imageView setImage:icon];
[self addSubview:imageView];
[imageView release];
}
return self;
}
Perfetto, adesso non ci resta che occuparci dell’animazione.

Le animazioni di entrata e uscita delle nostre notifiche
Io ho previsto tre stili di animazione:
- lo slide da destra
- lo slide da sinistra
- il fading
Il concetto alla base è identico per tutti e tre gli stili, quindi commenterò in questo articolo solo il primo, potete provare da voi a completare il resto e se proprio non riuscite (ma sforzatevi ^^) trovate tutto il codice nel progetto allegato.
L’idea di base è che abbiamo tre eventi successivi, l’apparizione della notifica, la sua scomparsa ed il tempo che resterà ferma e visibile. Dichiariamo anche in questo caso tre #define nel file .h per la durata in secondi di questi eventi:
#define kDefaultShowAnimationDuration .7
#define kDefaultShowTime 1
#define kDefaultDismissAnimationDuration .7
Il metodo che si occupa di far apparire la view da destra è il seguente:
- (void)showFromRight {
CGRect endREct;
endREct = CGRectMake(768 - 300 + 10, 55, 300, 80);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:kDefaultShowAnimationDuration];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(endRightAnimation)];
self.frame = endREct;
[delegate notificationViewWillAppear:self];
[UIView commitAnimations];
}
In questo codice abbiamo dichiarato un rettangolo dove dovrà apparire la notifica (768 – 300 + 10 serve per ricordarmi che la view non appare tutta, ma lascio 10 px fuori dallo schermo).
Con il metodo setAnimationDidStopSelector definiamo che alla fine dell’animazione verrà invocato il metodo endRightAnimation.
Il metodo endRightAnimation viene invocato quando la view è appena arrivata in posizione, quindi dovremmo attendere un certo lasso di tempo e poi far ritornare la view fuori dallo schermo. Io ho ottenuto questo risultato così facendo:
- (void)endRightAnimation {
[self performSelector:@selector(dismissFromRight) withObject:self afterDelay:kDefaultShowTime];
}
- (void)dismissFromRight {
CGRect endREct;
endREct = CGRectMake(768 , 55 , 300, 80);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:kDefaultDismissAnimationDuration];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(remove)];
self.frame = endREct;
[UIView commitAnimations];
}
In pratica all’interno del metodo endRightAnimation non faccio altro che invocare il metodo performSelector dopo un delay di “kDefaultShowTime” secondi. In questo modo la view resterà visibile per questo lasso di tempo. Allo scadere del tempo viene invocato il metodo dismissFromRight che si occupa di far tornare la view nella posizione originale (fuori dallo schermo).
Per maggiore pulizia ho fatto in modo che alla fine di questa animazione venga invocato il metodo remove:
- (void)remove {
[self removeFromSuperview];
}
NOTA: Se avete avuto difficoltà a seguire qualche passaggio consultate il file del progetto allegato.
Usiamo le notifiche in stile growl nelle nostre applicazioni iPhone e iPad
In qualunque parte della nostra applicazione, se vogliamo far apparire una notifica, non dovremo far altro che scrivere:
NotificationView *n;
n = [[NotificationView alloc] initWithTitleMessageImage:@"Mail"
message:@"You have new email"
image:[UIImage imageNamed:@"AlertICON.png"]];
[n setDelegate:self];
[self.view addSubview:n];
[n showFromRight];
Una chicca per i più smaliziati
Come possiamo fare ad essere avvisati quando la notifica sta per apparire e quando invece viene dismessa?
In pratica vorremmo ricreare quello che accade con le UIAlertView, in cui settando la nostra classe come suo delegate riusciamo ad intercettarne tutti gli eventi.
Ok, inventiamoci il NotificationViewDelegateProtocol e definiamo che debba avere questi due metodi:
@protocol NotificationViewDelegateProtocol
- (void)notificationViewDidDismiss:(NotificationView *)alertView;
- (void)notificationViewWillAppear:(NotificationView *)alertView;
@end
(Il mio precedente articolo parla proprio del costrutto @protocol leggetelo se avete difficoltà e non capite di cosa stiamo parlando ;))
Modifichiamo quindi la classe NotificationView aggiungendo tra le sue proprietà anche un oggetto che implementi tale protocollo:
@interface NotificationView : UIView {
NSString *title;
NSString *message;
UIImage *icon;
id delegate;
}
e modifichiamo i metodi showFromRight e remove aggiungendo l’invocazione di tali metodi del delegate:
- (void)showFromRight {
CGRect endREct;
endREct = CGRectMake(768 - 300 + 10, 55, 300, 80);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:kDefaultShowAnimationDuration];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(endRightAnimation)];
self.frame = endREct;
[delegate notificationViewWillAppear:self];
[UIView commitAnimations];
}
- (void)remove {
[self removeFromSuperview];
[delegate notificationViewDidDismiss:self];
}
In questo modo la classe che vorrà fare da delegate alle nostre NotificationView potrà dichiararlo nel file di implementazione tramite:
;
ed utilizzare i due metodi per intercettare gli eventi della NotificationView:
#pragma mark -
#pragma mark NotificationViewDelegateProtocol Methods
-(void)notificationViewWillAppear:(NotificationView *)notificationView {
NSLog(@"La notificationView dal titolo: \"%@\" sta per apparire",[notificationView title]);
}
-(void)notificationViewDidDismiss:(NotificationView *)notificationView {
NSLog(@"La notification dal titolo: \"%@\" è stata dismessa",[notificationView title]);
}
Problemi noti
Il codice che ho scritto è sicuramente migliorabile.
Due modifiche sono sicuramente più urgenti di altre (se volete collaborare possiamo organizzarci e mettere su goolecode o altre piattaforme). La prima riguarda la lunghezza del messaggio, che in questo caso è molto limitata perchè la view non si ridimensiona automaticamente (non l’abbiamo previsto :P). La seconda, riguarda invece l’apparizione di più view contemporaneamente: se provate a farne apparire più di una, infatti, noterete un effetto fastidioso e sicuramente non voluto: le notifiche vengono sovrapposte, quindi il risultato non è ottimale.
Quindi.. sviluppatori di tutto il mondo.. unitevi.. e miglioriamo insieme questo progetto 😀
Alla prossima!










6 Responses to “T#087 – Notifiche in stile growl per le nostre applicazioni iPhone e iPad”
1 Marzo 2011
Junior B.progetto interessante, io ho creato una cosa molto simile, ma per iPhone e l’ho chiamata InfoBar… ci sarà anche nel progetto completo 🙂
Se metti il codice su github posso aiutarti, qualche idea la ho già 😉
1 Marzo 2011
ignaziocovviamente si, purtroppo non sono pratico di questi sistemi di condivisione del codice 🙁 manda pure una mail a ignazioc at gmail.com
24 Aprile 2011
Zeciao ignazio.come mai il compilatore mi da errore se cerco di accedere alle proprietà dell oggetto self.layer??
24 Aprile 2011
Ignaziocprobabilmente non hai importato il framework quartz.
24 Aprile 2011
ZeSi Ignazio ,grazie mille.Chissà per quanti giorni avrei sbattuto la testa sul muro in cerca di una soluzione:) grazie ancora.Luca
23 Settembre 2013
FabioCiao Ignazio Grazie tante per questa splendida risorsa… Utile e carinissima… Ti volevo chiedere… ma se volessi aggiungere un UIActivityIndicator al posto dell’Immagine? ho provato varie cose ma non sono riuscito… magari mi perdo da qualche parte…
Grazie Tante
Fabio