自動レイアウトを使用して、ストーリーボードで定義されたカスタム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の使用方法に非常に精通しているため、これは実際のバグのように見えますが、この問題に時々出くわします。回避策として機能するものをようやく見つけたことがうれしいです。
cellForRowAtIndexPath
に[cell layoutIfNeeded]
を追加しても、最初に表示範囲外にスクロールされるセルでは機能しません。
[cell setNeedsLayout]
で始まることもありません。
正しくサイズを変更するには、特定のセルをスクロールして表示に戻す必要があります。
ほとんどの開発者は、この厄介なケースを除いて、Dynamic Type、AutoLayout、およびSelf-Sizing Cellsが適切に機能しているため、これはかなりイライラします。このバグは、すべての「長めの」Table View Controllerに影響します。
私は自分のプロジェクトの1つで同じ経験をしました。
なぜ起こるのですか?
ストーリーボードで設計されたセルで、一部のデバイスにある程度の幅があります。たとえば、400px。たとえば、ラベルの幅は同じです。ストーリーボードから読み込むと、幅は400ピクセルになります。
問題があります:
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;
}
私は同様の問題を抱えており、最初のロードで行の高さは計算されませんでしたが、スクロールまたは別の画面に移動してこの画面に戻ると行が計算されます。最初の読み込みでアイテムがインターネットから読み込まれ、2番目の読み込みでアイテムが最初にCore Dataから読み込まれ、インターネットから再読み込みされ、行の高さがインターネットからの再読み込みで計算されることに気付きました。そのため、セグエアニメーション中にtableView.reloadData()が呼び出されると(Pushと現在のセグエと同じ問題)、行の高さが計算されないことに気付きました。だから私はビューの初期化で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)に設定することでした。試してみる
私はこの質問に対する答えのほとんどを試してみましたが、どれも機能させることができませんでした。私が見つけた唯一の機能的な解決策は、以下をUITableViewController
サブクラスに追加することでした:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIView.performWithoutAnimation {
tableView.beginUpdates()
tableView.endUpdates()
}
}
UIView.performWithoutAnimation
呼び出しが必要です。それ以外の場合は、View Controllerのロード時に通常のテーブルビューアニメーションが表示されます。
テーブルビューのカスタムセル内のすべてのコンテンツに制約を追加し、テーブルビューの行の高さを推定し、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
}
}
このページのすべてのソリューションを試しましたが、使用サイズクラスのチェックを外してから再度チェックすると、問題が解決しました。
編集:サイズクラスのチェックを外すと、ストーリーボードで多くの問題が発生するため、別の解決策を試しました。 View ControllerのviewDidLoad
メソッドとviewWillAppear
メソッドにTable Viewを追加しました。これで問題が解決しました。
preferredMaxLayoutWidth
を設定すると、私の場合に役立ちます。追加した
cell.detailLabel.preferredMaxLayoutWidth = cell.frame.width
私のコードで。
ILabelでは1行のテキストに2行かかる および http://openradar.appspot.com/17799811 も参照してください。
cell.layoutIfNeeded()
をcellForRowAt
内で呼び出すと、ios 10およびios 11で機能しましたが、ios 9では機能しませんでした。
iOS 9でもこの機能を実現するために、cell.layoutSubviews()
を呼び出しましたが、うまくいきました。
私の場合、他のサイクルで更新していました。したがって、labelViewの設定後に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
// }
}
上記の解決策のどれも私にとってはうまくいきませんでしたが、うまくいったのはこの魔法のレシピです:この順序で呼び出します:
tableView.reloadData()
tableView.layoutIfNeeded() tableView.beginUpdates() tableView.endUpdates()
tableViewデータはWebサービスから読み込まれ、接続のコールバックで上記の行を記述します。
ラベルのサイズ変更に問題がありますので、
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)
}
テーブルビューの 'willdisplaycell'デリゲートメソッドでラベルテキストを設定していないことを確認してください。動的な高さ計算のために 'cellForRowAtindexPath'デリゲートメソッドでラベルテキストを設定します。
どういたしまして :)
問題は、有効な行の高さを取得する前に初期セルがロードされることです。回避策は、ビューが表示されたときにテーブルを強制的に再ロードすることです。
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView reloadData];
}
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
まだこの問題に直面している人は、この魔法のラインを試してください
tableview.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
テーブルをデータで埋めた後にこれを呼び出すようにしてください。そうすれば、セクション0と行0が常に使用可能であることを確認できます。そうでない場合はクラッシュします。
私の場合、セルの高さの問題は、最初のテーブルビューが読み込まれた後に発生し、ユーザーアクションが発生します(セルの高さを変更する効果があるセルのボタンをタップします)。私はそうしない限り、セルの高さを変えることができませんでした:
[self.tableView reloadData];
やってみた
[cell layoutIfNeeded];
しかし、それはうまくいきませんでした。
私の場合、セル内のスタックビューが問題の原因でした。明らかにバグです。削除すると、問題は解決しました。
- (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;
}
このコードは、ラベルに表示されるテキストの動的な高さを見つけるのに役立ちます。
上記のソリューションはいずれも機能しませんでしたが、次の提案の組み合わせは機能しました。
ViewDidLoad()に以下を追加する必要がありました。
DispatchQueue.main.async {
self.tableView.reloadData()
self.tableView.setNeedsLayout()
self.tableView.layoutIfNeeded()
self.tableView.reloadData()
}
ReloadData、setNeedsLayout、layoutIfNeededの上記の組み合わせは機能しましたが、その他は機能しませんでした。ただし、プロジェクト内のセルに固有である可能性があります。そして、はい、それを機能させるためにreloadDataを2回呼び出さなければなりませんでした。
また、viewDidLoadで以下を設定します
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = MyEstimatedHeight
TableView(_ tableView:UITableView、cellForRowAt indexPath:IndexPath)
cell.setNeedsLayout()
cell.layoutIfNeeded()
この問題にぶつかり、ビュー/ラベルの初期化コードをtableView(willDisplay cell:)
からtableView(cellForRowAt:)
に移動して修正しました。