Programing

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

lottogame 2020. 8. 17. 09:32
반응형

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


나는이 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;
}

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

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

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


이것이 명확하게 문서화되었는지 여부는 모르지만 [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;
}

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

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

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


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

또한 [cell setNeedsLayout].

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

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


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

왜 발생합니까?

일부 장치에 대해 약간의 너비가있는 스토리 보드에서 디자인 된 셀. 예 : 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;
}

비슷한 문제가 있습니다. 첫 번째로드에서 행 높이가 계산되지 않았지만 스크롤하거나 다른 화면으로 이동하면이 화면으로 돌아와 행이 계산됩니다. 첫 번째로드에서 내 항목은 인터넷에서로드되고 두 번째로드에서는 내 항목이 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를 넣어 문제를 해결하는지 설명합니다.


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

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

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

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


preferredMaxLayoutWidth제 경우에는 설정이 도움이됩니다. 나는 추가했다

cell.detailLabel.preferredMaxLayoutWidth = cell.frame.width

내 코드에서.

또한 한 줄 텍스트는 UILabelhttp://openradar.appspot.com/17799811 에서 두 줄을 사용함을 참조하십시오 .


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


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

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


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

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


레이블 크기 조정에 문제가 있으므로
텍스트를 설정 한 후 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)

  }

제 경우에는 다른 주기로 업데이트했습니다. 따라서 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
     // }
}

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

천만에요 :)


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

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

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

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

my tableView data are populated from a web service, in the call back of the connection I write the above lines.


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


//  call the method dynamiclabelHeightForText
}

use the above method which return the height for the row dynamically. And assign the same dynamic height to the the lable you are using.

-(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;


}

This code helps you finding the dynamic height for text displaying in the label.


In my case, the issue with the cell height takes place after the initial table view is loaded, and a user action takes place (tapping on a button in a cell that has an effect of changing the cell height). I have been unable to get the cell to change its height unless I do:

[self.tableView reloadData];

I did try

[cell layoutIfNeeded];

but that didn't work.


In Swift 3. I had to call self.layoutIfNeeded() each time I update the text of the reusable cell.

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

None of the above solutions worked but the following combination of the suggestions did.

Had to add the following in viewDidLoad().

DispatchQueue.main.async {

        self.tableView.reloadData()

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

        self.tableView.reloadData()

    }

The above combination of reloadData, setNeedsLayout and layoutIfNeeded worked but not any other. Could be specific to the cells in the project though. And yes, had to invoke reloadData twice to make it work.

Also set the following in viewDidLoad

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = MyEstimatedHeight

In tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)

cell.setNeedsLayout()
cell.layoutIfNeeded() 

In my case, a stack view in the cell was causing the problem. It's a bug apparently. Once I removed it, the problem was solved.


I ran into this issue and fixed it by moving my view/label initialization code FROM tableView(willDisplay cell:) TO tableView(cellForRowAt:).


For anyone still facing this issue, try this magic line

  tableview.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)

데이터로 테이블을 채운 후에 이것을 호출하여 섹션 0과 행 0을 항상 사용할 수 있는지 확인하십시오. 그렇지 않으면 충돌이 발생하고 가용성을 먼저 확인할 수 있습니다.

참고 URL : https://stackoverflow.com/questions/25971147/uitableview-dynamic-cell-heights-only-correct-after-some-scrolling

반응형