UITableView 동적 셀 높이는 일부 스크롤 후에 만 ​​수정됩니다.


117

나는이 UITableView사용자 정의로 UITableViewCell자동 레이아웃을 사용하여 스토리 보드에 정의. 셀에는 여러 줄이 있습니다 UILabels.

에서 UITableView셀 높이를 올바르게 계산 하는 것처럼 보이지만 처음 몇 셀의 경우 해당 높이가 레이블간에 제대로 나뉘 지 않습니다. 조금 스크롤하면 모든 것이 예상대로 작동합니다 (처음에 올바르지 않은 셀도 포함).

- (void)viewDidLoad {
    [super viewDidLoad]
    // ...
    self.tableView.rowHeight = UITableViewAutomaticDimension;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TestCell"];
    // ...
    // Set label.text for variable length string.
    return cell;
}

내가 놓칠 수있는 것이 있는데, 이로 인해 자동 레이아웃이 처음 몇 번 작업을 수행 할 수 없습니까?

이 동작을 보여주는 샘플 프로젝트 를 만들었습니다 .

샘플 프로젝트 : 첫 번째로드시 샘플 프로젝트의 테이블 상단보기. 샘플 프로젝트 : 아래로 스크롤하고 위로 스크롤 한 후 동일한 셀.


1
이에 어떤 도움을 나는 또한이 문제에 직면하고있다, 그러나 아래의 대답은 나를 위해 작동하지 않습니다
— Shangari C

1
layoutIfNeeded for cell을 추가해도 같은 문제가 발생합니까? 더 많은 제안
— Max

1
그레이트 스팟. 이것은 2019 년에도 여전히 문제입니다 : /
— Fattie

다음 링크에서 도움이 된 것을 찾았습니다. 가변 높이 테이블 뷰 셀에 대한 꽤 포괄적 인 토론입니다. stackoverflow.com/a/18746930/826946
— Andy Weinstein

답변:


139

이것이 명확하게 문서화되었는지 여부는 모르지만 [cell layoutIfNeeded]셀을 반환하기 전에 추가하면 문제가 해결됩니다.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TestCell"];
    NSUInteger n1 = firstLabelWordCount[indexPath.row];
    NSUInteger n2 = secondLabelWordCount[indexPath.row];
    [cell setNumberOfWordsForFirstLabel:n1 secondLabel:n2];

    [cell layoutIfNeeded]; // <- added

    return cell;
}

3
그래도 처음에만 필요한 이유는 무엇입니까? 전화를 -layoutIfNeeded셀의 -awakeFromNib. layoutIfNeeded필요한 이유를 아는 곳 에서만 전화를 걸고 싶습니다 .
— blackp 2014 년

2
나를 위해 일했습니다! 그리고 왜 이것이 필요한지 알고 싶습니다!
— Mustafa

당신은 크기 클래스, 어떤 X 어떤 다르지 렌더링 작업을하지 않은 경우
— 안드레이 콘스탄티노

@AndreyKonstantinov Yup 크기 클래스에서는 작동하지 않습니다. 크기 클래스에서는 어떻게 작동합니까?
— z22 2015

크기 등급없이 작동하며 크기 등급이있는 솔루션이 있습니까?
— Yuvrajsinh

38

이것은 다른 유사한 솔루션이 그렇지 않은 경우 저에게 효과적이었습니다.

override func didMoveToSuperview() {
    super.didMoveToSuperview()
    layoutIfNeeded()
}

AutoLayout과 UITableViewAutomaticDimension을 사용하는 방법에 매우 익숙하기 때문에 실제 버그처럼 보이지만 가끔이 문제가 발생합니다. 마침내 해결 방법으로 작동하는 것을 발견하게되어 기쁩니다.


1
모든 셀 표시 대신 xib에서 셀이로드 될 때만 호출되므로 허용되는 답변보다 낫습니다. 훨씬 적은 코드 라인도 있습니다.
— 로버트 Wagstaff

