[iOS] View의 Layout 잡기(Frame, Auto Layout)

김민석·2024년 9월 1일
0

iOS

목록 보기
11/12
post-thumbnail

우선 View의 Layout을 잡기전에 View가 어떠한 방식으로 설정되고 그려지는지 먼저 고민해야합니다.

1. View는 어떻게 그려지는가?

iOS에서 View를 그리는 부분은 위 그림과 같이 크게 3가지 phase를 나눕니다

  1. View의 제약조건을 설정
  2. View를 조건에 맞추어서 layout
  3. View를 그림(draw)




예시

위와 같이 예시가 있으면

---UPDATE CONSTRAINTS---
ThirdV updateConstraints
SecondV updateConstraints
SecondOfSecondV updateConstraints
FirstV updateConstraints
	//updateViewConstraints in VC
---LAYOUT SUBVIEWS---
FirstV layoutSubviews
SecondOfSecondV layoutSubviews
SecondV layoutSubviews
ThirdV layoutSubviews
---DRAW RECT---
FirstV draw-rect
SecondOfSecondV draw-rect
SecondV draw-rect
ThirdV draw-rect

View의 계층구조 상에서 Left -> Root순(BotttomUp)으로 제약 설정
나머지는 Root -> Left순(TopDown)으로 Layout되고 Draw된다.






이렇게 View가 어떻게 그려지는지 알았으므로
제일 기본적인 개념으로 좌표 시스템에 대해 학습해야합니다.

2. iOS에서 Layout을 설정 하는 방법


  1. 좌표 기반 기반
    1. Frame
    2. Bounds
    3. Center
  2. Auto Layout 기반




3. 뷰 좌표 시스템(View Coordinate System)


iOS에서 뷰 좌표 시스템은 각 뷰가 자체적으로 가지는 2D 좌표 공간을 의미

화면 왼쪽 상단을 원점(0, 0)으로 하는 직교 좌표계

  • x축: 오른쪽으로 갈수록 값이 커짐
  • y축: 아래쪽으로 갈수록 값이 커짐
  • 독립성 : 각 뷰는 자신의 좌표 시스템을 가지며, 부모 뷰에 대해 상대적으로 위치가 결정됩니다. 즉, 뷰가 중첩되어 있는 경우, 자식 뷰의 좌표는 부모 뷰의 좌표 시스템을 기준으로 계산됩니다.



3-1. UIView Frame

frameUIView 클래스의 주요 속성 중 하나로, 뷰의 위치와 크기를 정의합니다. frame은 슈퍼뷰(superview)의 좌표 시스템에서 뷰의 사각형 영역을 나타냅니다.

frame의 구조

frameCGRect 타입이며, CGRect는 다음과 같은 속성으로 구성됩니다 :

  • origin : 뷰의 원점을 나타내는 CGPoint 구조체입니다. xy 좌표를 포함하며, 부모 뷰의 좌표 시스템을 기준으로 합니다.
  • size : 뷰의 크기를 나타내는 CGSize 구조체입니다. widthheight를 포함합니다.

frame의 구성 요소

  1. origin : 뷰의 왼쪽 상단 모서리의 좌표로, 부모 뷰의 좌표 시스템을 기준으로 설정됩니다.
  2. size : 뷰의 너비와 높이를 정의하며, 뷰의 실제 크기를 결정합니다.

특징

  • View가 많아지면 다른 모델에(특히 해상도가 달라지면) 대해서도 하나한 관리해 주어야 하기에 개발자 입장에서 중노동이 될 수있음.
  • AutoResizing Masks는 SuperView에 대해(외부 요인) 자동으로 사이즈를 맞추어 주지만 이는 극히 일부에 도움이 될 뿐 모든 문제를 해결해 주지는 않는다.

예제

다음은 UIViewframe을 사용하여 뷰를 배치하는 간단한 예제입니다.

let parentView = UIView(frame: CGRect(x: 50, y: 50, width: 300, height: 500))
parentView.backgroundColor = .lightGray

let childView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 150))
childView.backgroundColor = .blue

parentView.addSubview(childView)

self.view.addSubview(parentView)

