Creați o animație pliantă în 3D a paginii Schița paginii și efectul de pliere a coloanei vertebrale

Această mini-serie din două părți vă va învăța cum să creați un efect impresionant de pliere a paginii cu Core Animation. În această tranșă, veți învăța mai întâi cum să creați o vizualizare a schemei de schițe și apoi cum să aplicați o animație de pliere a coloanei vertebrale în acea vizualizare. Citește mai departe!


Demonstrația finală a proiectului


Prezentare generală a tutorialului

Lucrul cu UIView clasa este esențială pentru dezvoltarea SDK-ului iOS. Vizionările au atât un aspect vizual (adică ceea ce vedeți pe ecran), cât și un aspect de control (interacțiunea utilizatorului prin atingeri și gesturi). Aspectul vizual este de fapt tratat de o clasă care aparține Core Animation Framework, numită CALayer (care, la rândul său, este pusă în aplicare prin OpenGL, doar nu ne pasă să mergem la acel nivel de abstractizare aici). Obiectele Layer sunt instanțe ale CALayer clasa sau una dintre subclasele sale. Fără a trece prea adânc în teorie (acesta este, la urma urmei, ar trebui să fie un tutorial practic!), Trebuie să ținem cont de următoarele aspecte:

  • Fiecare vizualizare din iOS este susținută de un strat, care este responsabil pentru conținutul său vizual. Programat, aceasta este accesată ca proprietate a stratului din vizualizare.
  • Există o ierarhie a stratului paralel care corespunde ierarhiei de vizualizare pe ecran. Aceasta înseamnă că, dacă (să zicem) o etichetă este o subiectivă a unui buton, atunci stratul etichetei este substratul stratului butonului. Strict vorbind, acest paralelism este valabil numai atâta timp cât nu adăugăm propriile substraturi la amestec.
  • Mai multe proprietăți pe care le-am setat pe vizualizări (în special, vizionările legate de aspect) sunt în realitate proprietăți pe stratul de bază. Cu toate acestea, stratul expune câteva proprietăți care nu sunt disponibile la nivelul vizualizării. În acest sens, straturile sunt mai puternice decât vederile.
  • În comparație cu vederile, straturile sunt obiecte mai ușoare. Prin urmare, dacă pentru un aspect al aplicației noastre avem nevoie de vizualizare fără interactivitate, atunci straturile sunt probabil opțiunea mai performantă.
  • A CALayerconținutul este format dintr-un bitmap. În timp ce clasa de bază este destul de utilă așa cum este, ea are, de asemenea, mai multe subclase importante în Core Animation. În special, există CAShapeLayer care ne permite să reprezentăm forme prin intermediul căilor vectoriale.

Deci, ce putem realiza cu straturi pe care nu le putem face cu ușurință cu vederile direct? 3D, pentru unul, la care ne vom concentra aici. CALayercapabilitățile 3D dau efecte 3D și animații destul de sofisticate la îndemână, fără a fi nevoiți să coborâți la nivelul OpenGL. Acesta este obiectivul din spatele acestui tutorial: demonstrarea unui efect 3D interesant care oferă un gust mic de ceea ce putem realiza CALayers.


Obiectivul nostru

Avem o aplicație de desen care permite unui utilizator să deseneze pe ecran cu degetul (pentru care voi reutiliza codul dintr-un tutorial anterior al meu). Dacă utilizatorul execută apoi un gest de pe panza de desen, acesta se îndoaie de-a lungul unei linii verticale care rulează de-a lungul centrului pânzei (la fel ca și coloana vertebrală a unei cărți). Cartea pliată ridică chiar și o umbră în fundal.

Partea de desen al aplicației pe care o împrumut nu este cu adevărat importantă aici și am putea folosi foarte bine orice imagine pentru a demonstra efectul de pliere. Cu toate acestea, efectul face o metaforă vizuală foarte frumoasă în contextul unei aplicații de desenare în care acțiunea de ciupire expune o carte cu mai multe frunze (care conțin desenele noastre anterioare) pe care le putem naviga. Această metaforă este văzută în special în aplicația Hârtie. În timp ce pentru scopurile tutorialului implementarea noastră va fi mai simplă și mai puțin sofisticată, dar nu este prea departe ... și, bineînțeles, puteți să luați ceea ce învățați în acest tutorial și să îl faceți mai bine!


