ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [CoreAnimation] Ch8. Getting started with Layer Animations. (레이어 애니메이션)
    Ray Wenderlich/Core Animation 2018. 7. 31. 22:20


    8장 Getting Started with Layer Animations


    Section 3. Layer Animations

    View vs Layer

    Layer는 View와 다르다. Layer는 View를 표현하는데 필요한 데이터를 갖는 모델 객체다. 


    Layer는 View와 어떻게 다른가?

    - 말 그대로 모델 객체! 데이터만 포함한다. 뷰의 로직에는 전혀 관여하지 않는다. 오토레이아웃 의존성이나 사용자 인터렉션에도 물론 관여하지 않는다.

    - View에 없는 visible trait가 존재한다. borderLine, borderColor, position, shadow 등

    - GPU는 레이어 정보를 캐싱하여, 화면에 그림을 그린다.



    Layer, View. 다시 한 번 비교

    Views

    - 복잡한 뷰 계층 구조를 갖는다. 오토레이아웃 등이 이에 포함

    - 사용자 인터렉션에 관여함

    - 로직이 포함되거나, drawing에 필요한 커스텀 코드가 추가되기도 한다. CPU가 메인스레드에서 이 로직과 코드를 작동시킨다.

    - 서브클래스들이 많아서 유연하다.

    Layers

    - 간단한 계층구조를 갖고, 레이아웃을 빠르게 계산하여 빠르게 그릴 수 있다.

    - No responder chain overhead

    - 커스텀로직을 갖지 않는다. GPU가 다이렉트로 그린다.

    - 서브클래스가 많지 않다. 그래서 유연하지 못하다.



    View animation, Layer animation 언제 무엇을 쓰는게 좋은가?

    적절히 섞어서 쓰셈 ㅎㅎ



    Ch. 8

    Animatable properties

    CALayer에 포함된 Animatable 프로퍼티는 대부분 UIView의 그것에 연결된다.

    아래는 CALayer의 Animatable 프로퍼티를 설명한다. 이 외에도 매우 많다. (CALayer의 서브클래스들이 꽤나 있음)


    Position and size

    - bounds

    - position: position.x, position.y 처럼 inner 프로퍼티를 바로 수정하는 것도 가능하다. (UIView에서 frame.origin.x -= 10 이런건 안됨)

    - transform: move, rotate, scale 등의 변화 가능


    Border

    - borderColor

    - borderWidth

    - cornerRadius: 모서리의 둥근 정도


    Shadow

    - shadowOffset: layer로 부터 그림자의 위치를 조정

    - shadowOpacity: 그림자의 투명도

    - shadowPath: 그림자의 모양을 변경할 수 있음. 3D효과 가능

    - shadowRadius: 그림자의 blur변경할 때 사용. 뷰가 화면의 표면으로부터 이동하는 효과를 나타내기에 좋음


    Contents

    - contents: raw데이터(TIFF, PNG)를 뿌려줄 때 사용

    - mask: 레이어에서 보여줄 영역을 마스킹할 수 있음. Ch.13에서 배우도록 합시다.

    - opacity: 레이어의 투명도 수정



    Your first layer animation

    UIView.animte를 CALayer의 애니메이션으로 대체해보자.

    let flyRight = CABasicAnimation(keyPath: "position.x") 

    flyRight.fromValue = -view.bounds.size.width/2 

    flyRight.toValue = view.bounds.size.width/2 

    flyRight.duration = 0.5 

    Core Animation의 애니메이션 객체는 모델 그 자체다. 위 코드에서 보면 숫자로 이루어진 데이터만 담을 뿐이다.

    애니메이션 객체를 통해 지금 애니메이션을 수행할 수도, 딜레이를 줄 수도, 수행하지 않을 수도 있다.


    1. String타입 keyPath를 통해 CALayer의 프로퍼티에 쉽게 접근한다.

    2. fromValue, toValue설정은 keyPath에 해당하는 프로퍼티의 변경 사항을 말한다.(예제에서는 왼쪽에서 오른쪽으로 이동하도록 셋팅하였음)

    3. duration은 UIView.animate를 사용할 때와 같다. 0.5초간 애니메이션을 진행시키겠단 뜻



    heading.layer.add(flyRight, forKey: nil)

    위 코드를 수행하면 애니메이션이 진행된다.

    1. add(_:forKey:)메소드는 flyRight객체를 복사하여 heading.layer이 애니메이션을 수행하도록 한다.

    2. forKey에는 해당 애니메이션을 식별할 수 있는 key를 전달한다. 이 애니메이션을 변경하거나 멈추고 싶다면 key값을 통해 식별해낼 수 있다.



    More elaborate layer animations

    ID입력필드 애니메이션 변경을 해보자.


    그 전에 두가지만 다시 짚고 넘어가보자.

    1. CABasicAnimation은 데이터 모델일 뿐이다. 즉, 특정 레이어에 종속되지 않는다.

    2. add(_:forKey:)는 애니메이션 객체를 복사한다.


    위 두 내용으로 미루어보아, flyRight를 재사용할 수 있을 것으로 기대된다.


    username.layer.add(flyRight, forKey: nil)

    이 코드 만으로 username의 애니메이션이 가능하다.


    그러데 heading,username 두 개가 같이 들어오니 구리다. 그래서 timeOffset을 추가해보겠다.
