-Today's Learning Content-

  • Intrinsic Content Size
  • Priority

1. Intrinsic Content Size

개념 정리

Intrinsic Content Size란 어떠한 컨텐츠가 가지는 본질적인 크기를 의미한다. View 요소에서는 Label, Button, Switch, TextField 등의 내부 컨텐츠가 가지는 사이즈를 의미한다.

1) Intrinsic Content Size란 뭘까

우리는 UIKit으로 앱을 만들 때 뷰 요소를 배치하기 위해 오토레이아웃을 사용한다. 이 때, 오토레이아웃을 사용하려면 뷰 요소의 크기와 위치가 모두 정의되거나 유추할 수 있어야만 에러가 발생하지 않는다.

// 예시
NSLayoutConstraint.activate([
	button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
	button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])

그런데 UILabel의 경우 이렇게 해도 오류가 발생하지 않는다.

// UILabel의 경우
NSLayoutConstraint.activate([
	label1.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
	label1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30)
])

어? topAnchorleadingAnchor만 설정했는데 왜 오류가 안날까??

그 이유는 UILabelWidthHeight에 대해Intrinsic Content Size를 가지고 있기 때문이다.

그렇다면 Intrinsic Content Size 가 뭘까?

애플의 공식문서에 따르면 Intrinsic Content Size란 뷰 자체의 속성만 고려한 서브 뷰의 자연스러운 크기라고 한다.

이게 무슨 소리람...
코드 예시를 통해 알아보자.

예를 들어 UILabel의 길이와 높이를 아래와 같이 제한해보자.

label1.text = "Hello~"

NSLayoutConstraint.activate([
	label1.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
	label1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
	label1.widthAnchor.constraint(equalToConstant: 100),
	label1.heightAnchor.constraint(equalToConstant: 50)
])

그럼 화면에는 이렇게 표시될 것이다.

그런데 만약 레이블의 길이가 길어지면 어떻게 될까?

label1.text = "Hello~ I don't want to use TIL~"

위처럼 텍스트가 짤리는 현상을 볼 수 있다. 그러나 만약 UILabel의 높이와 길이를 제한하지 않으면...

label1.text = "Hello~ I don't want to use TIL~"

NSLayoutConstraint.activate([
	label1.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
	label1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30)
])

이렇게 글씨가 짤리지 않고 모두 출력되는 모습을 볼 수 있다.
UILabel레이블 안에 들어가는 텍스트의 길이, 폰트 등의 속성값에 따라 본질적인 Width, Height를 자체적으로 갖고있기 때문에 오토레이아웃 등을 통해 사이즈를 지정해주지 않아도 에러가 발생하지 않는 것이다.

이것을 Intrinsic Content Size라고 한다.

하지만 모든 UIView가 이것을 가지고 있지는 않다.

let view = UIView()

print(view.view.intrinsicContentSize) 

// 출력 
// (-1, -1)

그러나 원한다면 Intrinsic Content Size을 적용할 수는 있다.

class CustomView: UIView { 
  let button: UIButton = UIButton()
	
  override var intrinsicContentSize: CGSize {
    let height = button.frame.size.height
    let width =  button.frame.origin.x + btn.frame.size.width
    return CGSize(width: width, height: height)
  }
}

대표적으로 Intrinsic Content Size를 가지는 UIView는 아래와 같다.

구분Intrinsic Content Size WidthIntrinsic Content Size Height
UIViewXX
UISliderOX
UILabel, UIButton, UISwitch, UITextFieldOO
TextView, ImageViewContent에 따라 변함Content에 따라 변함

invalidateIntrinsicContentSize도 있는데, 이 코드는 Intrinsic Content Size를 무효화 한다.
즉, View의 크기 변화가 발생했을 때 invalidateIntrinsicContentSize 메소드를 호출하여 기존의 Intrinsic Content Size를 무효화하고, 새로운 Intrinsic Content Size를 계산하여 변화된 크기를 적용할 수 있도록 도와주는 코드이다.

2) Priority는 뭘까

Priority는 우선순위란 뜻으로, View 요소를 배치할 때 어느 뷰를 더 우선할 것인지 정의하는 옵션이다.

Priority는 두가지 타입이 있는데, Content Hugging PriorityContent Compression Resistance Priority이 있다. 이는 오토레이아웃에서 사용되는 메소드로, UIView가 자신의 컨텐츠 크기에 대해 얼마나 더 강하게 붙잡고 있으려고 하는지 결정하는 우선순위(Priority)를 설정할 수 있다.

  1. Content Hugging Priority:

    • Content Hugging Priority는 뷰가 Intrinsic Content Size보다 커지는 것을 허용하지 않는다. 즉, 우선순위가 낮은 뷰의 크기가 늘어나게 된다.
  2. Content Compression Resistance Priority:

    • Content Compression Resistance Priority는 뷰가 Intrinsic Content Size보다 작아지는 것을 허용하지 않는다. 즉, 우선순위가 낮은 뷰의 크기가 작아지게 된다.