3
전화하는 것을 잊지 마세요super.didMoveToSuperview()
— cicerocamargo

@cicerocamargo Apple은 didMoveToSuperview"이 메서드의 기본 구현은 아무것도하지 않습니다." 라고 말합니다 .
— Ivan Smetanin

4
@IvanSmetanin 기본 구현이 아무 작업도 수행하지 않더라도 상관 없습니다. 앞으로 실제로 수행 할 수있는 수퍼 메서드를 호출해야합니다.
— TNguyen

(단지 btw 당신은 반드시 super.라고 부르셔야합니다. 저는 팀이 전체적으로 셀을 두 번 이상 서브 클래 싱하지 않는 프로젝트를 본 적이 없으므로, 물론 모든 셀을 선택해야합니다. 그리고 TNguyen이 말한 그대로 별도의 문제.)
— Fattie

22

처음에보기 밖으로 스크롤 된 셀에는 추가 기능 [cell layoutIfNeeded]이 cellForRowAtIndexPath작동하지 않습니다.

또한 [cell setNeedsLayout].

특정 셀의 크기를 올바르게 조정하려면 여전히 특정 셀을보기로 스크롤해야합니다.

대부분의 개발자는이 성가신 경우를 제외하고는 Dynamic Type, AutoLayout 및 Self-Sizing Cell이 제대로 작동하므로 매우 실망 스럽습니다. 이 버그는 모든 "더 큰"테이블 뷰 컨트롤러에 영향을줍니다.


저도 마찬가지입니다. 처음 스크롤 할 때 셀 크기는 44px입니다. 스크롤하여 셀을 보이지 않게하고 돌아 오면 크기가 적절하게 조정됩니다. 해결책을 찾았습니까?
— Max

@ ashy_32bit 감사합니다.
— ImpurestClub

평범한 버그 인 것 같습니다. @Scenario 맞나요? 끔찍한 물건.
— Fattie 2015-09-23

3
[cell layoutSubviews]layoutIfNeeded 대신 사용 하는 것이 가능한 수정일 수 있습니다. 참조 stackoverflow.com/a/33515872/1474113
— ypresto

14

내 프로젝트 중 하나에서 같은 경험을했습니다.

왜 발생합니까?

일부 장치에 대해 약간의 너비가있는 스토리 보드에서 디자인 된 셀. 예 : 400px. 예를 들어 라벨의 너비가 동일합니다. 스토리 보드에서로드 할 때 너비는 400px입니다.

여기에 문제가 있습니다.

tableView:heightForRowAtIndexPath: 셀 레이아웃 전에 호출됩니다.

그래서 레이블과 셀의 높이를 400px 너비로 계산했습니다. 하지만 화면이있는 기기 (예 : 320px)에서 실행합니다. 그리고이 자동 계산 된 높이는 올바르지 않습니다. 라벨을 수동으로 설정 layoutSubviews한 tableView:heightForRowAtIndexPath:후에도 셀이 발생 하기 때문에 도움이되지 않습니다.preferredMaxLayoutWidthlayoutSubviews

내 솔루션 :

1) 하위 클래스 UITableView및 재정의 dequeueReusableCellWithIdentifier:forIndexPath:. 셀 너비를 테이블 너비와 동일하게 설정하고 셀 레이아웃을 적용합니다.

- (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [super dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
    CGRect cellFrame = cell.frame;
    cellFrame.size.width = self.frame.size.width;
    cell.frame = cellFrame;
    [cell layoutIfNeeded];
    return cell;
}

2) 하위 클래스 UITableViewCell. preferredMaxLayoutWidth에서 레이블을 수동으로 설정 합니다 layoutSubviews. 또한 contentView셀 프레임 변경 후 자동으로 레이아웃되지 않기 때문에 수동 레이아웃이 필요합니다 (이유는 모르겠지만 그렇습니다)

