ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [iOS] 객체 레이아웃, Code base UI 구현
    카테고리 없음 2025. 1. 13. 13:52

    UI 그리는 법

    1. storyboard(IB)
    2. xib
    3. 코드

    UI를 그리는 방법은 총 3가지가 있다. 이번엔 코드베이스 중점으로 보게 될텐데
    코드 베이스와 스토리보드의 큰 차이점은?

    • 스토리보드에서 다뤘던 뷰 객체, 뷰 컨트롤러, 시작점, info.plist ⇒ 전부 코드로 변화해서 시작해야한다.
    • 코드베이스여도, Launch Screen은 두고 하는 편
    • 파일이 누락됐다고 생각할 수도 있기 때문에 심사가 멈출 수 있다.
    • 팀 협업, 유지보수에 코드 베이스가 더 유리하게 작용한다.

    객체 레이아웃

    우선 코드로 구성하기 전 객체 레이아웃에 대해 살펴본다.
    뷰 객체들을 배치하기 위해선 레이아웃이 항상! 필요하다.
    스토리보드에서 자주 사용하던 AutoLayout은, Frame based layout부터 여러 기기를 대응하기 위해 더 나아진 레이아웃이라고 생각하면 된다.

    총 4가지의 레이아웃을 갖고 있고, 점차 나아지는 레이아웃이라고 생각해주면 된다.
    (*Adative layout은 아직 다루지 않아 설명에선 제외했다.)

    1. Frame based layout
    2. Auto ResizingMask
    3. Auto layout (핸드폰 기기에 맞춰 다양하게)
    4. Adaptive layout (아이패드, 워치 등 다양한 기기)

    Frame based layout


    왼쪽과 같이, 왼쪽 상단을 기준으로 화면을 좌표로 수치화 해, 상수를 기반으로 frame을 정하는 것을 의미한다.
    문제는 이렇게 되면 여러 기기에 대응하기가 어려워진다.

    각 기기별 width, height에 따라 숫자가 달라지기 때문에 나누기도 쉽지 않고,
    원하는 대로 안나올 가능성이 높다.

     

     

     

    AutoResizingMask

    Auto layout은 많이 들었을텐데, 그 전에 나온게 Autoresizing Mask 이라는 동적인 레이아웃을 구성할 수 있는 Apple이 도입한 예전 방식의 기능이다.

    기기별 사이즈로 인해 슈퍼뷰의 경계가 변경될 때, 서브뷰의 크기를 조정하는 방법을 결정하는 정수형 비트마스크이다.

    • Autoresizing Mask로 결정된 레이아웃은 Auto layout contstraint로 변환되기 때문에, 바뀐 이후 코드로 제약 조건을 수정하는 것이 불가능하다.
      즉, 뷰에서 설정한 Autoresizing Mask 값은 Auto Layout constraint로 자동으로 변환되는 것이다.
    • 그러므로, Autoresizing & Auto Layout을 동시에 적용해 수정하면 레이아웃 충돌이 발생할 수 있다. 충돌을 방지하기 위해 코드로 Auto Layout 방식을 사용할 때는 반드시 translatesAutoresizingMaskIntoConstraints 속성을 false로 설정해줘야한다.

    AutoResizingMask 사용 예시

    • Autoresizing Mask는 gui 상에서 아래와 같이 constraint 화살표를 선택할 수 있고, 코드로도 설정 가능하다.
      코드 sampleSubview.autoresizingMask = [.flexibleWidth, .flexibleHeight]
      • bottom, trailing 기준으로 AutoResizingMask를 설정한 상황에서, 기기별 경계값을 바꾸기 위해 아이폰 16 → SE로 바꿔주었다.
      • 설정한 bottom, trailing의 여백을 기준으로 기기별로 대응하는 것을 확인할 수 있다.

    좌 - 아이폰 16, 우 - 아이폰 SE

    • 슈퍼뷰를 기준으로 뷰의 크기를 늘리거나 줄이는 것인데, 이 기준을 설정한 constraint를 기준으로 얼마만큼 늘릴지 말지를 결정한다.
    • 아래 사진으로 예시를 들면, 현재 화살표를 보면 bottom, trailing 에 제약 조건을 준다.

    Auto layout

    모든 뷰에 크기, 위치를 동적으로 계산하도록 제약 조건을 걸어준다.
    constraint 값으로 슈퍼뷰, 다른 뷰 객체들에 관계나 여백을 기준으로 조건을 걸어주면, 기기에 따라 변하는 것에 동적으로 반응하는 레이아웃을 만들 수 있다.

    우리가 자주 사용하는 아래와 같은 constraint 들이 전부 Auto Layout에 해당한다.

    스토리보드에서 위처럼 잡던 방식을, 코드로 바꾸면 NSLayoutConstraint 이다.

    Code based UI

    객체 선언 및 등록

    스토리보드로 구성할 때는 아래와 같이 구성했다.

     1. 스토리보드에서 객체 얹히기
     2. 객체 레이아웃 잡기
     3. 아웃렛 연결하기
     4. 객체 속성 코드로 조절하기

    코드베이스로 위를 바꾼다면 크게 이런 순서이다.

    뷰 객체 선언 + addSubView(뷰 계층 구조에 등록) -> 객체 레이아웃 설정(뷰 크기도 설정 가능) -> 객체 속성 코드로 조절

    • addSubView 다음에 꼭 속성을 조절해야한다. (안그럼 런타임 에러 발생)
    • addSubView의 순서에 따라 겹쳐지는 뷰가 안보일 수 있다. (순서도 중요)
    • AutoResizing mask 부분 false로 바꿔주는 걸 잊지말자!(우리는 autolayout을 사용할 것이기 때문에)
    class ViewController: UIViewController {
        // 1. 객체 선언
        let emailTextField = UITextField()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // 기본적으로 AutoResizing mask가 깔려있기 때문에 없애줘야한다.
            emailTextField.translatesAutoresizingMaskIntoConstraints = false
    
            // 2. addSubView 로 뷰 추가
            view.addSubview(emailTextField)
            // 3. 객체 속성 코드로 조절
            emailTextField.backgroundColor = .gray
        }
    
    }

    객체 레이아웃 설정 & 실행

    • 객체 레이아웃을 NSLayoutConstraint 으로 설정하고, 그 constraint를 실행시키는 방법에는 총 3가지가 있다.
    • isActive, addConstraint, activate
    1. isActive: NSLayoutConstraint에 프로퍼티를 이용해 실행시키는 방법
    NSLayoutConstraint(item: emailTextField,
                       attribute: .top,
                       relatedBy: .equal,
                       toItem: view,
                       attribute: .top,
                       multiplier: 1,
                       constant: 50).isActive = true

    2.  addConstraint: 한번에 배열로 전달하는 방법

    let leading = NSLayoutConstraint(item: emailTextField,
                     attribute: .leading,
                     relatedBy: .equal,
                     toItem: view,
                     attribute: .leading,
                     multiplier: 1,
                     constant: 40)
    let trailig = NSLayoutConstraint(item: emailTextField,
                     attribute: .trailing,
                     relatedBy: .equal,
                     toItem: view,
                     attribute: .trailing,
                     multiplier: 1,
                     constant: -40)
    let height = NSLayoutConstraint(item: emailTextField,
                     attribute: .height,
                     relatedBy: .equal,
                     toItem: nil,
                     attribute: .height,
                     multiplier: 1,
                     constant: 50)
    
    
    view.addConstraints([leading, trailig, height])

    3. activate: NSLayoutConstraint.activate의 배열에 제약조건을 넣는 방법

    func autoLayoutAnchor() {
        NSLayoutConstraint.activate([
            nameTextField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            nameTextField.widthAnchor.constraint(equalToConstant: 300),
            nameTextField.heightAnchor.constraint(equalToConstant: 50),
            nameTextField.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }

    보통 3번이 간결해서 많이 사용하는 것 같다.

    아무래도 스토리보드로 짜던 constraint가 코드베이스가 되니 너무 코드가 길어지게 되는데…
    이를 보완하고자 많은 기업에서도 SnapKit 프레임워크를 사용한다.
    글이 너무 길어지기에… 다음 글에선 SnapKit 사용법 작성을 해보려 한다.

Designed by Tistory.