[iOS] Bounds 와 Frame

Minsang Kang·2023년 9월 22일
0

iOS Basic

목록 보기
1/3
post-thumbnail

iOS 면접질문의 기본으로, Bounds와 Frame의 차이점에 대해 한번 직접 공부해봤습니다!

먼저 Bounds 와 Frame의 공식문서를 찾아봤어요

Bounds

https://developer.apple.com/documentation/uikit/uiview/1622580-bounds

var bounds: CGRect { get set }

공식 문서를 살펴보면

  • CGRect 값은 위치인 origin 값과 크기인 size 값을 지닌 사각형이다.
  • bounds는 자신의 좌표 시스템의 origin 값과 size 값을 지녔다.
  • origin 기본값은 (0, 0)이다.
  • 좌표계 단위는 points 이다.
  • size가 조절 -> 뷰의 크기가 조절 -> 확대되거나 축소된다.
  • bounds 변화는 애니메이션이 가능하다.
  • bounds 변화는 draw(_:) 메소드 호출 없이 자동으로 뷰가 redisplay 된다.
  • draw(_:) 메소드가 불리려면 contentMode 속성을 .redraw로 설정해야 한다.

이런식으로 정리가 되었어요!

그리고 읽다보니 추가로 궁금한것들로

  • 그러면 bounds의 origin 값을 조정했을때는 어떻게 될까요?
  • 그리고 draw(_:) 함수가 어떤 역할일까요? 뷰의 업데이트와 어떤 관련이 있을까요?

일단 Frame 부터 살펴보겠습니다!

Frame

https://developer.apple.com/documentation/uikit/uiview/1622621-frame

var frame: CGRect { get set }

요약 정리

  • 같은 CGRect 값이므로, origin 값과 size 값을 지닌다.
  • frame은 superview의 좌표 시스템의 origin 값과 size 값을 지닌다.
  • layout 배치할 때 frame을 통해 origin 과 size를 설정한다.
  • 좌표계 단위는 points 이다.
  • transform 속성 값이 identity 값이 아닌 경우 frame 값은 무시된다.
  • 이때 center 속성으로 뷰의 위치를 변경하고 bounds 속성으로 size를 조정해야 한다.
  • frame 변화는 draw(_:) 메소드 호출 없이 자동으로 뷰가 redisplay 된다.
  • draw(_:) 메소드가 불리려면 contentMode 속성을 .redraw로 설정해야 한다.

이를 토대로 Bounds랑 Frame은 좌표 시스템이 다른 점이 특징이지만, 모두 동일하게 CGRect 타입으로 위치 값인 origin 값과 크기인 size 값을 지니고 있는 경계 사각형이였습니다!

그치만 위의 궁금증들을 해결해보고자 직접 UIView를 생성하여 그려봤습니다!

frame의 origin 값 변화

먼저 직접 UIView를 생성하고 origin 변화를 살펴볼까 합니다!

let view = UIView()
view.bounds = CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 100, height: 100))

bounds를 통해 위치와 크기를 잡은 상태에서 addSubView를 통해 표시를 해봤더니

이런 결과가 나왔네요!

먼저 frame값의 size가 bounds의 size 값과 동일하다는 것을 알 수 있었습니다
그런데 frame의 origin 값이 bounds origin 값의 -절반값들이더라구요?

그러면 frame의 origin 값을 0, 0으로 설정하면?

상자가 원하는대로 이동된 모습을 확인할 수 있었어요!

이를 통해 뷰의 위치와 관련된 레이아웃은 frame의 origin 값과 관련있다는 것을 확인할 수 있었습니다!

그리고 frame의 기본 origin 값은 아마도 center와 연관이 있는 것 같다는 생각이 들었어요!
이런 실수를 안하려면 frame을 기준으로 view를 추가하는게 더 안전하겠다는 생각이 들었습니다!

근데 아직도 안풀린 bounds.origin의 변화는 대체 무엇일까요...?
혹시 subView를 추가해보면 view 내부 좌표계상의 변화는 어떤지 확인할 수 있지 않을까요?

