Bedre iOS-animationer med CATransaction

Core Animation, Cocoa-rammen, der er ansvarlig for de fleste iOS- og macOS-animationer, er et komplekst system med en masse skjulte og mindre kendte funktionaliteter.

Core Animation håndterer dine animationer bag kulisserne, og ved at vide lidt mere, hvordan det fungerer, kan vi drage fuld fordel af det og oprette finjusterede animationer.

Animationer er lette. Bortset fra når de ikke er det.
BetterAnimations.playground

Animationer er den bedste måde at vise ændringer til en bruger. Apples UIKit (eller AppKit til macOS) gør at springe ind i animationer til en leg. Du har sandsynligvis brugt UIKit til de fleste af dine animationsmøder, og koden ser sandsynligvis sådan ud:

Det er rent og let at bruge. UIKit / AppKit-rammerne på højere niveau (som sidder over Core Animation) gør et godt stykke arbejde for at gøre animationer lette. Der er dog nogle faldgruber, når du ønsker mere komplekse bevægelser.

Eksempel Animation

Overvej dette: du har en UIB-knap, en efterkommer af UIView, og du vil gøre det til et cirkulært billede, og når det tappes, ændrer det størrelsen på visningen.

Lyder lige frem. Vi kan animere styledButton.layer.cornerRadius og indstille den til halve bredden, når vores ramme ændres. Men vent ... der er et problem. UIView.animate (...) håndterer kun visningsejendomanimationer og ikke lagegenskabsanimationer. Hvis vi forsøgte at ændre rammen for stylet knap og hjørnet radius af stylet knap i animationsblokken nedenfor får vi en utilsigtet effekt:

Resultat af kodesegment

Rammen animerer korrekt, men hjørneRadius springer direkte til sin nye værdi uden at animere. Senere vil vi tale om CATransaction og hvordan det løser dette problem. Men først er det godt at vide, hvad der sker under hætten på vores animationer, og hvorfor dette sker.

Inde i kerneanimation

UIView-forekomster (såvel som lagerstøttede NSView-forekomster) har en CALayer-egenskab kaldet lag, som animationer fungerer på. Visningen delegerer gengivelsen af ​​deres lag til Core Animation. Når animationer føjes til laget, ændres lagets egenskaber ikke direkte.

Core Animation har to parallelle træhierarkier: et modellagtræ og et præsentationslagtræ. Disse kan ses i CALayers præsentationLayer og modelLayer egenskaber. Det er præsentationslaget, der er ansvarlig for at vise enhver ændring i løbet af en animation. Hvis du skulle animere et lags borderWidth-egenskab og observere værdien under animationen, ændres borderWidth-værdien kun i præsentationslagtræet under animationen.

Gennem CABasicAnimation kan vi animere lagegenskaber. Men lad ikke ordet "Grundlæggende" afskrække dig - dette er et af de mest kraftfulde animationsværktøjer til finjustering af dine animationer.

En praktisk note: Det er en god animationspraksis at indstille den værdi, du animerer, til den nye værdi, før du tilføjer animationen. Det er almindeligt at se egenskaben fillMode indstillet til kCAFillModeForwards og erRemovedOnCompletion indstillet til falske. Men hvad dette gør er i det væsentlige "pause" vores præsentationslag i slutningen af ​​animationen. Det er god praksis at holde modellen og præsentationslagene synkroniserede, og ved at indstille værdien, før du tilføjer animationen, kan vi gøre netop dette.

CATransaction - For at udjævne dine animationer

Okay. Tilbage til vores UIB-knapeksempel fra tidligere. Vi ønsker at animere lagets hjørneRadius-egenskab og animere visningens rammeejendom på samme tid og sikre, at de forbliver perfekt synkroniseret for den bedste animation.

CATransaction til redning!

CATransaction er en ofte overset klasse af de fleste udviklere. Jobbet med CATransaction er at gruppere flere animationsrelaterede handlinger sammen. Det sikrer, at de ønskede animationsændringer forpligtes til Core Animation på samme tid.

Her starter vi en transaktion og kan ændre alle egenskaber, der er aktiveret af Core Animation, som vil blive animeret ved siden af ​​hinanden, når de først er begået.

Samler det hele

Så vi ønsker at animere lag og se egenskaber på samme tid. Dette er den perfekte anvendelse til CATransaction. Vi kan endda bruge vores eksisterende UIView.animate () og CABasicAnimation-kode!

Resulterende animation

I dette eksempel bruger jeg både en UIView-animation og en CABasicAnimation til at fuldføre denne opgave. Alt, der koordineres med CATransaction, overføres til andre animationer. F.eks. Vil funktionen setAnimationDuration (dur: CFTimeInterval) gælde for CABasicAnimation, så vi behøver ikke at specificere nogen varighed. Da UIView.animate () får os til at specificere varigheden, kan vi imidlertid blot indstille varigheden til den samme, der er specificeret i CATransaction's setAnimationDuration.

Tilpassede Keyframe-animationer

En anden pæn anvendelse er at bruge en CATransaction's timingFunction til at definere din egen bezier-animationskurve. Hvis du pakker din UIView.animate () inde i en CATransaction, kan du få præcis kontrol over din animationskurve:

Endelig animation med timefunktion

Resumé

CATransaction er en stærk koordinator for dine animationer og sparer dig masser af tid, mens du giver dig præcis kontrol over dine animationer. Der er desuden ikke en masse indlejrede blokke - hvilket altid er et læsbarhed plus.

Det komplette Swift 3-legepladsprojekt, der bruger CATransaction sammen med UIKit og CABasicAnimation, kan findes her:

Tak for at have læst! Hvis du har spørgsmål eller forslag - er du velkommen til at nå ud!