cell의 넓이는 고정값으로 줄 수 있지만 높이는 내용에 따라 동적으로 할당 받아야하는데 어떻게 해야할지 뒤적뒤적이다 UICollectionViewDelegateFlowLayout라는 protocol이 있다는 것을 발견
요놈을 활용해 동적 높이를 할당해보자
공식문서
UICollectionViewDelegateFlowLayout
The methods that let you coordinate with a flow layout object to implement a grid-based layout.
The methods of this protocol define the size of items and the spacing between items in the grid.
공식문서를 요약하면 이렇게 나온다.
즉 아이템과 grid사이의 size와 spacing 조절해주는 protocol이런건데 이걸 보고 딱 감이왔다 아 이놈이네?
여담이지만 공부하다보니 이 프로토콜을 사용하여 셀의 크기, 헤더와 푸터의 크기, 섹션간 간격 등을 지정할 수 있다고 한다.
해결해보자
메서드들의 쓰임세를 정리하면 아래와 같다.
- 셀 크기 조정: sizeForItemAt 셀의 크기를 지정
- 섹션 패딩 조정: insetForSectionAt 섹션의 내부 여백을 설정
- 행 간의 간격 지정: minimumLineSpacingForSectionAt
- 아이템 간의 간격 지정: minimumInteritemSpacingForSectionAt
1트
나는 셀 크기를 동적으로 지정해줘야하니 sizeForItemAt를 사용해서 임시로 아래와 같이 만들어줬다.
extension DetailChatViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let message = viewModel.messageRepository.getMessages()[indexPath.row].content
return CGSize(width: collectionView.frame.width, height: 100)
}
}
처음 코드를 보고 오 쉽네 ? 그냥 데이터 내용을 height에 넣고 리턴해주면 되는건가라고 생각 할 수 있지만
height 100이 넘어가지 안는 내에서 만 작동을 하게 된다.
즉 sizeToFit()를 사용할때처럼 크기를 딱 맞춰서 동적인 UI를 적용하고 싶었다.
retrun타입을 보니 CGSize, message를 CGFloat타입으로 만들어버리면 끝이구만
2트
boundingRect(with:options:attributes:context:)라는 메서드는 문자열에 대해 지정된 속성을 사용하여 그 텍스트가 차지할 최소 영역을 계산하고 그 결과를 CGRect로 반환하는 메서드이다. 즉 문자열의 구성을 보고 크기가 얼마인지 정확하게 계산해 retrun을 해주는 메서드, 이것을 활용해 texrtView에서 나오는 text들의 크기를 측정하고 반환하도록 했다.
- boundingRect() 를 사용하기 위해 CGSize가 필요하다.
- height는 greatestFiniteMagnitude**를이용해서 무한대로 조정
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let message = viewModel.messageRepository.getMessages()[indexPath.row].content
let maxWidth = collectionView.frame.width - 20
let textSize = CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude)
let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)]
let estimatedFrame = NSString(string: message).boundingRect(with: textSize, options: .usesLineFragmentOrigin, attributes: nil, context: nil)
return CGSize(width: collectionView.frame.width, height: ceil(estimatedFrame.height) + 20) // 추가 마진
}
결과 …

