오토레이아웃

해당 뷰에 적용된 제약조건에 따라 뷰 계층 구조에 있는 모든 뷰의 크기와 위치를 동적으로 계산한다.

 

외부변경

SuperView의 크기나 모양이 변경되면 외부변경이 발생한다. 변경할 때마다 사용 가능한 공간을 최대한 활용하도록 레이아웃을 업데이트해야 한다.

 

내부변경

View의 크기 or UI 조작시 발생한다.

 

속성

SafeArea

애플리케이션이 상태바, 네비게이션바, 탭바 등을 가릴 수 없는 애플리케이션만의 영역이다.

iPhoneX에서부터 Notch(노치)가 생겨나며 생겨났다.

최대한 SafeArea를 이용해야 하며, SuperView를 사용하지 않는 편이 좋다. (동영상에서는 사용하는 것 같기는 한데, 가로모드와 세로모드의 차이때문인지 가이드가 다른 건지는 확실히 모르겠다...)

 

Constraint

뷰와 뷰 사이의 관계(하나의 뷰는 여러개의 뷰와 관계를 생성할 수 있다)를 정의하는 것이다.

위치는 방적식으로 나타낸다.

속성 설명
Width 너비
Height 높이
Top 상단
Bottom 하단
Baseline 텍스트 하단
Leading 텍스트 방향 시작
Trailing 텍스트 방향 끝
CenterX 수평 중심
CenterY 수직 중심

 

제약의 방정식 :

A.Leading = 1.0 * B.Trailing + 12.0 -> A의 Leading(텍스트방향의 시작위치)는 B의 Trailing(텍스트 방향의 끝위치)의 1.0배에 12.0을 더한 위치 -> (Interface Builder로 직접해보면 감이 쉽게 온다...)

 

Intrinsic Content Size

뷰가 원래 가지고 있었던 크기

 

Priority Constraint

제약 사이의 우선도

  • hugging Priority
    • 우선 순위(가로/세로)가 겹칠 때(정확한 판단이 어려울 때) 높은 우선순위의 Constraint를 적용
  • Compression Priority
    • 낮은 우선순위를 가진 제약(뷰)를 밀면서까지 높은 우선순위를 가진 제약을 지킴

Margin

레이아웃에 사용되는 간격

 

 

구현 방식

Programmatically

코드를 사용하여 AutoLayout을 적용한다.

iOS를 공부하는 초반에는 굉장히 어렵다. -> 내가 적용한 제약조건들이 적용되었을 때, 어떤 모양을 가질 지 상상이 가지 않기 때문

(분명 나는 사자를 그렸는데, 꼬리, 머리, 다리 위치가 뒤죽박죽인 상황...? 혹은 아예 없어지는 상황?)

 

하지만, 장점도 존재한다.

IB(Interface Builder)를 사용하는 것보다 훨씬 가독성이 좋아 협업에 적합하다.

-> IB로 제약사항을 작성 시, 변경/유지보수할 때, 하나하나 확인해야하며 굉장히 불편하다 즉, 자신 외에는 알아보는 데에 오래걸릴 수 있다.

(같은 View를 그리기 위해 다양한 방법이 존재하기 때문, 자신도 며칠 지나면 못 알아봄...)

BUT, 코드로 작성한다면, 누가 봐도 IB보다는 쉽고 빠르게 이해할 수 있을 것이다.

 

IB(Interface Builder)

XCode내에 내장된 IB를 사용하여 AutoLayout을 적용한다.

iOS를 공부하는 초반에 권장(이후에도 IB를 사용하여 개발을 진행한다고도 한다) -> 내가 적용한 제약조건들이 적용되는 것을 실시간으로 볼 수 있기 때문에, 보다 쉽게 View를 그릴 수 있다.

또한 IBOutlet, IBAction등을 사용하여 쉽게 UI Components와 Event를 동작/구현할 수 있음

 

 

StoryBoard vs Code

iOS 프로젝트를 처음 그리고 초반에 진행을 할 때는 무조건 스토리보드로 UI를 작성하고 ViewController와 연동하였다.

why?

