CLOSE SEARCH

UIKit Dynamics #2 – UIGravityBehavior. Galileo Galilei is coming to iOS

iOS 6의 뷰는 무중력 상태에서 지정된 위치를 둥둥 떠다니기만 했다면, iOS 7의 뷰는 갈리레오 갈리레이가 피사의 사탑에서 떨어뜨린 물체처럼 중력이 작용하는 방향으로 떨어집니다. 물론 실제로 떨어지는 것이 아니라 UIKit Dynamics가 제공하는 UIGravityBehavior 클래스를 통해 뷰에 중력백터를 설정하여 뷰가 질량을 가진 물체처럼 동작(에니메이션)하도록 하는 것입니다.

물리 시간에 배웠던 지구 중력에 대한 표준 중력가속도처럼, iOS도 중력가속도라는 개념을 가지고 있이며 UIKit Gravity라는 새로운 용어를 통해 이것을 표현하고 있습니다.

[one_half]

Earth Gravity

9.80665 m/s2

[/one_half]

[one_half_last]

UIKit Gravity

1000 pt/s2

[/one_half_last]

Gravity Vector

UIKit 중력벡터의 기본값은 (0.0, 1.0)으로 뷰의 하단으로 향하는 중력가속도가 1000 pt/s2라는 것을 의미합니다. UIGravityBehavior 클래스는 수평, 수직 방향으로 작용되는 중력백터를 설정할 수 있는 xComponent, yComponent 속성을 가지고 있습니다. 이 속성이 가질수 있는 값은 0.0~1.0(또는 -1.0)사이로 각각 중력백터가 적용되지 않은 상태와 표준 중력백터가 적용된 상태를 나타냅니다. 값이 음수인 경우에는 기본방향과 반대로 중력백터가 작용된다는 것을 나타냅니다. 즉, (0.0, -1.0)인 경우 Gravity Behavior에 추가된 Dynamic Item들은 뷰의 위쪽으로 이동하게 됩니다.

 

Sample : Drop Box

UIKit Dynamics를 이용해서 작은 사각형 뷰에 중력 애니메이션을 적용하는 예제를 만들어보겠습니다.

sample_drop_box이 예제의 UI는 중력 애니메이션을 적용한 뷰와 동작을 제어하는 버튼, 뷰의 y좌표 값을 출력하는 레이블, 중력백터의 크기를 조절하는 슬라이더로 구성되어 있습니다. 각 UI 요소들은 아웃렛을 통해 뷰 컨트롤러와 연결되어 있습니다.

Code1은 이번 샘플코드의 인터페이스를 보여줍니다. initialFram과 dropped 속성은 뷰의 위치를 초기화하는데 사용됩니다. box, btn, yPosLabel은 앞에서 설명한 UI 요소들을 연결하는 아웃렛입니다. 그리고 가장 중요한 UIDynamicAnimator와 UIGravityBehavior 속성을 추가해 주고, 애니메이션의 시작과 완료에 따라 적절한 동작을 추가하기 위해 UIDynamicAnimatorDelegate 프로토콜을 구현하고 있습니다.

Code2에서는 기본적인 초기화 작업을 수행합니다. 8번 라인에서는 UIDynamicAnimator 객체를 생성하고 현재 뷰 컨트롤러의 뷰를 레퍼런스 뷰로 설정합니다. 이 에니메이터를 통해서 에니메이션 되는 모든 아이템들은 레퍼런스 뷰의 좌표체계를 사용하게 됩니다. 9번 라인에서는 에니메이터의 델리게이터를 현재 뷰 컨트롤러로 설정하고 있습니다. 10번 라인에서는 화면에 있는 파란색 박스(box)를 매개변수로 UIGravityBehavior 객체를 생성합니다. 파런색 뷰에는 기본 중력백터인 (0.0, 1.0)이 적용되고 애니메이션이 실행되면 1000 pt/s2의 가속도로 y방향으로 이동하게 됩니다. 18번 라인에서는 action 코드블럭을 설정합니다. 이전 글에서도 설명했듯이 모든 UIDynamicBehavior은 action이라는 속성을 통해 애니메이션 사이클마다 반복적으로 실행할 수 있는 코드블록을 설정할 수 있습니다. 그만큼 성능에 영향을 많이 주는 부분이기 때문에 이 블록내에서 실행되는 코드는 되도록 단순하고 빠르게 완료되어야 합니다. 참고로 블록에서 self.을 통해 뷰 컨트롤러의 속성에 접근할 때 발생할 수 있는 Retain Cycle의 문제를 피하기 위해 14~17번 라인과 같이 임시 변수들을 사용하고 있습니다.

피사의 사탑에서 떨어뜨린 물체는 바닥에 닿을때까지 계속 중력이 작용하는 방향으로 이동합니다. UIGravityBehavior은 현실세계와 달리 바닥이라는 개념을 가지고 있지 않기 때문에 자신이 애니메이터에서 제거되거나 앱이 종료되기 전까지 계속해서 y방향으로 이동하게 됩니다. 20~22번 라인의 코드는 뷰가 화면의 하단에 위치할 때 바닥에 닿은 것으로 간주하고 애니메이션을 종료하는 역할을 합니다. 21번 라인에서와 같이 애니메이터에서 UIDynamicBehavior 객체를 제거하면 해당 객체의 애니메이션도 즉시 제거됩니다. 21번 라인을 주석처리하고 예제를 실행하면 계속해서 y 좌표값이 증가하는 것을 확인할 수 있습니다.

