- 오토 레이아웃의 본질
- Input: Constraint들
- 연산: Constraint -> Equations -> (Linear Algebra)
- Output: 연관된 뷰들의 프레임
- Maintainable Layouts
- 뷰를 여러개로 쌓아야 한다면, 스택뷰를 쓰라
- 오토레이아웃 기반으로 만들어졌고, 방향 지정이 가능하다.
- 각 뷰의 비율 까지도 조정할 수 있다.
- Hidden할 때도 스무스하게 처리된다.(애니메이션도)
// iOS
UIView.animateWithDuration(1.0) {
self.subviewToHide.isHidden = !self.subViewToHide.isHidden
}
NSAnimationContext.runAnimationGroup({ context in
context.duration = 1.0
self.subviewToHide.animator().hidden = !self.subviewToHide.hidden
}, completionHandler: nil)
- stackView의 하위 뷰이지만 stack되지 않는 뷰는 그냥 addSubView 해주면 된다.
- IB에서의 스택뷰
- 빌드가 편하다
- 유지보수가 편하다
- 합성이 가능하다.
- 단순히 레이아웃이기 때문에 렌더링하는 비용이 적게 든다.
- Changing Constraints
- constraint 선언 -> 레이아웃 엔진 -> 레이아웃
- 기존 컨셉은 constraint를 add/remove하는 것이였는데, 그러지 말라
- 대신activate/deactivate해라.
- Constraint가 뷰를 직접 찾는다.
- 효율적으로 constarints를 추가할 수 있따.
- 모든 뷰를 가지고 있을 필요가 없다.
- 염두할 것
- 절대로 self.view.constraint를 한꺼번에 deactivate하지 마라.
- 개발자가 직접 만들지 않은 것도 포함되어 있다.
- 대신 바꿀 constraint에 대한 참조를 따로 가지고 있다가 이걸로 다루라.
- constraint 변경은 애니매이션이 가능하다. 애니매이션 할 수 있으면 하라
- View Sizing
- Constraint이외에도 추가적으로 쓸 수 있는 정보가 있다면 좀 더 좋은 결과를 낼 수 있다.
- intrinsic context size: 라벨, 이미지 뷰 등은 본연의 사이즈를 가지고 있다.
- 이 정보를 받아서 엔진이 크기 constraint를 만들어준다.
- 다만 크기가 유지될 것 이란 보장은 하지 못한다.
- 뷰 크기를 결정할 때
- constraint를 우선 사용하라.
- intrinsicContentSize를 오버라이드 해야 될 때는
- constraint에서 size 정보를 구할 수 없을 때
- 커스텀 드로잉을 할 때(꼭 그런 것은 아님)
- 대신 오버라이드를 하면 직접 intrinsicContentSize를 invalidate할 책임이 있다.
- Self-Sizing Table View Cells
- Self-Sizing Cell은 constraint를 기반으로 사이즈를 구한다.
- width는 이미 주어져 있으니 height만 신경 쓰면 된다. -> height에 대한 constraint가 반드시 주어져야 한다. -> 비율로 지정해 주는 것이 좋다.
- Priorities
- ambiguity 문제가 있을 수 있다.
- constraint 수가 부족하거나
- 우선순위가 같은 Constraint가 충돌할 때
- 우선순위는 1~1000의 값을 가진다.
- 1000은 required(필수)
- default High: 750, default low: 250
- 일부 우선순위 값음 시스템에서 이미 사용하고 있다.
- window 에서 대략 500 정도의 값 사용
- 1000으로 설정하면 문제가 생겼을 때 unsatisfiable constraint 문제가 생길 수 있다.
- content자체의 제약
- content hugging: 컨텐츠보다 뷰가 줄어들려는 것 기본이 250(low)
- content compression resistance: 컨텐츠보다 뷰가 늘어나려는 것. 기본이 750(high)
- Alignment: 레이아웃 엔진의 마지막 입력
- baseline Alignment: 텍스트가 들어간 뷰를 정렬할 때는 top, bottom 보다 좋다.
- 텍스트가 포함 된 뷰의 변화에 따른 대응을 하기 좋다.
- Leading / Trailing: 언어에 따른 방향 변화에 대응하기 좋다. Left / Right보다 좋다.
- Alignment Rect: 레이아웃을 위해 사용
- 기본적으로는 프레임과 동일하다(not always)
- 핵심 컨텐츠만 포함한다.
- 뷰가 트랜스폼 되어도 변하지 않는다.
- 필요하면 alignmentRectInsets를 오버라이드 할 수 있다.
- 디버깅하는 법
- IB에서는 Show Alignment Rectangle을 디버그 메뉴에서 활성화 시켜라
- alignmentRectForFrame: 메소드를 써도 된다.
- Part2에서 보강할 예정
- 여기까지 해서, 레이아웃 엔진의 입력은
- 뷰 사이의 constraint
- 뷰의 크기 constraint,
- intrinsicContentSize
- Constraint들의 우선순위
- Content에 의해 자연적으로 생기는 Constraint들의 우선순위(Hugging, Compression Resistance)
- alignment
- Baseline alignment
- Vertical alignment
- Horizontal alignment
- Alignment rect insets
- 레이아웃 엔진은 alignment rects를 만들어서 배치하고, 이를 통해서 실제 레이아웃을 만들어 낸다
Part2
- The Layout Cycle
- App RunLoop -> Constraint Change -> Deferred Layout Pass -> App RunLoop
- Constraint Change
- activating / deactivating
- Constant / Priority 변경
- 뷰 추가 및 제거
- Constraint가 바뀌면
- Engine은 새로운 값들을 받아서 재계산을 수행함
- 바뀐 뷰의 슈퍼 뷰에 대해서 setNeedsLayout이 호출됨(deferred Layout Pass)
- 이후 재계산 된 뷰를 재 배치함
- Constraint 업데이트: setNeedsUpdateConstraints()
- 최초 초기화 시에는 viewDidLoad가 좋다.
- 로직 중간에 바꿔야 하는 경우에는 거기서 하는게 유지보수 하기 좋다.
- 그럼에도 해야 하는 경우
- 성능 최적화: 여기서 한꺼번에 하는게 성능상 좋을 것이다.
- 중복해서 바꾸게 되는 경우, 여기서 한번만 바꾸도록하는게 좋다.
- 뷰 프레임 재배치: layoutSubviews()(macOS에서는 layout())
- top-down 방식으로 재귀적으로 뷰를 순회함
- 즉, 프레임은 즉시 갱신되지 않는다.
- layout engine에서 뷰의 프레임 값을 가져와서 대입함
- OS X에서는 setFrame()
- iOS에서는 setBounds(), setCenter()
- 커스텀 레이아웃을 위해 layoutSubviews()를 오버라이드 할 수 있다.
- 다만 주의해야 된다. Constraint로 부족할 때만 오버라이드 해야한다.
- 이 메소드가 호출 될 때, 일부 뷰는 이미 레이아웃이 잡힌 상황이다.
- Do
- super.layoutSubviews()를 반드시 호출해야 한다.
- 서브트리의 뷰를 invalidate하는 것은 좋지만 슈퍼 클래스에서의 구현을 통해 invalidate되기 전에 하는 게 좋다.
- Don’t
- setNeedsUpdateConstraints()는 호출하지 말아야 한다.(이미 타이밍이 늦었다)
- 서브트리 바깥에 있는 뷰를 invalidate하지 말아야 한다.
- Constraint를 함부로 바꾸면 안된다. -> 무한 피드백 루프에 빠질 수 있다.
- legacy layout과의 소통
- frame을 직접 세팅하는 레이아웃 방식과의 공존을 하려면 translatesAutoResizingMaskIntoConstraints 옵션을 키면 된다.
- 프레임을 세팅하면 자동으로 constraint로 만들어준다.
- autoresizingMask도 반영한다.
- constraint를 사용할 때는 꺼야 한다.
- 코드로 만든 뷰는 기본이 true기 때문에 주의해야 한다.
- Constraint 만들기
- 기본적으로는 NSLayoutConstraint로 만들다.