[TIL]AutoLayout

rbw·2022년 7월 7일
0

TIL

목록 보기
33/99

Autolayout 에 관하여

아직은 자세히 모르는 오토레이아웃에 관해 작성해보려고 한다.

오토레이아웃은 뷰에 부과된 제약 조건에 따라 뷰 계층 구조의 모든 뷰의 크기와 위치를 동적으로 계산 해주는 친구이다.

뷰 계층의 레이 아웃은, 일련의 1차 방정식이다. 각 제약은 단일 방정식을 나타낸다.

  • Item1 : 방정식의 첫 번째 항목입니다. 항목은 뷰 또는 레이아웃 가이드 여야 합니다.

    레이아웃 가이드 : 자동 레이아웃과 상호 작용할 수 있는 직사각형 영역

  • Attribute1 : 첫 번째 항목에 제한될 속성 위 경우에는 왼쪽 선이다.
  • Relationship : 왼쪽과 오른쪽의 관계이다. 관계는 같거나, 크거나 같음, 작거나 같음 세가지 중 하나의 값을 가질 수 있다.
  • Multiplier : Attribute2의 값에 이 값을 곱함.
  • Item2 : 방정식의 두번째 항목 첫번째 항목과 달리 공백으로 둘 수 있다.
  • Attribute2 : 두번째 항목에 대해 제한되는 속성, 위 경우 오른쪽 가장자리
  • Constant : 상수, 이 값은 Attribute2의 값에 추가된다.

대부분의 제약 조건은 사용자 인터페이스의 두 항목 간의 관계를 정의 합니다.

일반적으로 제약 조건은 각 뷰의 위치와 크기를 모두 정의해야 합니다.

명확하고 만족스러운 레이아웃 만들기

다음 3가지 레이아웃은 모두 명확하고 만족스러운 레이아웃을 생성합니다.

첫 번째 레이아웃

이는 상위 뷰의 왼쪽 에지를 기준으로 뷰의 왼쪽 에지를 제한합니다. 또한 뷰에게 고정 너비를 제공합니다. 그렇게 함으로써, 오른쪽 에지의 위치는 슈퍼뷰의 크기와 기타 제약 조건을 기반으로 계산이 가능합니다.

두 번째 레이아웃

이는 상위 뷰의 왼쪽 에지를 기준으로 뷰의 왼쪽 에지를 제한하고, 오른쪽 에지에도 같이 제한을 주었다. 그런 다음 뷰의 너비는 상위 뷰의 크기와 기타 제약조건을 바탕으로 계산이 가능하다.

세 번째 레이아웃

이는 상위 뷰의 왼쪽 에지와 뷰의 왼쪽 에지를 제한하고, 뷰와 상위 뷰를 가운데 정렬한다. 너비와 오른쪽 에지의 위치는 모두 슈퍼뷰의 크기와 다른 조건을 기반으로 계산이 가능하다.

슈퍼뷰의 너비가 변경이 되면 ?

첫 번째의 경우, 뷰의 너비가 변경되지 않는다. 원칙적으로, 뷰에 일정한 크기를 할당 하는 것은 피해야 한다. 뷰에 고정크기를 줄 때 마다, 오토레이아웃의 능력을 감소 시키기 때문이다.

두번째, 세번째 레이아웃은 동일한 동작을 수행한다.

슈퍼뷰의 너비가 변경되어도, 뷰의 너비를 계산이 가능하고, 일정한 간격을 유지한다. 두번째의 경우가 이해하기 쉽지만, 보통 세번쨰의 경우가 더 유용한 경우가 많다. 특히 여러 항목을 가운데 정렬시에 더 유용하다.

두 개의 뷰가 존재하고, 항상 너비가 같고 일정한 간격이 있을 때

위와 같은 뷰를 만드려고 할 때, 2가지 접근 방식이 존재한다.

// vertical constraints
Red.top = 1.0 * SuperView.top + 20.0
SuperView.bottom = 1.0 * Red.bottom + 20.0
Blue.top = 1.0 * SuperView.top + 20.0
SuperView.bottom = 1.0 * Blue.Bottom + 20.0

// Horizontal Constraints
Red.leading = 1.0 * SuperView.leading + 20.0
Blue.leading =  1.0 * Red.trailing + 8.0
SuperView.trailing = 1.0 * Blue.trailing + 20.0
Red.widh = 1.0 * Blue.width + 0.0

