View and Window Architecture

IOS 2016. 1. 12. 21:24

View Architecture Fundamentals

View들은 렌더링과 애니메이션을 수행하기 위해 Core Animation layer들(일반적으로 CALayer 클래스이다)과 협력한다. 대부분의 동작들은 UIView interface를 통해 수행하지만, 렌더링 애니메이션에 있어서 좀 더 세세한 동작들은 layer객체들을 사용한다.

UIView객체 위에 CALayer가 덮여져 있는 형태이다. 다만 default상태에서는 clear한 형태라 보이지 않을 뿐이다.

Core Animation layer objects들은 굉장히 중요하다. 왜냐하면, 실제 뷰를 그리는 코드실행을 최소로 만들어 주기 때문이다. 처음 뷰를 그리는 코드가 실행되어 화면에 그려진 후에, 그 결과를 Core Animation 객체들이 캐싱하고 그 결과를 재사용한다. 이러한 재사용은 애니메이션 효과를 구현할때 특히 중요하다. 애니메이션 동작시 기존에 캐싱되있던 content들을 재사용하여 조작하면서 해당 효과를 구현하면 일일이 다시 그리지 않아도 되기 때문이다.


The View Drawing Cycle

view가 처음 화면에 나타날때 시스템이 View에게 contents를 그릴것을 요청한다. 시스템은 해당 content의 스냅샷을 캡쳐해놓고 재사용한다. 뷰의 content가 바뀌지 않으면 drawing code는 절대 불리지 않는다. 스냅샷 이미지는 뷰와 관련된 대부분의 동작의 재사용된다. content가 바뀌면 시스템에게 뷰가 바뀌었다고 알린다. 그러면 view는 위 동작(draw -> capture -> reuse)을 다시 반복한다.

 뷰가 바뀌면 "setNeedsDisplay" 또는 "setNeedsDisplayInRect"을 호출해서 "다음 기회"에 뷰를 다시 그려야 한다고 알린다. 여기서 "다음 기회"가 뜻하는 바는 현재 run loop가 끝나는 시점이다. 즉 다른 뷰들의 컨텐트들이 바뀌는것을 기다렸다가 컨텐트들이 바뀐 모든 뷰의 drawing code를 한꺼번에 실행한다.

system view들은 자신의 content들을 그릴때 "drawRect:"를 사용한다. UIview를 subClassing해서 커스텀 뷰를 만들면 "drawRect:"을 이용해 컨텐츠를 그릴 수 있다. 뷰의 Layer 객체를 이용해서 그릴수 있지만, 보통 "drawRect:"을 사용한다.

Content Modes

view가 렌더링 되면 그 결과는 layer객체의 bitmap에 저장된다. 그리고 view의 geometry가 바뀌면 bitmap을 다시 만들어야 될지 아니면 새 bounds에 맞춰 content도 늘려야 할지 아니면 단순히 코너 한구석에 밀착시키기만 할지등을 정하는데 이는 view의 content mode에 따라 달라진다.

content mode는 다음과 같은 경우에 적용된다.
- 뷰의 frame or bounds의 width와 heigt가 변한 경우.
- 뷰의 transform 속성에 scailing factor가 포함된 transform이 할당된 경우.

위 속성은 캡쳐된 view의 content들은 재사용한다. 그러나 UIVIewContentModeredraw 속성을 택하면 뷰는 컨텐트들을 항상 redraw한다. 즉, resizing, scaling할때 "drawRect:"이 강제로 불린다. 일반적으로 이 속성은 사용하지 않는것이 좋다.

contentStretch 속성을 사용하여 strechable한 영역을 설정할 수 있다. (CGRect으로 설정)
이 속성은 뷰의 컨텐트가 scale 될때 유효하다. 즉, UIViewContentModeScaleAspectFill, UIViewContentModeScaleAspectFit, UIViewContentModeScaleToFill 속성일때 유효하다.


Built-In Animation Support 

UIView의 속성에서 animatable한 것들
- frame
- bounds
- center
- transform : rotate or scale할때 사용
- alpha
- backgroundColor
- contentStretch

UIKitClass를 이용해서 Animation을 만들수도 있지만, Core animation layer들을 사용해서 만들기도 한다. Core animation layer들을 사용해서 Animation을 만들면 좀 더 세밀한 타이밍 조절과 더 다양한 애니메이션 속성들을 사용할 수 있다. (애플 문서 검색: "About Core Animation")


View Geometry and Coordinate Systems

iOS는 자기 자신의 default coordinate systems을 쓴다.
예를 들어, UIKit의 Coordinate system은 좌측 최상단이 0, 0이다. 하지만 Core Graphics, OpenGL ES는 좌측 최하단이 0, 0이다. 

*subview들의 clipsToBounds 속성과 상관없이 subview들의 이벤트는 superview의 bounds안에 이벤트만 인식한다.


Coordinate System Transformations

- (애플 문서 검색 : Translating, Scaling, and Rotating ViewsDrawing and Printing Guide for iOS )