이 예제에서:

  • parentView : main 아래의 슈퍼뷰로, 메인으로 부터 (50, 50)의 원점을 가지며, 크기는 300x500입니다.
  • childView : 자식 뷰로, parentView의 좌표 시스템에서 (50, 50) 위치에 놓이며, 크기는 100x150입니다. 즉, childView의 원점은 parentView의 왼쪽 상단 모서리에서 오른쪽으로 50포인트, 아래쪽으로 50포인트 떨어진 위치에 있습니다.

frame 외에도 UIView에는 뷰의 위치와 크기를 정의하는 데 사용되는 다른 두 가지 중요한 속성이 있습니다 : boundscenter.




3-2. UIView Bounds

bounds

  • 설명 : bounds는 뷰의 내부 좌표 시스템에서 뷰의 사각형 영역을 정의합니다. boundsCGRect로 정의되며, 원점은 보통 (0, 0)이지만 뷰의 컨텐츠가 스크롤되거나 이동할 수 있는 경우 다른 값이 될 수 있습니다.
  • 구성 요소 :
    • origin : 기본적으로 (0, 0)이지만, 뷰의 컨텐츠가 스크롤되거나 움직이는 경우에는 달라질 수 있습니다.
    • size : 뷰의 크기를 나타내며, framesize와 동일합니다.
  • 용도 : bounds는 뷰의 내부 레이아웃을 정의할 때 사용됩니다. 특히, 뷰가 스크롤되거나 클립핑될 때 유용합니다.

예제

let view = UIView(frame: CGRect(x: 20, y: 20, width: 100, height: 100))
view.backgroundColor = .lightGray
print(view.bounds) // 출력: (0.0, 0.0, 100.0, 100.0)

// bounds의 origin을 (10, 10)으로 변경
view.bounds = CGRect(x: 10, y: 10, width: view.bounds.width, height: view.bounds.height)

print(view.bounds) // 출력: (10.0, 10.0, 100.0, 100.0)

self.view.addSubview(view)
  • frame과 달리 boundsorigin(0, 0)이고, sizeframe과 동일합니다. 이 bounds는 뷰의 자체 내부 좌표계를 기준으로 한 사각형을 정의합니다.
  • bounds.origin을 변경하면, 뷰의 내용이 해당 위치만큼 이동한 것처럼 보이게 됩니다. 하지만, 뷰의 실제 위치는 frame에 의해 결정되기 때문에 화면에 보이는 위치는 변하지 않습니다. bounds의 origin을 조정하는 것은 주로 뷰 내에서 콘텐츠의 스크롤 또는 다른 내부 좌표 조작을 위해 사용됩니다.

결국 origin을 변경해도 Frame에 의해 결정되기 때문에 UI가 변경된 부분은 없음




3-3. UIView Center

Center

  • 설명 : center는 뷰의 중심점의 좌표를 나타내며, 부모 뷰의 좌표 시스템을 기준으로 합니다. centerCGPoint로 정의되며, frameoriginsize를 기반으로 계산됩니다.
  • 구성 요소 :
    • x : 뷰의 중심점의 x 좌표.
    • y : 뷰의 중심점의 y 좌표.
  • 용도 : 뷰를 이동하거나 배치할 때 center를 사용하면, 뷰의 중심을 기준으로 위치를 설정할 수 있어 더 직관적입니다.

예제

let view1 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view1.backgroundColor = .lightGray
print(view1.center) // 출력: (50.0, 50.0)

self.view.addSubview(view1)

let view2 = UIView(frame: CGRect(x: 20, y: 20, width: 100, height: 100))
view2.backgroundColor = .lightGray
print(view2.center) // 출력: (70.0, 70.0)

self.view.addSubview(view2)

centerframe의 중심을 나타냄

(width: 100, height: 100)의 중심 좌표는 (50, 50) 인데

  • 1번 예제는 시작점이 (0, 0)이므로 (50, 50)이 나옴
  • 2번 예제는 시작점이 (20, 20)이므로 (70, 70)이 나옴




3-4. 정리

frame, bounds, center의 차이점

  • frame : 부모 뷰의 좌표 시스템을 기준으로 뷰의 위치와 크기를 정의합니다.
  • bounds : 뷰의 자체 좌표 시스템을 기준으로 뷰의 내부 영역을 정의합니다.
  • center : 부모 뷰의 좌표 시스템을 기준으로 뷰의 중심점을 정의합니다.