위와 같은 방식이 하나 있고, 아래와 같은 방식이 존재한다.

// Vertical Constraints
Red.top = 1.0 * SuperView.top + 20.0
SuperView.bottom = 1.0 * Red.bottom + 20.0
Red.top = 1.0 * Blue.top + 0.0
Red.bottom = 1.0 * Blue.bottom + 0.0

// Horizontal Constraints
Red.leading = 1.0 * SuperView.leading + 20.0
Blue.leading = 1.0 * Red.trailing + 8.0
SuperView.trailing = 1.0 * Blue.trailing + 20.0
Red.width = 1.0 * Blue.width + 0.0

위 두가지 방식 다 만족스럽게 동작을 한다. 장단점으로는,

1번 방식의 경우 View가 삭제 될 때 더 강력하다. Red 가 사라져도 Blue 에는 3가지 제약 조건이 남아있으므로, 한가지 제약만 추가하면 유효한 레이아웃이 생성된다.

하지만 2번 방식의 경우는 뷰가 삭제시 제약 조건이 한가지만 남는다.

반면에, 1번에서 View의 위 아래 정렬시에는 위쪽 및 아래 쪽 제약조건이 동일한 상수 값을 사용하는지 확인해야하고, 하나의 상수를 변경시에 다른 상수도 변경해야 한다.

고유 콘텐츠 크기

지금까지 예제에는 제약 조건을 사용하여 모든 뷰의 위치와 크기를 정의했지만, 일부 뷰는 현재 콘텐츠에 따라 자연스러운 크기를 가집니다. 이를 고유 콘텐츠 크기라고 합니다.

  • UIView, NSView : 고유 콘텐츠 크기가 존재하지 x
  • Slider : 너비(iOS)만 정의 합니다. 슬라이더 유형(OS X)에 따라 너비, 높이 또는 둘 다를 정의합니다.
  • Label, Button, Switch, Text Field : 높이와 너비를 모두 정의합니다.
  • Text View, Image View : 고유 콘텐츠 크기는 다를 수 있습니다.

코드로 오토레이아웃 작성 하는 방법

위의 Red, Blue 예제를 pink, purple로 해서 만들어 보았다. 최종 결과는 위의 Red, Blue의 예제와 동일하다.

private var pinkView: UIView = UIView()
private var purpleView: UIView = UIView()

override func viewDidLoad() {
    super.viewDidLoad()
    pinkView.backgroundColor = .systemPink
    purpleView.backgroundColor = .purple
    
    // 먼저 addSubView로 뷰를 추가해주어야한다.
    self.view.addSubview(pinkView)
    self.view.addSubview(purpleView)
    
    // 이 과정 필수 ! false는 AutoLayout을 따르겠다는 의미 !
    pinkView.translatesAutoresizingMaskIntoConstraints = false
    purpleView.translatesAutoresizingMaskIntoConstraints = false
    
    /// Vertical Constraint
    pinkView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0).isActive = true
    purpleView.leadingAnchor.constraint(equalTo: pinkView.trailingAnchor, constant: 15.0).isActive = true
    view.trailingAnchor.constraint(equalTo: purpleView.trailingAnchor, constant: 20.0).isActive = true
    pinkView.widthAnchor.constraint(equalTo: purpleView.widthAnchor).isActive = true
    
    /// Horizontal Constraint
    pinkView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
    purpleView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
    view.bottomAnchor.constraint(equalTo: pinkView.bottomAnchor, constant: 50.0).isActive = true
    view.bottomAnchor.constraint(equalTo: purpleView.bottomAnchor, constant: 50.0).isActive = true
}
    

isActive = true 줄이는 방법 ?

아래의 코드 처럼 작성을 하면 .isActive = true 작성하지 않아도 된다.

NSLayoutConstraint.activate([
    pinkView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0),
    purpleView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0),
    view.bottomAnchor.constraint(equalTo: pinkView.bottomAnchor, constant: 50.0),
    view.bottomAnchor.constraint(equalTo: purpleView.bottomAnchor, constant: 50.0)
])

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

안전영역에 붙이려면 아래와 같이 작성을 해주면 된다.

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

오토레이아웃에 대해서 아직 다루지 않은 부분이 더 있을거라고 생각하지만, 차후에 더 작성해보도록 하겠다. 스냅킷이나 컨텐츠 허깅등 여러가지,,,


참조

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

https://babbab2.tistory.com/155

profile
hi there 👋

0개의 댓글