The Runtime Interaction Model for Views

- event가 발생시점 부터 실제 뷰의 반영되는 시점까지의 sequence logic이다.


1. 유저가 스크린을 터치한다.
2. 하드웨어가 UIKit Framework에 터치 이벤트를 report한다.(iOS가 UIApplication 인스턴스 객체로 전달)
3. UIKit Framework는 해당 터치 이벤트를 UIEvent객체로 패키징 한다. 그리고 UIEvent객체를 핸들링 할 view로 보낸다. UIApplication 인스턴스가 UIWindow 인스턴스로 전달하고 UIWindow가 적절한 인스턴스로 이벤트를 전달한다.(핸들링 할 적절한 뷰를 찾는과정 - 애플문서 검색:Event HandlingGuide)
4. event-handling code가 구현된다. 예를들어 setNeedsLayout(layout update가 필요할 때), setNeedsDisplay(redraw가 필요할 때) 등등을 호출한다.
5. 4번의 코드 실행으로 인해 뷰의 geometry가 변경되었을 때, UIKit은 다음 룰에 따라 subviews들을 변경한다.
  5-1) autoResizingMask를 설정했다면, 그에 맞게 subview들을 변경한다.
  5-2) view가 layoutSubviews를 구현했다면, 호출한다. customView에서 해당 메서드를 override할 수 있다. 이 메서드가 drawRect메서드 이전에 불리기 때문에(모든 drawing code는 run roop가 끝나기 직전에 한꺼번에 불린다) draw작업이 필요없는 것들을 invalidate시켜 놓으면 불필요한 drawing작업을 줄일 수 있다. 
6. UIKit이 view에게 redraw가 필요한 부분은 redraw하라고 시킨다. view에 drawRect가 구현되어 있다면 UIKit은 drawRect을 호출한다.(drawRect 메서드 안에서는 view의 draw 작업만 최대한 빨리 실행하고 종료 되어야 한다. 이 메서드에 비즈니스 로직같은 draw작업 이외의 코드를 넣지 마라.)
standard system views들은 drawRect메서드를 구현해 놓지 않는다. 하지만, 그들의 draw로직은 이 타이밍에 실행된다.
7. 변경된 부분들이 반영되고 graphic hardware에 전달된다.
8. graphic hardware는 렌더링 된 content를 스크린에 display한다.

즉, 쉽게 요약하자면, event-handling method들이 불리고 이 메서드에서 뷰의 geometry가 변경되었으면 layoutsubviews가 불리고 그다음 drawRect이 구현되어 있다면 마지막으로 불린다.


Tips for Using Views Effectively

1. Views Do Not Always Have a Corresponding ViewController 
- 하나의 view가 굳이 대응되는 viewController를 가질 필요없다. 기본적으로 viewController는 여러개의 view hierarchy를 관리하는 객체이기 때문이다.
- viewController를 사용해야 한다면 viewController 사용룰에 맞게 사용해야 한다.(애플 문서, 
View Controller Programming Guide for iOS.)
2. Minimize Custom Drawing
- view drawing 작업은 performance를 상당히 떨어뜨리는 작업이므로 최소화 해야 한다.
3. Take Advantage of Content Modes
- content mode는 view가 redrawing하는 시간을 최소화 한다. 그러나 UIViewContentModeRedraw 사용을 피하라. setNeedsDisplay, setNeedsDisplayInRect를 사용해서 redraw하는걸로 구현하고자 하는 기능을 완성할 수 있다면, 차라리 이들을 사용하라.
4. Declare Views as Opaque Whenever Possible
- 가능하면 opaque속성을 YES로 설정하라. 이는 자신의 뒤에 위치한 뷰들에 대한 렌더링 작업을 할 필요가 없음을 UIKit에게 알리기 때문에, performance를 증가시킬 수 있다.
5. Adjust Your View's Drawing Behavior When Scrolling
- 스크롤할때 많은 뷰들이 나타난다. 이때 뷰들의 Drawing mode를 적절하게 조절하지 않으면 스크롤이 느려 질수가 있다. 즉, 스크롤이 느려지면 뷰들의 렌더링 qualtity를 낮추고 content mode를 조절해보는 것도 하나의 방법이다. 스크롤이 끝나면 원래의 quality와  content mode로 되돌려 놓으면 된다.
6. Do Not Customize Controls by Embedding Subviews
- UIControl을 상속받는 standard system control객체들에 add subview 하지않는 것이 좋다. 이미 standard system control 객체들은 충분히 커스터 마이징 할 수 있는 인터페이스를 가지고 있기 때문에 그것들을 활용해라. 예를들어, UIButton에 view나 label들을  add subview 하면 UIButton이 의도와는 다르게 작동될 수 있다. 지금은 제대로 동작하더라도, 추후에 애플에서 UIButton의 구현이 변경되었을때 오동작을 일으킬 수 있는 여지가 충분하다.



'IOS' 카테고리의 다른 글

애플의 개발자 가이드 읽는 순서  (0) 2016.12.18
Posted by 홍성곤
,