Nel primo tutorial di questa breve serie su UIKit Dynamics, abbiamo appreso le basi dell'API creando un componente di menu animato. In questo tutorial, continueremo a lavorare sul nostro progetto e implementeremo un altro componente animato, una visualizzazione di avviso personalizzata.
La vista di avviso predefinita su iOS è ottima, ma non è molto personalizzabile in termini di aspetto e comportamento. Se hai bisogno di una vista di avviso che sia personalizzabile, devi creare la tua soluzione e questo è ciò che faremo in questo tutorial. Il focus di questo tutorial è sul comportamento della vista di avviso e non tanto sulla sua funzionalità. Vediamo qual è il risultato che stiamo cercando
La vista di avviso sarà a UIView
istanza a cui aggiungeremo le seguenti sottoview:
UILabel
oggetto per la visualizzazione del titolo della vista di avvisoUILabel
oggetto per la visualizzazione del messaggio della vista di avvisoUIButton
istanze per consentire all'utente di interagire con la vista di avvisoUseremo il UISnapBehavior
classe per presentare la vista di avviso. Come indica il nome, questo UIDynamicBehavior
sottoclasse costringe un oggetto dinamico a scattare su un punto come se fosse magneticamente disegnato su di esso.
Il UISnapBehavior
la classe definisce una proprietà aggiuntiva, smorzamento
, che definisce la quantità di oscillazione quando l'oggetto dinamico ha raggiunto il punto a cui è attratto.
Useremo un comportamento gravitazionale, in combinazione con un comportamento di collisione e spinta, per eliminare la vista di avviso. Ricorda che abbiamo già utilizzato questi comportamenti nel tutorial precedente.
La vista di avviso si animerà dalla parte superiore dello schermo. Quando la vista di avviso sta per apparire, il comportamento dello snap lo farà cadere in vista e farà uno snap al centro dello schermo. Per ignorare la vista di avviso, un comportamento di spinta lo spingerà brevemente verso la parte inferiore dello schermo e un comportamento di gravità lo trascinerà in cima allo schermo e lo animerà fuori dallo schermo.
Creeremo un metodo di inizializzazione personalizzato per il componente di visualizzazione degli avvisi che accetta il titolo, il messaggio, i titoli dei pulsanti e la relativa visualizzazione genitore. Non implementeremo un protocollo delegato per la visualizzazione degli avvisi. Invece, faremo uso di blocchi, il che rende una soluzione più elegante e moderna. Il blocco o il gestore accetterà due parametri, l'indice e il titolo del pulsante toccato dall'utente.
Mostreremo anche una vista semitrasparente dietro la vista di avviso per impedire all'utente di interagire con la sua visualizzazione genitoriale fintanto che la vista di avviso è visibile. Iniziamo dando un'occhiata alle proprietà della vista di avviso e all'inizializzatore personalizzato.
stampa Comando-N sulla tastiera per creare un nuovo file e selezionare Classe Objective-C dalla lista di iOS modelli. Ne fanno una sottoclasse di NSObject e nominalo AlertComponent.
Il prossimo passo è dichiarare alcune proprietà private. Aperto AlertComponent.m, aggiungi un'estensione di classe nella parte superiore e dichiara le seguenti proprietà:
@interface AlertComponent () @property (nonatomic, strong) UIView * alertView; @property (nonatomic, strong) UIView * backgroundView; @property (nonatomic, strong) UIView * targetView; @property (nonatomic, strong) UILabel * titleLabel; @property (nonatomic, strong) UILabel * messageLabel; @property (nonatomic, strong) animatore UIDynamicAnimator *; @property (nonatomic, strong) NSString * title; @property (nonatomic, strong) messaggio NSString *; @property (nonatomic, strong) NSArray * buttonTitles; @property (nonatomic) CGRect initialAlertViewFrame; @fine
La funzione di ogni proprietà diventerà chiara mentre implementiamo il componente di avviso. È ora di creare l'inizializzatore personalizzato del componente.
Come ho già detto, useremo un inizializzatore personalizzato per rendere il lavoro con il componente di avviso il più semplice possibile. L'inizializzatore accetta quattro parametri, il titolo dell'avviso, il suo messaggio, i titoli dei pulsanti e la vista a cui verrà aggiunto il componente di avviso, la sua vista genitore. Aperto AlertComponent.h e aggiungere la seguente dichiarazione:
@interface AlertComponent: NSObject - (id) initAlertWithTitle: (NSString *) title andMessage: (NSString *) message andButtonTitles: (NSArray *) buttonTitles andTargetView: (UIView *) targetView; @fine
In questa parte verrà impostata la visualizzazione degli avvisi e verranno aggiunti tutti i sottoview. Inoltre, verrà impostata anche la vista di sfondo e l'animatore dinamico.
Aperto AlertComponent.m e dichiara i seguenti metodi privati nell'estensione della classe privata:
@interface AlertComponent () ... - (void) setupBackgroundView; - (void) setupAlertView; @fine
I nomi dei metodi sono autoesplicativi. Iniziamo implementando il setupAlertView
metodo in primo luogo poiché la maggior parte della configurazione dell'avviso avviene in questo metodo.
Nel setupAlertView
, facciamo tre cose:
Cominciamo calcolando la dimensione e la posizione della vista di avviso come mostrato nel frammento di codice seguente.
- (void) setupAlertView // Imposta la dimensione della vista di avviso. CGSize alertViewSize = CGSizeMake (250.0, 130.0 + 50.0 * self.buttonTitles.count); // Imposta il punto di origine iniziale in base alla direzione della vista di avviso. CGPoint initialOriginPoint = CGPointMake (self.targetView.center.x, self.targetView.frame.origin.y - alertViewSize.height);
Iniziamo impostando la dimensione della vista di avviso. Per rendere dinamica la visualizzazione degli avvisi, aggiungiamo 50.0
punta alla sua altezza per ogni pulsante. Si noti inoltre che l'origine iniziale della vista di avviso è fuori dallo schermo. Il prossimo passo è l'inizializzazione e l'impostazione della visualizzazione degli avvisi:
self.alertView = [[Allocazione UIView] initWithFrame: CGRectMake (initialOriginPoint.x, initialOriginPoint.y, alertViewSize.width, alertViewSize.height)]; // Colore di sfondo. [self.alertView setBackgroundColor: [UIColor colorWithRed: 0.94 green: 0.94 blue: 0.94 alpha: 1.0]]; // Crea la vista di avviso con gli angoli arrotondati. [self.alertView.layer setCornerRadius: 10.0]; // Imposta un bordo nella vista dell'avviso. [self.alertView.layer setBorderWidth: 1.0]; [self.alertView.layer setBorderColor: [UIColor blackColor] .CGColor]; // Assegna il frame di visualizzazione dell'avviso iniziale alla rispettiva proprietà. self.initialAlertViewFrame = self.alertView.frame;
utilizzando alertViewSize
e initialOriginPoint
, inizializziamo il alertView
oggetto e impostare il suo colore di sfondo. Giriamo attorno agli angoli della vista di avviso impostandone la sua strato
'S raggio dell'angolo
a 10.0
, suo larghezza del bordo
a 1.0
, e la sua colore del bordo
annerire. Memorizziamo anche la cornice iniziale della vista di avviso nella sua initialAlertViewFrame
proprietà come ne avremo bisogno in seguito.
Se Xcode ti dice che non sa di alertView
'S strato
proprietà, quindi aggiungere la seguente istruzione import nella parte superiore del file di implementazione:
#importare
È ora di aggiungere le etichette. Iniziamo con l'etichetta del titolo.
// Imposta l'etichetta del titolo. self.titleLabel = [[UILabel alloc] initWithFrame: CGRectMake (0.0, 10.0, self.alertView.frame.size.width, 40.0)]; [self.titleLabel setText: self.title]; [self.titleLabel setTextAlignment: NSTextAlignmentCenter]; [self.titleLabel setFont: [UIFont fontWithName: @ "Avenir-Heavy" size: 14.0]]; // Aggiungi l'etichetta del titolo alla visualizzazione degli avvisi. [self.alertView addSubview: self.titleLabel];
L'impostazione dell'etichetta del messaggio è abbastanza simile.
// Imposta l'etichetta del messaggio. self.messageLabel = [[UILabel alloc] initWithFrame: CGRectMake (0.0, self.titleLabel.frame.origin.y + self.titleLabel.frame.size.height, self.alertView.frame.size.width, 80.0)]; [self.messageLabel setText: self.message]; [self.messageLabel setTextAlignment: NSTextAlignmentCenter]; [self.messageLabel setFont: [UIFont fontWithName: @ "Avenir" size: 14.0]]; [self.messageLabel setNumberOfLines: 3]; [self.messageLabel setLineBreakMode: NSLineBreakByWordWrapping]; // Aggiungi l'etichetta del messaggio alla visualizzazione degli avvisi. [self.alertView addSubview: self.messageLabel];
Si noti che il NumberOfLines
la proprietà è impostata su 3
e lineBreakMode
è impostato per NSLineBreakByWordWrapping
.
L'ultima cosa che dobbiamo impostare sono i pulsanti della vista di avviso. Anche se il numero di pulsanti può variare, l'impostazione e il posizionamento dei pulsanti è piuttosto semplice. Separiamo i pulsanti per 5
punti e usa a per
loop per inizializzarli.
CGFloat lastSubviewBottomY = self.messageLabel.frame.origin.y + self.messageLabel.frame.size.height; per (int i = 0; i<[self.buttonTitles count]; i++) UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10.0, lastSubviewBottomY + 5.0, self.alertView.frame.size.width - 20.0, 40.0)]; [button setTitle:[self.buttonTitles objectAtIndex:i] forState:UIControlStateNormal]; [button.titleLabel setFont:[UIFont fontWithName:@"Avenir" size:13.0]]; [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [button setTitleColor:[UIColor yellowColor] forState:UIControlStateHighlighted]; [button setBackgroundColor:[UIColor colorWithRed:0.0 green:0.47 blue:0.39 alpha:1.0]]; [button addTarget:self action:@selector(handleButtonTap:) forControlEvents:UIControlEventTouchUpInside]; [button setTag:i + 1]; [self.alertView addSubview:button]; lastSubviewBottomY = button.frame.origin.y + button.frame.size.height;
Si noti che ogni pulsante richiama il handleButtonTap:
metodo quando è TAPpato. Possiamo determinare quale pulsante l'utente ha toccato controllando i pulsanti etichetta
proprietà.
Infine, aggiungi la vista di avviso alla vista target o genitore aggiungendo la seguente riga nella parte inferiore del metodo setupAlertView:
// Aggiungi la vista di avviso alla vista genitore. [self.targetView addSubview: self.alertView];
Il secondo metodo che dobbiamo implementare è setupBackgroundView
. La vista di sfondo impedisce all'utente di interagire con la vista genitore della vista di avviso finché viene visualizzata la vista di avviso. Inizialmente abbiamo impostato il suo alfa
proprietà a 0.0
, il che significa che è trasparente.
- (void) setupBackgroundView self.backgroundView = [[UIView alloc] initWithFrame: self.targetView.frame]; [self.backgroundView setBackgroundColor: [UIColor grayColor]]; [self.backgroundView setAlpha: 0.0]; [self.targetView addSubview: self.backgroundView];
Con setupAlertView
e setupBackgroundView
pronto per l'uso, implementiamo l'inizializzatore personalizzato che abbiamo dichiarato in precedenza.
- (id) initAlertWithTitle: (NSString *) title andMessage: (NSString *) message andButtonTitles: (NSArray *) buttonTitles andTargetView: (UIView *) targetView if (self = [super init]) // Assegna i valori dei parametri a local proprietà. self.title = titolo; self.message = message; self.targetView = targetView; self.buttonTitles = buttonTitles; // Imposta la vista di sfondo. [self setupBackgroundView]; // Imposta la visualizzazione degli avvisi. [self setupAlertView]; // Imposta l'animatore. self.animator = [[UIDynamicAnimator alloc] initWithReferenceView: self.targetView]; return self;
Abbiamo impostato il titolo
, Messaggio
, TargetView
, e buttonTitles
proprietà, invocare setupBackgroundView
e setupAlertView
, e inizializza l'animatore dinamico, passando dentro self.targetView
come la sua vista di riferimento.
Per mostrare la vista di avviso dopo che è stata inizializzata, dobbiamo dichiarare e implementare un metodo pubblico che può essere chiamato, ad esempio, dal controller della vista che ospita la vista di avviso. Aperto AlertComponent.h e aggiungi la seguente dichiarazione di metodo:
- (Vuoto) showAlertView;
Torna a AlertComponent.m implementare showAlertView
. Come ho detto prima in questo tutorial, useremo un nuovo UIDynamicBehavior
sottoclasse per mostrare la vista di avviso, UISnapBehavior
. Vediamo come usiamo questa classe in showAlertView
.
- (void) showAlertView [self.animator removeAllBehaviors]; UISnapBehavior * snapBehavior = [[UISnapBehavior alloc] initWithItem: self.alertView snapToPoint: self.targetView.center]; snapBehavior.damping = 0.8; [self.animator addBehavior: snapBehavior]; [UIView animateWithDuration: 0,75 animazioni: ^ [self.backgroundView setAlpha: 0.5]; ];
Iniziamo rimuovendo eventuali comportamenti dinamici esistenti dall'animatore dinamico per garantire che non compaiano conflitti. Ricorda che alcuni comportamenti dinamici possono essere aggiunti solo una volta all'animatore dinamico, come un comportamento gravitazionale. Inoltre, aggiungeremo altri comportamenti dinamici per eliminare la vista di avviso.
Come puoi vedere, usare un comportamento di snap non è difficile. Specifichiamo quale elemento dinamico deve essere applicato al comportamento e imposta il punto su cui deve scattare l'elemento dinamico. Impostiamo anche il comportamento smorzamento
proprietà come abbiamo discusso in precedenza. Si noti inoltre che animiamo il alfa
proprietà della vista di sfondo.
Per testare la visualizzazione degli avvisi, è necessario apportare alcune modifiche a ViewController
classe. Iniziamo aggiungendo a UIButton
istanza alla vista del controller della vista per mostrare la vista di avviso. Aperto Main.storyboard e trascinare a UIButton
istanza dal Libreria di oggetti alla vista del controller della vista. Posiziona il pulsante vicino alla parte inferiore della vista e assegnagli un titolo di Mostra vista avviso. Aggiungi un'azione a ViewController.h come mostrato di seguito.
@interface ViewController: UIViewController - (IBAction) showAlertView: (id) mittente; @fine
Tornate allo storyboard e collegate l'azione del controller di visualizzazione al pulsante. Aperto ViewController.m e importa il file di intestazione del file AlertComponent
classe.
#import "AlertComponent.h"
Quindi, dichiarare una proprietà nell'estensione di classe privata di tipo AlertComponent
e nominalo alertComponent
.
@interface ViewController () @property (nonatomic, strong) MenuComponent * menuComponent; @property (nonatomic, strong) AlertComponent * alertComponent; - (void) showMenu: (UIGestureRecognizer *) gestureRecognizer; @fine
Inizializziamo quindi il componente di avviso nel controller della vista viewDidLoad
metodo.
- (void) viewDidLoad ... // Initialize Alert Component self.alertComponent = [[AlertComponent alloc] initAlertWithTitle: @ "Custom Alert" andMessage: @ "Hai un nuovo messaggio di posta elettronica, ma non so da chi." andButtonTitles: @ [@ "Mostrami", @ "Non mi interessa", @ "Per me, davvero?"] eTargetView: self.view];
Per mostrare il componente di avviso, richiamare showAlertView:
nell'azione che abbiamo appena creato, showAlertView:
.
- (IBAction) showAlertView: (id) sender [self.alertComponent showAlertView];
Esegui la tua applicazione e tocca il pulsante per mostrare la vista dell'avviso. Il risultato dovrebbe essere simile a quello qui sotto.
Come abbiamo visto in precedenza, il handleButtonTap:
il metodo viene invocato quando l'utente tocca un pulsante della vista di avviso. La visualizzazione di avviso deve essere nascosta quando viene toccato uno dei pulsanti. Vediamo come funziona.
Rivisitare AlertComponent.m e, nell'estensione della classe privata, dichiarare il handleButtonTap:
metodo.
@interface AlertComponent () ... - (void) handleButtonTap: (UIButton *) mittente; @fine
In questo metodo, creiamo una serie di comportamenti dinamici e li aggiungiamo all'oggetto animatore dinamico. I comportamenti dinamici di cui abbiamo bisogno sono:
Dopo aver rimosso i comportamenti esistenti dall'animatore dinamico e inizializzato il comportamento push come mostrato di seguito.
- (void) handleButtonTap: (UIButton *) mittente // Rimuovi tutti i comportamenti dall'animatore. [self.animator removeAllBehaviors]; UIPushBehavior * pushBehavior = [[UIPushBehavior alloc] initWithItems: @ [self.alertView] modalità: UIPushBehaviorModeInstantaneous]; [pushBehavior setAngle: M_PI_2 magnitude: 20.0]; [self.animator addBehavior: pushBehavior];
Il angolo
la proprietà del comportamento push definisce la direzione della spinta. Impostando l'angolo su M_PI_2
, la forza del comportamento di spinta è diretta verso la parte inferiore dello schermo.
Il prossimo passo è l'aggiunta del comportamento gravitazionale. Il vettore a cui passiamo setGravityDirection
si tradurrà in una forza verso la parte superiore dello schermo, tirando la vista di avviso verso l'alto.
UIGravityBehavior * gravityBehavior = [[UIGravityBehavior alloc] initWithItems: @ [self.alertView]]; [gravityBehavior setGravityDirection: CGVectorMake (0.0, -1.0)]; [self.animator addBehavior: gravityBehavior];
Ciò che è interessante del comportamento di collisione è che definiamo un confine che è fuori dallo schermo.
UICollisionBehavior * collisionBehavior = [[UICollisionBehavior alloc] initWithItems: @ [self.alertView]]; [collisionBehavior addBoundaryWithIdentifier: @ "alertCollisionBoundary" fromPoint: CGPointMake (self.initialAlertViewFrame.origin.x, self.initialAlertViewFrame.origin.y - 10.0) toPoint: CGPointMake (self.initialAlertViewFrame.origin.x + self.initialAlertViewFrame.size.width, self.initialAlertViewFrame.origin.y - 10.0)]; [self.animator addBehavior: collisionBehavior];
Abbiamo anche bisogno di un comportamento dinamico degli oggetti per impostare l'elasticità della collisione. Il risultato è che la vista di avviso rimbalzerà un po 'quando si scontra con il limite fuori dallo schermo.
UIDynamicItemBehavior * itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems: @ [self.alertView]]; itemBehavior.elasticity = 0,4; [self.animator addBehavior: itemBehavior];
Dobbiamo anche rendere nuovamente trasparente la vista di sfondo. Lo facciamo impostando la vista dello sfondo alfa
proprietà a 0.0
in un blocco di animazione.
[UIView animateWithDuration: 2.0 animazioni: ^ [self.backgroundView setAlpha: 0.0]; ];
Esegui la tua applicazione ancora una volta per vedere il risultato.
Anche se la vista di avviso risponde all'interazione dell'utente, al momento non sappiamo quale pulsante l'utente ha toccato. Questo è ciò su cui ci concentreremo in questa sezione.
Come abbiamo fatto con il componente del menu, faremo uso di blocchi per risolvere questo problema. I blocchi rappresentano una soluzione elegante e spesso possono essere più facili da utilizzare rispetto a un protocollo delegato.
Iniziamo aggiornando il pubblico showAlertView
metodo. Il metodo deve accettare un gestore di completamento che richiama la vista di avviso quando l'utente ha toccato uno dei pulsanti. Nel AlertComponent.h, aggiorna la dichiarazione del showAlertView
metodo da:
- (Vuoto) showAlertView;
a:
- (void) showAlertViewWithSelectionHandler: (void (^) (NSInteger buttonIndex, NSString * buttonTitle)) handler;
Il gestore di completamento accetta due parametri, l'indice, di tipo NSInteger
, e il titolo, di tipo NSString
, del pulsante che è stato toccato dall'utente. Se si desidera richiamare il gestore di completamento quando l'utente tocca un pulsante della vista di avviso, è necessario mantenere un riferimento al gestore di completamento. Ciò significa che dobbiamo dichiarare una proprietà per il gestore di completamento. Lo facciamo nell'estensione della classe privata in AlertComponent.m.
@interface AlertComponent () ... @property (nonatomic, strong) void (^ selectionHandler) (NSInteger, NSString *); ... @end
Ancora dentro AlertComponent.m, aggiornare la descrizione del metodo come abbiamo fatto nel file di intestazione un momento fa e memorizzare il gestore di completamento nel file selectionHandler
proprietà, che abbiamo appena dichiarato.
- (void) showAlertViewWithSelectionHandler: (void (^) (NSInteger, NSString *)) handler self.selectionHandler = handler; ...
L'ultimo pezzo del puzzle sta invocando il gestore di completamento in handleButtonTap:
, passando il tag e il titolo del pulsante.
- (void) handleButtonTap: (UIButton *) mittente // Chiama il gestore di selezione. self.selectionHandler (sender.tag, sender.titleLabel.text); ...
AlertComponent è completo. È tempo di testare tutto. Torna a ViewController.m e aggiorna ShowAlertView: azione come mostrato di seguito. Come puoi vedere, invochiamo il nuovo showAlertViewWithSelectionHandler:
metodo e passare in un blocco, che verrà chiamato quando un pulsante nella vista di avviso viene toccato dall'utente.
- (IBAction) showAlertView: (id) sender [self.alertComponent showAlertViewWithSelectionHandler: ^ (NSInteger buttonIndex, NSString * buttonTitle) NSLog (@ "% ld,% @", (long) buttonIndex, buttonTitle); ];
Questo è tutto. Esegui ancora una volta la tua applicazione e ispeziona la console di Xcode per vedere il risultato del nostro lavoro.
UIKit Dynamics è stato introdotto per la prima volta in iOS 7 e può aiutarti a creare rapidamente animazioni realistiche. Questa breve serie ha dimostrato che sfruttare la dinamica di UIKit nei tuoi progetti non è difficile e non è necessario essere esperti in matematica o fisica.
Si noti che UIKit Dynamics è pensato principalmente per l'utilizzo in applicazioni basate su vista. Se stai cercando una soluzione simile per i giochi, ti consiglio di dare un'occhiata al kit Sprite di Apple, che è mirato allo sviluppo del gioco.