Geometria stratului pe scurt

Amintiți-vă că în sistemul de coordonate iOS originea se află în colțul din stânga sus al ecranului, axa x crescând spre dreapta și axa y în jos. Cadrul unei imagini descrie dreptunghiul său în sistemul de coordonate al superviziei. Straturile pot fi interogate și pentru cadrul lor, dar există un alt mod (preferat) de a descrie locația și dimensiunea unui strat. Vom motiva acestea cu un exemplu simplu: imaginați două straturi, A și B, ca bucăți rectangulare de hârtie. Ați dori să transformați stratul B într-un substrat A, astfel încât să fixați B deasupra A folosind un bolț, menținând laturile drepte în paralel. Pinul trece prin două puncte, unul în A și unul în B. Știind locația acestor două puncte ne oferă o modalitate eficientă de a descrie poziția lui B în raport cu A. Vom eticheta punctul în care pinul pierde în A " punctul de ancorare "și punctul din poziția B". Uitați-vă la următoarea figură, pentru care vom face matematica:

Cifra pare să se întâmple multe, dar să nu ne îngrijorăm, o vom examina puțin câte puțin:

  • Graficul superior afișează relația ierarhică: stratul purpuriu (A, din discuția noastră anterioară) este făcut substrat al stratului albastru (B). Cercul verde cu plus este locul unde A este fixat în B. poziţie (în sistemul de coordonate A) este dat ca 32.5, 62.5.
  • Acum, îndreptați atenția spre graficul inferior. punct de ancorare este specificat diferit. este rudă la dimensiunea stratului B, astfel încât colțul din stânga sus din 0.0, 0.0 și colțul din dreapta-jos în 1.0, 1.0. Deoarece pinul nostru este de un sfert distanța pe lățimea lui B și jumătate în jos, punctul de ancorare este 0,25, 0,5.
  • Cunoscând dimensiunea lui B (50 x 45), putem acum calcula coordonatele colțului din stânga sus. În raport cu colțul din stânga sus, punctul de ancorare este de 0,25 x 50 = 12,5 puncte în direcția x și 0,50 x 45 = 22,5 puncte în direcția y. Scoateți-le din coordonatele poziției și obțineți coordonatele originii lui B în sistemul lui A: 32.5 - 12.5, 62.5 - 22.5 = 20, 40. În mod clar, cadrul lui B este 20, 40, 50, 45.

Calculul este destul de simplu, așa că asigurați-vă că îl înțelegeți bine. Acesta vă va da o senzație bună despre relația dintre poziție, punct de ancorare și cadru.

Punctul de ancorare este destul de semnificativ, deoarece atunci când efectuăm transformări 3D pe strat, aceste transformări sunt efectuate în raport cu punctul de ancorare. Vom vorbi despre asta în continuare (și apoi veți vedea un cod, vă promit!).


Transformări de strat

Sunt sigur că sunteți familiarizat cu conceptul de transformări, cum ar fi scalarea, traducerea și rotația. În aplicația Fotografii din iOS 6, dacă prindeți două degete pentru a mări sau a micșora o fotografie din album, efectuați o transformare a scalei. Dacă faceți o mișcare de mișcare cu cele două degete, fotografia se rotește și, dacă o trageți în timp ce țineți părțile paralele, aceasta este traducerea. Core animație și CALayer atu UIView permițându-vă să efectuați transformări în 3D în loc de doar 2D. Desigur, în 2013, ecranele noastre iDevice sunt încă 2D, astfel încât transformările 3D folosesc o șmecherie geometrică pentru a ne păcăli ochii pentru a interpreta o imagine plat ca obiect 3D (procesul nu se deosebește decât imaginea unui obiect 3D într-un desen liniar realizat cu creion, într-adevăr). Pentru a face față celei de-a treia dimensiuni, trebuie să folosim o axă z pe care ne imaginăm să ne străpungem ecranul dispozitivului și să îl perpendicularizăm.

