UIKit 및 UIView를 사용하여 뷰에서 iOS 애니메이션 수행하기
게시 됨: 2022-03-10저는 10년 넘게 iOS 개발자로 일했으며 iOS에서 애니메이션을 수행하는 가능한 모든 방법을 통합하는 기사를 거의 본 적이 없습니다. 이 기사는 iOS 애니메이션을 수행하는 다양한 방법을 철저하게 다루기 위한 목적으로 iOS 애니메이션에 대한 입문서를 작성하는 것을 목표로 합니다.
주제의 광대함을 감안할 때 우리는 상당히 높은 수준에서 각 부분을 간결하게 다룰 것입니다. 목표는 iOS 앱에 애니메이션을 추가할 수 있는 일련의 선택 사항을 독자에게 교육하는 것입니다.
iOS와 관련된 주제를 시작하기 전에 애니메이션 속도에 대해 간단히 살펴보겠습니다.
60FPS에서 애니메이션
일반적으로 비디오에서 각 프레임은 이미지로 표현되며 프레임 속도는 시퀀스에서 뒤집힌 이미지의 수를 결정합니다. 이를 '초당 프레임 수' 또는 FPS라고 합니다.
FPS는 1초 안에 뒤집힌 정지영상의 수를 결정하는데, 말 그대로 영상/프레임 수가 많을수록 동영상에 더 많은 세부정보/정보가 표시됩니다. 이것은 애니메이션에서도 마찬가지입니다.
FPS는 일반적으로 애니메이션의 품질을 결정하는 데 사용됩니다. 좋은 애니메이션은 60fps 이상에서 실행되어야 한다는 일반적인 의견이 있습니다.
30FPS와 60FPS의 차이를 보고 싶습니까? 이것을 확인하십시오!
차이점을 눈치채셨나요? 인간의 눈은 낮은 fps에서 지터를 확실히 느낄 수 있습니다. 따라서 생성하는 모든 애니메이션이 60FPS 이상에서 실행된다는 기본 규칙을 준수하는지 항상 확인하는 것이 좋습니다. 그래서 더 현실적이고 생생하게 느껴집니다.
FPS를 살펴보았으므로 이제 애니메이션을 수행하는 방법을 제공하는 다양한 핵심 iOS 프레임워크에 대해 알아보겠습니다.
핵심 프레임워크
이 섹션에서는 보기 애니메이션을 만드는 데 사용할 수 있는 iOS SDK의 프레임워크에 대해 설명합니다. 관련 예제와 함께 기능 세트를 설명하면서 각각을 빠르게 살펴보겠습니다.
UIKit/UIView 애니메이션
UIView는 iOS 앱의 콘텐츠를 표시하는 모든 보기의 기본 클래스입니다.
UIView를 제공하는 프레임워크인 UIKit은 개발자가 더 적은 작업으로 더 많은 것을 달성할 수 있도록 하는 몇 가지 기본 애니메이션 기능을 이미 제공합니다.
API인 UIView.animate
는 블록 기반 구문의 속성 값을 제공하여 보기의 속성을 쉽게 애니메이션할 수 있으므로 보기를 애니메이션으로 만드는 가장 쉬운 방법입니다.
UIKit 애니메이션에서는 UIVIew의 애니메이션 가능한 속성만 수정하는 것이 좋습니다. 그렇지 않으면 애니메이션으로 인해 보기가 예기치 않은 상태가 될 수 있는 영향이 있습니다.
animation(withDuration: animations: 완료)
이 메서드는 애니메이션 지속 시간, 애니메이션을 적용해야 하는 뷰의 애니메이션 가능한 속성 변경 집합을 사용합니다. 완료 블록은 보기가 애니메이션 수행과 함께 완료되면 콜백을 제공합니다.
이 단일 API로 뷰에서 이동, 크기 조정, 회전, 페이딩 등과 같은 거의 모든 종류의 애니메이션을 수행할 수 있습니다.
이제 버튼 크기 변경에 애니메이션 효과를 주거나 특정 보기에서 화면을 확대하려는 경우를 고려하십시오. 다음은 UIView.animate
API를 사용하여 수행하는 방법입니다.
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 }
여기에서 하는 일이 다음과 같습니다.
- 블록 내부에 설명된 애니메이션이 실행되어야 하는 시간을 나타내는 지속 시간 값이 전달된
UIView.animate
메서드를 호출합니다. - 애니메이션의 최종 상태를 나타내는 버튼의 새 프레임을 설정합니다.
- 우리는 그것이 화면 중앙에 남도록 슈퍼뷰의 중앙과 함께 버튼
center
을 설정합니다.
위의 애니메이션 코드 블록은 현재 프레임에서 변경되는 버튼 프레임의 애니메이션을 트리거해야 합니다.
Width = 0, Height = 0
마지막 프레임으로:
Width = Height = newButtonWidth
애니메이션은 다음과 같습니다.
애니메이트WithDuration
이 메서드는 보기 애니메이션에 몇 가지 물리적 동작을 추가하여 이전 API에서 수행할 수 있는 모든 작업을 수행할 수 있는 animate 메서드의 확장과 같습니다.
예를 들어 위에서 수행한 애니메이션에서 스프링 댐핑 효과를 얻으려면 다음과 같은 코드가 표시됩니다.
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)
다음은 우리가 사용하는 매개변수 세트입니다.
-
duration
코드 블록이 실행되어야 하는 기간을 결정하는 애니메이션의 지속 시간을 나타냅니다. -
delay
애니메이션이 시작되기 전에 원하는 초기 지연을 나타냅니다. -
SpringWithDamping
뷰가 동작하기를 원하는 탄력 있는 효과의 값을 나타냅니다. 값은 0에서 1 사이여야 합니다. 값이 낮을수록 스프링 진동이 높아집니다. -
velocity
애니메이션이 시작되어야 하는 속도를 나타냅니다. -
options
보기 애니메이션에 적용하려는 애니메이션 곡선의 유형입니다. - 마지막으로 애니메이션이 필요한 버튼의 프레임을 설정하는 코드 블록입니다. 이전 애니메이션과 동일합니다.
위의 애니메이션 구성에서 애니메이션은 다음과 같이 표시됩니다.
UIViewPropertyAnimator
애니메이션을 좀 더 제어하기 위해 UIViewPropertyAnimator
는 애니메이션을 일시 중지하고 다시 시작하는 방법을 제공하는 곳에서 편리합니다. 사용자 지정 타이밍을 사용하고 애니메이션을 대화식으로 중단할 수 있도록 할 수 있습니다. 이는 사용자 동작과도 상호 작용할 수 있는 애니메이션을 수행할 때 매우 유용합니다.
고전적인 '밀어서 잠금 해제' 제스처와 플레이어 보기 닫기/확장 애니메이션(음악 앱에서)은 대화형 및 중단 가능한 애니메이션의 예입니다. 손가락으로 보기 이동을 시작한 다음 손을 떼면 보기가 원래 위치로 돌아갑니다. 또는 애니메이션 중에 보기를 잡고 손가락으로 계속 끌 수 있습니다.
다음은 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
우리가 하는 일은 다음과 같습니다.
- 지속 시간과 애니메이션 곡선을 전달하여
UIViewProperty
API를 호출합니다. - 위의 두 UIView.animate API와 달리 애니메이션은 직접 지정하지 않는 한 시작되지 않습니다. 즉, 전체 애니메이션 프로세스/흐름을 완전히 제어할 수 있습니다.
이제 애니메이션을 더 많이 제어하고 싶다고 가정해 보겠습니다. 예를 들어 애니메이션의 모든 프레임을 디자인하고 제어하려고 합니다. 이를 위한 또 다른 API인 animateKeyframes
가 있습니다. 그러나 자세히 알아보기 전에 애니메이션에서 프레임이 무엇인지 빠르게 살펴보겠습니다.
frame
이란 무엇입니까?
시작 상태에서 최종 상태까지 보기의 프레임 변경/전환 모음을 애니메이션으로 정의하고 animation
중 보기의 각 위치를 frame
이라고 합니다.
애니메이션 키프레임
이 API는 타이밍 및 전환이 서로 다른 여러 애니메이션을 정의할 수 있는 방식으로 애니메이션을 디자인하는 방법을 제공합니다. 이를 게시하면 API는 모든 애니메이션을 하나의 원활한 경험으로 통합합니다.
임의의 방식으로 화면에서 버튼을 이동하고 싶다고 가정해 보겠습니다. 키프레임 애니메이션 API를 사용하여 그렇게 하는 방법을 살펴보겠습니다.
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 } })
분석 내용은 다음과 같습니다.
-
duration
애니메이션 기간을 전달하여 API를 호출합니다. -
delay
애니메이션의 초기 지연 시간입니다. -
options
보기 애니메이션에 적용하려는 애니메이션 곡선의 유형입니다. -
animations
개발자/사용자가 디자인한 모든 키프레임 애니메이션을 가져오는 블록입니다. -
addKeyFrame
API를 호출하여 각각의 모든 애니메이션을 디자인합니다. 우리의 경우 버튼의 각 움직임을 정의했습니다. 우리는 필요한 만큼의 애니메이션을 블록에 추가할 수 있습니다. -
relativeStartTime
애니메이션 블록 컬렉션에서 애니메이션의 시작 시간을 정의합니다. -
relativeDuration
이 특정 애니메이션의 전체 지속 시간을 정의합니다. -
center
우리의 경우 버튼의 중앙 속성을 변경하여 화면에서 버튼을 이동합니다.
최종 애니메이션은 다음과 같습니다.
코어애니메이션
모든 UIKit 기반 애니메이션은 내부적으로 핵심 애니메이션으로 변환됩니다. 따라서 Core Animation 프레임워크는 모든 UIKit 애니메이션의 백본 레이어 또는 백본 역할을 합니다. 따라서 모든 UIKit 애니메이션 API는 쉽게 소비하거나 편리한 방식으로 핵심 애니메이션 API의 캡슐화된 레이어에 불과합니다.
UIKit 애니메이션 API는 뷰의 애니메이션 가능한 속성에 주로 사용되기 때문에 뷰에 대해 수행된 애니메이션에 대한 많은 제어를 제공하지 않습니다. 따라서 애니메이션의 모든 프레임을 제어하려는 경우 기본 핵심 애니메이션 API를 직접 사용하는 것이 좋습니다. 또는 UIView 애니메이션과 핵심 애니메이션을 함께 사용할 수도 있습니다.
UIView + 코어 애니메이션
UIView 및 Core Animation API를 사용하여 타이밍 곡선을 지정하는 것과 함께 동일한 버튼 변경 애니메이션을 재생성하는 방법을 살펴보겠습니다.
애니메이션 곡선을 지정하고 제어할 수 있는 CATransaction
의 타이밍 기능을 사용할 수 있습니다.
CATransaction
의 타이밍 기능과 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
분석 내용은 다음과 같습니다.
-
begin
애니메이션 코드 블록의 시작을 나타냅니다. -
duration
전체 애니메이션 지속 시간. -
curve
애니메이션에 적용해야 하는 타이밍 곡선을 나타냅니다. -
UIView.animate
버튼의 프레임을 변경하는 첫 번째 애니메이션입니다. -
CABasicAnimation
버튼의cornerRadius
를 키패스로 참조하여CABasicAnimation
개체를 생성합니다. 마찬가지로 키프레임 애니메이션에 대해 세분화된 수준으로 제어하려는 경우CAKeyframeAnimation
클래스를 사용할 수 있습니다. -
fromValue
애니메이션의 시작 값, 즉 애니메이션이 시작되어야 하는 버튼의 초기cornerRadius
값을 나타냅니다. -
toValue
애니메이션의 최종 값, 즉 애니메이션이 끝나야 하는 버튼의 최종cornerRadius
값을 나타냅니다. -
cornerRadius
버튼의cornerRadius
속성을 애니메이션의 최종 값으로 설정해야 합니다. 그렇지 않으면 버튼의 cornerRadius 값은 애니메이션이 완료된 후 초기 값으로 자동 복귀됩니다. -
addAnimation
애니메이션을 수행해야 하는 Keypath를 표시하여 전체 애니메이션 프로세스의 구성을 포함하는 애니메이션 개체를 레이어에 연결합니다. -
commit
애니메이션 코드 블록의 끝을 나타내고 애니메이션을 시작합니다.
최종 애니메이션은 다음과 같습니다.
이 블로그는 모든 단계를 안내하는 지침과 함께 대부분의 Core Animation 프레임워크 API를 깔끔하게 안내하므로 고급 애니메이션을 만드는 데 도움이 되는 훌륭한 글입니다.
UIKitDynamics
UIKit Dynamics는 충돌, 중력, 푸시, 스냅 등과 같은 물리 동작을 UIKit 컨트롤에 추가할 수 있게 해주는 UIKit용 물리 엔진입니다.
UIKitDynamicAnimator
이것은 주어진 UI 컨트롤에 의해 트리거되는 모든 애니메이션을 규제하는 UIKit Dynamics 프레임워크의 관리자 클래스입니다.
UIKitDynamicBehavior
애니메이터에 물리 동작을 추가하면 연결된 뷰에서 수행할 수 있습니다.
UIKitDynamics에 대한 다양한 종류의 동작은 다음과 같습니다.
-
UIAttachmentBehavior
-
UICollisionBehavior
-
UIFieldBehavior
-
UIGravityBehavior
-
UIPushBehavior
-
UISnapBehavior
UIKitDynamics의 아키텍처는 다음과 같습니다. 항목 1~5는 단일 보기로 대체될 수 있습니다.
버튼에 물리적 동작을 적용해 보겠습니다. 버튼에 중력을 적용하여 실제 물체를 다루는 느낌을 주는 방법을 살펴보겠습니다.
var dynamicAnimator : UIDynamicAnimator! var gravityBehavior : UIGravityBehavior! dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1 gravityBehavior = UIGravityBehavior(items: [button]) //2 dynamicAnimator.addBehavior(gravityBehavior) //3
분석 내용은 다음과 같습니다.
-
UIKitDynamicAnimator
애니메이션 수행을 위한 조정자 역할을 하는UIKitDynamicAnimator
개체를 만들었습니다. 또한 버튼의 슈퍼뷰를 참조 뷰로 전달했습니다. -
UIGravityBehavior
UIGravityBehavior
개체를 만들고 이 동작이 주입되는 배열 요소에 버튼을 전달합니다. -
addBehavior
애니메이터에 중력 개체를 추가했습니다.
그러면 아래와 같이 애니메이션이 생성되어야 합니다.
애니메이터에게 화면 하단을 바닥으로 간주하도록 알려야 합니다. 여기에서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
UICollisionBehavior
개체를 만들고 버튼을 따라 전달하여 동작이 요소에 추가되도록 했습니다. -
translatesReferenceBoundsIntoBoundary
이 속성을 활성화하면 애니메이터가 참조 뷰 경계를 끝으로 사용하도록 지시합니다. 이 경계는 이 경우 화면 하단입니다. -
addBehavior
여기에서 애니메이터에 충돌 동작을 추가했습니다.
이제 버튼이 바닥에 닿아 아래와 같이 정지해 있어야 합니다.
꽤 깔끔하지 않나요?
이제 오브젝트가 더 사실적으로 느껴지도록 바운스 효과를 추가해 보겠습니다. 이를 위해UIDynamicItemBehavior
클래스를 사용합니다.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
UIDynamicItemBehavior
개체를 만들고 버튼을 따라 전달하여 요소에 동작이 추가되도록 했습니다. -
elasticity
값은 0-1 사이여야 하며 탄성을 나타냅니다. 즉, 물체가 부딪힐 때 지면에서 튕겨야 하는 횟수입니다. 여기서 마법이 발생합니다. 이 속성을 조정하면 공, 병, 단단한 물체 등과 같은 다양한 유형의 물체를 구별할 수 있습니다. -
addBehavior
여기에서 애니메이터에 충돌 동작을 추가했습니다.
이제 아래 표시된 것처럼 버튼이 땅에 닿으면 바운스되어야 합니다.
이 리포지토리는 매우 유용하며 모든 UIKitDynamics 동작을 실제로 보여줍니다. 또한 각 동작을 가지고 놀 수 있는 소스 코드를 제공합니다. 제 생각에는 보기에서 iOS 애니메이션을 수행하는 방법의 광범위한 목록 역할을 해야 합니다!
다음 섹션에서는 애니메이션 성능을 측정하는 데 도움이 되는 도구에 대해 간략히 살펴보겠습니다. 또한 Xcode 빌드를 최적화하는 방법을 살펴보는 것이 좋습니다. 개발 시간을 엄청나게 절약할 수 있기 때문입니다.
성능 조정
이 섹션에서는 iOS 애니메이션의 성능을 측정하고 조정하는 방법을 살펴보겠습니다. iOS 개발자는 전체 앱의 성능을 측정하기 위해 메모리 누수 및 할당과 같은 Xcode 도구를 이미 사용했을 수 있습니다. 마찬가지로 애니메이션의 성능을 측정하는 데 사용할 수 있는 도구가 있습니다.
Core Animation
도구
Core Animation
장비를 사용해 보면 앱 화면이 전달하는 FPS를 볼 수 있어야 합니다. 이것은 iOS 앱에서 렌더링된 모든 애니메이션의 성능/속도를 측정하는 좋은 방법입니다.
그림
그림자와 같은 효과가있는 이미지와 같은 무거운 콘텐츠를 표시하는 앱에서 FPS가 크게 낮아졌습니다. 이러한 경우 UIImageView
의 이미지 속성에 직접 Image를 할당하는 대신 Core Graphics API를 사용하여 컨텍스트에서 이미지를 별도로 그려보십시오. 이는 메인 스레드가 아닌 별도의 스레드에서 수행할 때 이미지 압축 해제 로직을 비동기적으로 수행하여 이미지 표시 시간을 과도하게 단축시킵니다.
래스터화
래스터화는 이러한 뷰가 렌더링될 때마다 다시 그려지지 않도록 복잡한 레이어 정보를 캐시하는 데 사용되는 프로세스입니다. 뷰를 다시 그리는 것은 FPS 감소의 주요 원인이므로 여러 번 재사용할 뷰에는 래스터화를 적용하는 것이 가장 좋습니다.
마무리
결론적으로 iOS 애니메이션에 유용한 리소스 목록도 요약했습니다. iOS 애니메이션 작업 시 이 기능이 매우 편리할 수 있습니다. 또한 이 디자인 도구 세트는 애니메이션을 탐구하기 전에 (디자인) 단계로 도움이 될 수도 있습니다.
iOS 애니메이션을 둘러싼 가능한 많은 주제를 다룰 수 있기를 바랍니다. 이 기사에서 내가 놓친 것이 있다면 아래의 댓글 섹션에 알려주십시오. 기꺼이 추가하겠습니다!