- (void)layoutSubviews {
    [super layoutSubviews];
    [self.contentView layoutIfNeeded];
    self.yourLongTextLabel.preferredMaxLayoutWidth = self.yourLongTextLabel.width;
}

1
이 문제가 발생했을 때 tableview의 프레임이 올바른지 확인해야했기 때문에 tableview가 채워지기 전에 (viewDidLoad에서) [self.tableview layoutIfNeeded]를 호출했습니다.
— GK100

나는 그것이 잘못된 너비와 관련이 있다고 생각하기 위해 멈추지 않았습니다. cell.frame.size.width = tableview.frame.width다음 cell.layoutIfNeeded()에서 cellForRowAt기능 나를 위해 속임수를 썼는지
— 캠 코너

10

비슷한 문제가 있습니다. 첫 번째로드에서 행 높이가 계산되지 않았지만 스크롤하거나 다른 화면으로 이동하면이 화면으로 돌아와 행이 계산됩니다. 첫 번째로드에서 내 항목은 인터넷에서로드되고 두 번째로드에서는 내 항목이 Core Data에서 먼저로드되고 인터넷에서 다시로드되며 인터넷에서 다시로드 할 때 행 높이가 계산되는 것을 확인했습니다. 그래서 segue 애니메이션 중에 tableView.reloadData ()가 호출되면 (푸시 및 현재 segue와 동일한 문제) 행 높이가 계산되지 않는다는 것을 알았습니다. 그래서 뷰 ​​초기화에서 tableview를 숨기고 사용자에게 추악한 영향을 미치지 않도록 활동 로더를 넣고 300ms 후에 tableView.reloadData를 호출하면 문제가 해결됩니다. UIKit 버그라고 생각하지만이 해결 방법이 트릭을 만듭니다.

항목로드 완료 핸들러에 논문 줄 (Swift 3.0)을 넣었습니다.

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300), execute: {
        self.tableView.isHidden = false
        self.loader.stopAnimating()
        self.tableView.reloadData()
    })

이것은 왜 어떤 사람들에게는 layoutSubviews에 reloadData를 넣어 문제를 해결하는지 설명합니다.


이것은 나에게도 일한 유일한 WA입니다. isHidden을 사용하지 않는 것을 제외하고는 데이터 소스를 추가 할 때 테이블의 알파를 0으로 설정 한 다음 다시로드 한 후 1로 설정합니다.
— PJ_Finnegan

6

위의 솔루션 중 어느 것도 저에게 효과가 없었습니다. 효과가 있었던 것은 마법의 레시피입니다. 다음 순서로 호출하십시오.

tableView.reloadData()
tableView.layoutIfNeeded() tableView.beginUpdates() tableView.endUpdates()

내 tableView 데이터는 웹 서비스에서 채워지며 연결의 콜백에서 위의 줄을 작성합니다.


1
스위프트 5의 경우 컬렉션 뷰에서도 작동했습니다.
— Govani Dhruv Vijaykumar

나를 위해 이것은 셀의 정확한 높이와 레이아웃을 반환했지만 데이터가 없었습니다
— Darrow Hartman

해당 줄 다음에 setNeedsDisplay ()를 시도하십시오
— JAHelia

5

제 경우에는 셀이 처음 표시 될 때 UILabel의 마지막 줄이 잘 렸습니다. 그것은 꽤 무작위로 발생했으며 올바르게 크기를 조정하는 유일한 방법은 뷰에서 셀을 스크롤하고 다시 가져 오는 것입니다. 지금까지 표시된 모든 가능한 솔루션 (layoutIfNeeded..reloadData)을 시도했지만 아무것도 효과가 없었습니다. 트릭은 "Autoshrink" 를 Minimuum Font Scale (저는 0.5) 로 설정하는 것이 었습니다 . 시도 해봐


이것은 위에 나열된 다른 솔루션을 적용하지 않고도 저에게 효과적이었습니다. 훌륭한 발견.
— mckeejm 2015 년