4. Auto Layout 기반


Auto Layout의 기본적인 아이디어는 View들간의 관계를 통해서 상대적인 위치를 설정하고자 함에 있다.

이 경우 이웃하는 View들에 대해 상대적인 위치를 정하게 되어서 모든 기기들에 대해 View의 위치와 크기를 확정할 수 있다.
물론 이 경우에도 View에 대한 x, y좌표와 너비, 높이가 모두 확정될 수 있어야한다.

💡일부 View의 경우 특성에 따라 자신의 size가 결정되는 View가 있다.
대표적으로 Label은 text의 길이나 폰트 등으로 스스로 크기를 결정한다.
ImageView 또한 별도의 설정이 없는 한, Image의 크기만큼 그 크기를 결정한다.
이를 Intrinsic Content Size라고 한다.

4-1. 속성

  • top
  • bottom
  • left or leading: 글이 시작하는 방향(왼쪽, 문화에 따라 다름)
  • right or trailing
  • centerX
  • centerY
  • Width -> 크기
  • Height -> 크기
  • baseline: 글자의 밑바닥

4-2. 사용 예제

let view = UIView()
view.backgroundColor = .lightGray

view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(view)

view.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true
view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 100).isActive = true
view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -80).isActive = true
view.heightAnchor.constraint(equalToConstant: 500).isActive = true
  1. View를 선언하고 백그라운드 Color 설정
  2. translatesAutoresizingMaskIntoConstraints를 줘서 AutoLayout 가능하게 설정
    • trueAutoLayout을 따르지 않고 frame을 따르겠다(Frame-Based Layout)
    • false - AutoLayout을 따르겠다(AutoLayout)
  3. 제약조건 설정하기(제약조건 내용은 5번 참고)
    1. 상단에서 100pt 떨어짐
    2. 왼쪽에서 100pt, 오른쪽에서 80pt 떨어짐
    3. 높이를 500pt로 만듬
    4. true로 설정해주기

주의점1

addSubview로 추가를 하고 제약조건을 설정해야함
그렇지 않으면 부모 뷰에 추가되지 않았기 때문에 제약조건 설정시 오류 발생

주의점2

trailingAnchor(오른쪽) / BottomAnchor(아래)에 한해서
-(minus)를 붙여줘야함

주의점3

상단에 컨트롤 부분을 건드리고 싶지 않고 SafeArea 부분만 건드리고 싶은 경우

self.view(rootView)에 붙일 경우 SafeAreaLayoutGuide를 사용

sodeulButton.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
sodeulButton.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 20).isActive = true






5. 제약 조건

5-1. 모호하지 않은 레이아웃

x축과 y축 모두에 제약사항을 명확히 해야함




5-2. 공통 컨트롤에 대한 내재적 콘텐츠 크기

ViewIntrinsic content Size
UIView와 NSView내재적인 콘텐츠 크기가 없습니다.
Slider너비만 정의(iOS)
슬라이더 유형(OS X)에 따라 너비, 높이 또는 둘 다를 정의
Labels, Buttons, Switches, Text Field높이와 너비를 모두 정의합니다.
Text View, Image Views내재적 콘텐츠의 크기는 다양할 수 있습니다.

왼쪽과 위쪽에 20씩의 간격을 줬을 때…

  • Label, Button 등은 텍스트의 양에 따라 사이즈가 정해짐(오류 X)
  • Image View는 이미지의 사이즈를 지정해야함(그렇지 않으면 오류)
  • slider의 경우 높이가 지정되어 있기 때문에 너비를 지정해줘야함




5-3. View가 Constraints를 설정하는 방식

  1. ViewWindow의 제어 아래에 있다.
  2. Window는 Constraint Engine과 소통한다.
  3. 이 상황에서 View에 제약을 추가해보자.
  4. 해당 제약은 특정 등식(또는 부등식)의 형태로 Engine에 전달되고 이는 View에 있는 Variable을 통해 resolve된다.
  1. EquationVariable을 통해 Engine이 View에게 값이 변경되었음을 전달
  2. ViewSuperView에게 setNeedsLayout()을 호출하게 된다.(이제 제약조건이 설정되었으니까 그 다음은 Layout을 해야하므로)