작동은 잘 되지만 …..
??? 어마무지한 Autolayout충돌이 되는 모습을 보여준다 ..
3트.. 해결
두번째 시도에서 attributes에 nil을 넣어줬던 것이 실수였다.
UICollectionViewLayout에서 attributes역할은
- 폰트 스타일: 텍스트의 폰트 종류와 크기를 지정 예를 들어, **UIFont.systemFont(ofSize: 15)**는 시스템 폰트로 15포인트 크기를 사용
- 텍스트 색상: **NSAttributedString.Key.foregroundColor**를 사용하여 텍스트의 색상을 지정
- 밑줄: NSAttributedString.Key.underlineStyle을 사용하여 텍스트에 밑줄을 추가
- 텍스트 정렬: NSAttributedString.Key.paragraphStyle를 사용하여 텍스트의 정렬(왼쪽, 가운데, 오른쪽)을 설정 가능.
- 배경 색상: NSAttributedString.Key.backgroundColor를 사용하여 텍스트의 배경 색상을 지정
- 삭제선: NSAttributedString.Key.strikethroughStyle을 사용하여 텍스트에 삭제선을 추가 가능
- 커스텀 속성: 개발자가 정의한 커스텀 속성을 텍스트에 적용할 가능
이러한 역할을 갖고 있다고 한다.
<예제코드>
let attributes: [NSAttributedString.Key: Any] = [
.font: UIFont.boldSystemFont(ofSize: 16),
.foregroundColor: UIColor.blue,
.backgroundColor: UIColor.yellow,
.underlineStyle: NSUnderlineStyle.single.rawValue
]
let attributedString = NSAttributedString(string: "Hello, world!", attributes: attributes)
두번째 코드에서 attributes를 추가해주고 width를 collectionView크기로 맞춰줘 통일감을 주었다..
//두번째 코드
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let message = viewModel.messageRepository.getMessages()[indexPath.row].content
let maxWidth = collectionView.frame.width - 20
let textSize = CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude)
let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)]
let estimatedFrame = NSString(string: message).boundingRect(with: textSize, options: .usesLineFragmentOrigin, attributes: nil, context: nil)
return CGSize(width: collectionView.frame.width, height: ceil(estimatedFrame.height) + 20) // 추가 마진
}
//리팩토링 코드 (해결코드)
extension DetailChatViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let message = viewModel.messageRepository.getMessages()[indexPath.row].content
let width = collectionView.frame.width
let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)]
let estimatedFrame = NSString(string: message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)
return CGSize(width: width, height: estimatedFrame.height + 20)
}
}
잘 돌아간다 ! 완료 !!

'🍎swift' 카테고리의 다른 글
swift indicator 구현 (0) | 2024.04.15 |
---|---|
Swift 배열, 동시성 문제 (0) | 2024.04.04 |
URLRequestBuilder만들기 .. feat 리팩토링 (0) | 2024.04.02 |
TLI - Concurrency (0) | 2024.01.17 |
[Swift] Stroyboard Navigation Controller를 활용한 화면이동 (1) | 2023.12.21 |
cell의 넓이는 고정값으로 줄 수 있지만 높이는 내용에 따라 동적으로 할당 받아야하는데 어떻게 해야할지 뒤적뒤적이다 UICollectionViewDelegateFlowLayout라는 protocol이 있다는 것을 발견
요놈을 활용해 동적 높이를 할당해보자
공식문서
UICollectionViewDelegateFlowLayout
The methods that let you coordinate with a flow layout object to implement a grid-based layout.
The methods of this protocol define the size of items and the spacing between items in the grid.
공식문서를 요약하면 이렇게 나온다.
즉 아이템과 grid사이의 size와 spacing 조절해주는 protocol이런건데 이걸 보고 딱 감이왔다 아 이놈이네?
여담이지만 공부하다보니 이 프로토콜을 사용하여 셀의 크기, 헤더와 푸터의 크기, 섹션간 간격 등을 지정할 수 있다고 한다.
해결해보자
메서드들의 쓰임세를 정리하면 아래와 같다.
- 셀 크기 조정: sizeForItemAt 셀의 크기를 지정
- 섹션 패딩 조정: insetForSectionAt 섹션의 내부 여백을 설정
- 행 간의 간격 지정: minimumLineSpacingForSectionAt
- 아이템 간의 간격 지정: minimumInteritemSpacingForSectionAt
1트
나는 셀 크기를 동적으로 지정해줘야하니 sizeForItemAt를 사용해서 임시로 아래와 같이 만들어줬다.
extension DetailChatViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let message = viewModel.messageRepository.getMessages()[indexPath.row].content
return CGSize(width: collectionView.frame.width, height: 100)
}
}
처음 코드를 보고 오 쉽네 ? 그냥 데이터 내용을 height에 넣고 리턴해주면 되는건가라고 생각 할 수 있지만
height 100이 넘어가지 안는 내에서 만 작동을 하게 된다.
즉 sizeToFit()를 사용할때처럼 크기를 딱 맞춰서 동적인 UI를 적용하고 싶었다.
retrun타입을 보니 CGSize, message를 CGFloat타입으로 만들어버리면 끝이구만
2트
boundingRect(with:options:attributes:context:)라는 메서드는 문자열에 대해 지정된 속성을 사용하여 그 텍스트가 차지할 최소 영역을 계산하고 그 결과를 CGRect로 반환하는 메서드이다. 즉 문자열의 구성을 보고 크기가 얼마인지 정확하게 계산해 retrun을 해주는 메서드, 이것을 활용해 texrtView에서 나오는 text들의 크기를 측정하고 반환하도록 했다.
- boundingRect() 를 사용하기 위해 CGSize가 필요하다.
- height는 greatestFiniteMagnitude**를이용해서 무한대로 조정
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let message = viewModel.messageRepository.getMessages()[indexPath.row].content
let maxWidth = collectionView.frame.width - 20
let textSize = CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude)
let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)]
let estimatedFrame = NSString(string: message).boundingRect(with: textSize, options: .usesLineFragmentOrigin, attributes: nil, context: nil)
return CGSize(width: collectionView.frame.width, height: ceil(estimatedFrame.height) + 20) // 추가 마진
}
결과 …

