CHCR을 아세요?

7과11사이·2023년 9월 24일
0

스파르타코딩클럽

목록 보기
64/90
post-thumbnail

Autolayout 관련해서 공부를 해보면서 배운 새로운 키워드가 있다!
UI를 그릴 때 내가 상상했던 그림과 다르게 나오던 이유를 설명하고 있기에 정리를 해본다.

*[1209] CHCR을 간단하게 적용하는 방법을 찾아 정리한다.
UIComponent의 경우, setContentHuggingPriority, setContentCompressionResistancePriority 메서드에서 호출하여 값을 적용할 수 있다. StackView 내에 있는 UIComponent들이 원하는 위치에 적용되지 않고 있을 경우, CHCR을 적용해볼 수 있는데, 이 때 사용하면 유용할 듯하다.

let noticeButton = UIButton().then {
        let image = UIImage(systemName: "square")?.withTintColor(ColorGuide.main, renderingMode: .alwaysOriginal)
        $0.setImage(image, for: .normal)
        $0.setContentHuggingPriority(.required, for: .horizontal)
        $0.setContentCompressionResistancePriority(.required, for: .horizontal)
    }

CHCR

(Content Hugging, Compression Resistance )

흔히 UIImage나 Label을 view에 배치할 때 한 UIComponent의 크기가 더 큰 상황을 겪어보지 않았나??
예를 들어 아래처럼 코드를 짠다고 했을 때 어떻게 보일지 잠깐 생각을 해보자!

TestRun

let topLeftLabel = makeLabel(withText: "Name")
let testTextfield = makeTextfield(withPlaceholder: "이름을 넣으세요!!")
        
view.addSubview(topLeftLabel)
view.addSubview(testTextfield)
        
NSLayoutConstraint.activate([
	topLeftLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8),
	topLeftLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),

	testTextfield.firstBaselineAnchor.constraint(equalTo: topLeftLabel.firstBaselineAnchor),
	testTextfield.leadingAnchor.constraint(equalTo: topLeftLabel.trailingAnchor, constant: 8),
    testTextfield.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8),
])

Label과 Textfield를 선언하고 Label 옆에 Textfield를 하나 나열했다.
Label의 경우, superview인 View의 leading과 Top에 제약을 걸어두었다.
Textfield는 반대로 leading을 label의 뒤를 잇고 view의 trailing에 제약을 걸어두었다.
추가로 firstBaseLine을 걸어둬서 동일 선상에서 배치될 수 있도록 했다.


예상되는 UI의 모습을 그려봤다.
topLeftLabel width는 text가 있기 때문에 intrinsic ContentSize 때문에 자동적으로 조절되겠고,
textfield가 우측 전반의 길이를 차지할 것으로 보인다.


실제 구현 모습

실제로 구현했을 때 모습은 아래와 같이 나오는데,

생각했던 것과 달리 label의 길이가 textfield보다 더 길게 나오게 된다!
왜 intrinsic contentsize는 무시가 된걸까??

위처럼 Label의 길이가 예상보다 길게 나온 이유는 IDE는 모호한 영역이 있으면 어떤 UIComponent의 길이를 우선적으로 늘려야할지 모르기 때문이라고 한다.
어떤 UIComponent가 늘어나거나 커져야하는지 모르기 때문에 IDE는 임의로 아무 영역의 길을 조절을 했던 것이다. 그렇기에 Label의 길이가 textfield의 길이보다 길게 나온 것!

이러한 상황은 UI를 그리면서 생각보다 자주 만나거나 만나게 될 것으로 보여지는데, 이 상황을 마주했을 때 고려해야하는 포인트가 CHCR이다.


CHCR 적용 모습

CHCR은 앞서 서술한대로 Content Hugging, Compression Resistance을 의미한다.
둘 다 일종의 '우선 순위'를 의미하는데,
Content Hugging은 UIComponent를 늘릴 의향이 있는지,
Compression Resistance는 UIComponent를 축소할 의향이 있는지 확인하는 것이다.



위 Label를 예시로 든다면,
topLeftLabel 안에 담긴 'name'이라는 text를 맞춰줄 것인지 아닌지 의미한다.

앞서 문제점은 어떤 UIComponent 길이를 늘려야할지 모르는 IDE의 입장이라고 적었다.
따라서 label이 담고 있는 content의 hugging 비율을 조절하면 문제가 해결이 된다!

let topLeftLabel = makeLabel(withText: "Name")
let testTextfield = makeTextfield(withPlaceholder: "이름을 넣으세요!!")
        
view.addSubview(topLeftLabel)
view.addSubview(testTextfield)
        
NSLayoutConstraint.activate([
	topLeftLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8),
	topLeftLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),

	testTextfield.firstBaselineAnchor.constraint(equalTo: topLeftLabel.firstBaselineAnchor),
	testTextfield.leadingAnchor.constraint(equalTo: topLeftLabel.trailingAnchor, constant: 8),
    testTextfield.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8),
])

// 아래 코드 적용 🙌🏻
topLeftLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal)

해당 코드를 적용했을 때 label이 완전 축소되는 점을 볼 수 있다!
이 이유는 UILabel은 담고 있는 content의 길이의 기본값을 250으로 두는데,
contentHuggingSize를 251로 늘리게 되면서 - UILabel은 본인이 가지고 있는 content 길이에 충실하겠다는 우선순위를 가지게 된 것이다.
한마디로, Label 길이를 content 길이보다 길게 만들 필요를 못 느껴 textfield에 남은 자리를 양보한 셈이다.

반대로 249로 지정을 하게 될 경우, textfield 길이가 필요성을 잃고 label에 자리를 양보한다.
이 이유는 textfield 또한 Label과 마찬가지로 특정 width 값을 지정받지 않았기 때문이다!


다시 정리를 한다면,
화면에 넣은 UIComponent의 공간이 여유롭고 기종에 따라 자유롭게 늘리고 싶다면 ContentHugging 값을 기본 값 250보다 낮게 적용하고 화면에 너무 많은 요소들이 있는데, 꼭 한 가지 UIComponent 내용을 소비자가 읽을 수 있도록 사이즈를 조절한다면 compression resistance를 250보다 높게 적용해서 UIComponent 값이 화면에 따라 줄어들지 않도록 적용하면 된다.

참고

0개의 댓글