bounds의 origin 값 변화

상위 뷰를 A, 하위 뷰를 B 라고 하고

A.frame = CGRect(origin: CGPoint(x: 50, y: 50), size: CGSize(width: 300, height: 300))
B.frame = CGRect(origin: CGPoint(x: 50, y: 50), size: CGSize(width: 200, height: 200))

이렇게 설정 후 B를 A 내부에 addSubView 하여 표시해봤습니다.

일단 예상대로 bounds 값들의 origin은 기본값으로 0, 0이고, frame의 size와 동일한것도 확인할 수 있었어요!

origin 값 변화

이 상태에서 A의 bounds.origin 값을 (0, 0)에서 (50, 50)으로 조정해볼께요

그랬더니!
A는 그대로이고, subView인 B가 위로 이동한 효과가 생겼어요!

이 현상에 대해서 일단은 "bounds.origin이 이동된 만큼 하위 뷰들의 위치에 영향이 생기는구나" 라고 생각됐지만, 정확히 무슨현상이지 싶더라구요..

일단 정확한것은

  • bounds.origin이 변화된다는 것은 자신의 좌표계 기준으로 위치값이 변경된것이다.
  • subView들이 상위 view의 origin 변화만큼 이동되어 표시된다.

그리고 좀 더 알아보던 중 두 가지 블로그글들을 통해 정리를 해볼 수 있었습니다!
https://zeddios.tistory.com/203
https://velog.io/@nnnyeong/iOS-Frame-Bounds

정리

두 블로그 글을 통해 알 수 있었던 점은 viewPort, 즉 뷰가 보여지는 시점의 좌표가 움직이기에 내부 뷰가 이동되어 표시된다. 라고 볼 수 있었으며
사실 이러한 원리를 통해 UIScrollView의 스크롤이 구현되어 표시된다는 점이였습니다!


이미지 출처: https://zeddios.tistory.com/203

즉, 세로 UIScrollView 라면 bounds.origin.y 값이 +로 변화되며 아래 방향으로 스크롤이 된다는 점!
그리고 가로 UIScrollView라면 bounds.origin.x 값이 +로 변화되며 좌측 방향으로 스크롤이 된다는 점이였습니다!



좋아요 그러면 bounds.origin 변화도 이해했으니
Bounds, 또는 Frame이 변화되면 draw(_:) 함수호출 없이도 redisplay 된다는 내용을 토대로
draw(_:) 함수도 한번 알아보겠습니다!

draw(_:)

https://developer.apple.com/documentation/uikit/uiview/1622529-draw

요약 정리

  • UIView의 인스턴스 메소드 입니다.
  • 뷰에서 파라미터로 전달된 bounds를 다시 그려 (업데이트를 하여) 표시합니다.
  • 뷰가 처음 그려지는 경우는 뷰의 전체 bounds를 통해 그려집니다.
  • 뷰가 그려진 이후에는 업데이트 하고자 하는 일부 bounds를 파라미터로 전달하여 업데이트 합니다.

여기까지 내용만을 토대로는 draw() 메소드를 통해 화면에 업데이트하여 표시하고자 하는 bounds를 넘기면 표시해주는 메소드인 것 같습니다.

하지만..? 저희는 이제것 draw() 메소드를 모르는채로도 잘 그려지고, 수정되어 표시되고 그랬는데..?

특징들

  • 특수하게 뷰의 컨텐츠를 그리는 경우(UIKit 또는 Core Graphics, OpenGL ES 를 사용하여 뷰의 컨텐츠를 그려야 하는 경우)는 draw를 override 해서 정의해야 합니다.
  • 그 외의 경우(배경색을 검은색으로 설정하는 등)는 override 할 필요가 없습니다.
  • 이 메소드는 뷰가 처음 표시될 때 또는 뷰의 보이는 부분을 무효화하는 이벤트가 발생할 때 호출됩니다.
  • 이 메소드를 직접 호출하면 안되며, 뷰의 일부를 무효화하여 다시 그리려면 아래 두 가지 메소드를 사용해야 합니다.
  • setNeedsDisplay()
  • setNeedsDisplay(_:)