작동은 잘 되지만 …..
??? 어마무지한 Autolayout충돌이 되는 모습을 보여준다 ..
3트.. 해결
두번째 시도에서 attributes에 nil을 넣어줬던 것이 실수였다.
UICollectionViewLayout에서 attributes역할은
- 폰트 스타일: 텍스트의 폰트 종류와 크기를 지정 예를 들어, **UIFont.systemFont(ofSize: 15)**는 시스템 폰트로 15포인트 크기를 사용
- 텍스트 색상: **NSAttributedString.Key.foregroundColor**를 사용하여 텍스트의 색상을 지정
- 밑줄: NSAttributedString.Key.underlineStyle을 사용하여 텍스트에 밑줄을 추가
- 텍스트 정렬: NSAttributedString.Key.paragraphStyle를 사용하여 텍스트의 정렬(왼쪽, 가운데, 오른쪽)을 설정 가능.
- 배경 색상: NSAttributedString.Key.backgroundColor를 사용하여 텍스트의 배경 색상을 지정
- 삭제선: NSAttributedString.Key.strikethroughStyle을 사용하여 텍스트에 삭제선을 추가 가능
- 커스텀 속성: 개발자가 정의한 커스텀 속성을 텍스트에 적용할 가능
이러한 역할을 갖고 있다고 한다.
<예제코드>
let attributes: [NSAttributedString.Key: Any] = [
.font: UIFont.boldSystemFont(ofSize: 16),
.foregroundColor: UIColor.blue,
.backgroundColor: UIColor.yellow,
.underlineStyle: NSUnderlineStyle.single.rawValue
]
let attributedString = NSAttributedString(string: "Hello, world!", attributes: attributes)
두번째 코드에서 attributes를 추가해주고 width를 collectionView크기로 맞춰줘 통일감을 주었다..
//두번째 코드
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let message = viewModel.messageRepository.getMessages()[indexPath.row].content
let maxWidth = collectionView.frame.width - 20
let textSize = CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude)
let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)]
let estimatedFrame = NSString(string: message).boundingRect(with: textSize, options: .usesLineFragmentOrigin, attributes: nil, context: nil)
return CGSize(width: collectionView.frame.width, height: ceil(estimatedFrame.height) + 20) // 추가 마진
}
//리팩토링 코드 (해결코드)
extension DetailChatViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let message = viewModel.messageRepository.getMessages()[indexPath.row].content
let width = collectionView.frame.width
let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)]
let estimatedFrame = NSString(string: message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)
return CGSize(width: width, height: estimatedFrame.height + 20)
}
}
잘 돌아간다 ! 완료 !!

'🍎swift' 카테고리의 다른 글
swift indicator 구현 (0) | 2024.04.15 |
---|---|
Swift 배열, 동시성 문제 (0) | 2024.04.04 |
URLRequestBuilder만들기 .. feat 리팩토링 (0) | 2024.04.02 |
TLI - Concurrency (0) | 2024.01.17 |
[Swift] Stroyboard Navigation Controller를 활용한 화면이동 (1) | 2023.12.21 |