LayoutSubview에서는 어떤일이 일어날까?
Layout을 만들라고 요청을 받은 View는 다시 Engine으로부터 실질적인 값을 받아와서 subView의 bounds와 center를 설정한다.

5-4. 추가적인 내용..

많은 내용이 있어서 핵심만 정리하고 아래의 공식문서에서 확인 가능

https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/AnatomyofaConstraint.html#//apple_ref/doc/uid/TP40010853-CH9-SW1

6. Draw, Display

아래 공식문서 참고.. 너무 어려운 내용..

https://developer.apple.com/library/archive/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html

7. 여러가지 Auto Layout

7-1. .isActive = true 일일이 달아주는 부분 없애기

  • NSLayoutConstraint.activate ( constraints배열 )
let cons = [
    lbl1.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
    lbl1.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100),
    lbl1.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 300)
]
NSLayoutConstraint.activate(cons)

7-2. 쉽게 사용 가능하도록 변경(extension 이용)

extension UIView {
    func anchor(top: NSLayoutYAxisAnchor?,
                right: NSLayoutXAxisAnchor?,
                bottom: NSLayoutYAxisAnchor?,
                left: NSLayoutXAxisAnchor?,
                padding: UIEdgeInsets = .zero,
                size: CGSize = .zero) {
        
        translatesAutoresizingMaskIntoConstraints = false
        
        if let top = top {
            topAnchor.constraint(equalTo: top, constant: padding.top).isActive = true
        }
        
        if let right = right {
            rightAnchor.constraint(equalTo: right, constant: -padding.right).isActive = true
        }
        
        if let bottom = bottom {
            bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom).isActive = true
        }
        
        if let left = left {
            leftAnchor.constraint(equalTo: left, constant: padding.left).isActive = true
        }
        
        if size.width != 0 {
            widthAnchor.constraint(equalToConstant: size.width).isActive = true
        }
        
        if size.height != 0 {
            heightAnchor.constraint(equalToConstant: size.height).isActive = true
        }
    }
}

사용

greenView.anchor(top: view.safeAreaLayoutGuide.topAnchor,
                 right: view.safeAreaLayoutGuide.rightAnchor,
                 bottom: nil,
                 left: nil,
                 padding: .init(top: 0, left: 0, bottom: 0, right: 30),
                 size: .init(width: 100, height: 200)
                )
 
 redView.anchor(top: greenView.bottomAnchor,
                right: greenView.rightAnchor,
                bottom: nil,
                left: nil,
                padding: .init(top: 15, left: 0, bottom: 0, right: 0),
                size: .init(width: 100, height: 100)
                )

7-3. 설정된 Auto Layout 보여주기

드래그한 ()부분을 아래 홈페이지에 넣어보기

https://www.wtfautolayout.com/

7-4. SnapKit 사용하기

https://ios-development.tistory.com/90

✍️ 요약

  1. Swift에서 View를 그리는 방식
    1. 제약조건 설정
    2. 제약 조건에 맞게 Layout 설정
    3. Draw
    4. 제약조건은 계층구조 상에서 Left → Root 다른 부분은 Root → Left
  2. iOS에서 Layout 설정 방법
    1. 좌표 기반
      1. Frame: 부모 뷰의 좌표 시스템을 기준으로 뷰의 위치와 크기 정의
      2. Bounds: 뷰의 자체 좌표 시스템을 기준으로 뷰의 내부 영역 정의
      3. Center: 부모 뷰의 좌표 시스템을 기준으로 뷰의 중심점 정의
    2. Auto Layout 기반
      1. 제약 조건 설정해주기
      2. SnapKit 사용하기 등

📚 출처

iOS에서 View는 어떻게 그려질까?
뷰 좌표 시스템과 UIView의 frame 구조
iOS) Auto Layout 정복하기 (5/5) - 코드로 Auto Layout 설정하기
제약조건 공식 문서
Constraint 공식 문서
https://ios-development.tistory.com/67
https://ios-development.tistory.com/90

profile
개발을 배우는 대학생입니다!

0개의 댓글

관련 채용 정보