2
그러나 이것은 텍스트를 자동 축소합니다 ... 왜 테이블보기에서 다른 크기의 텍스트를 원할까요? 끔찍해 보입니다.
— lucius degeer

5

테이블보기 사용자 지정 셀 내의 모든 콘텐츠에 대한 제약 조건을 추가 한 다음 테이블보기 행 높이를 추정하고 viewdid로드에서 행 높이를 자동 차원으로 설정합니다.

    override func viewDidLoad() {
    super.viewDidLoad()

    tableView.estimatedRowHeight = 70
    tableView.rowHeight = UITableViewAutomaticDimension
}

초기 로딩 문제를 해결하려면 사용자 정의 테이블 뷰 셀에서 layoutIfNeeded 메소드를 적용하십시오.

class CustomTableViewCell: UITableViewCell {

override func awakeFromNib() {
    super.awakeFromNib()
    self.layoutIfNeeded()
    // Initialization code
}
}

estimatedRowHeight값을> 0 으로 설정 하고 UITableViewAutomaticDimension(-1)이 아닌 값 으로 설정 하는 것이 중요합니다 . 그렇지 않으면 자동 행 높이가 작동하지 않습니다.
— PJ_Finnegan

5

나는이 질문에 대한 대부분의 답변을 시도했지만 그들 중 어느 것도 작동하지 못했습니다. 내가 찾은 유일한 기능적 솔루션은 내 UITableViewController하위 클래스에 다음을 추가하는 것입니다 .

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    UIView.performWithoutAnimation {
        tableView.beginUpdates()
        tableView.endUpdates()
    }
}

UIView.performWithoutAnimation호출 그렇지 않으면 당신은 뷰 컨트롤러의 부하로 일반 테이블 뷰 애니메이션을 볼 필요합니다.


두 번 reloadData를 호출보다 더 확실히 작업과
— 지미 조지 토마스

2
마법. 에서하는 것은 viewWillAppear나에게는 효과가 없었지만에서하는 것은 효과 viewDidAppear가있었습니다.
— Martin

이 솔루션은 viewDidAppear에서 구현 될 때 나를 위해 작동했습니다. 올바른 크기 조정이 발생함에 따라 테이블 내용이 이동하므로 사용자 경험에 최적이 아닙니다.
— richardpiazza 2017

놀라운 난 당신이 악마있는 예를 많이 시도했지만 실패했다
— 샤킬 아메드

@martin과 동일
— AJ Hernandez


3

cell.layoutIfNeeded()내부 전화 cellForRowAt는 ios 10 및 ios 11에서는 작동했지만 ios 9에서는 작동하지 않았습니다.

iOS 9에서도이 작업을 수행하기 위해 전화를 걸어 cell.layoutSubviews()트릭을 수행했습니다.


2

추천을위한 스크린 샷 첨부나에게 이러한 접근 방식 중 어느 것도 작동하지 않았지만 레이블이 Preferred WidthInterface Builder에 명시 적으로 설정되어 있음을 발견했습니다 . 이를 제거 ( "명시 적"선택 해제) 한 다음 UITableViewAutomaticDimension예상대로 작동했습니다.


2

이 페이지의 모든 솔루션을 시도했지만 크기 클래스 사용을 선택 취소 한 다음 다시 확인하면 문제가 해결되었습니다.

편집 : 크기 클래스를 선택 취소하면 스토리 보드에서 많은 문제가 발생하므로 다른 솔루션을 시도했습니다. 내 뷰 컨트롤러 viewDidLoad와 viewWillAppear메서드 에 테이블 뷰를 채웠습니다. 이것은 내 문제를 해결했습니다.


1

레이블 크기 조정에 문제가 있으므로
텍스트를 설정 한 후 chatTextLabel.text = chatMessage.message chatTextLabel? .updateConstraints ()를 수행하면됩니다.

// 전체 코드

