오늘은 이전에 ios 스터디를 하며 발표를 했던 UIVIew에 대해 다뤄보려고 합니다. 내려가면서 보겠지만 다른 블로그의 여러 글들을 봐도 UIView의 frame 과 bounds에 따른 좌표의 변화가 아마 가장 이해하기 어려웠던 부분이라고 생각합니다. 완벽하지 않지만 제가 이해한 방향으로 정리를 해보려고 합니다.
UIView
공식문서에 따르면 UIView란 "화면의 직사각형 모양을 관리하는 객체" 라고 정의되어 있습니다.
UIView는 화면을 나타내고 User Interaction을 하는 주요한 방법중 하나이기 때문에 약간은 광범위하게 정의되어있다는 느낌을 받았습니다.
UIView는 역할에는 크게 3가지가 있습니다.
- Draw, Animation
- 뷰는 UIKit Core Graphics를 사용하여 직사각형 영역에 콘텐츠를 그립니다.
- Layout, SubView관리
- 뷰에는 0개 이상의 subview가 존재할 수 있습니다. 뷰는 subview의 크기와 위치를 조정할 수 있죠. 레이아웃을 사용해서 뷰 계층 구조의 변경에 따라 뷰의 크기, 위치를 조정하는 규칙을 만들 수 있습니다.
- Event handling
- 터치나 기타 이벤트에 응답할 수 있습니다. 제스처를 처리하기 위해 제스처 인식기를 사용할 수 있습니다.
여기서 Layout, SubView에 대해 중점적으로 다뤄보려고 합니다.
우리가 무의식적으로 스토리보드상에 View를 올려놓는 행위는 사실 subview를 추가해주는 행위입니다.
스토리 보드상의 가장 최상단의 superview(부모뷰)에 또다른 view를 subview(자식뷰)로 추가해준 것이죠. 그리고 이후 아무설정을 해주지 않는다면 오른쪽 상단에 노란 경고표시가 뜨게 됩니다. 이유는 크기와 위치를 표시해주지 않았기 때문이죠. 즉, 이부분이 Layout입니다.
Layout과 Subview는 새로운 뷰를 추가하고 원하는 UI를 만드는 것에 있어 가장 기본적이고 중요합니다. 특히 복잡한 뷰를 만들어야 하는 상황에서는 더욱더 헷갈리고 복잡하기떄문입니다. 그래서 오늘은 이 부분을 중점적으로 다뤄보려고 합니다.
Frame&Bounds
Frame은 superview좌표계에서 view의 위치와 크기를 나타냅니다.
이때, superview란 view로 화면을 구성할때는 계층구조라는것이 나타나는데 위 이미지에서 최상단뷰(편의상 루트뷰)는 Frame&Bounds라고 이름붙인 Button과 View의 superview 즉, 부모뷰라는 것을 의미합니다.
반대로 Button과 View는 View(루트뷰)의 subview 즉, 자식뷰라는 것을 의미합니다.
- Frame의 좌표, 즉 origin(x, y)의 값은 superview의 원점으로 부터 얼만큼 떨어져있는가를 의미합니다.
우리가 일상적으로 생각하는 상위뷰에 대한 현재 view의 위칫 값이기 때문에 이해하는데 무리없을거라고 생각 됩니다. - Frame의 크기, 즉size는 view의 영역을 감싸는 사각형의 크기를 의미합니다.
다음에서 노란색view의 프레임 x,y좌표는 50, 120이며 넓이는 250, 높이는 500입니다.
하지만 노란색뷰를 CGAffineTransform을 사용하여 30도 회전시키게 된다면 결과는 다음과 같습니다.
넓이 390.xxx 와 552.xxx는 빨간색으로 표시해놓은 사각형의 넓이와 높이를 의미하며 각각 x, y좌표인 -20.xxx 와 93.xxx는 빨간색 사각형의 x,y좌푯값을 의미 합니다.
따라서 frame의 x, y 좌푯값과 넓이는 view를 감싸고있는 사각형의 좌푯값, 넓이와 일치합니다.
Bounds는 자신의 좌표계에서 view의 위치와 크기를 나타냅니다.
즉, 자신의 원점(origin)을 (0, 0)으로 놓습니다.
이미지를 기준으로 보겠습니다
그림과 같이 bounds에서는 좌푯값이나, 넓이에 변화가 없습니다 자신의 원점이 곧 좌표의 시작점이기 때문입니다. 따라서 view의 순수한 넓이값을 계산할수 있습니다. 여기서 bounds의 원점은 항상(0, 0)인데 무슨 의미가 있는것이냐는 의문에 대해 다뤄보자면 대표적으로 UIScrollView를 예로 들수 있습니다. 아래에서 자세히 살펴보겠습니다.
Frame&Bounds의 좌표(origin) 변경 Frame의 좌표변경
1. Frame에서의 좌표의 변경은 매우 상식적이고 직관적입니다. 이미지를 보고 넘어가겠습니다.
myView2.frame.origin.x += 70
myView2.frame.origin.y += 70
다음과 같은 코드를 통해 원래의 origin에서 각각 70만큼 이동한 결과는 다음과 같습니다.
2. Bounds의 좌표변경
myView2.bounds.origin.x += 70
myView2.bounds.origin.y += 70
다음과 같이 bounds의 origin을 변경 하면 결과는 다음과 같습니다.
다음과 같이 변화가 없습니다. 그 이유는 자신의 좌표계에서 view의 위치와 크기를 나타낸다는 정의 때문입니다. 즉, 자신의 좌표계를 기준으로 자신의 subview들의 좌표에 영향을 줍니다. 따라서 노란색 view에 subview를 추가해 결과를 본다면
다음과 같이 우리의 예상과 다르게 노란색 view의 subview는 음의 방향으로 70씩 이동한 모습으로 보이게 됩니다. 그 이유를 설명하기 위해 상황을 조금 더 간단하게 만들어보면
다음과 같이 볼수 있습니다. 위에서 설명했듯 bounds는 자기 자신의좌표계를 기준으로 하며 그 대표적인 예시는 UIScrollView라고 설명했습니다. 이제 노란색 화면은 자신의 스마트폰의 카메라 화면이며 파란색은 사물이라고 가정했을때 우리의 카메라 화면을 양의 방향으로 각각 70만큼 이동시켰다고 한다면 카메라에 비춰지는 파란사물의 위치는 오른쪽의 이미지와 같을 것입니다.
ScrollView도 같은 원리입니다. 앱스토어 앱을 기준으로 화면의 contetnts들이 아닌 화면이 아래로 움직인다면 실제 우리가 스크롤 했을때의 화면을 순간적으로 포착했을 때의 위치와 같은 위치이지만 우리가 실제로 기기를 조작할때는 기기를 움직이는 것이아닌 손가락을 이용해 기기내 화면의 컨텐츠들을 움직이는 것처럼 보이기 때문에 우리가 bounds에 준 좌푯값과는 반대로 움직이는 것처럼 보이게 됩니다.
여러 블로그들을 참고하여 이해해보려했지만 이론적으로는 잘 이해가 가지 않았고 여러가지 직관적인 예를 생각하고 적용해봤을때 위의 예시가 가장 이해가 잘가게 되어 설명하게 되었습니다.