Componenti animati con UIKit Dynamics parte 2

introduzione

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.

1. Panoramica

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:

  • un UILabel oggetto per la visualizzazione del titolo della vista di avviso
  • un UILabel oggetto per la visualizzazione del messaggio della vista di avviso
  • uno o più UIButton istanze per consentire all'utente di interagire con la vista di avviso

Useremo 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.

2. Proprietà e inizializzazione

Passaggio 1: creazione della classe di visualizzazione degli avvisi

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.

Passaggio 2: dichiarazione delle proprietà

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.

Passaggio 3: inizializzazione

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

3. Impostazione della visualizzazione degli avvisi

Passaggio 1: dichiarazione dei metodi di installazione

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.

Passaggio 2: impostazione della visualizzazione degli avvisi

Nel setupAlertView, facciamo tre cose:

  • inizializza e configura la visualizzazione degli avvisi
  • inizializza e configura le etichette della vista di avviso
  • inizializza e configura i pulsanti della vista di avviso

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 alertViewSizeinitialOriginPoint, 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];

Passaggio 3: impostazione della vista di sfondo

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]; 

Passaggio 4: implementazione di Initializer

Con setupAlertViewsetupBackgroundView 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.

4. Visualizzazione della vista di avviso

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.

5. Nascondere la vista di avviso

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:

  • un comportamento gravitazionale che tira la vista di avviso verso la parte superiore dello schermo
  • un comportamento di collisione con un limite fuori dallo schermo che arresta la vista di avviso
  • un comportamento di spinta che dà alla vista di avviso una piccola spinta verso la parte inferiore dello schermo

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.

6. Gestire l'interazione dell'utente

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.

Conclusione

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.