Wykonywanie animacji iOS na widokach za pomocą UIKit i UIView
Opublikowany: 2022-03-10Jestem programistą iOS od ponad dekady i rzadko widziałem artykuły, które konsolidują wszystkie możliwe sposoby wykonywania animacji w iOS. Ten artykuł ma być wprowadzeniem do animacji na iOS z zamiarem wyczerpującego omówienia różnych sposobów robienia tego samego.
Biorąc pod uwagę obszerność tematu, każdą część omówilibyśmy zwięźle na dość wysokim poziomie. Celem jest edukowanie czytelnika za pomocą zestawu opcji dodawania animacji do jego aplikacji na iOS.
Zanim zaczniemy od tematów związanych z iOS, przyjrzyjmy się pokrótce szybkości animacji.
Animacja przy 60 klatkach na sekundę
Ogólnie rzecz biorąc, w filmach każda klatka jest reprezentowana przez obraz, a szybkość klatek określa liczbę obrazów odwróconych w sekwencji. Jest to określane jako „klatki na sekundę” lub FPS.
FPS określa liczbę nieruchomych obrazów odwróconych w ciągu sekundy, co dosłownie oznacza, że im więcej obrazów/ramek, tym więcej szczegółów/informacji jest wyświetlanych w filmie. Dotyczy to również animacji.
FPS jest zwykle używany do określenia jakości animacji. Panuje powszechna opinia, że każda dobra animacja powinna działać przy 60 klatkach na sekundę lub wyższej — wszystko, co jest mniejsze niż 60 klatek na sekundę, mogłoby się trochę pogorszyć.
Czy chcesz zobaczyć różnicę między 30 FPS a 60 FPS? Sprawdź to!
Czy zauważyłeś różnicę? Ludzkie oczy zdecydowanie wyczuwają drgania przy niższych fps. Dlatego zawsze dobrą praktyką jest upewnienie się, że każda tworzona animacja jest zgodna z podstawową zasadą działania z prędkością 60 klatek na sekundę lub wyższą. To sprawia, że jest bardziej realistyczny i żywy.
Po przyjrzeniu się FPS, przyjrzyjmy się teraz różnym podstawowym frameworkom iOS, które zapewniają nam sposób na wykonywanie animacji.
Podstawowe ramy
W tej sekcji omówimy frameworki w iOS SDK, które można wykorzystać do tworzenia animacji widoku. Zrobimy szybki przegląd każdego z nich, wyjaśniając ich zestaw funkcji na odpowiednim przykładzie.
Animacje UIKit/UIView
UIView to klasa bazowa dla każdego widoku, który wyświetla zawartość w aplikacjach na iOS.
UIKit, framework, który daje nam UIView, już dostarcza nam kilka podstawowych funkcji animacji, które ułatwiają programistom osiąganie więcej, robiąc mniej.
Interfejs API, UIView.animate
, to najłatwiejszy sposób animowania widoków, ponieważ właściwości dowolnego widoku można łatwo animować, podając wartości właściwości w składni opartej na blokach.
W animacjach UIKit zaleca się modyfikowanie tylko właściwości animowalnych UIVIew, w przeciwnym razie animacje mogą spowodować nieoczekiwany stan widoku.
animacja(with Duration: animacje: ukończenie)
Ta metoda obejmuje czas trwania animacji, zestaw animowanych zmian właściwości widoku, które muszą być animowane. Blok zakończenia daje wywołanie zwrotne, gdy widok zakończy wykonywanie animacji.
Za pomocą tego pojedynczego interfejsu API można uzyskać prawie każdy rodzaj animacji, taki jak przesuwanie, skalowanie, obracanie, zanikanie itp. w widoku.
Teraz zastanów się, czy chcesz animować zmianę rozmiaru przycisku lub chcesz, aby określony widok powiększył ekran. Oto jak możemy to zrobić za pomocą API UIView.animate
:
let newButtonWidth: CGFloat = 60 UIView.animate(withDuration: 2.0) { //1 self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) //2 self.button.center = self.view.center //3 }
Oto, co tutaj robimy:
- Wywołujemy metodę
UIView.animate
z przekazaną do niej wartością czasu trwania, która reprezentuje czas trwania animacji opisanej wewnątrz bloku. - Ustawiamy nową ramkę przycisku, która powinna reprezentować końcowy stan animacji.
- Ustawiamy
center
przycisku z jego środkiem podglądu tak, aby pozostawał na środku ekranu.
Powyższy blok kodu animacji powinien wywołać animację zmiany klatki przycisku z bieżącej klatki:
Width = 0, Height = 0
Do ostatniej klatki:
Width = Height = newButtonWidth
A tak wyglądałaby animacja:
animacja z czasem trwania
Ta metoda jest jak rozszerzenie metody animate, w której możesz zrobić wszystko, co możesz wykonać w poprzednim interfejsie API, z pewnymi zachowaniami fizycznymi dodanymi do animacji widoku.
Na przykład, jeśli chcesz uzyskać efekty tłumienia sprężyny w animacji, którą wykonaliśmy powyżej, kod będzie wyglądał tak:
let newButtonWidth: CGFloat = 60 UIView.animate(withDuration: 1.0, //1 delay: 0.0, //2 usingSpringWithDamping: 0.3, //3 initialSpringVelocity: 1, //4 options: UIView.AnimationOptions.curveEaseInOut, //5 animations: ({ //6 self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) self.button.center = self.view.center }), completion: nil)
Oto zestaw parametrów, których używamy:
-
duration
Reprezentuje czas trwania animacji określającej, jak długo blok kodu powinien działać. -
delay
Reprezentuje początkowe opóźnienie, które chcemy mieć przed rozpoczęciem animacji. -
SpringWithDamping
Reprezentuje wartość efektu sprężystości, który ma zachowywać widok. Wartość musi mieścić się w zakresie od 0 do 1. Im niższa wartość, tym wyższe oscylacje sprężyny. -
velocity
Reprezentuje szybkość, z jaką powinna się rozpocząć animacja. -
options
Typ krzywej animacji, którą chcesz zastosować do animacji widoku. - Na koniec blok kodu, w którym ustawiamy ramkę przycisku, który ma być animowany. Jest taki sam jak w poprzedniej animacji.
A oto jak animacja wyglądałaby z powyższą konfiguracją animacji:
UIViewPropertyAnimator
Aby uzyskać nieco większą kontrolę nad animacjami, przydatny jest UIViewPropertyAnimator
, który umożliwia wstrzymywanie i wznawianie animacji. Możesz mieć niestandardowe taktowanie i mieć interaktywną i przerywaną animację. Jest to bardzo pomocne podczas wykonywania animacji, które również wchodzą w interakcję z działaniami użytkownika.
Klasyczny gest „Przesuń, aby odblokować” oraz animacja zamykania/rozwijania widoku odtwarzacza (w aplikacji Muzyka) to przykłady animacji interaktywnych, które można przerywać. Możesz rozpocząć przesuwanie widoku palcem, a następnie zwolnić go, a widok powróci do swojej pierwotnej pozycji. Alternatywnie możesz uchwycić widok podczas animacji i kontynuować przeciąganie go palcem.
Poniżej znajduje się prosty przykład tego, jak możemy uzyskać animację za pomocą UIViewPropertyAnimator
:
let newButtonWidth: CGFloat = 60 let animator = UIViewPropertyAnimator(duration:0.3, curve: .linear) { //1 self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) self.button.center = self.view.center } animator.startAnimation() //2
Oto, co robimy:
- Wywołujemy interfejs API
UIViewProperty
, przekazując czas trwania i krzywą animacji. - W przeciwieństwie do obu powyższych interfejsów API UIView.animate, animacja nie rozpocznie się, chyba że określisz ją samodzielnie, tj. masz pełną kontrolę nad całym procesem/przepływem animacji.
Załóżmy teraz, że chcesz mieć jeszcze większą kontrolę nad animacjami. Na przykład chcesz zaprojektować i kontrolować każdą klatkę w animacji. Jest do tego inny interfejs API, animateKeyframes
. Ale zanim się w to zagłębimy, przyjrzyjmy się szybko, czym jest klatka w animacji.
Co to jest frame
?
Zbiór zmian/przejść ramek widoku, od stanu początkowego do stanu końcowego, jest definiowany jako animation
, a każda pozycja widoku podczas animacji jest nazywana frame
.
animacja klatek kluczowych
Ten interfejs API umożliwia zaprojektowanie animacji w taki sposób, aby można było zdefiniować wiele animacji z różnymi czasami i przejściami. Opublikuj to, API po prostu integruje wszystkie animacje w jedno, płynne środowisko.
Powiedzmy, że chcemy losowo przesuwać nasz przycisk na ekranie. Zobaczmy, jak możemy w tym celu wykorzystać interfejs API animacji klatek kluczowych.
UIView.animateKeyframes(withDuration: 5, //1 delay: 0, //2 options: .calculationModeLinear, //3 animations: { //4 UIView.addKeyframe( //5 withRelativeStartTime: 0.25, //6 relativeDuration: 0.25) { //7 self.button.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.maxY) //8 } UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.25) { self.button.center = CGPoint(x: self.view.bounds.width, y: start.y) } UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25) { self.button.center = start } })
Oto podział:
-
duration
Wywołaj interfejs API, przekazując czas trwania animacji. -
delay
Początkowy czas opóźnienia animacji. -
options
Typ krzywej animacji, który chcesz zastosować do animacji widoku. -
animations
Blok, który przyjmuje wszystkie animacje klatek kluczowych zaprojektowane przez programistę/użytkownika. -
addKeyFrame
Wywołaj interfejs API, aby zaprojektować każdą animację. W naszym przypadku zdefiniowaliśmy każdy ruch przycisku. Możemy mieć tyle takich animacji, ile potrzebujemy, dodanych do bloku. -
relativeStartTime
Definiuje czas rozpoczęcia animacji w kolekcji bloku animacji. -
relativeDuration
Definiuje całkowity czas trwania tej konkretnej animacji. -
center
W naszym przypadku po prostu zmieniamy właściwość center przycisku, aby przesuwać przycisk po ekranie.
A tak wyglądają finalne animacje:
CoreAnimacja
Każda animacja oparta na UIKit jest wewnętrznie tłumaczona na animacje podstawowe. W ten sposób platforma Core Animation działa jako warstwa podkładowa lub szkielet dla dowolnej animacji UIKit. W związku z tym wszystkie interfejsy API animacji UIKit są niczym innym jak hermetyzowanymi warstwami podstawowych interfejsów API animacji w łatwy do użytku lub wygodny sposób.
Interfejsy API animacji UIKit nie zapewniają dużej kontroli nad animacjami, które zostały wykonane w widoku, ponieważ są one używane głównie do animowanych właściwości widoku. Dlatego w takich przypadkach, w których zamierzasz mieć kontrolę nad każdą klatką animacji, lepiej jest użyć bezpośrednio podstawowych interfejsów API animacji. Alternatywnie zarówno animacje UIView, jak i animacje podstawowe mogą być również używane w połączeniu.
UIView + podstawowa animacja
Zobaczmy, jak możemy odtworzyć tę samą animację zmiany przycisku wraz z określeniem krzywej czasu przy użyciu interfejsów API UIView i Core Animation.
Możemy użyć funkcji czasowych CATransaction
, która pozwala określić i kontrolować krzywą animacji.
Przyjrzyjmy się przykładowi animacji zmiany rozmiaru przycisku z promieniem narożnika wykorzystującym funkcję pomiaru czasu CATransaction
i kombinację animacji UIView:
let oldValue = button.frame.width/2 let newButtonWidth: CGFloat = 60 /* Do Animations */ CATransaction.begin() //1 CATransaction.setAnimationDuration(2.0) //2 CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)) //3 // View animations //4 UIView.animate(withDuration: 1.0) { self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) self.button.center = self.view.center } // Layer animations let cornerAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.cornerRadius)) //5 cornerAnimation.fromValue = oldValue //6 cornerAnimation.toValue = newButtonWidth/2 //7 button.layer.cornerRadius = newButtonWidth/2 //8 button.layer.add(cornerAnimation, forKey: #keyPath(CALayer.cornerRadius)) //9 CATransaction.commit() //10
Oto podział:
-
begin
Reprezentuje początek bloku kodu animacji. -
duration
Ogólny czas trwania animacji. -
curve
Reprezentuje krzywą czasu, którą należy zastosować do animacji. -
UIView.animate
Nasza pierwsza animacja zmiany ramki przycisku. -
CABasicAnimation
Tworzymy obiektCABasicAnimation
, odwołując się docornerRadius
przycisku jako ścieżki klucza, ponieważ to właśnie chcemy animować. Podobnie, jeśli chcesz mieć szczegółową kontrolę nad animacjami klatek kluczowych, możesz użyć klasyCAKeyframeAnimation
. -
fromValue
Reprezentuje początkową wartość animacji, tj. początkową wartośćcornerRadius
przycisku, od której animacja musi się zaczynać. -
toValue
Reprezentuje końcową wartość animacji, tj. końcową wartośćcornerRadius
przycisku, w której animacja musi się kończyć. -
cornerRadius
Musimy ustawić właściwośćcornerRadius
przycisku na końcową wartość animacji, w przeciwnym razie wartość cornerRadius przycisku zostanie automatycznie przywrócona do wartości początkowej po zakończeniu animacji. -
addAnimation
Do warstwy dołączamy obiekt animacji, który zawiera konfigurację całego procesu animacji, reprezentując ścieżkę klucza, dla której animacja ma być wykonana. -
commit
Reprezentuje koniec bloku kodu animacji i rozpoczyna animację.
Tak wyglądałaby ostateczna animacja:
Ten blog to świetna lektura, która pomoże Ci tworzyć bardziej zaawansowane animacje, ponieważ zgrabnie przeprowadzi Cię przez większość interfejsów API platformy Core Animation z instrukcjami, które poprowadzą Cię przez każdy krok.
UIKitDynamics
UIKit Dynamics to silnik fizyki dla UIKit, który umożliwia dodawanie dowolnych zachowań fizycznych, takich jak kolizja, grawitacja, pchanie, przyciąganie itp. do elementów sterujących UIKit.
UIKitDynamicAnimator
Jest to klasa administracyjna struktury UIKit Dynamics, która reguluje wszystkie animacje wyzwalane przez dowolną kontrolkę interfejsu użytkownika.
Dynamiczne zachowanie interfejsu użytkownika
Umożliwia dodanie dowolnego zachowania fizyki do animatora, co następnie umożliwia jego działanie na dołączonym do niego widoku.
Różne rodzaje zachowań dla UIKitDynamics obejmują:
-
UIAttachmentBehavior
-
UICollisionBehavior
-
UIFieldBehavior
-
UIGravityBehavior
-
UIPushBehavior
-
UISnapBehavior
Architektura UIKitDynamics wygląda mniej więcej tak. Zwróć uwagę, że pozycje od 1 do 5 można zastąpić pojedynczym widokiem.
Zastosujmy pewne zachowanie fizyczne do naszego przycisku. Zobaczymy, jak przyłożyć grawitację do przycisku, aby dać nam poczucie obcowania z prawdziwym obiektem.
var dynamicAnimator : UIDynamicAnimator! var gravityBehavior : UIGravityBehavior! dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1 gravityBehavior = UIGravityBehavior(items: [button]) //2 dynamicAnimator.addBehavior(gravityBehavior) //3
Oto podział:
-
UIKitDynamicAnimator
Stworzyliśmy obiektUIKitDynamicAnimator
, który działa jako koordynator do wykonywania animacji. Przekazaliśmy również superwidok naszego przycisku jako widok referencyjny. -
UIGravityBehavior
Stworzyliśmy obiektUIGravityBehavior
i przekazaliśmy nasz przycisk do elementów tablicy, do których wstrzykiwane jest to zachowanie. -
addBehavior
Do animatora dodaliśmy obiekt grawitacyjny.
Powinno to stworzyć animację, jak pokazano poniżej:
Powinniśmy powiedzieć animatorowi, aby uznał dół ekranu za ziemię. W tym miejscu pojawia sięUICollisionBehavior
.var dynamicAnimator : UIDynamicAnimator! var gravityBehavior : UIGravityBehavior! var collisionBehavior : UICollisionBehavior! dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1 gravityBehavior = UIGravityBehavior(items: [button]) //2 dynamicAnimator.addBehavior(gravityBehavior) //3 collisionBehavior = UICollisionBehavior(items: [button]) //4 collisionBehavior.translatesReferenceBoundsIntoBoundary = true //5 dynamicAnimator.addBehavior(collisionBehavior) //6
-
UICollisionBehavior
Stworzyliśmy obiektUICollisionBehavior
i przekazaliśmy przycisk, aby zachowanie zostało dodane do elementu. -
translatesReferenceBoundsIntoBoundary
Włączenie tej właściwości powoduje, że animator przyjmuje granicę widoków referencyjnych jako koniec, czyli w naszym przypadku dolną część ekranu. -
addBehavior
Dodaliśmy tutaj zachowanie kolizji do animatora.
Teraz nasz przycisk powinien uderzyć w ziemię i stać nieruchomo, jak pokazano poniżej:
To całkiem fajne, prawda?
Spróbujmy teraz dodać efekt odbijania, aby nasz obiekt wydawał się bardziej rzeczywisty. W tym celu użyjemy klasyUIDynamicItemBehavior
.var dynamicAnimator : UIDynamicAnimator! var gravityBehavior : UIGravityBehavior! var collisionBehavior : UICollisionBehavior! var bouncingBehavior : UIDynamicItemBehavior! dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1 gravityBehavior = UIGravityBehavior(items: [button]) //2 dynamicAnimator.addBehavior(gravityBehavior) //3 collisionBehavior = UICollisionBehavior(items: [button]) //4 collisionBehavior.translatesReferenceBoundsIntoBoundary = true //5 dynamicAnimator.addBehavior(collisionBehavior) //6 //Adding the bounce effect bouncingBehavior = UIDynamicItemBehavior(items: [button]) //7 bouncingBehavior.elasticity = 0.75 //8 dynamicAnimator.addBehavior(bouncingBehavior) //9
-
UIDynamicItemBehavior
Stworzyliśmy obiektUIDynamicItemBehavior
i przekazaliśmy przycisk, aby zachowanie zostało dodane do elementu. -
elasticity
Wartość musi mieścić się w zakresie 0-1, reprezentuje elastyczność, tj. ile razy obiekt musi odbić się od ziemi, gdy zostanie uderzony. W tym miejscu dzieje się magia — modyfikując tę właściwość, możesz rozróżniać różne rodzaje obiektów, takich jak piłki, butelki, twarde przedmioty i tak dalej. -
addBehavior
Dodaliśmy tutaj zachowanie kolizji do animatora.
Teraz nasz przycisk powinien odbić się, gdy uderzy w ziemię, jak pokazano poniżej:
To repozytorium jest bardzo pomocne i pokazuje wszystkie zachowania UIKitDynamics w akcji. Zapewnia również kod źródłowy do zabawy z każdym zachowaniem. To moim zdaniem powinno służyć jako obszerna lista sposobów wykonywania animacji iOS na widokach!
W następnej sekcji przyjrzymy się pokrótce narzędziom, które pomogą nam mierzyć wydajność animacji. Polecam również przyjrzeć się sposobom optymalizacji kompilacji Xcode, ponieważ zaoszczędzi to ogromną ilość czasu na programowanie.
Podnoszenie wydajności
W tej sekcji przyjrzymy się sposobom mierzenia i dostrajania wydajności animacji na iOS. Jako programista iOS mogłeś już używać instrumentów Xcode, takich jak wycieki pamięci i alokacje, do mierzenia wydajności całej aplikacji. Podobnie istnieją instrumenty, które można wykorzystać do pomiaru wydajności animacji.
Core Animation
Wypróbuj instrument Core Animation
i powinieneś być w stanie zobaczyć FPS, który zapewnia ekran Twojej aplikacji. To świetny sposób na zmierzenie wydajności/szybkości dowolnej animacji renderowanej w aplikacji na iOS.
Rysunek
FPS jest znacznie obniżony w aplikacji, która wyświetla ciężkie treści, takie jak obrazy z efektami, takimi jak cienie. W takich przypadkach zamiast przypisywania Image bezpośrednio do właściwości obrazu UIImageView
, spróbuj narysować obraz osobno w kontekście przy użyciu interfejsów API Core Graphics. To nadmiernie skraca czas wyświetlania obrazu, wykonując asynchronicznie logikę dekompresji obrazu, gdy jest to wykonywane w osobnym wątku zamiast w wątku głównym.
Rasteryzacja
Rasteryzacja to proces używany do buforowania złożonych informacji o warstwach, aby te widoki nie były ponownie rysowane za każdym razem, gdy są renderowane. Przerysowywanie widoków jest główną przyczyną zmniejszenia liczby klatek na sekundę, dlatego najlepiej zastosować rasteryzację widoków, które będą wielokrotnie używane.
Zawijanie
Podsumowując, podsumowałem również listę przydatnych zasobów do animacji na iOS. Może się to okazać bardzo przydatne podczas pracy nad animacjami na iOS. Ponadto ten zestaw narzędzi do projektowania może być również pomocny jako etap (projektowania) przed zagłębieniem się w animacje.
Mam nadzieję, że udało mi się omówić jak najwięcej tematów związanych z animacjami iOS. Jeśli jest coś, co mogłem pominąć w tym artykule, daj mi znać w sekcji komentarzy poniżej, a chętnie dokonam dodania!