Punctul de ancorare este important deoarece rezultatul precis al aceleiași transformări aplicate va diferi de obicei în funcție de acesta. Acest lucru este deosebit de important - și cel mai ușor de înțeles - cu o rotație transformată prin același unghi aplicat cu privire la două puncte de ancorare diferite în dreptunghiul din figura de mai jos (punctul roșu și punctul albastru). Rețineți că rotația se află în planul imaginii (sau în jurul axei z, dacă preferați să vă gândiți astfel).


Pagina de animație Fold

Deci, cum implementăm efectul de pliere pe care îl urmăm? În timp ce straturile sunt foarte cool, nu le puteți împături în mijloc! Soluția este - așa cum sunt sigur că ați dat seama - să folosiți două straturi, câte una pentru fiecare pagină de pe ambele părți ale foliei. Pe baza celor discutate anterior, să analizăm în prealabil proprietățile geometrice ale acestor două straturi:

  • Am ales un punct de-a lungul "coloanei vertebrale" pentru a fi punctul nostru de ancorare pentru ambele straturi, pentru că acolo se întâmplă pliul nostru (adică transformarea rotației). Rotirea are loc în jurul unei linii verticale (adică axa y) - asigurați-vă că vizualizați acest lucru. Este bine, ai putea spune, dar de ce am ales punctul central al coloanei vertebrale (în loc să spun, un punct în partea de jos sau de sus)? De fapt, în acest caz special, nu se face o diferență în ceea ce privește rotația. Dar, de asemenea, dorim să facem o transformare a scării (făcând straturile puțin mai mici pe măsură ce se îndoaie) și menținând punctul de ancorare la mijloc înseamnă că cartea va rămâne frumoasă și centrată atunci când se pliază. Asta pentru că pentru scalare, punctul care coincide cu punctul de ancorare rămâne fixat în poziție.
  • Punctul de ancorare pentru primul strat este 1,0, 0,5 iar pentru al doilea strat este 0,0, 0,5, în spațiile lor de coordonate. Asigurați-vă că confirmați acest lucru din figura înainte de a continua!
  • Punctul situat sub punctul de ancorare al superlayerului (adică "poziția") este punctul central, deci coordonatele acestuia sunt lățime / 2, înălțime / 2. Amintiți-vă că proprietatea poziției este în coordonate standard, nu este normalizată.
  • Dimensiunea fiecărui strat este lățime / 2, înălțime.

Punerea în aplicare

Acum știm destule pentru a scrie un cod!

Creați un nou proiect Xcode cu șablonul "Empty Application" și apelați-l LayerFunTut. Faceți-o o aplicație iPad și activați numărarea automată a numerelor de referință (ARC), dar dezactivați opțiunile pentru testele principale de date și unități. Salvați-l.

În pagina Target> Sumar care se afișează, derulați în jos până la "Orientările interfeței suportate" și alegeți cele două orientări peisaj.

Parcurgeți mai jos până când ajungeți la "Cadre și Biblioteci conectate", faceți clic pe "+" și adăugați cadrul central QuartzCore, care este necesar pentru Core Animation și CALayers.

Vom începe prin includerea aplicației noastre de desen în proiect. Creați o nouă clasă Obiectiv-C numită CanvasView, făcându-i o subclasă de UIView. Lipiți următorul cod CanvasView.h:

 // // CanvasView.h // #import  @interface CanvasView: UIView @property (nonatomic, puternic) UIImage * incrementalImage; @Sfârșit