func setContent() {
    chatTextLabel.text = chatMessage.message
    chatTextLabel?.updateConstraints()

    let labelTextWidth = (chatTextLabel?.intrinsicContentSize().width) ?? 0
    let labelTextHeight = chatTextLabel?.intrinsicContentSize().height

    guard labelTextWidth < originWidth && labelTextHeight <= singleLineRowheight else {
      trailingConstraint?.constant = trailingConstant
      return
    }
    trailingConstraint?.constant = trailingConstant + (originWidth - labelTextWidth)

  }

1

제 경우에는 다른 주기로 업데이트했습니다. 따라서 labelText가 설정된 후 tableViewCell 높이가 업데이트되었습니다. 비동기 블록을 삭제했습니다.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
     let cell = tableView.dequeueReusableCell(withIdentifier:Identifier, for: indexPath) 
     // Check your cycle if update cycle is same or not
     // DispatchQueue.main.async {
        cell.label.text = nil
     // }
}

비동기 블록도 있지만 필요합니까? 대안이 없습니까?
— Nazar Medeiros

1

테이블보기의 'willdisplaycell'대리자 메서드에서 레이블 텍스트를 설정하지 않았는지 확인하십시오. 동적 높이 계산을 위해 'cellForRowAtindexPath'대리자 메서드에서 레이블 텍스트를 설정합니다.

천만에요 :)


1

제 경우에는 셀의 스택 뷰가 문제를 일으켰습니다. 분명히 버그입니다. 제거하면 문제가 해결되었습니다.


1
난 당신의 솔루션을 위해 일 ONY, 다른 모든 솔루션을 시도 그것을 공유 주셔서 감사합니다
— tamtoum1987을

1

문제는 유효한 행 높이를 갖기 전에 초기 셀이로드된다는 것입니다. 해결 방법은 뷰가 나타날 때 테이블을 강제로 다시로드하는 것입니다.

- (void)viewDidAppear:(BOOL)animated
{
  [super viewDidAppear:animated];
  [self.tableView reloadData];
}

이것은 나를 도왔던 훨씬 더 나은 솔루션이었습니다. 세포의 100 %를 수정하지는 않았지만 최대 80 %가 실제 크기를 차지하고있었습니다. 고마워요
— TheTravloper

1

iOS 12+ 만 해당, 2019 이후 ...

문자 그대로 몇 년 동안 문제가 계속되는 Apple의 가끔 기이 한 무능력의 지속적인 예입니다.

그것은 경우 인 것 같습니다

        cell.layoutIfNeeded()
        return cell

그것을 고칠 것입니다. (물론 성능이 약간 저하됩니다.)

이것이 Apple과의 삶입니다.


0

제 경우에는 초기 테이블 뷰가로드 된 후 셀 높이 문제가 발생하고 사용자 작업이 발생합니다 (셀 높이를 변경하는 효과가있는 셀의 버튼을 탭함). 내가하지 않으면 셀의 높이를 변경할 수 없었습니다.

[self.tableView reloadData];

나는 시도했다

[cell layoutIfNeeded];

그러나 그것은 작동하지 않았습니다.


0

Swift 3에서는 재사용 가능한 셀의 텍스트를 업데이트 할 때마다 self.layoutIfNeeded ()를 호출해야했습니다.

import UIKit
import SnapKit

class CommentTableViewCell: UITableViewCell {

    static let reuseIdentifier = "CommentTableViewCell"

    var comment: Comment! {
        didSet {
            textLbl.attributedText = comment.attributedTextToDisplay()
            self.layoutIfNeeded() //This is a fix to make propper automatic dimentions (height).
        }
    }

    internal var textLbl = UILabel()

