Crea un'animazione di piegatura di pagina 3D Effetto Piegatura pagina e Spina dorso

Questa miniserie in due parti ti insegnerà come creare un impressionante effetto di piegatura delle pagine con Core Animation. In questa puntata, imparerai innanzitutto come creare una vista del riquadro di schizzo e come applicare un'animazione basculante di base sulla vista. Continuare a leggere!


Demo del progetto finale


Panoramica dell'esercitazione

Lavorare con il UIView la classe è fondamentale per lo sviluppo dell'SDK iOS. Le viste hanno sia un aspetto visivo (cioè quello che vedi sullo schermo) che di solito un aspetto di controllo (interazione dell'utente tramite tocchi e gesti). L'aspetto visivo è in realtà gestito da una classe appartenente al Core Animation Framework, chiamato CALayer (che a sua volta è implementato tramite OpenGL, solo che non ci interessa passare al livello di astrazione qui). Gli oggetti di livello sono istanze di CALayer classe o una delle sue sottoclassi. Senza scavare troppo in profondità nella teoria (questo, dopo tutto, dovrebbe essere un tutorial pratico!) Dovremmo tenere presenti i seguenti punti:

  • Ogni vista in iOS è supportata da un livello, che è responsabile del suo contenuto visivo. A livello di codice, si accede come proprietà layer alla vista.
  • Esiste una gerarchia di livelli paralleli che corrisponde alla gerarchia della vista sullo schermo. Ciò significa che se (diciamo) un'etichetta è una sottoview di un pulsante, il livello dell'etichetta è il sottolivello del livello del pulsante. A rigor di termini, questo parallelismo vale solo finché non aggiungiamo i nostri sottotitoli al mix.
  • Diverse proprietà che impostiamo sulle viste (in particolare, le viste relative all'aspetto) sono in realtà proprietà sul livello sottostante. Tuttavia, il livello espone alcune proprietà che non sono disponibili a livello di vista. In questo senso, i livelli sono più potenti delle visualizzazioni.
  • Rispetto alle viste, i livelli sono più oggetti "leggeri". Pertanto, se per alcuni aspetti della nostra app richiediamo la visualizzazione senza interattività, i livelli sono probabilmente l'opzione più performante.
  • UN CALayerIl contenuto è costituito da una bitmap. Mentre la classe base è abbastanza utile così com'è, ha anche diverse sottoclassi importanti in Core Animation. In particolare, c'è CAShapeLayer che ci permette di rappresentare le forme attraverso percorsi vettoriali.

Quindi, cosa possiamo ottenere con livelli che non possiamo facilmente fare con le viste direttamente? 3D, per esempio, su cui ci concentreremo qui. CALayerLe sue capacità mettono a disposizione effetti 3D e animazioni piuttosto sofisticati, senza dover scendere al livello OpenGL. Questo è l'obiettivo alla base di questo tutorial: dimostrare un interessante effetto 3D che dà un piccolo assaggio di ciò che possiamo ottenere con CALayerS.


Il nostro obiettivo

Abbiamo un'app di disegno che consente all'utente di disegnare sullo schermo con il dito (per il quale riutilizzerò il codice di un precedente tutorial del mio). Se l'utente esegue un gesto di pizzicamento sulla tela del disegno, si ripiega lungo una linea verticale che corre lungo il centro della tela (molto simile al dorso di un libro). Il libro piegato getta persino un'ombra sullo sfondo.

La parte del disegno dell'app che sto prendendo in prestito non è molto importante qui, e potremmo benissimo usare qualsiasi immagine per dimostrare l'effetto pieghevole. Tuttavia, l'effetto rende una metafora visiva molto bella nel contesto di un'app di disegno in cui l'azione di pizzicamento espone un libro con diverse foglie (contenenti i nostri disegni precedenti) che possiamo sfogliare. Questa metafora è vista in modo particolare nell'app Carta. Mentre per gli scopi del tutorial la nostra implementazione sarà più semplice e meno sofisticata, ma non è troppo lontana ... e, naturalmente, puoi prendere ciò che impari in questo tutorial e renderlo ancora migliore!


Layer Geometry In breve

Ricorda che nel sistema di coordinate iOS l'origine si trova nell'angolo in alto a sinistra dello schermo, con l'asse x crescente verso destra e l'asse y verso il basso. La cornice di una vista descrive il suo rettangolo nel sistema di coordinate della sua superview. I livelli possono essere interrogati anche per il loro frame, ma c'è un altro (preferito) modo di descrivere la posizione e le dimensioni di un layer. Li motiveremo con un semplice esempio: immagina due strati, A e B, come pezzi di carta rettangolari. Ti piacerebbe rendere il livello B un sottolivello di A, quindi aggiusti B in cima ad A usando un perno, mantenendo i lati diritti in parallelo. Il pin passa attraverso due punti, uno in A e uno in B. Conoscendo la posizione di questi due punti ci dà un modo efficace di descrivere la posizione di B rispetto a A. Etichetteremo il punto che il piercing punta in A " punto di ancoraggio "e il punto in B" posizione ". Dai un'occhiata alla figura seguente, per la quale faremo i conti:

Sembra che la figura abbia parecchie cose in corso, ma non preoccuparti, la esamineremo un po 'alla volta:

  • Il grafico superiore mostra la relazione gerarchica: il livello viola (A, dalla nostra discussione precedente) è un sottolivello del livello blu (B). Il cerchio verde con il segno più è dove A è bloccato in B. posizione (nel sistema di coordinate di A) è dato come 32.5, 62.5.
  • Adesso rivolgi la tua attenzione al grafico inferiore. Il punto di ancoraggio è specificato diversamente. Suo parente alla dimensione del livello B, tale che l'angolo in alto a sinistra in 0.0, 0.0 e l'angolo in basso a destra in 1.0, 1.0. Dato che il nostro pin è un quarto della distanza tra la larghezza di B e metà della via verso il basso, il punto di ancoraggio è 0,25, 0,5.
  • Conoscendo le dimensioni di B (50 x 45) possiamo ora calcolare le coordinate dell'angolo in alto a sinistra. Rispetto all'angolo superiore sinistro di B, il punto di ancoraggio è 0,25 x 50 = 12,5 punti nella direzione x e 0,50 x 45 = 22,5 punti nella direzione y. Sottrai queste dalle coordinate della posizione e otterrai le coordinate dell'origine di B nel sistema di A: 32.5 - 12.5, 62.5 - 22.5 = 20, 40. Chiaramente, il frame di B è 20, 40, 50, 45.

Il calcolo è abbastanza semplice, quindi assicurati di averlo compreso a fondo. Ti darà una buona sensazione della relazione tra posizione, punto di ancoraggio e cornice.

Il punto di ancoraggio è abbastanza significativo perché quando eseguiamo trasformazioni 3D sul livello, queste trasformazioni vengono eseguite rispetto al punto di ancoraggio. Ne parleremo dopo (e poi vedrai del codice, te lo prometto!).


Trasformazioni di livello

Sono sicuro che tu abbia familiarità con il concetto di trasformazioni come ridimensionamento, traduzione e rotazione. Nell'app Foto in iOS 6, se pizzichi con due dita per ingrandire o ridurre una foto del tuo album, stai eseguendo una trasformazione di scala. Se fai un movimento con le tue due dita, la foto ruota e se la trascini tenendo i lati paralleli, questa è la traduzione. Animazione core e CALayer briscola UIView consentendo di eseguire trasformazioni in 3D anziché solo 2D. Naturalmente, nel 2013 i nostri schermi iDevice sono ancora in 2D, quindi le trasformazioni 3D impiegano alcuni trucchi geometrici per ingannare i nostri occhi nell'interpretazione di un'immagine piatta come un oggetto 3D (il processo non è diverso dalla raffigurazione di un oggetto 3D in un disegno a tratteggio realizzato con un matita, davvero). Per affrontare la 3a dimensione, dobbiamo usare un asse z che immaginiamo di perforare il nostro schermo del dispositivo e perpendicolare ad esso.

Il punto di ancoraggio è importante perché il risultato preciso della stessa trasformazione applicata di solito differisce a seconda di esso. Ciò è particolarmente importante - e più facilmente comprensibile - con una trasformazione di rotazione attraverso lo stesso angolo applicato rispetto a due diversi punti di ancoraggio nel rettangolo nella figura sottostante (il punto rosso e il punto blu). Nota che la rotazione è nel piano dell'immagine (o attorno all'asse z, se preferisci pensarla in questo modo).