Și apoi înăuntru CanvasView.m:

 // // CanvasView.m // #import "CanvasView.h" @implementation CanvasView cale UIBezierPath *; Punctele CGPoint [5]; uint ctr;  - (id) initWithCoder: (NSCoder *) aDecoder dacă (self = [super initWithCoder: aDecoder]) auto.backgroundColor = [UICcolor clearColor]; [self setMultipleTouchEnabled: NU]; cale = [UIBezierPath bezierPath]; [cale setLineWidth: 6.0];  întoarce-te;  - (id) initWithFrame: Cadrul (CGRect) auto = [super initWithFrame: cadru]; dacă (auto) auto.backgroundColor = [UICcolor clearColor]; [self setMultipleTouchEnabled: NU]; cale = [UIBezierPath bezierPath]; [cale setLineWidth: 6.0];  întoarce-te;  - (void) DraRect: (CGRect) rect [auto.incrementalImage drawInRect: rect]; [[UICcolor blueColor] setStroke]; [accident vascular cerebral];  - (void) atingeBegan: (NSSet *) atinge cuEvent: (UIEvent *) eveniment ctr = 0; UITouch * touch = [atinge oriceObject]; pts [0] = [atingeți locațiaInView: auto];  - (void) atingeModed: (NSSet *) atinge cu EventEvent: (UIEvent *) eveniment UITouch * touch = [atinge anyObject]; CGPoint p = [atingeți locațiaInView: auto]; ctr ++; pct [ctr] = p; dacă (ctr == 4) pts [3] = CGPointMake ((pct. [2] .x + pts [4] .x) /2.0. ); [calea moveToPoint: pts [0]]; [cale addCurveToPoint: pct. [3] controlPoint1: pts [1] controlPoint2: pts [2]]; [self setNeedsDisplay]; pts [0] = puncte [3]; pts [1] = puncte [4]; ctr = 1;  - (void) atingeEnded: (NSSet *) atinge cuEvent: (UIEvent *) eveniment [self drawBitmap]; [self setNeedsDisplay]; [path removeAllPoints]; ctr = 0;  - (void) touchesCancelled: (NSSet *) atinge cu EventEvent: (UIEvent *) eveniment [touch touchesEnded: touches withEvent: event];  - (void) drawBitmap UIGraphicsBeginImageContextWithOptions (auto.bounds.size, NO, 0.0); dacă (! self.incrementalImage) UIBezierPath * rectpath = [UIBezierPath bezierPathWithRect: self.bounds]; [[UICcolor clearColor] setFill]; [umplutură de rectatură];  [auto.incrementalImage drawAtPoint: CGPointZero]; [[UICcolor blueColor] setStroke]; [accident vascular cerebral]; self.incrementalImage = UIGraphicsGetImageFromCurrentImageContext (); UIGraphicsEndImageContext ();  @Sfârșit

După cum am menționat anterior, acesta este doar codul de la un alt tutorial pe care l-am scris (cu unele modificări minore). Asigurați-vă că verificați dacă nu sunteți sigur cum funcționează codul. În scopul acestui tutorial, totuși, este important acest lucru CanvasView permite utilizatorului să deseneze curse netede pe ecran. Am declarat o proprietate numită incrementalImage care stochează o versiune bitmap a desenului utilizatorului. Aceasta este imaginea pe care o vom "împăca" cu CALayers.

Este timpul să scriem codul de control al vizualizării și să implementăm ideile pe care le-am elaborat anterior. Un lucru pe care nu l-am discutat este cum obținem imaginea desenată în noi CALayer astfel încât jumătate din imagine să fie atrasă în partea stângă, iar cealaltă jumătate în pagina dreaptă. Din fericire, sunt doar câteva linii de cod, despre care voi vorbi mai târziu.

Creați o nouă clasă Obiectiv-C numită ViewController, faceți o subclasă de UIViewController, și nu verificați niciuna dintre opțiunile care apar.