username.layer.add(flyRight, forKey: nil) 이전에 아래 코드를 추가해보자.


    flyRight.beginTime = CACurrentMediaTime() + 0.3 // CACurrentMediaTime()은 지금 시간을 구함

    beginTime은 애니메이션이 시작되는 절대시간을 의미한다. 0.3초 뒤에 실행하란 뜻이지 뭐.


    Using fillMode

    fillMode의 디폴트 값은 kCAFillModeRemoved다. 


    - kCAFillModeRemoved(default)

        - beginTime에 애니메이션을 시작한다. (beginTime이 따로 없으면 바로 시작함)

        - 애니메이션이 끝나면 프로퍼티의 변경사항을 원복한다.


    - kCAFillModeBackwards

        - 애니메이션을 시작하기 전부터 프로퍼티가 변경된 상태를 보여준다.


    - kCAFillModeForwards

        - 애니메이션이 끝난 뒤 변경된 프로퍼티의 상태를 유지한다. (애니메이션을 제거하기 전까지 유지함)


    - kCAFillModeBoth 

        - backwards, forwards를 합친 것

        - 애니메이션의 초기상태도 반영해주고, 애니메이션 끝난 뒤 변경사항도 유지



    애니메이션을 layer에 적용하기 전에 fillMode를 변경해보자.

    flyRight.fillMode = kCAFillModeBoth

    이 코드가 적용되기 전에는 username이 화면에 보였다가 사라진 뒤, 애니메이션이 진행되었다.

    하지만 위 코드를 적용하면 username이 잠깐 보이는 현상이 해결된다.


    지금까지는 모든 layer가 정확한 위치에서 애니메이션을 종료했다.

    하지만 항상 이렇지는 않다.

    아래 섹션에서 그런 상황을 살펴보고 고쳐보도록 하자.


    Debugging basic animations

    username과 password의 초기 위치를 화면밖으로 잡아보자.

    username.layer.position.x -= view.bounds.width 

    password.layer.position.x -= view.bounds.width


    이렇게 하고 애니메이션을 실행해보면 username과 password가 애니메이션이 끝남과 동시에 화면에서 사라진다.

    왜 그럴까?

    실제 UI요소와 화면에 보이는 애니메이션은 별개로 보아야 한다.

    무슨말이냐?? 다음 섹션에서 설명


    Animations vs. real content

    textField를 애니메이션할 때, textField 자체가 화면에서 움직이는 것이 아니다!!!

    대신, 캐싱된 presentation layer가 움직이는 것이다. 애니메이션이 끝나면 이 layer는 화면에서 사라지게 되고 원래 layer가 화면에 나타나게 된다.

    >> 그래서 애니메이션 도중에는 User Interaction이 안먹힌다. 왜냐면 우리가 보고 있는건 가짜니까!!!


    이런 상황을 위해 제공되는 프로퍼티가 isRemovedOnCompletion이다. (디폴트는 true)

    위에서 fillMode를 kCAFillModeBoth로 설정하면 애니메이션이 끝나도 원래 프로퍼티로 원복시키지 않는다고 했다.

    이를 제대로 수행하기 위해서는 isRemovedOnCompletion를 적절히 셋팅해주어야 한다. 두 프로퍼티의 콜라보가 필요한 것이다!


    fillMode를 수정한 코드 다음줄에 아래 코드를 넣어보자.

    flyRight.isRemovedOnCompletion = false // 디폴트는 true


    이제 애니메이션이 끝나도 layer가 끝난 자리에 멈춰 있다.

    하지만!! 여기에 멈춰 있는 layer는 뭐다?? presentation layer다. 즉 짭퉁이다. 그래서 텍스트필드를 터치해봤자 아무 반응도 없을 것이다.


    Updating the layer model

    flyRight.isRemovedOnCompletion = false

    이 코드는 삭제하겠다. 앞으로 이 코드를 사용하는 것은 왠만하면 피하길 바란다.

    presentation layer를 계속 남겨두는 것은 퍼포먼스에도 좋지 않다. 대신 원래 layer의 프로퍼티를 업데이트해주도록 하자.


    메모

    - 여태껏 presentation layer의 존재를 몰랐다. Core Animation이 그동안 어려웠던 이유 중 하나가 presentation layer가 아니었을까…

    - fromValue셋팅을 생략하면 현재 프로퍼티에서 알아서 수행



    댓글 1

Designed by Tistory.