Irbisa · Cheatsheet
Animations & Core Animation
CALayer, implicit animations, UIViewPropertyAnimator, transitions, PhaseAnimator, KeyframeAnimator
This dot slides to x = 300 and stays there. What does
printshow a second later, and why does moving it to 100 change nothing on screen?let dot = ballView.layer // ballView is centred at x = 40 let slide = CABasicAnimation(keyPath: "position.x")slide.fromValue = 40slide.toValue = 300slide.duration = 0.5slide.fillMode = .forwardsslide.isRemovedOnCompletion = falsedot.add(slide, forKey: "slide") DispatchQueue.main.asyncAfter(deadline: .now() + 1) { print(dot.position.x) dot.position.x = 100 // nothing moves on screen}Why does changing a property on a standalone
CALayeranimate by itself, while the same change on a view's backing layer just snaps?// A standalone layer: no delegate, so the default 0.25 s action runslet badge = CALayer() // added to view.layer in an earlier run-loop turn, already on screenbadge.opacity = 0 // fades out on its own over 0.25 s // A view's backing layer: the view answers NSNull outside an animation blockview.layer.opacity = 0 // snaps…What is
CATransaction, and how do you stop a single layer change from animating?// No animation for this change: the action search never runsCATransaction.begin()CATransaction.setDisableActions(true)gradient.frame = boundsCATransaction.commit()…Where is the card drawn with transform
aand with transformb, and what doesprintshow each time?// card is 100 x 100, centred at (200, 200), with an identity transformlet a = CGAffineTransform(translationX: 100, y: 0).rotated(by: .pi / 2)let b = CGAffineTransform(rotationAngle: .pi / 2).translatedBy(x: 100, y: 0) card.transform = aprint(card.center) // what prints, and where is the card drawn? card.transform = bprint(card.center) // and now?Which
CALayersubclasses do you use, and why build an animated progress ring withCAShapeLayerinstead ofdraw(_:)?// draw(_:) version: 300 x 300 pt at 3x = 900 x 900 px x 4 B, about 3.2 MB, repainted per change // Shape layer version: no bitmap, and progress is one animatable propertylet ring = CAShapeLayer()ring.frame = CGRect(x: 0, y: 0, width: 300, height: 300)ring.path = UIBezierPath(ovalIn: ring.bounds.insetBy(dx: 4, dy: 4)).cgPath…What is
CADisplayLink, when do you need it instead of Core Animation, and how do you get 120 Hz from it on a ProMotion iPhone?final class WaveformView: UIView { private var link: CADisplayLink? private var startTime: CFTimeInterval = 0 private var phase: Double = 0 override func didMoveToWindow() {…What does
UIViewPropertyAnimatorgive you overUIView.animate, and how do you drive one from a pan gesture?final class SheetController: UIViewController { let sheet = UIView() private var animator: UIViewPropertyAnimator? @objc func handlePan(_ pan: UIPanGestureRecognizer) { let travel = sheet.bounds.height…Find the bug: tapping Filters should slide the panel open, but it jumps to full height with no animation.
final class FiltersViewController: UIViewController { let panel = UIView() private var panelHeight: NSLayoutConstraint! // panel.heightAnchor, starts at 0 private var isExpanded = false @objc func toggleFilters() { isExpanded.toggle() UIView.animate(withDuration: 0.3) { self.panelHeight.constant = self.isExpanded ? 320 : 0 } }}How do you implement a custom view controller transition, and who adds and removes the views in the container?
final class SlideAnimator: NSObject, UIViewControllerAnimatedTransitioning { let operation: UINavigationController.Operation init(operation: UINavigationController.Operation) { self.operation = operation } func transitionDuration(using context: UIViewControllerContextTransitioning?) -> TimeInterval { 0.32 }…When does a SwiftUI
.transitionactually run, and why does a view sometimes just appear without it?struct Inbox: View { @State private var showBanner = false @State private var items = Message.samples var body: some View { VStack {…How do you run code when a SwiftUI animation finishes, and how do you chain one animation after another?
struct SavedToast: View { @State private var visible = false @State private var offset: CGFloat = 0 let onFinished: () -> Void var body: some View {…What is the difference between
phaseAnimatorandkeyframeAnimator, and when is arepeatForeveranimation enough?struct Pose { var scale = 1.0, angle = Angle.zero, lift = 0.0 } struct BellBadge: View { @State private var taps = 0 var body: some View {…This
Ringjumps to the new progress instead of animating: why, and what doAnimatableandanimatableDatahave to do with it?struct Ring: Shape { var progress: Double // 0...1 func path(in rect: CGRect) -> Path { var path = Path() path.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.width / 2, startAngle: .degrees(-90), endAngle: .degrees(-90 + 360 * progress), clockwise: false) return path }} // Ring(progress: done).stroke(.teal, lineWidth: 8)// .animation(.easeOut(duration: 0.6), value: done)What is the difference between implicit and explicit Core Animation animations, and what are
CABasicAnimation,CAKeyframeAnimationandCAAnimationGroup?// Basic: one key path, from one value to anotherlet fade = CABasicAnimation(keyPath: "opacity")fade.fromValue = 1.0fade.toValue = 0.3fade.duration = 0.3badge.opacity = 0.3 // the model value is yours to set…What is the difference between a layer's model and presentation layers, and why can't the user tap a view while it animates?
final class GameViewController: UIViewController { let ball = UIView(frame: CGRect(x: 20, y: 200, width: 44, height: 44)) func launch() { UIView.animate(withDuration: 2) { self.ball.center.x = 320 } print(ball.center.x) // 320: the model is already there…How does
matchedGeometryEffectwork, and why does a card that should grow into a detail view just fade and snap instead?struct CardGallery: View { @Namespace private var cards // must outlive both sides of the pair @State private var expanded: Int? var body: some View { ZStack {…How do you hand a gesture's release velocity to a spring animation, and what do a spring's
durationandbouncemean?// UIKit: the release keeps the throwfunc endDrag(_ pan: UIPanGestureRecognizer) { let target = shouldDismiss ? view.bounds.maxY + sheet.bounds.height / 2 : restingY let distance = target - sheet.center.y // initialVelocity is in "remaining distances per second", not points per second let relative = abs(distance) < 1 ? 0 : pan.velocity(in: view).y / distance…How do you make a custom transition interactive, and what must happen when the user cancels it halfway?
final class DismissInteraction: UIPercentDrivenInteractiveTransition { private(set) var isDriving = false weak var presented: UIViewController? @objc func handle(_ pan: UIPanGestureRecognizer) { let progress = min(max(pan.translation(in: pan.view).y / 300, 0), 1)…Which layer properties are cheap to animate, and what forces the render server into offscreen rendering?
An animation hitches on a real iPhone: how do you find out whether your app's commit or the render server is late?