    override func layoutSubviews() {
        super.layoutSubviews()

        if textLbl.superview == nil {
            textLbl.numberOfLines = 0
            textLbl.lineBreakMode = .byWordWrapping
            self.contentView.addSubview(textLbl)
            textLbl.snp.makeConstraints({ (make) in
                make.left.equalTo(contentView.snp.left).inset(10)
                make.right.equalTo(contentView.snp.right).inset(10)
                make.top.equalTo(contentView.snp.top).inset(10)
                make.bottom.equalTo(contentView.snp.bottom).inset(10)
            })
        }
    }
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let comment = comments[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: CommentTableViewCell.reuseIdentifier, for: indexPath) as! CommentTableViewCell
        cell.selectionStyle = .none
        cell.comment = comment
        return cell
    }

commentsTableView.rowHeight = UITableViewAutomaticDimension
    commentsTableView.estimatedRowHeight = 140

0

위의 솔루션 중 어느 것도 작동하지 않았지만 다음 제안 조합은 작동했습니다.

viewDidLoad ()에 다음을 추가해야했습니다.

DispatchQueue.main.async {

        self.tableView.reloadData()

        self.tableView.setNeedsLayout()
        self.tableView.layoutIfNeeded()

        self.tableView.reloadData()

    }

위의 reloadData, setNeedsLayout 및 layoutIfNeeded 조합은 작동했지만 다른 것은 작동하지 않았습니다. 하지만 프로젝트의 셀에 따라 다를 수 있습니다. 그리고 예, 작동하도록 reloadData를 두 번 호출해야했습니다.

또한 viewDidLoad에서 다음을 설정하십시오.

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = MyEstimatedHeight

tableView (_ tableView : UITableView, cellForRowAt indexPath : IndexPath)에서

cell.setNeedsLayout()
cell.layoutIfNeeded() 

내 유사도 reloadData/beginUpdates/endUpdates/reloadData작동합니다. 두 번째로 호출 reloadData 해야합니다 . 으로 포장 할 필요가 없습니다 async.
— ray

0

이 문제가 발생하여보기 / 레이블 초기화 코드를 FROM tableView(willDisplay cell:)TO 로 이동하여 문제를 해결했습니다 tableView(cellForRowAt:).


셀을 초기화하는 데 권장되는 방법 이 아닙니다 . willDisplay보다 나은 성능을 제공합니다 cellForRowAt. lastest 만 사용하여 올바른 셀을 인스턴스화하십시오.
— Martin

2 년 후, 제가 적어 놓은 오래된 댓글을 읽고 있습니다. 이것은 나쁜 조언이었습니다. 하더라도 willDisplay더 나은 성능을 가지고, 당신의 세포 UI를 초기화하는 것이 좋습니다 cellForRowAt레이아웃이 자동으로됩니다. 실제로 레이아웃은 willDisplay 이전에 cellForRowAt 이후 UIKit에 의해 계산됩니다 . 따라서 셀 높이가 내용에 따라 달라지는 경우에서 레이블 내용 (또는 기타)을 초기화합니다 . cellForRowAt
— Martin

-1
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{


//  call the method dynamiclabelHeightForText
}

행의 높이를 동적으로 반환하는 위의 방법을 사용하십시오. 그리고 사용중인 레이블에 동일한 동적 높이를 할당합니다.

-(int)dynamiclabelHeightForText:(NSString *)text :(int)width :(UIFont *)font
{

    CGSize maximumLabelSize = CGSizeMake(width,2500);

    CGSize expectedLabelSize = [text sizeWithFont:font
                                constrainedToSize:maximumLabelSize
                                    lineBreakMode:NSLineBreakByWordWrapping];


    return expectedLabelSize.height;


}

이 코드는 레이블에 표시되는 텍스트의 동적 높이를 찾는 데 도움이됩니다.


실제로 Ramesh는 tableView.estimatedRowHeight 및 tableView.rowHeight = UITableViewAutomaticDimension 속성이 설정되어있는 한 필요하지 않습니다. 또한 사용자 지정 셀에 다양한 위젯 및 contentView에 대한 적절한 제약 조건이 있는지 확인하십시오.
— BonanzaDriver
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.