TableView Cell 크기 동적 할당

이세진·2022년 9월 20일
0

iOS

목록 보기
37/46

TableView의 Cell의 크기(높이)가 Cell 내부 콘텐츠의 크기에 따라 동적으로 조정되도록 구현해보자

초기 화면

목표 화면

초기 화면에는 Feed의 내용(Caption으로 지칭)이 길어지면 해당 내용이 잘리게 되고 Feed(TableView Cell)의 높이 자체도 고정된 값으로 모든 Cell이 값은 높이를 가지게 된다.

이를 수정하여 사진의 높이, Caption의 길이에 맞게 Cell의 크기가 유동적으로 변하도록 구현하여 목표 화면처럼 UI를 구성하도록 하는 것이 목표이다.

1. automaticDimension 사용

기존에 Cell의 높이를 위해 사용했던 heightForRowAt 함수를 없애고

TableView를 담고 있는 ViewController의 viewDidLoad()에

tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 500

위와 같은 코드를 넣어준다. automaticDimension으로 rowHeight를 주게 되면 tableView의 Cell 높이가 오토레이아웃으로 결정된 높이를 동적으로 가지게 된다. (Cell의 xib파일의 오토레이아웃을 잘 설정해주어야 정상적으로 동작한다. Cell 내부의 UIImage와 superView.Bottom 사이의 cosntraint를 없애고 Cell의 가장 하단에 위치한 “댓글 n개 모두 보기" Label에 하단 constraint를 주어 Cell의 크기가 오토레이아웃으로 결정될 수 있도록 수정하였다.)

estimatedRowHeight를 이용하여 default 높이를 500으로 주었다.

이렇게 하면 이미지의 크기에 따라 Cell의 높이가 변하는 것을 확인 할 수 있다. 하지만 Label의 길이가 길어지면 해당 Label이 잘려서 보이는 상황(위에서 첨부한 초기 화면)은 유지되었다.

2. FeedTableViewCell.xib 수정

현재까지 FeedTableViewCell.xib의 상태는 위와 같다.

Caption이 담길 Label을 Caption 작성자 이름 Label 옆에 위치시킨 상태이며 넓이, 우측 간격에는 제약조건을 주지 않은 상태이다.

Label의 Lines가 1인 상태여서 한줄로 모든 내용이 표현되는 문제라 판단되어 해당 값을 0으로 설정하였으나 여전히 내가 원하는대로 Label이 여러줄로 표현되지 않고 한 줄로 잘려서 실행되었다.

  • 실행 화면

문제 파악

  • Label의 Lines를 0으로 수정했지만 여전히 1줄로 나온다
  • Label의 크기는 Label의 text의 길이, font에 따라 자동적으로 결정된다. 하지만 인위적으로 높이와 넓이를 지정해줄 수도 있다.
  • 현재 나의 xib파일의 Caption Label에는 넓이에 대한 constraint가 없다.
  • 이로 인해 Label은 text가 길어지면 넓이의 제약 없이 무한정 길어지게 되고 따라서 화면 밖까지 Label이 길어지게 된 것이다.
  • Label의 넓이를 알맞게 지정하면 한 줄이 아닌 여러 줄로 보여질 것으로 추측 할 수 있다.

해결 방안 추측

1. Label이 계속 우측으로 길어져서 화면 밖으로 나가는 것이 문제라면 Label의 우측에constraint를 추가하여 superview와의 간격을 지정해주면 되지 않을까?

  • 결과
  • Caption Label에 우측으로 12만큼 constraint를 주니까 hugging priority 문제가 발생했다.
  • Username Label과 Caption Label 사이에 준 constraint와 Caption Label과 SuperView 사이에 준 constraint가 충돌하여 어떤 constraint를 우선시 해야하는지 모호하여 발생하는 문제이다.
  • 이를 바로 해결해보고자 두 Label의 Hugging Priority를 1씩 높이거나 낮춰보았는데 Xcode상의 오류는 사라졌으나 실행시켜보면

Username이 사라지는 문제가 발생한다. Hugging Priority를 변경했기 때문에 priority가 낮은 쪽의 constriant가 무용지물이 되면서 UI가 깨진 것이다.

  • 따라서 Label의 우측에 constraint를 주어 Label의 넓이의 한계를 설정하는 방법은 실패했다.

2. Caption Label에 직접적으로 width값을 주는 방법

  • 1번 방안이 Label의 좌우 간격 constraint를 이용하여 Label의 넓이를 간접적으로 주는 방법이었으나 실패했기 때문에 Label에 넓이를 직접적으로 설정해보았다. (width를 300으로 설정)
  • 실행결과 (iPhone 13 mini)
  • 내가 원하는 것과 유사하게 Label이 여러 줄로 출력이 되었으나 문제점이 존재한다.
    • iPhone 13 Pro Max로 실행시킨 결과
  • iPod touch (7th generation)으로 실행시킨 결과
  • 위와 같이 화면의 크기가 다른 기기로 실행을 시켜보면 Label의 넓이를 고정 값으로 주었기 때문에 화면이 큰 기기에서는 우측의 여백이 커지게 되고, 화면이 작은 기기에서는 우측 여백이 없어지는 문제가 발생하게 된다.
  • 문제 해결 방안
    • 현재 발생한 문제는 Caption Label의 Width를 300인 고정값으로 주었기 때문에 발생한다.

    • 그렇다면, 사용중인 기기의 스크린 넓이에 맞게 Caption Label의 Width를 설정하면 Label이 알맞은 크기를 가질 수 있을거라고 추측 할 수 있다.

      ⇒ 3번 해결 방안으로 연결

3. Caption Label의 Width를 코드와 연결하여 iPhone 스크린 넓이에 맞게 변경되도록 구현

  1. FeedTableViewCell.swift에 captionLabel 넓이 제약조건을 연결한다.
  2. captionLabelWidth.constant = UIScreen.main.bounds.width - (captionWriterNameLabel.intrinsicContentSize.width + 12 + 4 + 12)

2번 코드를 추가한다. (⚠️ 위치 중요! : viewDidLoad에 추가하게 되면 captionWriterNameLabel의 넓이가 새롭게 지정되기 전(setData함수 실행 전)에 값을 계산하게 되기 때문에 정상적으로 작동하지 않는다. )

아래와 같이 코드를 구성

class FeedTableViewCell: UITableViewCell {
    static let identifier = "FeedTableViewCell"
    
    weak var delegate: FeedTableViewCellDelegate?
    
    @IBOutlet weak var profileImageView: UIImageView!
    @IBOutlet weak var profileNameLabel: UILabel!
    @IBOutlet weak var feedImageView: UIImageView!
    @IBOutlet weak var likeButton: UIButton!
    @IBOutlet weak var likeCountLabel: UILabel!
    @IBOutlet weak var captionWriterNameLabel: UILabel!
    @IBOutlet weak var captionLabel: UILabel!
    @IBOutlet weak var commentCountButton: UIButton!
    
// CaptionLabel Width constraint 연결
    @IBOutlet weak var captionLabelWidth: NSLayoutConstraint!
    
    //MARK: - Lifecycle
    override func awakeFromNib() {
        super.awakeFromNib()
    }

    //MARK: - Helpers

    func setData(feedData: FeedDataModel) {
        profileImageView.image = UIImage(named: feedData.profileImageName)
        profileNameLabel.text = feedData.profileName
        feedImageView.image = UIImage(named: feedData.feedImageName)
        likeCountLabel.text = "좋아요 \(feedData.likeCount)개"
        captionWriterNameLabel.text = feedData.profileName
        captionLabel.text = feedData.caption
        commentCountButton.setTitle("댓글 \(feedData.commentCount)개 모두 보기", for: .normal)
        
// 화면 크기에 따라 captionLabel의 Width 변경하도록 설정
        captionLabelWidth.constant = UIScreen.main.bounds.width - (captionWriterNameLabel.intrinsicContentSize.width + 12 + 4 + 12)
    }
    
}
  • 스크린 사이즈에 맞게 Label 넓이 주기
    captionLabelWidth.constant = UIScreen.main.bounds.width - (captionWriterNameLabel.intrinsicContentSize.width + 12 + 4 + 12)
    위 코드를 setData함수에 추가했는데 이 코드의 의미는 다음과 같다.
    • UIScreen.main.bounds.width : 현재 실행 중인 기기의 스크린 넓이값

    • captionWriterNameLabel.intrinsicContentSize.width : captionLabel 좌측에 위치한 caption 작성자 이름 Label의 넓이값

    • 12 + 4 + 12 : 좌우 간격을 위한 값 (여백)

      ⇒ captionLabelWidth.constant 를 전체 스크린 넓이에서 caption 작성자 이름을 위한 Label의 넓이 만큼과 여백(간격)을 뺀 값으로 설정한 것이다. 이로 인해 기기가 달라지면 captionLabel의 width도 같이 달라져서 화면에 알맞게 들어가게 된다.

  • 실행 결과 (iPod touch)
  • caption Label이 화면 밖으로 나가지 않고 여러 줄로 실행되는 것을 확인할 수 있다.
  • 다른 기기기로 실행 시켜 보아도 정상적으로 작동한다.
  • 수정해야할 점
    • captionLabel의 첫줄이 길게 나오지 않고 이모티콘(시계 모양) 이후에 줄바꿈이 되는 문제가 있다.
  • 해결책
  • captionLabel의 Line Break를 Character Wrap으로 변경한다.

최종 실행 결과

  1. iPhone 13 mini
  1. iPhone 13 Pro Max
  1. iPod touch (7th generation)

주의

automaticDimention을 이용한 셀 동적 크기 설정이 안되는 경우를 만났다.

override func layoutSubviews() {
  super.layoutSubviews()
  contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0))
}

위와 같은 layoutSubviews를 사용했을 때이다. 이 함수는 tableView cell간의 간격을 설정해주기 위해 사용하는 함수이다. 정확한 이유는 파악하지 못했지만 layoutSubviews함수를 사용하면 오토레이아웃에서 높이를 지정해주지 않은 요소들이 동적으로 커지지 못하는 결과를 확인했다.

profile
나중은 결코 오지 않는다.

0개의 댓글