Lipiți următorul cod ViewController.m

 // // ViewController.m // #import "ViewController.h" #import "CanvasView.h" #import "QuartzCore / QuartzCore.h" #define D2R (x) (x * (M_PI / 180.0) pentru a converti gradele la radiani @ interfață ViewController () @end @implementation ViewController CALayer * leftPage; CALayer * rightPage; UIView * curtainView;  - (void) loadView auto.view = [[CanvasView alin] initWithFrame: [[UIScreen mainScreen] applicationFrame]];  - (void) viewDidLoad [vizualizare superDidLoad]; auto.view.backgroundColor = [UICcolor blackColor];  - (void) vizualizareDidAppear: (BOOL) animat [super viewDidAppear: animat]; self.view.backgroundColor = [UICcolor whiteColor]; Dimensiunea CGSize = auto.view.bounds.size; leftPage = [strat CALayer]; dreaptaPage = [strat CALayer]; 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 = [UICcolor alb Culoare] .CGColor; rightPage.backgroundColor = [Culoare UICcolor alb] .CGColor; leftPage.borderWidth = 2.0; // marginile adăugate pentru moment, astfel încât să putem distinge vizual între paginile stânga și dreapta rightPage.borderWidth = 2.0; leftPage.borderColor = [UICcolor închisGrayColor] .CGColor; rightPage.borderColor = [UICcolor închisGrayColor] .CGColor; //leftPage.transform = makePerspectiveTransform (); // deconectați mai târziu //rightPage.transform = makePerspectiveTransform (); // deconectați mai târziu perdeaView = [[UIView alocare] initWithFrame: self.view.bounds]; curtainView.backgroundColor = [UICcolor scrollViewTexturedBackgroundColor]; [curtainView.layer addSublayer: leftPage]; [curtainView.layer addSublayer: rightPage]; UITapGestureRecognizer * foldTap = [[UITapGestureRecognizer alocare] initWithTarget: acțiune auto: @selector (fold :)]; [auto.view addGestureRecognizer: foldTap]; UITapGestureRecognizer * unfoldTap = [[UITapGestureRecognizer alloc] initWithTarget: actiune auto: @selector (desfasurare :); unfoldTap.numberOfTouchesRequired = 2; [auto.view addGestureRecognizer: unfoldTap];  - (void) ori: (UITapGestureRecognizer *) gr // desenarea bitmapului "incrementalImage" în straturile noastre CGImageRef imgRef = ((CanvasView *) self.view) .incrementalImage.CGImage; leftPage.contents = (id id) imgRef; rightPage.contents = (id id) imgRef; leftPage.contentsRect = CGRectMake (0.0, 0.0, 0.5, 1.0); // acest dreptunghi reprezintă jumătatea stângă a imaginii rightPage.contentsRect = CGRectMake (0.5, 0.0, 0.5, 1.0); // acest dreptunghi reprezintă jumătatea dreaptă a imaginii 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) desfășurați: (UITapGestureRecognizer *) gr leftPage.transform = CATransform3DIdentity; rightPage.transform = CATransform3DIdentity; // leftPage.transform = makePerspectiveTransform (); // deconectați mai târziu // rightPage.transform = makePerspectiveTransform (); // deconectați mai târziu [curtainView removeFromSuperview];  // UNCOMMENT LATER: / * CATransform3D makePerspectiveTransform () CATransform3D transform = CATransform3DIdentity; transform.m34 = 1,0 / -2000; transformare întoarcere;  */ @Sfârșit

Dacă ignorați codul comentat afară, puteți observa că stratul setat este exact așa cum am planificat mai sus.