Code2에서 기본적인 설정을 모두 해주었지만, 아직 애니메이션은 실행되지 않습니다.

Code3은 Drop 버튼과 연결된 메소드의 구현을 보여줍니다. 뷰의 애니메이션이 완료된 경우에는 UIView 애니메이션을 통해 최초 위치로 재설정하고 버튼의 타이들을 Drop로 변경합니다(2~10번 라인). 처음 실행되었거나 뷰의 위치가 재설정된 경우에는 애니메이터에 UIGravityBehavior 객체를 추가하여 애니메이션을 실행합니다(11~13번 라인).

Code4는 UIDynamicAnimatorDelegate를 구현합니다. 이 델리게이트에 있는 두개의 메소드는 모두 필수 메소드이므로 반드시 구현해주어야 합니다. dynamicAnimatorWillResume: 메소드는 애니메이터에 새로운 Dynamic Behavior 객체가 추가되어 애니메이션이 (재)시작될 때 호출되고, dynamicAnimatorDidPause: 메소드는 모든 애니메이션이 완료되었거나, Dynamic Behavior 객체가 제거되어 더이상 실행할 애니메이션이 없을 때 호출됩니다.

이 예제에서는 Code2의 21번 라인에서 UIGravityBehavior 객체가 제거될 때 dynamicAnimatorDidPause: 메소드가 호출됩니다. 이 메소드에서는 버튼의 타이들을 Reset으로 업데이트하고 dropped 속성의 값을 YES로 설정하여 애니메이션이 완료에 대한 피드백을 제공합니다. Reset 버튼을 눌러 뷰의 위치를 초기화한 후 다시 Drop 버튼을 누르면 Code3의 12번 라인에서 addBehavior: 메소드를 호출하여 UIGravityBehavior 객체를 다시 추가합니다. 이 때에는 애니메이션이 다시 시작되므로 dynamicAnimationWillResume: 메소드가 호출됩니다. 여기에서는 버튼의 타이들을 “Dropping..”으로 업데이트하여 애니메이션이 진행중이라는 간단한 피드백만을 제공합니다.

마지막으로 슬라이더를 통해 yComponent 값을 업데이트 할 수 있도록 구현합니다. 중력백터의 값은 애니메이션이 실행중인 동안에도 동적으로 업데이트 될 수 있기 때문에, 슬라이더를 움직여보면 중력 가속도가 실시간으로 변하는 것을 확인할 수 있습니다.




만약 Code2의 21번 라인이 없다면? 파란박스는 화면을 벗어나 계속 떨어지고, 불필요한 CPU 자원이 낭비됩니다.:( 화면에 보이지 않기 때문에 중력 애니메이션이 완료되었다고 착각할 수도 있으므로 주의해야 합니다.


Filed under: Apple WWDC 2013, iOS

  1. Jacky
    안녕하세요, 제가 꼭 필요한 정보였는데, 공유해 주셔서 감사합니다. 한가지 궁금한 점이 있는데, UIKit Dynamics은 Core Animation은 상관이 없는 모듈인가요? 상관관계가 있는지요? Dynamics와 Core animation을 View등에 동시에 설정하는게 가능한가요? 즉, 일반 animation과 physics가 동시에 사용가능한가요? 그리고, touch에 의해 해당 view의 property가 변경되면 즉시 반영되나요? 여유되실때 답변 부탁드립니다.
    • Kei
      UIKit Dynamics은 Core Animation은 상관이 없는 모듈인가요? => 서로 사용 목적이 다른 모듈(또는 기술)이라고 볼 수 있을것 같습니다. 즉, 일반 animation과 physics가 동시에 사용가능한가요? => Core Animation과 View Animation의 경우 지정된 속성값을 보간법으로 계산하는 방식으로 에니메이션을 생성하지만, UIKit Dynamics는 뷰 또는 앵커와의 상호작용에 따라 에니메이션에 필요한 값들이 자동적으로 계산됩니다. 두가지 방식을 동시에 사용하게 되는 경우, 어떤 값에 우선순위를 두어야 하는지에 대한 기준도 없고, UIKit Dynamics가 적용된 항목의 경우 최초 애니메이션이 시작되는 시점을 제외하고는 외부에서 코드로 위치나 크기를 변경할 수 없기 때문에 동시에 사용할 수는 없을 것으로 생각됩니다. touch에 의해 해당 view의 property가 변경되면 즉시 반영되나요? => 터치가 시작될 때, UIDynamicBehavior를 통해 뷰와 뷰(또는 앵커) 사이의 상호작용을 올바르게 설정해주면 터치에 반응하는 뷰(또는 앵커)의 위치나 크기가 변경될 때, UIKit Dynamics가 적용된 아이템도 즉각적으로 반응합니다.