Eccoci ancora una volta con un piccolo tip sulle UIAlertView. Questi sono sicuramente tra i componenti più utilizzati e anche più versatili, ma non è facile cambiarne l’aspetto grafico.
Può capitare, infatti, che la classica UIAlertView “stoni” con il design della nostra applicazione, rendendo il tutto un po’ meno piacevole. Personalizzarne il colore e lo stile non è facile ed immediato, ma ho trovato su Internet una classe che permette di creare delle alert con colori personalizzati, semplificandoci davvero il lavoro!
Eccovi un esempio di ciò che tale classe permette di fare:
Per implementare questo componente create una nuova classe di nome “CustomAlert” e inserite nel file .h questo codice:
1 2 3 4 5 6 7 8 9 |
#import <UIKit/UIKit.h> @interface CustomAlert : UIAlertView{ } + (void)setBackgroundColor:(UIColor *)background withStrokeColor:(UIColor *) stroke; @end |
e nel file .m questo codice:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
#import "CustomAlert.h" @interface CustomAlert (Private) - (void) drawRoundedRect:(CGRect) rect inContext:(CGContextRef) context withRadius:(CGFloat) radius; @end static UIColor *fillColor = nil; static UIColor *borderColor = nil; @implementation CustomAlert + (void) setBackgroundColor:(UIColor *) background withStrokeColor:(UIColor *) stroke { if(fillColor != nil) { [fillColor release]; [borderColor release]; } fillColor = [background retain]; borderColor = [stroke retain]; } - (id)initWithFrame:(CGRect)frame { if((self = [super initWithFrame:frame])) { if(fillColor == nil) { fillColor = [[UIColor blackColor] retain]; borderColor = [[UIColor colorWithHue:0.625 saturation:0.0 brightness:0.8 alpha:0.8] retain]; } } return self; } - (void)layoutSubviews { for (UIView *sub in [self subviews]) { if([sub class] == [UIImageView class] && sub.tag == 0) { // The alert background UIImageView tag is 0, // if you are adding your own UIImageView's // make sure your tags != 0 or this fix // will remove your UIImageView's as well! [sub removeFromSuperview]; break; } } } - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextClearRect(context, rect); CGContextSetAllowsAntialiasing(context, true); CGContextSetLineWidth(context, 0.0); CGContextSetAlpha(context, 0.8); CGContextSetLineWidth(context, 2.0); CGContextSetStrokeColorWithColor(context, [borderColor CGColor]); CGContextSetFillColorWithColor(context, [fillColor CGColor]); // Draw background CGFloat backOffset = 2; CGRect backRect = CGRectMake(rect.origin.x + backOffset, rect.origin.y + backOffset, rect.size.width - backOffset*2, rect.size.height - backOffset*2); [self drawRoundedRect:backRect inContext:context withRadius:8]; CGContextDrawPath(context, kCGPathFillStroke); // Clip Context CGRect clipRect = CGRectMake(backRect.origin.x + backOffset-1, backRect.origin.y + backOffset-1, backRect.size.width - (backOffset-1)*2, backRect.size.height - (backOffset-1)*2); [self drawRoundedRect:clipRect inContext:context withRadius:8]; CGContextClip (context); //Draw highlight CGGradientRef glossGradient; CGColorSpaceRef rgbColorspace; size_t num_locations = 2; CGFloat locations[2] = { 0.0, 1.0 }; CGFloat components[8] = { 1.0, 1.0, 1.0, 0.35, 1.0, 1.0, 1.0, 0.06 }; rgbColorspace = CGColorSpaceCreateDeviceRGB(); glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations); CGRect ovalRect = CGRectMake(-130, -115, (rect.size.width*2), rect.size.width/2); CGPoint start = CGPointMake(rect.origin.x, rect.origin.y); CGPoint end = CGPointMake(rect.origin.x, rect.size.height/5); CGContextSetAlpha(context, 1.0); CGContextAddEllipseInRect(context, ovalRect); CGContextClip (context); CGContextDrawLinearGradient(context, glossGradient, start, end, 0); CGGradientRelease(glossGradient); CGColorSpaceRelease(rgbColorspace); } - (void) drawRoundedRect:(CGRect) rrect inContext:(CGContextRef) context withRadius:(CGFloat) radius { CGContextBeginPath (context); CGFloat minx = CGRectGetMinX(rrect), midx = CGRectGetMidX(rrect), maxx = CGRectGetMaxX(rrect); CGFloat miny = CGRectGetMinY(rrect), midy = CGRectGetMidY(rrect), maxy = CGRectGetMaxY(rrect); CGContextMoveToPoint(context, minx, midy); CGContextAddArcToPoint(context, minx, miny, midx, miny, radius); CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius); CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius); CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius); CGContextClosePath(context); } - (void)dealloc { [super dealloc]; } @end |
Per utilizzare delle UIAlertView personalizzate dovrete utilizzare questa classe nello stesso modo in cui utilizzare gli alert tradizionali, eccovi un esempio:
1 2 3 4 5 6 |
// Imposto i colori [CustomAlert setBackgroundColor:[UIColor blueColor] withStrokeColor:[UIColor cyanColor]]; // Creo e visualizzo l'alert personalizzata CustomAlert *alert=[[CustomAlert alloc] initWithTitle:@"Prova!" message:@"Eccovi un'alert personalizzata.." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; |
Questo è l’unico modo che ho trovato per personalizzare il colore di una UIAlertView, e devo dire che i risultati non sono male. Se siete a conoscenza di altri metodi fatemelo sapere nei commenti!
Aggiornamento 21/01/2011: il codice è stato aggiornato (come nella versione originale) per funzionare anche con iOS 4.2 😉
I meriti di tale classe sono di kwigbo, trovate l’articolo originale a questo indirizzo.
Ciao,
il codice è interessante e può tornare utile.
Mi viene un dubbio però.
Non è che poi si vada contro le User Interface Gudelines e la Apple rifiuti l’app?
Ho poi un altro paio di dubbi riguardo il codice.
Perché il metodo setBackgroundColor:withStrokeColor: è dichiarato come metodo di classe?
E perché i puntatoti ai due colori fillColor e borderColor vengono dichiarati come statici al di fuori dell’interfaccia della classe?
E in ultimo, chi li rilascia poi quegli oggetti, dato che viene fatto il retain?
In pratica, non si poteva mettere i due colori come variabili d’istanza e usare il metodo come d’istanza?
Scusa l’ignoranza, ma non riuscendo a capire bene la cosa volevo maggiori deluciadzioni.
Grazie!
grazie
Bella questa !
Grazie Andrea !
Mi capita proprio a fagiolo per la mia app
@Fast: prego 😉
@FM: perchè “setBackgroundColor:withStrokeColor” sia definito statico non l’ho ben capito nemmeno io, si sarebbe potuto fare un metodo d’istanza classico.. io ho trovato l’esempio così, se riuscite ad apportare miglioramenti aggiorno l’articolo 😉
P.S: anche il creatore della classe sul suo blog ha detto che non è perfetta, quindi penso si possa migliorare in qualche aspetto 😉
Intanto ringrazio per la guida , in volevo comunicare che ho utilizzato la guida nella mia app che è stata appena approvata , quindi sembra non ci siano problemi con le UIG !
@Ragazzetto: ottimo, grazie mille della conferma 😉
Prego !
🙂
Aggiorno !
Con il 4.2 beta la classe non funziona più correttamente , ci sono problemi di visualizzazione !
@Ragazzetto: grazie della segnalazione! proverò a darci un’occhiata! 😉
Andrea hai mica avuto modo di darci l’ occhiata ?
Intanto ti posso dire che è stato aggiunto un messaggio anche sull articolo di kwigbo per cumunicargli il mal funzionamento !
E’ arrivata la soluzione dall autore !!!!!
http://kwigbo.com/post/318396305/iphone-sdk-custom-uialertview-background-color
@Ragazzetto: ottimo!! provo a guardarci e nel caso aggiorno l’articolo 😉
Articolo aggiornato, ora il codice è compatibile anche con iOS 4.2 😉 (testato su simulatore, funziona senza problemi)