[UIKit] Storyboard 탈출기 (4편) - AutoLayout을 Programmatically하게 적용하기

3 minute read

뷰를 구성하다 보니 frame-based layout의 한계를 느끼고 오토레이아웃의 필요성을 깨닫게 되어 이번 글에서는 코드로 Autolayout을 적용하는 기본적인 방법에 대해 알아보도록 하겠습니다.

참고사항: 이번 글은 Autolayout의 작동방식을 알고 스토리보드에서 Autolayout을 통해 UI를 구성할 수 있는 분을 예상 독자로 작성하였습니다.

1. 사용하는 방법

각 뷰를 화면의 하위 뷰로 넣어준 다음 해당 뷰에 대해 다음과 같이 .translatesAutoresizingMaskIntoConstraints = false 처리해줍니다.

myView.translatesAutoresizingMaskIntoConstraints = false 

그리고 원하는 기준점을 바탕으로 오토레이아웃을 구현하면 됩니다.

myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true // x축 기준으로 화면 중앙에 오도록 함

예제를 한 번 살펴보겠습니다. 아래의 표와 같이 구현하고자 할 경우를 생각해보면,

버튼 하나와 텍스트라벨 하나가 있는 다음과 같은 화면을 오토레이아웃을 활용해 구성한다고 가정해보겠습니다. 이때 버튼은 가로세로 기준 정중앙에 배치되도록, 아래의 라벨은 x기준 중앙에 위치하도록 정렬하고 라벨의 top과 버튼의 bottom을 연결하는 오토레이아웃을 구현해보겠습니다. 오토레이아웃 예제
   

우선 각각의 ui 프로퍼티를 만들어주고, 각 프로퍼티에 대해 오토레이아웃을 사용할 수 있도록 myView.translatesAutoresizingMaskIntoConstraints = false 를 해줍니다.

private lazy var myBtn : UIButton = {
  let btn = UIButton()
  btn.setTitle("autoLayout적용예제", for: .normal)
  btn.backgroundColor = .blue
  btn.translatesAutoresizingMaskIntoConstraints = false
  return btn
}()

private lazy var titleLabel: UILabel = {
  let title = UILabel()
  title.text = "위의 버튼에 오토레이아웃 걸기"
  title.translatesAutoresizingMaskIntoConstraints = false
  return title
}()

그리고 요구사항을 충족할 수 있게끔 다음과 같이 오토레이이아웃을 걸어주면 됩니다.

override func viewDidLoad() {
  super.viewDidLoad()
  self.view.addSubview(titleLabel)
  self.view.addSubview(myBtn)

  myBtn.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
  myBtn.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
  myBtn.leadingAnchor.constraint(equalTo:
                                 view.safeAreaLayoutGuide.leadingAnchor, constant: 30.0).isActive = true

  titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
  titleLabel.topAnchor.constraint(equalTo: myBtn.bottomAnchor, constant: 20.0).isActive = true

}

이렇게 하면 원하는 대로 오토레이아웃이 걸리게 됩니다.

2. 코드 살펴보기

2.1. translatesAutoresizingMaskIntoConstraints 란 무엇인가?

translates + AutoresizingMask + Into + Constraints 이므로 AutoresizingMask를 Constraints로 바꾼다는 말같은데, 무엇인지 문서로 검색해보니 다음과 같이 설명하고 있습니다.

translatesAutoresizingMaskIntoConstraints

ㅇㅖ…? 더 자세히 보면 아래와 같이 적혀있습니다.

Declaration

var translatesAutoresizingMaskIntoConstraints: Bool { get set }

Discussion

If this property’s value is true, the system creates a set of constraints that duplicate the behavior specified by the view’s autoresizing mask. This also lets you modify the view’s size and location using the view’s frame, bounds, or center properties, allowing you to create a static, frame-based layout within Auto Layout.

Note that the autoresizing mask constraints fully specify the view’s size and position; therefore, you cannot add additional constraints to modify this size or position without introducing conflicts. If you want to use Auto Layout to dynamically calculate the size and position of your view, you must set this property to false, and then provide a non ambiguous, nonconflicting set of constraints for the view.

By default, the property is set to true for any view you programmatically create. If you add views in Interface Builder, the system automatically sets this property to false.

만약 이 프로퍼티값이 참일 경우, frame기반인 autoresizing mask에 의해 만들어진 값을 오토레이아웃의 constraints로 바꾸어주는 것이라고 하네요. 하지만 그렇게 되면 오토레이아웃의 동적인 뷰의 크기나 위치 변화를 만들어낼 수 없게 된다는 점이 문제가 됩니다. 따라서 오토레이아웃에 따라 하위 뷰의 위치나 크기를 변경시키기 위해서는 해당 프로퍼티의 값을 false로 바꿔줘야 합니다.

코드로 뷰를 만들 경우 translatesAutoresizingMaskIntoConstraints 값은 기본값이 true 이고, 인터페이스 빌더로 뷰를 만들 경우 translatesAutoresizingMaskIntoConstraints 의 기본값이 false 라고 하네요. 따라서 코드로 뷰를 만들어 줄 때는 autolayout을 사용하려면 해당 인스턴스의 값을 false 로 만들어줘야 하나 봅니다.

2.2. .isActive = true 는 무엇인가?

isActive

isActive 는 이러한 오토레이아웃의 constraints를 활성화 하기 위한 프로퍼티네요.

3. 소소한 팁 : NSLayoutConstraint.activate(consantsArray)

모든 constraints를 활성화시키기 위해서는 각각 .isActive = true 를 붙여줘야 하는데요, 반복되는 것이 귀찮다 싶으면 각각의 NSLayoutConstraint를 배열에 담아 한꺼번에 전달한 후 activate() 메서드를 사용해 한꺼번에 activate할 수 있습니다.

스크린샷 2021-11-19 오후 4.11.49

위의 예제를 NSLayoutConstraint.activate(consantsArray) 를 사용해 바꾸면 다음과 같습니다.

  override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(titleLabel)
        self.view.addSubview(myBtn)
        
        let consArr : [NSLayoutConstraint] = [
            myBtn.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            myBtn.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            myBtn.leadingAnchor.constraint(equalTo:
                                            view.safeAreaLayoutGuide.leadingAnchor, constant: 30.0),
            
            titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            titleLabel.topAnchor.constraint(equalTo: myBtn.bottomAnchor, constant: 20.0)
        ]
        
        NSLayoutConstraint.activate(consArr)
    }

중복되는 isActivate = true 를 없애고 배열로 간단하게 처리한 것을 볼 수 있습니다.


지금까지 programmatically하게 오토레이아웃을 쓰는 기본적인 방법을 알아보았습니다. 다음에는 SnapKit 을 활용한 방식도 소개해보도록 하겠습니다. 그럼 이만 총총..

References

  • https://baked-corn.tistory.com/36
  • https://terry-some.tistory.com/122

Comments