AutoLayout의 설정에서 Changing Constraints에 대한 방법을 적어보려고 합니다.
이전 까지는 priority를 사용하여 변경을 하여 해결을 해왔었습니다.
하지만 그래프 라이브러리를 만들면서 priority만으로 해결하기 복잡한 경우에 어떤 방법이 있는지 말하려고 합니다.
제약 조건을 변경하는 방법
- 제약 조건 활성화 또는 비활성화하기
- 제약 조건의 상수 값 변경하기
- 제약 조건의 우선 순위 변경
- 뷰 계층에서 뷰 제거하기
이상적으로 대부분의 제약 조건은 아래와 같이 만들어야 합니다
- Interface Builder에서 설정
- 컨트롤러의 initial setup 설정 중에 ViewController 에서 programming 방식으로 설정(예: viewDidLoad)
런타임에 제약 조건을 동적으로 변경해야 하는 경우 일반적으로 애플리케이션의 상태가 변경될 때 제약 조건을 변경하는 것이 가장 좋습니다.
방법 1. 제약 조건 활성화 또는 비활성화
private lazy var leftConstraints = [
axisYView.leadingAnchor.constraint(equalTo: leadingAnchor),
axisXView.leadingAnchor.constraint(equalTo: axisYView.trailingAnchor, constant: 5),
axisXView.trailingAnchor.constraint(equalTo: trailingAnchor),
]
private lazy var rightConstraints = [
axisYView.trailingAnchor.constraint(equalTo: trailingAnchor),
axisXView.leadingAnchor.constraint(equalTo: leadingAnchor),
axisXView.trailingAnchor.constraint(equalTo: axisYView.leadingAnchor, constant: -5),
]
private func updateLayouts() {
switch axisY.position {
case .left:
NSLayoutConstraint.deactivate(rightConstraints)
NSLayoutConstraint.activate(leftConstraints)
case .right:
NSLayoutConstraint.deactivate(leftConstraints)
NSLayoutConstraint.activate(rightConstraints)
}
}
방법 2. 제약 조건 상수 값 변경
let postLeading: postLabel.leadingAnchor.constraint(greaterThanEqualTo: leadingAnchor, constant: 50)
postLeading.constant = 70
constraint 설정할 때, equalTo로 값을 지정하게되면 변경이 되지 않습니다.
방법 3. 제약 조건 우선 순위 변경
var postWidth: NSLayoutConstraint!
postWidth = postLabel.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.6)
postWidth.priority = .required
우선 순위 변경은 기존에 존재하는 제약 조건보다 우선 순위 높은 걸 실행하도록 만듭니다.
라이브러리에서 레이아웃을 변경을 하려고 하였습니다. 코드에서는 bounds.width 값으로 계산을 하기 위해서는 layoutSubviews로 사용하고 있었으나 레이아웃이 변경될 때마다 다시 그려지는 상황에 비효율적인 코드라 판단하였습니다.
제약 조건을 변경하기 위해서는 위에서 언급된 방법을 사용하기가 힘들었습니다.
- 제약 조건 multiplier를 변경 불가
- stackView의 너비를 변경하는 코드라 lessThanEqualTo를 사용하게 되면 distribution이 사라지는 문제
방법 5. 제약 조건 삭제 후, 재할당
private lazy var widthConstraint = stackView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1 - distribution)
var distribution: CGFloat {
didSet {
removeConstraint(widthConstraint)
widthConstraint = stackView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1 - distribution)
widthConstraint.isActive = true
}
}
변경 내용 일괄 처리
영향을 주는 변경이 발생한 직후에 제약 조건을 업데이트하는 것이 거의 더 깔끔하고 쉽습니다.
그러나 성능상의 이유로 변경 내용을 일괄 처리해야 하는 경우가 있습니다.
- 현재 제약 조건을 변경하는 속도가 너무 느린 경우
- 뷰가 여러 번 중복 변경을 수행하는 경우
변경 사항을 일괄 처리하려면 직접 변경하는 대신 제약 조건이 있는 뷰에서 setNeedsUpdateConstraints를 호출합니다.
그리고 updateConsraints를 재정의하여 양향을 받는 제약 조건을 수정합니다.
항상 updateConstraints 구현의 마지막 단계로 슈퍼 클래스 구현을 호출해야 합니다.
updateConstraints 내에서 setNeedsUpdateConstraints를 호출하지 마세요. 호출하면 다른 업데이트 패스가 예약되어 피드백 루프가 생성됩니다.
updateConstraints 구현은 가능한 효율적이어야 합니다. 변경해야 하는 항목만 변경하세요.
사용자 지정 레이아웃
viewWillLayoutSubviews 또는 layoutSubviews 메서드를 재정의하여 레이아웃 엔진에서 반환된 결과를 수정합니다.
뷰 계층 구조를 수정하는 방법에 대해 주의해야 하며, 그렇지 않으면 루프백 루프를 만들 수 있습니다.
- 메서드의 어딘가에서 수퍼 클래스의 구현을 호출
- 하위 트리의 뷰 레이아웃을 안전하게 무효화할 수 있습니다. 그러나 슈퍼클래스의 구현을 호출하기 전에 이 작업을 수행
- 하위 트리 외부에 있는 뷰의 레이아웃을 무효화하지 마세요
- setNeedsUpdateConstraints를 호출하지 마세요. 피드백 루프가 생성
- setNeedsLayout을 호출하지 마세요. 피드백 루프가 생성
가능하면 제약 조건을 사용하여 모든 레이아웃을 정의합니다. 그 결과 레이아웃은 더 강력하고 디버그하기 쉽습니다.
제약조건만으로는 표현할 수 없는 레이아웃을 만들어야 하는 경우에만 viewWillLayoutSubviews, layoutSubviews 메서드를 재정의해야 합니다.
Reference
- 아카이브 링크
아카이브 링크에는 스크롤 뷰 및 자체 크기 조정하는 테이블 뷰 셀 작업에 대한 내용도 있습니다.
'Language > Swift' 카테고리의 다른 글
[Swift] AttributedString (0) | 2023.11.16 |
---|---|
[Swift] UIButton Configuration (0) | 2023.11.15 |
[SwiftUI] WWDC23 Discover Observation in SwiftUI (1) | 2023.09.17 |
ARC, Reference Count 그리고 optional unowned (with WWDC21 ARC in Swift) (0) | 2023.08.12 |
[WWDC16] Understanding Swift Performance (3) (0) | 2023.08.02 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!