Transition / Animation 최적화
1. 들어가며
iOS 앱에서 애니메이션은 단순한 “시각 효과”가 아닌 사용자 경험(UX)의 핵심 요소입니다.
부드러운 전환(transition), 자연스러운 인터랙션, 적절한 타이밍은 앱의 완성도를 극적으로 높입니다.
하지만 실무에서는 다음과 같은 문제가 자주 발생합니다.
- 화면 전환 시 끊김(stutter)
- custom transition 중 gesture 충돌
- SwiftUI <-> UIKit 혼합 시 애니메이션 불일치
- Keyboard 전환과 애니메이션 충돌
- heavy view hierarchy 때문에 FPS 저하
이 문서에서는 iOS에서 애니메이션/전환을 성능 좋게, 안정적으로, 재사용 가능한 구조로 만드는 실무 노하우를 정리합니다.
2. iOS 애니메이션 엔진 이해
UIKit 애니메이션
- Core Animation 기반
- UIView.animate
- transitionCoordinator로 시스템 전환과 동기화 가능
SwiftUI 애니메이션
- 선언적(Declarative)
- 애니메이션 상태 기반 적용
- transition / matchedGeometry / withAnimation
UIKit과 SwiftUI는 본질적으로 다른 방식으로 애니메이션을 처리합니다.
→ 혼합 시 이를 이해해야 충돌을 방지할 수 있습니다.
3. UIView.animate 핵심 패턴
UIView.animate(withDuration: 0.25, delay: 0, options: [.curveEaseInOut]) {
view.alpha = 1
view.transform = .identity
}
옵션 추천
- .curveEaseInOut → 기본적이고 자연스러움
- .curveEaseOut → UI 등장 애니메이션에서 자연스러움
- .transitionCrossDissolve → 빠른 화면 교체
4. transitionCoordinator 활용 (문서화된 실무 핵심)
화면 전환(push/pop/present/dismiss)과 완벽하게 동기화된 애니메이션을 만들 때 필요.
transitionCoordinator?.animate(alongsideTransition: { _ in
self.containerView.alpha = 1
})
장점
- 시스템 애니메이션과 타이밍 완전 일치
- Navigation/Modal transition과 충돌 없음
- Keyboard와 함께 애니메이션 조정 가능
실무에서 custom transition을 만드는 가장 안전한 방식.
5. Custom Transition — UIViewControllerTransitioningDelegate
5.1 구조
presentingVC.transitioningDelegate = self
presentingVC.modalPresentationStyle = .custom
5.2 Animator 객체 구현
final class SlideInAnimator: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using context: UIViewControllerContextTransitioning?) -> TimeInterval { 0.3 }
func animateTransition(using context: UIViewControllerContextTransitioning) {
let toView = context.view(forKey: .to)!
let container = context.containerView
toView.transform = CGAffineTransform(translationX: 0, y: container.frame.height)
container.addSubview(toView)
UIView.animate(withDuration: 0.3, animations: {
toView.transform = .identity
}, completion: { finished in
context.completeTransition(finished)
})
}
}
5.3 장점
- bottom sheet, modal slide, fade 등 모든 커스텀 가능
- Edge gesture 커스텀 가능
6. Interactive Transition (손가락 따라오는 dismiss)
final class InteractiveTransition: UIPercentDrivenInteractiveTransition {
var isInteracting = false
}
Gesture 기반 dismiss 구현 시 필수이며,
modalPresentationStyle = .custom 환경에서 강력함.
7. SwiftUI 애니메이션과 최적화
withAnimation
withAnimation(.easeInOut(duration: 0.25)) {
isVisible.toggle()
}
transition
.transition(.move(edge: .bottom).combined(with: .opacity))
matchedGeometryEffect
두 요소의 위치를 부드럽게 연결하는 SwiftUI의 고급 기능.
8. SwiftUI ↔ UIKit 혼합 시 발생하는 문제
8.1 문제: SwiftUI 애니메이션이 UIKit present 애니메이션과 대립
해결:
- UIKit쪽에서 animated: false로 전환 후 SwiftUI 내부에서 withAnimation 사용
- 또는 SwiftUI View를 호스팅하기 전에 transform/opacity 초기값 설정
8.2 문제: Keyboard와 애니메이션 충돌
- SwiftUI는 기본적으로 keyboard avoidance 처리
- UIKit custom transition 사용 시 레이아웃이 jump 발생
해결:
- hostingController.view.insetsLayoutMarginsFromSafeArea = false
- keyboard notification 기반 frame animation 직접 제어
9. 애니메이션 성능을 높이는 방법
9.1 Core Animation에서 GPU-friendly 속성만 변경
- alpha
- transform
- position
- bounds
지양:
- frame 반복 변경
- layoutIfNeeded 연속 호출
- shadowPath 미지정 shadow 사용
9.2 CALayer 기반 애니메이션 사용
복잡한 애니메이션은 UIView.animate보다 CAAnimation 사용:
let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.25
layer.add(animation, forKey: nil)
10. Heavy View Hierarchy 최적화
- view 계층 단순화
- reuse 가능한 view 분리
- shadows/radius 최소화 (특히 list cell 내부)
- rasterization(레이어 캐싱) 적절히 사용
view.layer.shouldRasterize = true
view.layer.rasterizationScale = UIScreen.main.scale
11. FPS(Frames Per Second) 확인 방법
iOS, macOS 앱의 실시간 FPS (프레임 속도) 및 성능을 모니터링하는 최신 방법은 다음과 같습니다.
11.1 Debug Navigator (가장 일반적인 실시간 확인)
앱을 실행 중일 때(디버깅 모드), Xcode 화면의 왼쪽 패널에 있는 Debug Navigator를 통해 FPS를 포함한 핵심 성능 지표를 실시간으로 확인할 수 있습니다.
- 위치: Xcode 윈도우의 왼쪽 패널에서 Debug Navigator (측정기 모양 아이콘)를 선택합니다.
- 확인 방법:
- CPU/GPU 탭을 선택합니다.
- FPS(Frames Per Second)를 포함하여 CPU, GPU 사용량, 메모리 사용량 등의 변화를 실시간 그래프로 모니터링할 수 있습니다.
- 애니메이션이 시작되거나 복잡한 뷰가 로드될 때 프레임이 60 FPS에서 떨어지는지 확인합니다.
11.2 Core Animation Instrument (상세 분석)
특정 애니메이션이나 뷰 렌더링 시 왜 FPS 드롭이 발생하는지 상세하게 분석하려면 Instruments 도구를 사용해야 합니다.
- 경로: Xcode → Product → Profile (단축키: Cmd + I)
- 도구 선택: Instruments가 실행되면, Core Animation 템플릿을 선택하고 앱을 프로파일링합니다.
- 상세 확인: Core Animation 트레이스에서는 다음과 같은 상세 지표를 제공합니다.
- Frame Rate (FPS): 시간 경과에 따른 정확한 프레임 속도 변화.
- Color Blended Layers: 오버 블렌딩(Over-blending)으로 인해 렌더링 성능을 저해하는 요소를 시각적으로 표시해줍니다. (빨간색으로 표시됨)
- Offscreen Rendered: 화면 밖에서 렌더링되는 요소가 있는지 확인하여 GPU 부하를 줄일 수 있습니다.
요약: 단순한 실시간 FPS 확인은 Debug Navigator를 사용하고, 프레임 드롭의 원인을 분석할 때는 Instruments의 Core Animation 도구를 사용하시면 됩니다.
12. 실전 예제 — Bottom Sheet 애니메이션
초기 상태
toView.transform = CGAffineTransform(translationX: 0, y: container.bounds.height)
등장
UIView.animate(withDuration: 0.32,
delay: 0,
usingSpringWithDamping: 0.92,
initialSpringVelocity: 0.5,
animations: {
toView.transform = .identity
})
스프링 애니메이션을 사용하면 자연스러움 + 사용자 친화적.
13. 실전 예제 — SwiftUI 카드 전환(card flip)
.rotation3DEffect(.degrees(isFlipped ? 180 : 0),
axis: (x: 0, y: 1, z: 0))
.animation(.easeInOut, value: isFlipped)
14. 체크리스트
- 전환과 시스템 애니메이션을 맞출 때 transitionCoordinator 사용
- modalPresentationStyle = .custom + Transition Delegate 이해
- Heavy view hierarchy 최소화
- GPU-friendly 속성만 애니메이션 처리
- SwiftUI → UIKit 애니메이션 충돌 방지
- Keyboard 전환과 레이아웃 점프 방지
- Spring animation으로 자연스러운 움직임 구현
15. 결론
Transition/Animation 최적화는 단순한 UI 개선이 아닌
전체 사용자 경험의 완성도를 결정하는 핵심 요소입니다.
UIKit과 SwiftUI 각각의 애니메이션 시스템을 정확히 이해하고,
GPU-friendly 방식으로 최적화하면
빠르고 부드러운 자연스러운 앱 전환을 구현할 수 있습니다.
'Dev Study > iOS' 카테고리의 다른 글
| iOS 색상 토큰(Role-based Color Tokens) 역할 설명 가이드 (0) | 2025.12.04 |
|---|---|
| TCA(The Composable Architecture) 사용 가이드(v1.23.1) (1) | 2025.11.18 |
| 테스트 작성 (Unit Test + Snapshot Test) (0) | 2025.11.14 |
| 앱 시작 속도 개선(App Launch Optimization) (0) | 2025.11.14 |
| AutoLayout — Hugging / Compression Resistance 이해하기 (0) | 2025.11.14 |
| 모듈화(Modularization)로 빌드 속도 최적화 (0) | 2025.11.14 |
| 네트워크 계층 — DTO / Domain 분리 (0) | 2025.11.14 |
| Swift Concurrency — 구조적 동시성 완전 이해 (0) | 2025.11.14 |