Să discutăm scurt acest cod:

  • Noi decidem să ignorăm -viewDidAppear: în loc de -viewDidLoad (la care s-ar putea să fii mai obișnuit), deoarece atunci când această metodă este numită limitele vizualizării sunt încă în modul portret - dar aplicația noastră rulează în modul peisaj. Până când viewDidAppear: este numit, limitele au fost setate corect și așa că am plasat codul nostru acolo (am adăugat temporar granițe groase, astfel încât să putem explica straturile stânga și dreapta când le aplicăm transformări).
  • Am adăugat un dispozitiv de recunoaștere a gesturilor care înregistrează un robinet, iar pentru fiecare robinet, paginile devin puțin mai mici (95% din dimensiunea anterioară) și le fac să se rotească cu 7,5 grade. Semnele sunt diferite deoarece una dintre pagini se rotește în sensul acelor de ceasornic, în timp ce celelalte se rotesc în sens invers acelor de ceasornic Ar fi trebuit să intrăm în matematică pentru a vedea ce semn corespunde direcției, dar din moment ce există doar două opțiuni, este mai ușor doar să scrieți codul și să verificați! Apropo, funcțiile de transformare acceptă unghiuri în radiani, deci folosim macroul D2R () pentru a converti de la radiani la grade. O observație importantă este că funcțiile care se transformă în argumentul lor (cum ar fi CATransform3DScale și CATransform3DRotate) "lanț împreună", o transformare cu alta (valoarea curentă a proprietății de transformare a stratului). Alte funcții, cum ar fi CATransform3DMakeRotation, CATransform3DMakeScale, CATransform3DIdentity construi doar matricea de transformare adecvată. CATransform3DIdentity este "transformarea identității" pe care un strat o are atunci când o creați. Este analoagă cu numărul "1" într-o multiplicare prin aceea că aplicarea unei transformări de identitate pe un strat lasă transformarea sa neschimbată, la fel ca înmulțirea unui număr cu unul.
  • În ceea ce privește desenul, setăm proprietatea conținutului straturilor noastre ca imagine. Este foarte important să rețineți că am setat dreptunghiul conținutului (normalizat între 0 și 1 de-a lungul fiecărei dimensiuni) astfel încât fiecare pagină să afișeze doar jumătate din imaginea corespunzătoare. Acest sistem de coordonate normalizat este același cu cel pe care l-am discutat anterior când vorbim despre punctul de ancorare, astfel încât ar trebui să puteți valorifica valorile pe care le-am folosit pentru fiecare jumătate a imaginii.
  • curtainView obiectul pur și simplu acționează ca un container pentru straturile de pagină (mai precis, ele sunt realizate substraturi în stratul substrat al perdelelor). Amintiți-vă că am calculat deja poziția și geometria straturilor, iar acestea sunt cu privire la stratul curtainView. Atingând o singură dată, această vizualizare se adaugă în partea de sus a vizualizării panzei și se aplică transformarea pe strat. Dublu atingere îl îndepărtează, pentru a dezvălui încă o dată panza, precum și pentru a readuce transformarea straturilor la transformarea identității.
  • Notați utilizarea CGImage aici - și, de asemenea CGColor mai devreme - în loc de UIImage și UIColor. Asta pentru ca CALayer funcționează la un nivel mai jos decât UIKit și funcționează cu tipuri de date "opace" (care înseamnă, în general, să nu întrebi implementarea lor de bază!) care sunt definite în cadrul Core Graphics. Obiective-C clase ca UIColor și UIImage pot fi considerate wrapper-uri orientate obiect în jurul versiunilor CG mai primitive. Din fericire, multe obiecte UIKit expun tipul lor de bază CG ca proprietate.

În AppDelegate.m fișier, înlocuiți tot codul cu următoarele (singurul lucru pe care l-am adăugat este să includeți fișierul antet ViewController și să faceți o ViewController exemplu controlerul de vizualizare rădăcină):

 // // AppDelegate.m // #import "AppDelegate.h" #import "ViewController.h" @implementation Cerere AppDelegate - (BOOL): aplicație (UIApplication *) didFinishLaunchingWithOptions: (NSDictionary *) launchOptions self.window = [UIWindow alocare] initWithFrame: [[UIScreen mainScreen] limite]]; self.window.rootViewController = [[ViewController alloc] init]; auto.window.backgroundColor = [UICcolor whiteColor]; [auto.window makeKeyAndVisible]; reveniți DA;  @Sfârșit

Construiți proiectul și executați-l pe simulator sau pe dispozitiv. Scribble pe pânză pentru un pic, apoi atingeți pe ecran cu un singur deget pentru a declanșa acțiunea recunoașterii gestului (atingerea cu două degete face ca efectul 3D să se încheie și panza de desen să reapară).

Nu destul de efectul pentru care mergem! Ce se întâmplă?


Obținerea corectă a perspectivelor noastre

