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)에서 실행합니다. 그리고이 자동 계산 된 높이는 올바르지 않습니다. 라벨을 수동으로 설정 layoutSubviews
한 tableView:heightForRowAtIndexPath:
경우에도 세포가 발생 하기 때문에 도움이되지 않습니다.preferredMaxLayoutWidth
layoutSubviews
내 솔루션 :
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
내 코드에서.
또한 한 줄 텍스트는 UILabel 및 http://openradar.appspot.com/17799811 에서 두 줄을 사용함을 참조하십시오 .
나에게는 이러한 접근 방식 중 어느 것도 작동하지 않았지만 레이블 Preferred Width
에 Interface Builder에 명시적인 세트 가 있음을 발견했습니다 . 이를 제거 ( "명시 적"선택 해제) 한 다음 UITableViewAutomaticDimension
예상대로 작동했습니다.
cell.layoutIfNeeded()
내부 전화 cellForRowAt
는 ios 10 및 ios 11에서는 작동했지만 ios 9에서는 작동하지 않았습니다.
iOS 9에서도이 작업을 수행하기 위해 전화를 걸어 cell.layoutSubviews()
트릭을 수행했습니다.
이 페이지의 모든 솔루션을 시도했지만 크기 클래스 사용을 선택 취소 한 다음 다시 확인하면 문제가 해결되었습니다.
편집 : 크기 클래스를 선택 취소하면 스토리 보드에서 많은 문제가 발생하므로 다른 솔루션을 시도했습니다. 내 뷰 컨트롤러 viewDidLoad
와 viewWillAppear
메서드 에 테이블 뷰를 채웠습니다. 이것은 내 문제를 해결했습니다.
레이블 크기 조정에 문제가 있으므로
텍스트를 설정 한 후 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을 항상 사용할 수 있는지 확인하십시오. 그렇지 않으면 충돌이 발생하고 가용성을 먼저 확인할 수 있습니다.
'Programing' 카테고리의 다른 글
iOS-프로그래밍 방식으로 UISwitch를 설정하는 방법 (0) | 2020.08.17 |
---|---|
WebException 본문으로 전체 응답을 얻는 방법은 무엇입니까? (0) | 2020.08.17 |
Android ADT 제거 (0) | 2020.08.17 |
scheduleAtFixedRate 대 scheduleWithFixedDelay (0) | 2020.08.17 |
CSV 데이터를 처리 할 때 데이터의 첫 줄을 무시하려면 어떻게해야합니까? (0) | 2020.08.17 |