오토 레이아웃이란, 제약 조건에 따라 뷰 계층 구조에 있는 모든 뷰의 크기와 위치를 동적으로 지정하는 것이다.
만약 iPhone 12Pro Max에서 Frame으로 구성한다면,
let myView: UIImageView = .init(frame: .init(x: 30, y: 80, width: 350, height: 200))
이렇게 구성해주면, iPhone 12 Pro Max에서는 잘 나오지만, iPhone 12 Pro Max와 해상도가 다른 iPhone8, iPad화면에서는 위치가 이상하게 나온다. Frame
은 나에게 주어진 값을 그대로 그려버리기 때문이다. 현재 화면의 크기와 해상도가 어떻든 간에 무조건 원점에서 (30, 80) 떨어진 곳에서 (350, 200)크기의 imageView
를 그려버린다. 만약 모든 해상도에 대해서 다 iPhone 12 Pro Max 와 같은 모습으로 그리고 싶다면 오토 레이아웃
을 사용한다.
Auto Layout은 제약조건(Constraint)
를 이용해서 View의 위치나 크기를 정하는 것이다. view
의 frame
좌표를 직접 지정해주는 것이 아니라, imageView의 위치나 크기를 다른 객체(Safe Area)
를 이용해 "상대적"
으로 제약을 주는 것이다. 내 imageView
는 어떤 해상도일지라도, 기준 객체(SafeArea)
의 왼쪽으로 30만큼, 오른쪽으로 30만큼, 위로는 50만큼 margin
이 있어야 하고, height의 크기는 200
이다 라는 제약조건을 주는 것이다. 여기서 왼쪽30, 오른쪽30 이렇게 지정하는 것이 Constraint
이다. 이처럼 내 위치를 다른 객체로부터 상대적으로 나타내는 것이 Constraint 이다.
따라서 해상도가 변하더라도
기준 객체(Safe Area)로부터 왼쪽으로 30, 오른쪽으로 30, 위로 50 만큼의 margin이 있으며, height가 200이란 제약 조건이 있는 ImageView를 그리는 것이다.
이렇게 설정할 경우, imageView의 width를 별도로 지정해주지 않았지만, 이 경우엔 Leading/Trailing Constaraints에 의해 해상도 별로 width가 자동으로 지정된다.
만약 가로의 크기가 100인 해상도일 경우, imageView는 leading 30 + trailing 30 margin을 뺀 40만큼 width를 가질 것이고, 만약 가로의 크기가 500인 해상도일 경우, imageView는 leading30 + trailing 30 margin을 뺀 440만큼 width를 가질 것이다. 이렇게 Auto Layout으로 설정할 경우, 화면의 해상도에 따라 Frame이 동적으로 달라질 수 있다. 이렇게, imageVi
ew의 width가 Constraint에 의해 동적으로 지정되는데,
만약 이렇게 width를 또 직접 지정하는 Constraint를 추가해버리면, Constraint가 충돌했다며, leading/ trailing/ width 중 하나를 지우라는 에러가 뜬다. Constraints로 인해 width가 동적으로 결정되는데 이를 따를 것인지 300이란 숫자를 따를 것인지 몰라서 에러가 생기는 것이다.
Top, Bottom은 위쪽, 아래쪽의 제약을 설정하는 것이다.
Left, Right 또한 왼쪽, 오른쪽 제약을 설정하는 것이다.
근데, 보통은 Left/ Right 대신 Leading/ Trailing을 왼쪽 오른쪽 처럼 사용한다.
Xcode 9 부터는 ViewController를 생성하면 Default로 SafeArea가 설정되어 있다고 한다. Root View에다가 View 하나를 추가해서 Constraint를 모두 0으로 설정하면, 기준이 Safe Area로 잡힌다. Safe Area로 Constraints가 잡힌 경우엔, 다음과 같은 시스템 마진이 알아서 잡혀서 View가 그려진다.
이렇게 시스템에 의해 가려질 수 있는 부분의 마진을 자체적으로 가직는 것이 Safe Area이다.
Safe Area로 설정되어 있는 Constraint를 더블 클릭 하고 Safe Area로 되어 있는 기준 뷰를 SuperView로 바꿔주면 Trailing Constraint에 한해서 Safe Area 적용을 풀 수 있다.
viewController에서 Use Safe Area Layout Guides를 꺼주면, Safe Area는 사라진다. 하지만 특별한 경우가 아닌이상, Safe Area 사용을 권장한다.
다음과 같이 노란 UIView를 만들 때 Auto Layout을 이용해서
Safe Area와의 간격을 직접 Top은 50, Leading은 30, Trailing은 30 만큼, Height는 200으로 고정 이렇게 지정할 수 있다.
Auto Layout도 View의 위치와 크기를 지정하는 것이기 때문에, 결과적으로 Auto Layout을 통해 View "위치"와 "크기"를 알 수 있어야한다.
근데 위에서 Height는 200인 값으로 고정시켰지만, Width 값은 설정하지 않았다. 하지만 Constraints를 통해 Width를 계산해 낼 수 있다. Leading / Trailing Constraints에 의해 해상도 별로 width가 자동으로 지정된다.
만약 여기서 내가 Trailing을 삭제하면 Width를 동적으로 계산할 수 없어서 Width Constraints를 추가하라며 레이아웃단 에러가 난다. 이 에러가 나는 이유는 UIView의 Width는 Instrinsic Content Size를 가지지 않기 때문이다. 그러나 UILabel의 경우 Intrinsic Content Size를 가지기 때문에 에러가 나지 않는다.
컨텐츠의 본질적인 크기
Intrinsic Content Size란 말 그대로 컨텐츠의 본질적인 크기를 가르킨다. 위에서 Label의 Width를 우리가 지정하지 않았지만, 레이아웃단 에러가 나지 않은 이유는 Label은 Width 뿐만 아니라 Height에 대해서도 Intrinsic Content Size를 가지기 때문이다.
예를들어, 우리가 다음과 같이 만약 Label의 Width와 Height를 각각 100과 50으로 주면,
Label의 글자 크기가 위와 같을 땐 문제가 없지만 만약 해당 글자의 크기를 키우고 폰트도 바꾼다면
Constraints에 의해 Width가 100, Height가 50으로 지정됐기 때문에, 그 크기를 넘길 시 글자들은 짤려버린다.
따라서 Width와 Height Constraints를 지워버리게 되면, Label은 자체적으로 본질적인 Width와 Height, 즉 Intrinsic Content Size를 가지기 때문에 알아서 Size가 조절이 된다.
이렇게 Label 안에 들어가는 텍스트의 길이, 폰트에 따라 본질적인 Width, Height를 자체적으로 갖기 때문에, Label에 대해 Width, Height를 지정해주지 않아도 에러가 나지 않는다.
여러 개의 뷰를 정렬해서 나타낼 때 능률적인 인터페이스다.
스택뷰란 바둑판처럼 행&열로 나열되는 뷰들의 AutoLayout을 설정할 때 간편한 인터페이스이다.
스택뷰는 총 두가지가 존재한다.
인터페이스 빌더에서 StackView를 생성하거나 스택뷰에 넣고 싶은 뷰들을 모두 클릭한 상태로 Stack View를 클릭해서 생성할 수 있다.
스택뷰 안의 뷰들의 Constraints을 설정하지 않아도 되지만 스택뷰의 위치정도는 AutoLayout으로 잡아줘야 한다. 스택뷰는 내부 뷰의 크기에 따라 사이즈(width, height)가 자동으로 정해지기 때문에 Top, Leading(혹은 Trailing)으로 스택뷰의 위치만 설정해주어도 AutoLayout설정이 된다. 이런식으로 스택뷰의 AutoLayout을 맞춰주면, 내부 뷰들은 그에 따라 별도의 Constraints 설정 없이 정렬이 된다.
Spacing은 스택뷰 안의 뷰 간 간격을 설정할 때 사용한다.
잘 읽었습니다. 좋은 정보 감사드립니다.