-
UICollectionView서 헤더로 다이나믹하게 높이 계산하는 로직 (UITextView) 넣기iOS 2018. 7. 25. 15:09반응형
개인적으로 파는 소스에서 콜렉션뷰를 짜는데, 헤더 부분에 텍스트뷰가 있어서 글을 쓰면 쓰는 만큼 늘어나는 레이아웃이 필요하게 되었습니다.
안드로이드라면 퍽 쉽게 가능한데... iOS는 굉장히 복잡하단 말이죠..
몇 번의 검색과 삽질, 연구 끝에 나름대로 구현.
기본 높이 (50)을 주되, 그 이상 늘어나면 헤더의 높이가 리사이징됩니다.
이게 예제.
import Foundation
import UIKit
import SnapKit
class TestEditorViewController : UIViewController, UICollectionViewDelegateFlowLayout{
lazy var collectionView : UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.headerReferenceSize = CGSize(width: view.frame.width, height: 50)
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
cv.dataSource = self
cv.delegate = self
return cv
}()
let cellId = "cellId"
let headerId = "headerId"
func setUpView(){
view.addSubview(collectionView)
collectionView.snp.makeConstraints { (make) in
make.top.equalTo(topLayoutGuide.snp.bottom)
make.leading.equalTo(view)
make.trailing.equalTo(view)
make.bottom.equalTo(bottomLayoutGuide.snp.top)
}
collectionView.register(TextEditCell.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: headerId)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
}
override func viewDidLoad() {
setUpView()
setUpNavigationBar()
}
func setUpNavigationBar(){
self.navigationController?.navigationBar.isTranslucent = false
self.navigationItem.title = "글쓰기"
let dissmissButon = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.cancel, target: self, action: #selector(actionDismiss))
self.navigationItem.leftBarButtonItem = dissmissButon
}
@objc func actionDismiss(sender : Any){
self.dismiss(animated: true, completion: nil)
}
}
extension TestEditorViewController : UICollectionViewDelegate {
func resizeCollectionView(height : CGFloat){
if let layout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
//
layout.headerReferenceSize = CGSize(width: view.frame.width, height: max(height, 200))
self.collectionView.layoutIfNeeded()
}
}
}
extension TestEditorViewController : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerId, for: indexPath) as! TextEditCell
header.textView.delegate = self
return header
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
cell.backgroundColor = UIColor.red
return cell
}
}
extension TestEditorViewController : UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
print(textView.text)
let size = CGSize(width: view.frame.width, height: CGFloat.infinity)
let estimatedSize = textView.sizeThatFits(size)
if let layout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.headerReferenceSize = CGSize(width: view.frame.width, height: max(estimatedSize.height, 50))
self.collectionView.layoutIfNeeded()
}
}
}
뷰컨트롤러가 UICollectionViewDelegateFlowLayout을 상속하는 것에 주목합니다.
layout.headerReferenceSize = CGSize(width: view.frame.width, height: 50)에서 헤더뷰의 사이즈를 직접 정해주는 것에 주의합니다.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
<#code#>
}
을 구현해서 여기서 헤더뷰의 사이즈를 정해주면, 다이나믹한 헤더 변경이 가능하지 않으니
오버라이딩 하지 않도록 주의합니다.
/**
텍스트 에디팅
*/
class TextEditCell : BaseCell{
let placeholder = "무슨 일이 일어나고 있나요?"
let textView : UITextView = {
let tv = UITextView()
tv.isScrollEnabled = false
tv.font = UIFont.systemFont(ofSize: 16)
tv.textContainerInset = UIEdgeInsetsMake(10, 10, 10, 10)
return tv
}()
let placeHolderLabel : UILabel = {
let label = UILabel()
label.text = "무슨 일이 일어나고 있나요?"
label.font = UIFont.systemFont(ofSize: 16)
label.textColor = UIColor.lightGray
label.sizeToFit()
return label
}()
var content : TextEditContent? {
didSet {
textView.text = content?.text
}
}
override func setupViews() {
self.addSubview(textView)
textView.delegate = self
textView.snp.makeConstraints { (make) in
make.top.equalTo(self)
make.trailing.equalTo(self)
make.leading.equalTo(self)
make.bottom.equalTo(self)
}
textView.addSubview(placeHolderLabel)
placeHolderLabel.frame.origin = CGPoint(x: 15, y: 10 )
self.textViewDidChange(textView)
}
}
해당 콜렉션 셀입니다. 제약 조건에 높이를 달지 않고, 그냥 뷰에 꽉 텍스트뷰를 채우도록 했습니다.func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerId, for: indexPath) as! TextEditCell
header.textView.delegate = self
return header
}
에서 헤더뷰의 텍스트뷰의 델리게이션을 구현합니다.
extension TestEditorViewController : UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
print(textView.text)
let size = CGSize(width: view.frame.width, height: CGFloat.infinity)
let estimatedSize = textView.sizeThatFits(size)
if let layout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.headerReferenceSize = CGSize(width: view.frame.width, height: max(estimatedSize.height, 50))
self.collectionView.layoutIfNeeded()
}
}
}
텍스트의 크기가 바뀔 때마다 알맞은 하이트를 계산하고, 레이아웃의 헤더 레퍼런스 사이즈를 수정합니다.
그리고 self.collectionView.layoutIfNeeded() 을 호출하면 텍스트뷰의 사이즈가 알맞게 늘어나는 것을 확인할 수가 있습니다.
여기선 편의상 하이트를 max(estimatedSize.height, 50) 로 두어서, 아무리 텍스트 사이즈가 적어도 높이가 50보단 줄어들진 않습니다.
반응형'iOS' 카테고리의 다른 글
iOS 앱 간의 통신을 구현하기 (0) 2018.08.18 Alamofire를 이용한 api service 설계 (0) 2018.08.12 [swift]WKWebview 스크롤 맨 아래로 정확하게 계산해서 내리기 (0) 2018.07.25 iOS swift 네이버 프로필 api 샘플 코드 (0) 2018.07.10 iOS Swift- 카카오링크 샘플코드 (0) 2018.07.10