L'animazione Piegatura pagina

Quindi, come possiamo implementare l'effetto di piegatura che stiamo cercando? Mentre i livelli sono davvero fantastici, non puoi piegarli a metà! La soluzione è - come sono sicuro tu abbia capito - di utilizzare due livelli, uno per ogni pagina su entrambi i lati della piega. Sulla base di ciò che abbiamo discusso in precedenza, analizziamo in anticipo le proprietà geometriche di questi due strati:

  • Abbiamo scelto un punto lungo la "spina dorsale" per essere il nostro punto di ancoraggio per entrambi i nostri strati, perché è lì che avviene la nostra piega (cioè la trasformazione di rotazione). La rotazione avviene attorno a una linea verticale (cioè l'asse y) - assicurati di visualizzarla. Va bene, si potrebbe dire, ma perché ho scelto il punto medio della colonna vertebrale (invece di dire, un punto in basso o in alto)? In realtà, in questo caso particolare, non fa differenza per quanto riguarda la rotazione. Ma vogliamo anche trasformare una scala (rendendo gli strati leggermente più piccoli mentre si piegano) e mantenendo il punto di ancoraggio al centro significa che il libro rimarrà bello e centrato quando si piega. Questo perché per il ridimensionamento, il punto che coincide con il punto di ancoraggio rimane fisso in posizione.
  • Il punto di ancoraggio per il primo strato è 1.0, 0.5 e per il secondo strato lo è 0.0, 0.5, nei loro rispettivi spazi di coordinate. Assicurati di confermarlo dalla figura prima di procedere!
  • Il punto che si trova sotto il punto di ancoraggio nel superlayer (cioè la "posizione") è il punto medio, quindi le coordinate sono larghezza / 2, altezza / 2. Ricorda che la proprietà position è in coordinate standard, non normalizzata.
  • La dimensione di ciascuno dei livelli è larghezza / 2, altezza.

Implementazione

Ora sappiamo abbastanza per scrivere del codice!

Crea un nuovo progetto Xcode con il modello "Empty Application" e chiamalo LayerFunTut. Falla diventare un'app per iPad e abilita il conteggio dei riferimenti automatico (ARC), ma disabilita le opzioni per i dati di base e i test delle unità. Salvarla.

Nella pagina Destinazione> Riepilogo visualizzata, scorri verso il basso fino a "Orientamenti dell'interfaccia supportati" e scegli i due orientamenti orizzontali.

Scorri verso il basso fino a "Linked Frameworks and Libraries", fai clic su "+" e aggiungi il framework QuartzCore, che è richiesto per Core Animation e CALayers.

Inizieremo incorporando la nostra app di disegno nel progetto. Crea una nuova classe Objective-C chiamata CanvasView, rendendolo una sottoclasse di UIView. Incolla il seguente codice in CanvasView.h:

 // // CanvasView.h // #import  @interface CanvasView: UIView @property (nonatomic, strong) UIImage * incrementalImage; @fine

E poi dentro CanvasView.m:

 // // CanvasView.m // #import "CanvasView.h" @implementation CanvasView Percorso UIBezierPath *; Punti CGPoint [5]; uint ctr;  - (id) initWithCoder: (NSCoder *) aDecoder if (self = [super initWithCoder: aDecoder]) self.backgroundColor = [UIColor clearColor]; [self setMultipleTouchEnabled: NO]; percorso = [UIBezierPath bezierPath]; [percorso setLineWidth: 6.0];  return self;  - (id) initWithFrame: (CGRect) frame self = [super initWithFrame: frame]; if (self) self.backgroundColor = [UIColor clearColor]; [self setMultipleTouchEnabled: NO]; percorso = [UIBezierPath bezierPath]; [percorso setLineWidth: 6.0];  return self;  - (void) drawRect: (CGRect) rect [self.incrementalImage drawInRect: rect]; [[UIColor blueColor] setStroke]; [tratto del percorso];  - (void) touchesBegan: (NSSet *) tocca conEvent: (UIEvent *) event ctr = 0; UITouch * touch = [tocca anyObject]; pts [0] = [touch locationInView: self];  - (void) touchesMoved: (NSSet *) tocca conEvent: (UIEvent *) event UITouch * touch = [tocca anyObject]; CGPoint p = [touch locationInView: self]; ctr ++; pts [ctr] = p; if (ctr == 4) pts [3] = CGPointMake ((pts [2] .x + pts [4] .x) /2.0, (pts [2] .y + pts [4] .y) /2.0 ); [percorso moveToPoint: pts [0]]; [percorso addCurveToPoint: pts [3] controlPoint1: pts [1] controlPoint2: pts [2]]; [self setNeedsDisplay]; pts [0] = pts [3]; pts [1] = pts [4]; ctr = 1;  - (void) touchesEnded: (NSSet *) tocca conEvent: (UIEvent *) event [self drawBitmap]; [self setNeedsDisplay]; [percorso removeAllPoints]; ctr = 0;  - (void) touchesCancelled: (NSSet *) tocca conEvent: (UIEvent *) event [self touchesEnded: touch withEvent: event];  - (void) drawBitmap UIGraphicsBeginImageContextWithOptions (self.bounds.size, NO, 0.0); if (! self.incrementalImage) UIBezierPath * rectpath = [UIBezierPath bezierPathWithRect: self.bounds]; [[UIColor clearColor] setFill]; [riempimento rettale];  [self.incrementalImage drawAtPoint: CGPointZero]; [[UIColor blueColor] setStroke]; [tratto del percorso]; self.incrementalImage = UIGraphicsGetImageFromCurrentImageContext (); UIGraphicsEndImageContext ();  @fine

Come accennato in precedenza, questo è solo il codice di un altro tutorial che ho scritto (con alcune modifiche minori). Assicurati di controllare se non sei sicuro di come funziona il codice. Per gli scopi di questo tutorial, tuttavia, ciò che è importante è quello CanvasView consente all'utente di disegnare tratti morbidi sullo schermo. Abbiamo dichiarato una proprietà chiamata incrementalImage che memorizza una versione bitmap del disegno dell'utente. Questa è l'immagine con cui "ci piegheremo" CALayerS.

È tempo di scrivere il codice del controller di visualizzazione e implementare le idee che abbiamo elaborato in precedenza. Una cosa di cui non abbiamo discusso è come otteniamo l'immagine disegnata nella nostra CALayer in modo tale che metà dell'immagine venga disegnata nella pagina sinistra e l'altra metà nella pagina destra. Fortunatamente, sono solo alcune righe di codice, di cui parlerò più tardi.

Crea una nuova classe Objective-C chiamata ViewController, rendilo una sottoclasse di UIViewController, e non selezionare nessuna delle opzioni visualizzate.

Incolla il seguente codice in ViewController.m

 // // ViewController.m // #import "ViewController.h" #import "CanvasView.h" #import "QuartzCore / QuartzCore.h" #define D2R (x) (x * (M_PI / 180.0)) // macro per convertire i gradi in radianti @interface ViewController () @end @implementation ViewController CALayer * leftPage; CALayer * rightPage; UIView * curtainView;  - (void) loadView self.view = [[CanvasView alloc] initWithFrame: [[UIScreen mainScreen] applicationFrame]];  - (void) viewDidLoad [super viewDidLoad]; self.view.backgroundColor = [UIColor blackColor];  - (void) viewDidAppear: (BOOL) animato [super viewDidAppear: animato]; self.view.backgroundColor = [UIColor whiteColor]; CGSize size = self.view.bounds.size; leftPage = [CALayer layer]; rightPage = [CALayer layer]; leftPage.anchorPoint = (CGPoint) 1.0, 0.5; rightPage.anchorPoint = (CGPoint) 0.0, 0.5; leftPage.position = (CGPoint) size.width / 2.0, size.height / 2.0; rightPage.position = (CGPoint) size.width / 2.0, size.height / 2.0; leftPage.bounds = (CGRect) 0, 0, size.width / 2.0, size.height; rightPage.bounds = (CGRect) 0, 0, size.width / 2.0, size.height; leftPage.backgroundColor = [UIColor whiteColor] .CGColor; rightPage.backgroundColor = [UIColor whiteColor] .CGColor; leftPage.borderWidth = 2.0; // bordi aggiunti per ora, in modo che possiamo distinguere visivamente tra le pagine sinistra e destra rightPage.borderWidth = 2.0; leftPage.borderColor = [UIColor darkGrayColor] .CGColor; rightPage.borderColor = [UIColor darkGrayColor] .CGColor; //leftPage.transform = makePerspectiveTransform (); // uncomment tardi //rightPage.transform = makePerspectiveTransform (); // uncomment later curtainView = [[UIView alloc] initWithFrame: self.view.bounds]; curtainView.backgroundColor = [UIColor scrollViewTexturedBackgroundColor]; [curtainView.layer addSublayer: leftPage]; [curtainView.layer addSublayer: rightPage]; UITapGestureRecognizer * foldTap = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector (fold :)]; [self.view addGestureRecognizer: foldTap]; UITapGestureRecognizer * unfoldTap = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector (unfold :)]; unfoldTap.numberOfTouchesRequired = 2; [self.view addGestureRecognizer: unfoldTap];  - (void) fold: (UITapGestureRecognizer *) gr // disegna la bitmap "incrementalImage" nei nostri strati CGImageRef imgRef = ((CanvasView *) self.view) .incrementalImage.CGImage; leftPage.contents = (__bridge id) imgRef; rightPage.contents = (__bridge id) imgRef; leftPage.contentsRect = CGRectMake (0.0, 0.0, 0.5, 1.0); // questo rettangolo rappresenta la metà sinistra dell'immagine rightPage.contentsRect = CGRectMake (0.5, 0.0, 0.5, 1.0); // questo rettangolo rappresenta la metà destra dell'immagine leftPage.transform = CATransform3DScale (leftPage.transform, 0.95, 0.95, 0.95); rightPage.transform = CATransform3DScale (rightPage.transform, 0.95, 0.95, 0.95); leftPage.transform = CATransform3DRotate (leftPage.transform, D2R (7.5), 0.0, 1.0, 0.0); rightPage.transform = CATransform3DRotate (rightPage.transform, D2R (-7.5), 0.0, 1.0, 0.0); [self.view addSubview: curtainView];  - (void) unfold: (UITapGestureRecognizer *) gr leftPage.transform = CATransform3DIdentity; rightPage.transform = CATransform3DIdentity; // leftPage.transform = makePerspectiveTransform (); // decomprimere più tardi // rightPage.transform = makePerspectiveTransform (); // decommenta più tardi [curtainView removeFromSuperview];  // UNCOMMENT LATER: / * CATransform3D makePerspectiveTransform () CATransform3D transform = CATransform3DIdentity; transform.m34 = 1.0 / -2000; ritorno alla trasformazione;  */ @fine

Ignorando per ora il codice commentato, puoi vedere che il livello impostato è esattamente come pianificato in precedenza.

Discutiamo brevemente questo codice:

  • Decidiamo di sovrascrivere il -viewDidAppear: metodo invece di -viewDidLoad (a cui potresti essere più abituato) perché quando il secondo metodo è chiamato i limiti della vista sono ancora in modalità verticale, ma la nostra app funziona in modalità orizzontale. Per il momento in cui viewDidAppear: viene chiamato, i limiti sono stati impostati correttamente e quindi abbiamo inserito il nostro codice (abbiamo aggiunto temporaneamente bordi spessi in modo da poter distinguere i livelli sinistro e destro mentre applichiamo le trasformazioni su di essi).
  • Abbiamo aggiunto un riconoscitore di gesti che registra un tocco e, a ogni tocco, le pagine diventano leggermente più piccole (il 95% delle dimensioni precedenti) e le fanno ruotare di 7,5 gradi. I segni sono diversi perché una delle pagine gira in senso orario mentre l'altra ruota in senso antiorario. Dovremmo andare in matematica per vedere quale segno corrisponde a quale direzione, ma dal momento che ci sono solo due opzioni, è più semplice scrivere il codice e controllare! A proposito, le funzioni di trasformazione accettano gli angoli in radianti, quindi usiamo la macro D2R () convertire da radianti in gradi. Un'osservazione importante è che le funzioni che prendono una trasformazione nel loro argomento (come CATransform3DScale e CATransform3DRotate) "concatena" una trasformazione con un'altra (il valore corrente della proprietà di trasformazione del livello). Altre funzioni, come CATransform3DMakeRotation, CATransform3DMakeScale, CATransform3DIdentity basta costruire la matrice di trasformazione appropriata. CATransform3DIdentity è la "trasformazione dell'identità" che ha un livello quando ne crei uno. È analogo al numero "1" in una moltiplicazione in cui applicare una trasformazione dell'identità a un livello lascia invariata la sua trasformazione, proprio come moltiplicare un numero di uno.
  • Per quanto riguarda il disegno, impostiamo la proprietà dei contenuti dei nostri livelli come immagine. È abbastanza importante notare che impostiamo il rettangolo del contenuto (normalizzato tra 0 e 1 lungo ogni dimensione) in modo che ogni pagina visualizzi solo metà dell'immagine corrispondente ad essa. Questo sistema di coordinate normalizzato è uguale a quello che abbiamo discusso in precedenza quando si parla del punto di ancoraggio, quindi dovresti essere in grado di calcolare i valori che abbiamo usato per ciascuna metà dell'immagine.
  • Il curtainView l'oggetto agisce semplicemente come un contenitore per i livelli di pagina (più precisamente, vengono creati sottolivelli per il livello sottostante di curtainView). Ricorda che abbiamo già calcolato il posizionamento e la geometria dei livelli, e questi sono rispetto al livello di curtainView. Toccando una volta si aggiunge questa vista sulla parte superiore della vista dell'area di lavoro e si applica la trasformazione sul livello. Il doppio tocco lo rimuove, per rivelare ancora una volta la tela e ripristina la trasformazione dei livelli nella trasformazione dell'identità.
  • Notare l'uso di CGImage qui - e anche CGColor prima - invece di UIImage e UIColor. Questo è perché CALayer opera su un livello inferiore a UIKit e funziona con tipi di dati "opachi" (in parole povere, non chiedere informazioni sulla loro implementazione sottostante!) che sono definiti nel framework Core Graphics. Classi Objective-C come UIColor e UIImage possono essere pensati come involucri orientati agli oggetti attorno alle loro versioni CG più primitive. Per motivi di praticità, molti oggetti UIKit espongono il loro tipo CG sottostante come una proprietà.

Nel AppDelegate.m file, sostituire tutto il codice con il seguente (l'unica cosa che abbiamo aggiunto è includere il file di intestazione ViewController e creare un ViewController istanza il controller della vista radice):

 // // AppDelegate.m // #import "AppDelegate.h" #import "ViewController.h" @implementation AppDelegate - (BOOL) application: (applicazione UIApplication *) didFinishLaunchingWithOptions: (NSDictionary *) launchOptions self.window = [ [UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bound]]; self.window.rootViewController = [[ViewController alloc] init]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES;  @fine

Costruisci il progetto ed eseguilo sul simulatore o sul tuo dispositivo. Scribble sulla tela per un po ', quindi tocca lo schermo con un dito per attivare l'azione di riconoscimento dei gesti (toccando con un dito l'effetto 3D termina e riappare la tela del disegno).

Non abbastanza l'effetto che stiamo facendo! Cosa sta succedendo?


Ottenere la nostra prospettiva giusta

Prima di tutto, nota che le pagine diventano più piccole con ogni singolo tocco, quindi il problema non dipende dalla trasformazione di ridimensionamento, ma solo dalla rotazione. Il problema è che anche se la rotazione avviene in uno spazio (matematico) 3D, il risultato viene proiettato sui nostri schermi piatti praticamente allo stesso modo in cui un oggetto 3D proietta la sua ombra su un muro. Per trasmettere profondità, abbiamo bisogno di usare una sorta di spunto. L'indizio più importante è quello della prospettiva: un oggetto più vicino ai nostri occhi appare più grande di uno più lontano. Le ombre sono un'altra grande stecca, e ci arriveremo presto. Quindi, come incorporiamo la prospettiva nella nostra trasformazione?

Parliamo un po 'delle trasformazioni prima. Cosa sono, davvero? Matematicamente parlando, dovresti sapere che se rappresentiamo i punti nella nostra forma come vettori matematici, le trasformazioni geometriche come la scala, la rotazione e la traduzione sono rappresentate come trasformazioni di matrice. Ciò significa che se prendiamo una matrice che rappresenta una trasformazione e moltiplichiamo con un vettore che rappresenta un punto nella nostra forma, allora il risultato della moltiplicazione (anche un vettore) rappresenta dove quel punto finisce dopo essere stato trasformato. Non possiamo dire molto di più qui senza immergerci nella teoria (che vale davvero la pena di conoscere, se non lo conosci già - soprattutto se intendi incorporare fantastici effetti 3D nelle tue app!).

E il codice? In precedenza, abbiamo impostato la geometria del livello impostandone la sua punto di ancoraggio, posizione, e misura. Quello che vediamo sullo schermo è la geometria del livello dopo che è stata trasformata dal suo trasformare proprietà. Nota le chiamate alla funzione che assomigliano layer.transform = // ... . È lì che stiamo impostando la trasformazione, che internamente è solo una struct che rappresenta una matrice 4 x 4 di valori in virgola mobile. Si noti inoltre che le funzioni CATransform3DScale e CATransform3DRotate prendere la trasformata corrente del livello come parametro. Questo perché possiamo comporre diverse trasformazioni insieme (il che significa solo che moltiplichiamo le loro matrici insieme), con il risultato finale di essere come se aveste eseguito queste trasformazioni una alla volta. Nota che stiamo parlando solo del risultato finale della trasformazione, non di come Core Animation anima il livello!

Tornando al problema della prospettiva, quello che dobbiamo sapere è che c'è un valore nella nostra matrice di trasformazione che possiamo modificare per ottenere l'effetto prospettico che stiamo cercando. Quel valore è un membro della struttura di trasformazione, chiamato m34 (i numeri indicano la sua posizione nella matrice). Per ottenere l'effetto che vogliamo, dobbiamo impostarlo su un numero piccolo e negativo.

Decommentare le due sezioni commentate in ViewController.m file (il CATransform3D makePerspectiveTransform () funzione e le linee leftPage.transform = makePerspectiveTransform (); rightPage.transform = makePerspectiveTransform (); e costruisci di nuovo. Questa volta l'effetto 3D sembra più credibile.

Si noti inoltre che quando si modifica la proprietà di trasformazione di a CALayer, l'accordo prevede un'animazione "gratuita". Questo è ciò che vogliamo qui - al contrario dello strato che subisce la sua trasformazione bruscamente - ma a volte non lo è.

Ovviamente, la prospettiva va solo oltre, quando il nostro esempio diventa più sofisticato, useremo anche le ombre! Potremmo anche voler girare gli angoli del nostro "libro" e mascherare i nostri livelli di pagina con a CAShapeLayer può aiutare con quello. Inoltre, vorremmo usare un gesto di pizzicamento per controllare la piegatura / apertura in modo che si senta più interattivo. Tutto questo sarà trattato nella seconda parte di questa mini serie di tutorial.

Vi incoraggio a sperimentare con il codice, facendo riferimento alla documentazione dell'API, e cercare di implementare il nostro effetto desiderato in modo indipendente (potreste persino finire per farlo meglio!).

Divertiti con il tutorial e grazie per la lettura!

Leggi la parte 2