Mai întâi, observați că paginile devin mai mici cu fiecare robinet, astfel încât problema nu se află la transformarea de scalare, numai cu rotația. Problema este că, deși rotația se întâmplă într-un spațiu (matematic) 3D, rezultatul este proiectat pe ecrane plane aproape în același fel în care un obiect 3D își aruncă umbra pe un perete. Pentru a transmite profunzimea, trebuie să folosim un fel de tac. Cel mai important tac este cel al perspectivei: un obiect mai aproape de ochii noștri pare mai mare decât unul mai departe. Umbrele sunt un alt semn grozav, iar noi le vom ajunge la scurt timp. Deci, cum încorporăm perspectiva în transformarea noastră?

Să vorbim puțin despre transformări. Ce sunt ei, într-adevăr? Din punct de vedere matematic, trebuie să știți că dacă reprezentăm punctele în forma noastră ca vectori matematici, atunci transformările geometrice, cum ar fi scala, rotația și traducerea, sunt reprezentate ca transformări de matrice. Ce înseamnă acest lucru este faptul că dacă luăm o matrice reprezentând o transformare și se înmulțește cu un vector care reprezintă un punct în forma noastră, atunci rezultatul multiplicării (de asemenea, un vector) reprezintă locul în care punctul sfârșește după transformare. Nu putem spune mult mai mult aici fără să vă scufundăm în teorie (ceea ce merită să învățați, dacă nu sunteți deja familiarizat cu aceasta - mai ales dacă intenționați să includeți efecte 3D reci în aplicațiile dvs.).

Cum rămâne cu codul? Anterior, am setat geometria stratului prin setarea lui punct de ancorare, poziţie, și hotar. Ceea ce vedem pe ecran este geometria stratului după ce a fost transformată de ea transforma proprietate. Rețineți apelurile funcționale care arată ca acestea layer.transform = // ... . Aici plasăm transformarea, care la interior este doar a struct reprezentând o matrice de 4 x 4 de valori în virgulă mobilă. De asemenea, rețineți că funcțiile CATransform3DScale și CATransform3DRotate luați transformarea curentă a stratului ca parametru. Asta pentru că putem Compune mai multe se transformă împreună (ceea ce înseamnă doar că le multiplicăm matricile), rezultatul fiind ca și cum ați efectuat aceste transformări unul câte unul. Rețineți că vorbim numai despre rezultatul final al transformării, nu despre modul în care Core Animation animă stratul!

Revenind la problema de perspectivă, trebuie să știm că există o valoare în matricea noastră de transformare pe care o putem schimba pentru a obține efectul de perspectivă pe care îl urmăm. Această valoare este un membru al structurii transformate, numită m34 (numerele indică poziția sa în matrice). Pentru a obține efectul dorit, trebuie să-l setăm la un număr mic, negativ.

Dezactivați cele două secțiuni comentate din ViewController.m fișierul ( CATransform3D makePerspectiveTransform () funcția și liniile leftPage.transform = makePerspectiveTransform (); rightPage.transform = makePerspectiveTransform (); și construi din nou. De data aceasta, efectul 3D pare mai credibil.

De asemenea, rețineți că atunci când schimbăm proprietatea de transformare a CALayer, afacerea vine cu o animație "liberă". Aceasta este ceea ce vrem aici - spre deosebire de stratul care trece prin transformare brusc - dar uneori nu este.

Bineînțeles, perspectiva merge doar la fel de departe, când exemplul nostru devine mai sofisticat, vom folosi și umbre! Am putea dori, de asemenea, să rotunjim colțurile "cărții" noastre și să ne mascheze straturile de pagină cu un CAShapeLayer poate ajuta cu asta. În plus, am dori să folosim un gest de control pentru a controla plierea / derularea, astfel încât să fie mai interactivă. Toate acestea vor fi acoperite în partea a doua a acestui mini-serie tutorial.

Vă încurajez să experimentați codul, referindu-vă la documentația API și încercați să implementați în mod independent efectul dorit (s-ar putea să ajungeți chiar să faceți mai bine!).

Distrează-te cu tutorialul și mulțumesc pentru lectură!

Citiți partea 2

Cod