말로만 설명을 보면 어려우니 실제 코드를 예제로 확인해보자.

// Content Hugging Priority 예제
let label = UILabel()
let button = UIButton()

label.text = "Shot Text"
button.setTitle("Press Me", for: .normal")
// 설정 생략...

label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
button.setContentHuggingPriority(.defaultLow, for: .horizontal)

NSLayoutConstraint.activate([
	label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
	label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
            
	button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30),
	button.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
	button.leadingAnchor.constraint(equalTo: label.trailingAnchor) // 버튼과 관계 설정
])

위 코드에서 레이블은 UILayoutPriority.defaultHight(750)으로 설정하여 높은 우선순위를 가지도록 하고, 버튼은 .defaultLow(250)으로 설정하여 낮은 우선순위를 가지도록 하였다.
때문에 레이블은 여유 공간이 있더라도 현재 사이즈를 지키며 가로로 늘어나지 않으려고 하고, 버튼은 여유공간이 있다면 가로로 더 쉽게 확장이 될 것이다.

둘의 우선순위를 바꾸면 반대가 된다.

다음 예제를 살펴보자.

// Content Compression Resistance Priority 사용 예시
let label = UILabel()
let button = UIButton()

label.text = "Compression Resistance Priority"
button.setTitle("Press", for: .normal")
// 설정 생략...

label.setContentCompressionResistancePriority(.required, for: .horizontal)
button.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

NSLayoutConstraint.activate([
	label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
	label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
            
	button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30),
	button.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
	button.leadingAnchor.constraint(equalTo: label.trailingAnchor) // 버튼과 관계 설정
])

위 코드에서 레이블은 우선순위를 .required로 설정하여 사이즈가 감소하는 것을 강력하게 거부하도록 최고 우선순위를 설정하고, 버튼은 .defaultLow로 설정하여 필요에 따라 쉽게 축소되도록 설정하였다.
때문에 레이블의 텍스트가 길어지면 버튼은 사이즈가 계속해서 줄어들 것이고, 버튼의 크기가 아무리 커져도 레이블은 Intrinsic Content Size보다 작아지지 않을 것이다.

이 역시 반대로 적용한다면 반대의 결과가 나온다.

3) 사용 방법

그렇다면 위에서 배운 코드들은 어떤 상황에 사용할 수 있을까?

먼저 setContentHuggingPriority는 아래와 같은 상황에서 사용할 수 있다.

  1. UILabel과 UIButton이 나란히 있을 때:

    • UILabel은 보통 짧고 변경되지 않는 텍스트일 수 있어, 늘어나지 않도록 Content Hugging Priority를 높게 설정한다.
    • UIButton이나 다른 동적 뷰는 공간을 더 유연하게 사용할 수 있도록 낮게 설정한다.
  2. 동적 길이의 컨텐츠 처리:

    • 다양한 텍스트 길이를 처리할 때, 뷰의 컨텐츠가 뷰 크기보다 커지지 않게 고정하거나, 반대로 늘어나지 않도록 조정할 때 유용하다.

다음으로 setContentCompressionResistancePriority는 다음과 같은 상황에서 쓸 수 있다.

  1. UILabel의 긴 텍스트 처리
    • 레이블의 텍스트가 길 경우, 다른 뷰들이 줄어들도록 설정하고 레이블은 크기를 유지하게 만든다.
  2. 버튼이나 이미지 뷰 축소 방지:
    • 버튼이나 이미지는 축소되면 안 되는 경우가 많으므로, 우선순위를 높게 설정한다.
  3. 텍스트가 잘리지 않게 유지:
    • 중요한 텍스트가 포함된 뷰의 압축 저항 우선순위를 높게 설정하여, 화면이 좁아지더라도 텍스트가 잘리지 않도록 설정한다.

-Today's Lesson Review-

오늘은 UIKit에서 UILabel, UIButtonUIView의 컨텐츠 사이즈와
우선순위에 대한 내용을 학습하였다.
스토리보드에서의 사용법은 알고 있었지만, 코드베이스에서는 어떻게 사용하는지 몰랐기 때문에
오늘의 학습을 통해 앞으로 어떻게 사용하면 좋을지 알 수 있어서 유익한 시간이었다.
profile
이유있는 코드를 쓰자!!

2개의 댓글

comment-user-thumbnail
2024년 11월 22일

덕분에 저도 코드베이스 프리오뤼리에 대해 짚어볼 수 있어서 아주 유익했어요 구우웃!!

1개의 답글