쉬우니까! 간편하니까! 내가 작성한 것이니 내가 제일 잘 아니 헷갈리지도 않으니까!

BUT!

혹시라도 2명이상과 프로젝트를 진행하게 된다면 스토리보드의 가장 큰 단점이 보인다.

  • 하나의 스토리보드로 함께 작업시에 충돌할 가능성이 진부함
  • 스토리보드를 나눠서 작업을 해도 굳이 하나의 스토리보드에 들어갈 화면들이 나눠짐 -> 이후에 다시 합쳐야 하는 불편함
  • 이후에 다시 합치는 과정에서도 Ctrl+C/V를 하더라도 다시 AutoLayout을 위한 Constraint를 지정해주는 것이 어렵다!
    • 물론 정리된 문서가 있으면 금방할 수 있다고 생각했지만, 아니었다...
    • 내가 UI Component들에 지정한 이름을 보고 이게 이거였나? 이게 이거였겠지? 하면서 헷갈리는 순간 시간이 굉장히 딜레이됐다.
    • 그래서 이후로는 UI Component들의 이름도... 어떠한 체계로 명확히 작성해야한다는 것을 꺠달았따...

TabBar

예제로 진행할 탭바 StoryBoard

목표 : 이처럼 TabBarVC를 가지고 있고 2개의 VC를 삽입하여 간단한 TabBar를 구현하는 것!!

 

class TabBarViewController: UITabBarController{
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let profileVC = ProfileViewController()
        let vc2 = VC2()
        
        let icon1 = UITabBarItem(title: "프로필", image: UIImage(systemName: "person.circle"), selectedImage: UIImage(systemName: "person.circle"))
        let icon2 = UITabBarItem(title: "VC2", image: UIImage(systemName: "person.circle"), selectedImage: UIImage(systemName: "person.circle"))
        
        profileVC.tabBarItem = icon1
        vc2.tabBarItem = icon2
        let vcArr = [profileVC, vc2]
        self.setViewControllers(vcArr, animated: true)
    }
        
}

 

  1. TabBarVC라는 ContainerVC에 들어갈 ProfileVC, VC2를 생성한다.
  2. 각각의 VC들이 TabBar에서 보일 Item을 만든다.
  3. 2번에 이어서 붙여준다.
  4. VC들을  TabBarVC의 VC들로 지정한다.

이과정을 진행하면 문제없이 시뮬레이터에서 실행이될 것이다.

ProfileVC
VC2

보라! 정상적으로 TabBar가 구현되었다!!

응??? 근데 뭔가 이상하다??

스토리보드에 있는 ProfileVC

스토리보드에 있는 ProfileVC에는 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ라고 적힌 Label이 있는데??

아! ProfileVC에 Outlet으로 가져오면 되겠구나!

 

class ProfileViewController: UIViewController {
    
    @IBOutlet weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .white
        
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.label.text = "aa"
    }


}

하고 실행!

Outlet을 지정한 후의 ProfileVC

엥? 아직도 안뜨네? 거기다가

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value 라는 에러도 나타났다.

이 에러는 타입에 !를 선언해주고 nil에 접근했을 때 나타나는 건데....

스토리보드에서 profileVC와 ProfileViewController를 연결하고 Outlet에 Label을 연결했고! 아무 문제가 없어야하는데 문제가 발생??




해결 방법

guard let profileVC = self.storyboard?.instantiateViewController(withIdentifier: "ProfileVC") else { return }

위 코드처럼 바꿔주면된다.

해석하자면

  1. self가 존재하는 스토리보드에 있는 VC들 중에 "ProfileVC"라는 ID를 가진 VC를 가져온다.

스토리보드에 UI Component들을 작업하고 그 컴포넌트들을 코드로 생성한 VC에서 사용하고 싶으면 이런 방법을 써야한다.

물론 스토리보드에 UI Component들을 넣지 않고 코드로

let label = UILabel() 로 선언한 후에

text와 constraint들을 지정해 줘도 되지만... 아직까지는 그렇게 익숙하지 않으니 이 방법도 알아보게 되었다.

+ Recent posts