여기서 중요한 내용들이 있네요! 기본적으로 draw() 메소드는 특수하게 컨텐츠를 렌더링해서 표시하는 경우를 제외하고는
뷰가 처음에 그려질 때, 그리고 위의 Bounds나 Frame이 변경될 때 자동으로 draw()가 불려서 업데이트 되어 표시되는 원리였다는 사실!

그리고 저희가 뷰의 일부를 무효화하고 업데이트해줘야 하는 경우는 setNeedsDisplay() 또는 setNeedsDisplay(_:) 메소드를 통해 업데이트 해야 한다고 합니다!

그래서 Bounds 또는 Frame을 업데이트 해도 draw(_:) 메소드를 호출하지 않아도 redisplay 된다는 내용이였군요!

rotation 시 size 변화

추가로, 다른 블로그글들을 참고해보니 View 를 rotation 했을 때 Bounds와 Frame의 차이점이 있더라구요!
그래서 이 부분도 정리해보겠습니다!

위에서 origin 변화때처럼 A 내부에 B를 지닌채로 A를 돌려보고자 합니다!

A.frame = CGRect(origin: CGPoint(x: 50, y: 50), size: CGSize(width: 300, height: 300))
B.frame = CGRect(origin: CGPoint(x: 50, y: 50), size: CGSize(width: 200, height: 200))

그리고 돌려보면?

오! frame 값이 변화가 생겼네요!
그리고 bounds 변화는 물론 하위 뷰의 frame 까지 그대로인 것을 확인할 수 있었습니다!

이를 토대로 Frame의 sizeSuper View 기준으로 View가 차지하는 영역을 감싼 크기값을 의미한다는 것을 알 수 있었습니다!
그리고 Bounds의 size본인 자체의 크기값을 의미한다는 것을 알 수 있죠!

이런 부분에서는 Frame 과 Bounds가 명확히 차이점이 있군요!

Bounds 와 Frame의 차이점

지금까지 내용들을 토대로 Bounds와 Frame의 차이점을 정리해봤습니다!

Bounds와 Frame은 모두 CGRect 값으로 위치 값인 origin 값과 크기인 size 값을 지닌 경계 사각형이지만 좌표 시스템이 다르기에위치값과 크기값의 차이가 생깁니다.

먼저 Frame의 경우 SuperView 좌표 시스템 내에서의 위치와 크기를 나타냅니다.
위치 값의 경우 SupverView의 원점 기준으로 얼마나 떨어진 위치인가를 나타냅니다.
그렇기 때문에 위치값을 통해 레이아웃을 조정할 수 있습니다.
크기 값의 경우 SuperView 기준으로 차지하고 있는 영역의 크기를 나타냅니다.
이때 Frame의 크기는 뷰 자체의 크기를 나타내는 Bounds의 크기와 다른 값이 되는 경우가 됩니다.

그리고 Bounds의 경우 자신의 좌표 시스템 내에서의 위치와 크기를 나타내기에 위치 값은 기본값으로 (0, 0)을 지닙니다.
위치 값이 조정되는 경우 뷰가 보여지는 시점이 이동되어 subView가 이동되어 표시됩니다.
크기 값의 경우 뷰 자체의 크기를 나타냅니다.
따라서 회전 등 transform이 없는 경우 Frame와 같은 크기를 지니게 됩니다.

추가로 내용을 덧붙이면

추가로 Bounds 또는 Frame은 Points 단위이며 값 변화를 애니메이션으로 표시할 수 있습니다.
값 변화시 별도로 setNeedsDisplay()를 호출하지 않아도 자동으로 redisplay 되어 표시됩니다.

profile
 iOS Developer

0개의 댓글