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)에서 실행합니다. 그리고이 자동 계산 된 높이는 올바르지 않습니다. 라벨을 수동으로 설정 layoutSubviewstableView: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

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

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


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.