UITableViewCell
テーブルビューのs 内で自動レイアウトを使用して、スムーズなスクロールパフォーマンスを維持しながら、各セルのコンテンツとサブビューが行の高さを(それ自体/自動的に)決定できるようにする方法を教えてください。
UITableViewCell
テーブルビューのs 内で自動レイアウトを使用して、スムーズなスクロールパフォーマンスを維持しながら、各セルのコンテンツとサブビューが行の高さを(それ自体/自動的に)決定できるようにする方法を教えてください。
回答:
TL; DR:読書は嫌いですか?GitHubのサンプルプロジェクトに直接ジャンプします。
以下の最初の2つのステップは、開発しているiOSのバージョンに関係なく適用されます。
あなたにはUITableViewCell
、セルのサブビューは、セルのの端に固定それらの縁があるように、サブクラス、制約を追加contentView(最も重要なの上端と下端にします)。注:サブビューをセル自体に固定しないでください。セルのcontentView
!これらのサブビューの固有のコンテンツサイズによって、各サブビューの垂直方向のコンテンツの圧縮抵抗とコンテンツハグの制約が、追加した優先度の高い制約によって上書きされないようにすることで、テーブルビューセルのコンテンツビューの高さを高めます。(え?ここをクリック。)
アイデアは、セルのサブビューをセルのコンテンツビューに垂直に接続して、「圧力をかけ」、コンテンツビューをそれらに合わせて拡大できるようにすることです。いくつかのサブビューを持つサンプルセルを使用して、制約の一部 (すべてではない!)がどのように見える必要があるかを視覚的に示します。
上記の例のセルの複数行の本文ラベルにさらにテキストが追加されると、テキストに合わせて垂直方向に拡大する必要があり、セルの高さを効果的に拡大することが想像できます。(もちろん、これが正しく機能するためには、制約を正しく設定する必要があります!)
制約を正しく設定することは、自動レイアウトで動的なセルの高さを取得する上で、最も困難で最も重要な部分です。ここで間違えると、他のすべてが機能しなくなる可能性があります。時間をかけてください。コードに制約を設定することをお勧めします。どこに制約が追加されているかを正確に把握しており、問題が発生した場合のデバッグがはるかに簡単だからです。コードに制約を追加することは、レイアウトアンカーを使用したInterface BuilderやGitHubで利用できる素晴らしいオープンソースAPIの1つと同じくらい簡単で、はるかに強力です。
updateConstraints
は、UITableViewCellサブクラスのメソッド内から一度行う必要があります。updateConstraints
複数回呼び出される可能性があることに注意してください。同じ制約が複数回追加されるのを避けるために、制約を追加するコードupdateConstraints
をdidSetupConstraints
(制約を実行した後にYESに設定する)などのブールプロパティのチェック内にラップしてください。-コードを1回追加します)。一方、既存の制約を更新するコード(constant
一部の制約のプロパティの調整など)updateConstraints
がある場合は、didSetupConstraints
これをチェックの外に配置して、メソッドが呼び出されるたびに実行できるようにします。セル内の一意の制約セットごとに、一意のセル再利用識別子を使用します。つまり、セルに複数の一意のレイアウトがある場合、各一意のレイアウトは独自の再利用識別子を受け取る必要があります。(新しい再利用識別子を使用する必要があることの良いヒントは、セルバリアントのサブビューの数が異なるか、サブビューが異なる方法で配置されている場合です。)
たとえば、各セルに電子メールメッセージを表示する場合、4つの固有のレイアウトがある可能性があります。件名のみのメッセージ、件名と本文のメッセージ、件名と写真の添付ファイルのあるメッセージ、件名のあるメッセージ、ボディ、写真添付。各レイアウトにはそれを実現するために必要な完全に異なる制約があるため、セルが初期化され、これらのセルタイプのいずれかに制約が追加されると、セルはそのセルタイプに固有の一意の再利用識別子を取得します。これは、再利用するためにセルをデキューするとき、制約はすでに追加されており、そのセルタイプに移行する準備ができていることを意味します。
固有のコンテンツサイズの違いにより、同じ制約(タイプ)を持つセルでも高さが異なる場合があることに注意してください。コンテンツのサイズが異なるため、根本的に異なるレイアウト(異なる制約)と異なる計算ビューフレーム(同一の制約から解決)を混同しないでください。
自動サイズテーブルテーブルセルを有効にするには、テーブルビューのrowHeightプロパティをUITableViewAutomaticDimensionに設定する必要があります。また、estimatedRowHeightプロパティに値を割り当てる必要があります。これらのプロパティの両方が設定されるとすぐに、システムは自動レイアウトを使用して行の実際の高さを計算します
アップル:セルフサイジングテーブルビューセルの操作
iOS 8では、iOS 8より前にユーザーが実装する必要があった作業の多くがAppleによって内部化されています。セルサイズ調整セルメカニズムを機能させるrowHeight
には、まずテーブルビューのプロパティを定数に設定する必要がありますUITableViewAutomaticDimension
。次に、テーブルビューのestimatedRowHeight
プロパティをゼロ以外の値に設定して、行の高さの推定を有効にするだけです。次に例を示します。
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to whatever your "average" cell height is
これは、まだ画面に表示されていないセルの行の高さの一時的な見積もり/プレースホルダーをテーブルビューに提供することです。次に、これらのセルが画面上でスクロールしようとすると、実際の行の高さが計算されます。各行の実際の高さを決定するために、テーブルビューcontentView
は、コンテンツビューの既知の固定幅(テーブルビューの幅からセクションインデックスなどの追加の要素を差し引いたもの)に基づいて必要な高さを各セルに自動的に尋ねますまたはアクセサリビュー)、およびセルのコンテンツビューとサブビューに追加した自動レイアウト制約。この実際のセルの高さが決定されると、行の古い推定高さが新しい実際の高さで更新されます(必要に応じて、テーブルビューのcontentSize / contentOffsetに対する調整が行われます)。
一般的に言えば、提供する見積もりはそれほど正確である必要はありません。これは、テーブルビューのスクロールインジケーターのサイズを正しく設定するためにのみ使用され、テーブルビューは、誤った見積もりに対してスクロールインジケーターを適切に調整します。画面上のセルをスクロールします。estimatedRowHeight
テーブルビュー(viewDidLoad
または同様のもの)のプロパティを、「平均」の行の高さである定数値に設定する必要があります。行の高さが非常に変動しやすく(たとえば、1桁異なる)、スクロール中にスクロールインジケーターが「ジャンプ」していることに気づいた場合にのみ、各行tableView:estimatedHeightForRowAtIndexPath:
のより正確な推定値を返すために必要な最小限の計算を実行する必要があります。
最初に、高さの計算にのみ使用される、再利用識別子ごとに1つのインスタンスである、テーブルビューセルのオフスクリーンインスタンスをインスタンス化します。(オフスクリーンとは、セル参照がビューコントローラーのプロパティ/ ivarに保存されtableView:cellForRowAtIndexPath:
、テーブルビューが実際に画面上にレンダリングするために返されないことを意味します。)次に、セルは正確なコンテンツ(テキスト、画像など)で構成する必要があります。テーブルビューに表示された場合に保持されます。
すぐにサブビューをレイアウトに続いて、セルを強制した後、使用systemLayoutSizeFittingSize:
上のメソッドをUITableViewCell
のcontentView
セルの必要な高さが何であるかを見つけるために。UILayoutFittingCompressedSize
セルのすべての内容を収めるために必要な最小サイズを取得するために使用します。その後、高さはtableView:heightForRowAtIndexPath:
デリゲートメソッドから返すことができます。
テーブルビューに数十行以上ある場合、自動レイアウト制約の解決を実行するtableView:heightForRowAtIndexPath:
と、最初のロード時に各行で呼び出されるように、テーブルビューを最初にロードするときにメインスレッドがすぐに停止する可能性があることがわかります(スクロールインジケーターのサイズを計算するため)。
iOS 7以降estimatedRowHeight
では、テーブルビューのプロパティを使用できます(絶対に使用する必要があります)。これは、まだ画面に表示されていないセルの行の高さの一時的な見積もり/プレースホルダーをテーブルビューに提供することです。次に、これらのセルが画面上でスクロールしようとすると、実際の行の高さが計算され(を呼び出すことによりtableView:heightForRowAtIndexPath:
)、推定された高さが実際の高さで更新されます。
一般的に言えば、提供する見積もりはそれほど正確である必要はありません。これは、テーブルビューのスクロールインジケーターのサイズを正しく設定するためにのみ使用され、テーブルビューは、誤った見積もりに対してスクロールインジケーターを適切に調整します。画面上のセルをスクロールします。estimatedRowHeight
テーブルビュー(viewDidLoad
または同様のもの)のプロパティを、「平均」の行の高さである定数値に設定する必要があります。行の高さが非常に変動しやすく(たとえば、1桁異なる)、スクロール中にスクロールインジケーターが「ジャンプ」していることに気づいた場合にのみ、各行tableView:estimatedHeightForRowAtIndexPath:
のより正確な推定値を返すために必要な最小限の計算を実行する必要があります。
上記のすべてを実行しても、で制約の解決を行っているときにパフォーマンスが許容できないほど遅いことにまだ気付いているtableView:heightForRowAtIndexPath:
場合は、セルの高さのキャッシュを実装する必要があります。(これはAppleのエンジニアによって提案されたアプローチです。)一般的な考え方は、Autolayoutエンジンに初めて制約を解決させ、次にそのセルの計算された高さをキャッシュし、そのセルの高さに対する今後のすべてのリクエストにキャッシュされた値を使用することです。もちろん、トリックは、セルの高さを変更する可能性のある何かが発生したときに、セルのキャッシュされた高さを確実にクリアすることです。これは、主に、セルのコンテンツが変更されたとき、または他の重要なイベントが発生したとき(ユーザーが調整するような)ダイナミックタイプのテキストサイズスライダー)。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Determine which reuse identifier should be used for the cell at this
// index path, depending on the particular layout required (you may have
// just one, or may have many).
NSString *reuseIdentifier = ...;
// Dequeue a cell for the reuse identifier.
// Note that this method will init and return a new cell if there isn't
// one available in the reuse pool, so either way after this line of
// code you will have a cell with the correct constraints ready to go.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
// Configure the cell with content for the given indexPath, for example:
// cell.textLabel.text = someTextForThisCell;
// ...
// Make sure the constraints have been set up for this cell, since it
// may have just been created from scratch. Use the following lines,
// assuming you are setting up constraints from within the cell's
// updateConstraints method:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
// If you are using multi-line UILabels, don't forget that the
// preferredMaxLayoutWidth needs to be set correctly. Do it at this
// point if you are NOT doing it within the UITableViewCell subclass
// -[layoutSubviews] method. For example:
// cell.multiLineLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds);
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Determine which reuse identifier should be used for the cell at this
// index path.
NSString *reuseIdentifier = ...;
// Use a dictionary of offscreen cells to get a cell for the reuse
// identifier, creating a cell and storing it in the dictionary if one
// hasn't already been added for the reuse identifier. WARNING: Don't
// call the table view's dequeueReusableCellWithIdentifier: method here
// because this will result in a memory leak as the cell is created but
// never returned from the tableView:cellForRowAtIndexPath: method!
UITableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
if (!cell) {
cell = [[YourTableViewCellClass alloc] init];
[self.offscreenCells setObject:cell forKey:reuseIdentifier];
}
// Configure the cell with content for the given indexPath, for example:
// cell.textLabel.text = someTextForThisCell;
// ...
// Make sure the constraints have been set up for this cell, since it
// may have just been created from scratch. Use the following lines,
// assuming you are setting up constraints from within the cell's
// updateConstraints method:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
// Set the width of the cell to match the width of the table view. This
// is important so that we'll get the correct cell height for different
// table view widths if the cell's height depends on its width (due to
// multi-line UILabels word wrapping, etc). We don't need to do this
// above in -[tableView:cellForRowAtIndexPath] because it happens
// automatically when the cell is used in the table view. Also note,
// the final width of the cell may not be the width of the table view in
// some cases, for example when a section index is displayed along
// the right side of the table view. You must account for the reduced
// cell width.
cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));
// Do the layout pass on the cell, which will calculate the frames for
// all the views based on the constraints. (Note that you must set the
// preferredMaxLayoutWidth on multiline UILabels inside the
// -[layoutSubviews] method of the UITableViewCell subclass, or do it
// manually at this point before the below 2 lines!)
[cell setNeedsLayout];
[cell layoutIfNeeded];
// Get the actual height required for the cell's contentView
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
// Add an extra point to the height to account for the cell separator,
// which is added between the bottom of the cell's contentView and the
// bottom of the table view cell.
height += 1.0;
return height;
}
// NOTE: Set the table view's estimatedRowHeight property instead of
// implementing the below method, UNLESS you have extreme variability in
// your row heights and you notice the scroll indicator "jumping"
// as you scroll.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Do the minimal calculations required to be able to return an
// estimated row height that's within an order of magnitude of the
// actual height. For example:
if ([self isTallCellAtIndexPath:indexPath]) {
return 350.0;
} else {
return 40.0;
}
}
これらのプロジェクトは、UILabelsの動的コンテンツを含むテーブルビューセルによる行の高さが可変のテーブルビューの完全に機能する例です。
Xamarinを使用している場合は、@ KentBoogaartがまとめたこのサンプルプロジェクトを確認してください。
heightForRowAtIndexPath
、セルを保持して次にcellForRowAtIndexPath
呼び出されたときにそれを返すことで、完全にリークを回避できる可能性があります。
iOS8
実装はまだのために推奨されるiOS9
とiOS10
、またはこの回答が掲載されましたので、そこに新しいアプローチをしていますか?
上記のiOS 8の場合、それは非常に簡単です。
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 80
self.tableView.rowHeight = UITableView.automaticDimension
}
または
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableView.automaticDimension
}
しかしiOS 7の場合、重要なのは自動レイアウト後の高さを計算することです。
func calculateHeightForConfiguredSizingCell(cell: GSTableViewCell) -> CGFloat {
cell.setNeedsLayout()
cell.layoutIfNeeded()
let height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize).height + 1.0
return height
}
重要
複数行のラベルの場合は、numberOfLines
をに設定することを忘れないでください0
。
忘れないで label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)
完全なサンプルコードはこちらです。
Swift 3用に更新
ウィリアム・フーのスウィフトの回答は良いですが、何かを初めて学ぶときに、簡単で詳細な手順を実行するのに役立ちます。以下の例は、UITableView
可変セルの高さでを作成することを学ぶ間の私のテストプロジェクトです。Swiftのこの基本的なUITableViewの例に基づいています。
完成したプロジェクトは次のようになります。
単一のビューアプリケーションにすることもできます。
プロジェクトに新しいSwiftファイルを追加します。MyCustomCellという名前を付けます。このクラスは、ストーリーボードのセルに追加するビューのアウトレットを保持します。この基本的な例では、各セルにラベルが1つだけあります。
import UIKit
class MyCustomCell: UITableViewCell {
@IBOutlet weak var myCellLabel: UILabel!
}
このコンセントは後で接続します。
ViewController.swiftを開き、次のコンテンツがあることを確認します。
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// These strings will be the data for the table view cells
let animals: [String] = [
"Ten horses: horse horse horse horse horse horse horse horse horse horse ",
"Three cows: cow, cow, cow",
"One camel: camel",
"Ninety-nine sheep: sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep baaaa sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep",
"Thirty goats: goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat "]
// Don't forget to enter this in IB also
let cellReuseIdentifier = "cell"
@IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// delegate and data source
tableView.delegate = self
tableView.dataSource = self
// Along with auto layout, these are the keys for enabling variable cell height
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.animals.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
cell.myCellLabel.text = self.animals[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
重要な注意点:
可変セルの高さを可能にするのは、次の2行のコード(および自動レイアウト)です。
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
テーブルコントローラーをビューコントローラーに追加し、自動レイアウトを使用して4つの側面に固定します。次に、テーブルビューのセルをテーブルビューにドラッグします。そして、プロトタイプセルにラベルをドラッグします。自動レイアウトを使用して、ラベルをテーブルビューセルのコンテンツビューの4つの端に固定します。
重要な注意点:
カスタムクラス名と識別子
テーブルビューセルを選択し、カスタムクラスをMyCustomCell
(追加したSwiftファイル内のクラスの名前)に設定します。また、識別子をcell
(cellReuseIdentifier
上記のコードで使用したのと同じ文字列)に設定します。
ラベルのゼロ線
0
ラベルの行数を設定します。これは複数行を意味し、内容に基づいてラベルのサイズを変更できます。
アウトレットをフックアップ
tableView
変数へのドラッグコントロールViewController
。myCellLabel
、MyCustomCell
クラスの変数に対して同じことを行います。これでプロジェクトを実行して、高さが可変のセルを取得できるはずです。
リーディングエッジとトレーリングエッジ(左と右)を固定しない場合は、ラベルを設定して、preferredMaxLayoutWidth
行を折り返すタイミングを認識させる必要があります。たとえば、前のエッジと後のエッジを固定するのではなく、上のプロジェクトのラベルにCenter Horizontally制約を追加した場合は、次の行をtableView:cellForRowAtIndexPath
メソッドに追加する必要があります。
cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width
preferredMaxLayoutWidth
セルに対してを設定する方が実際には良いのではない contentSize
ですか?そのように、あなたがAccessoriesViewを持っているか、編集のためにスワイプされた場合、それはまだ考慮されますか?
私は、@ smileyborgによるこの賢い解決策をUICollectionViewCell+AutoLayoutDynamicHeightCalculation
カテゴリーにまとめることに決めました。
このカテゴリは、@ wildmonkeyの回答で概説されている問題も修正します(nibからセルをロードしてsystemLayoutSizeFittingSize:
返すCGRectZero
)
キャッシングは考慮されていませんが、今は私のニーズに適しています。自由にコピー、貼り付け、ハックしてください。
#import <UIKit/UIKit.h>
typedef void (^UICollectionViewCellAutoLayoutRenderBlock)(void);
/**
* A category on UICollectionViewCell to aid calculating dynamic heights based on AutoLayout contraints.
*
* Many thanks to @smileyborg and @wildmonkey
*
* @see stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights
*/
@interface UICollectionViewCell (AutoLayoutDynamicHeightCalculation)
/**
* Grab an instance of the receiving type to use in order to calculate AutoLayout contraint driven dynamic height. The method pulls the cell from a nib file and moves any Interface Builder defined contrainsts to the content view.
*
* @param name Name of the nib file.
*
* @return collection view cell for using to calculate content based height
*/
+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name;
/**
* Returns the height of the receiver after rendering with your model data and applying an AutoLayout pass
*
* @param block Render the model data to your UI elements in this block
*
* @return Calculated constraint derived height
*/
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width;
/**
* Directly calls `heightAfterAutoLayoutPassAndRenderingWithBlock:collectionViewWidth` assuming a collection view width spanning the [UIScreen mainScreen] bounds
*/
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block;
@end
#import "UICollectionViewCell+AutoLayout.h"
@implementation UICollectionViewCell (AutoLayout)
#pragma mark Dummy Cell Generator
+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name
{
UICollectionViewCell *heightCalculationCell = [[[NSBundle mainBundle] loadNibNamed:name owner:self options:nil] lastObject];
[heightCalculationCell moveInterfaceBuilderLayoutConstraintsToContentView];
return heightCalculationCell;
}
#pragma mark Moving Constraints
- (void)moveInterfaceBuilderLayoutConstraintsToContentView
{
[self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
[self removeConstraint:constraint];
id firstItem = constraint.firstItem == self ? self.contentView : constraint.firstItem;
id secondItem = constraint.secondItem == self ? self.contentView : constraint.secondItem;
[self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:firstItem
attribute:constraint.firstAttribute
relatedBy:constraint.relation
toItem:secondItem
attribute:constraint.secondAttribute
multiplier:constraint.multiplier
constant:constraint.constant]];
}];
}
#pragma mark Height
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block
{
return [self heightAfterAutoLayoutPassAndRenderingWithBlock:block
collectionViewWidth:CGRectGetWidth([[UIScreen mainScreen] bounds])];
}
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width
{
NSParameterAssert(block);
block();
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
self.bounds = CGRectMake(0.0f, 0.0f, width, CGRectGetHeight(self.bounds));
[self setNeedsLayout];
[self layoutIfNeeded];
CGSize calculatedSize = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return calculatedSize.height;
}
@end
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
MYSweetCell *cell = [MYSweetCell heightCalculationCellFromNibWithName:NSStringFromClass([MYSweetCell class])];
CGFloat height = [cell heightAfterAutoLayoutPassAndRenderingWithBlock:^{
[(id<MYSweetCellRenderProtocol>)cell renderWithModel:someModel];
}];
return CGSizeMake(CGRectGetWidth(self.collectionView.bounds), height);
}
ありがたいことに、iOS8でこのジャズを実行する必要はありませんが、今のところあります!
[YourCell new]
を使用して、それをダミーとして使用できるようにする必要があります。制約コード作成コードがインスタンスで実行され、プログラムでレイアウトパスをトリガーする限り、問題ありません。
UICollectionViews
ていることを実感しました。
これが私の解決策です:
あなたは指示する必要があり、それはビューをロードする前に。そうしないと、期待どおりに動作しません。TableView
estimatedHeight
Objective-C
- (void)viewWillAppear:(BOOL)animated {
_messageField.delegate = self;
_tableView.estimatedRowHeight = 65.0;
_tableView.rowHeight = UITableViewAutomaticDimension;
}
Swift 4.2へのアップデート
override func viewWillAppear(_ animated: Bool) {
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 65.0
}
estimatedRowHeight
、行ごとに変化している場合はどうなりますか?見積もりを過大または過小にする必要がありますか?私が使用する高さの最小値または最大値を使用しtableView
ますか?
estimatedRowHeight
ほとんどの場合、スクロールバーのサイズに影響し、実際のセルの高さには影響しません。選択する高さを太字にします。挿入/削除アニメーションに影響します。
@smileyborgによって提案されたソリューションはほぼ完璧です。カスタムセルがありUILabel
、動的な高さの1つ以上が必要な場合、AutoLayoutを有効にしたsystemLayoutSizeFittingSizeメソッドを組み合わせると、CGSizeZero
すべてのセル制約をセルからそのcontentViewに移動しない限り、が返されます(@TomSwiftで提案されているように、スーパービューのサイズを変更する方法すべてのサブビューをautolayout?でフィットしますか?)。
そのためには、カスタムUITableViewCell実装に次のコードを挿入する必要があります(@Adrianに感謝)。
- (void)awakeFromNib{
[super awakeFromNib];
for (NSLayoutConstraint *cellConstraint in self.constraints) {
[self removeConstraint:cellConstraint];
id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;
NSLayoutConstraint *contentViewConstraint =
[NSLayoutConstraint constraintWithItem:firstItem
attribute:cellConstraint.firstAttribute
relatedBy:cellConstraint.relation
toItem:seccondItem
attribute:cellConstraint.secondAttribute
multiplier:cellConstraint.multiplier
constant:cellConstraint.constant];
[self.contentView addConstraint:contentViewConstraint];
}
}
@smileyborgの回答とこれを混在させることで機能するはずです。
systemLayoutSizeFittingSize
セルではなくcontentViewで呼び出す必要がある
私がちょうど答えとして投稿するのに遭遇した重要な十分な落とし穴。
@smileyborgの答えはほとんど正しいです。ただし、layoutSubviews
カスタムセルクラスのメソッドにコードを設定している場合(たとえばを設定している場合)はpreferredMaxLayoutWidth
、次のコードでは実行されません。
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
それはしばらくの間私を混乱させました。次に、それらがcontentView
セル自体ではなく、layoutSubviewsをトリガーしているだけであることに気付きました。
私の作業コードは次のようになります:
TCAnswerDetailAppSummaryCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailAppSummaryCell"];
[cell configureWithThirdPartyObject:self.app];
[cell layoutIfNeeded];
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height;
新しいセルを作成している場合は、setNeedsLayout
すでに設定されているはずなので、呼び出す必要がないと確信しています。セルへの参照を保存する場合は、おそらくそれを呼び出す必要があります。どちらにしても、何も傷つけてはいけません。
のようなものを設定するセルサブクラスを使用している場合の別のヒントpreferredMaxLayoutWidth
。@smileyborgが述べているように、「テーブルビューセルの幅はまだテーブルビューの幅に固定されていません」。これは事実であり、ビューコントローラではなくサブクラスで作業をしている場合は問題があります。ただし、この時点でテーブルの幅を使用してセルフレームを設定するだけです。
たとえば、高さの計算では:
self.summaryCell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailDefaultSummaryCell"];
CGRect oldFrame = self.summaryCell.frame;
self.summaryCell.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, self.tableView.frame.size.width, oldFrame.size.height);
(私はたまたまこの特定のセルを再利用のためにキャッシュしていますが、それは無関係です)。
人々がまだこれで問題を抱えている場合に備えて。動的セルの高さにAutolayoutを活用するUITableViewsでAutolayoutを使用することに関する簡単なブログ投稿と、これをより抽象的で実装しやすくするためのオープンソースコンポーネントを書きました。 https://github.com/Raizlabs/RZCellSizeManager
セル内のレイアウトが適切である限り。
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
return [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}
更新:iOS 8で導入された動的サイズ変更を使用する必要があります。
tableView:cellForRowAtIndexPath:
、tableView:heightForRowAtIndexPath:
今すぐ電話してもいいですか?
systemLayoutSizeFittingSize:
にtableView:cellForRowAtIndexPath:
でていることを使用、その後、その後、結果をキャッシュし、tableView:heightForRowAtIndexPath:
それ限り制約が正しくもちろん設定されているとしてうまく機能!
(下部にあるXcode 8.x / Xcode 9.xの場合)
Xcode 7.xの次の問題に注意してください。これは混乱の原因となる可能性があります。
Interface Builderはセルの自動サイズ設定を適切に処理しません。制約が完全に有効であっても、IBは文句を言い、混乱する提案やエラーを提供します。その理由は、IBが制約の指示に応じて行の高さを変更することを望まないためです(セルがコンテンツの周囲に収まるようにするため)。代わりに、行の高さを固定したままにし、制約を変更するよう提案し始めます。これは無視してください。
たとえば、すべてを正常に設定し、警告もエラーもない、すべてが機能することを想像してみてください。
フォントサイズを変更すると(この例では、説明ラベルのフォントサイズを17.0から18.0に変更しています)。
フォントサイズが増加したため、ラベルは3行を占有するようになりました(以前は2行を占有していました)。
Interface Builderが期待どおりに動作した場合、新しいラベルの高さに合わせてセルの高さを変更します。ただし、実際に何が起こるかというと、IBは赤い自動レイアウトエラーアイコンを表示し、ハグ/圧縮の優先順位を変更するように提案します。
これらの警告は無視してください。代わりに*できることは、行の高さを手動で変更することです([セル]> [サイズインスペクター]> [行の高さ]を選択)。
赤い矢印のエラーが消えるまで、この高さを一度に1クリックずつ(上下ステッパーを使用して)変更していました。(実際には黄色の警告が表示されます。この時点で先に進んで「フレームの更新」を実行すると、すべてが機能するはずです)。
*実際には、Interface Builderでこれらの赤いエラーや黄色の警告を解決する必要はありません-実行時に、すべてが正しく機能します(IBにエラー/警告が表示されている場合でも)。実行時にコンソールログでAutoLayoutエラーが発生しないことを確認してください。
実際、IBで行の高さを常に更新しようとすると、非常に煩わしく、(分数値のため)不可能に近い場合があります。
迷惑なIB警告/エラーを防ぐために、関連するビューを選択Size Inspector
し、プロパティでAmbiguity
選択することができますVerify Position Only
Xcode 8.x / Xcode 9.xは(時々)Xcode 7.xとは異なる動作をするようですが、それでも正しくありません。たとえば、compression resistance priority
/ hugging priority
が必須(1000)に設定されている場合でも、Interface Builderは(ラベルの周囲に合うようにセルの高さをサイズ変更する代わりに)セルに合うようにラベルを引き伸ばしたりクリップしたりすることがあります。そのような場合、オートレイアウトの警告やエラーも表示されない可能性があります。または、上記のXcode 7.xとまったく同じように動作することもあります。
行の高さと推定行の高さの自動寸法を設定するには、次の手順に従って、自動寸法がセル/行の高さのレイアウトに効果的であることを確認します。
UITableViewAutomaticDimension
rowHeightと見積もられたRowHeightに割り当てheightForRowAt
、それに値UITableViewAutomaticDimension
を返す)-
目的C:
// in ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
@property IBOutlet UITableView * table;
@end
// in ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.table.dataSource = self;
self.table.delegate = self;
self.table.rowHeight = UITableViewAutomaticDimension;
self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
迅速:
@IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Don't forget to set dataSource and delegate for table
table.dataSource = self
table.delegate = self
// Set automatic dimensions for row height
// Swift 4.2 onwards
table.rowHeight = UITableView.automaticDimension
table.estimatedRowHeight = UITableView.automaticDimension
// Swift 4.1 and below
table.rowHeight = UITableViewAutomaticDimension
table.estimatedRowHeight = UITableViewAutomaticDimension
}
// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Swift 4.2 onwards
return UITableView.automaticDimension
// Swift 4.1 and below
return UITableViewAutomaticDimension
}
UITableviewCellのラベルインスタンスの場合
注:動的な長さのラベル(UIElements)が複数ある場合は、そのコンテンツサイズに応じて調整する必要があります。より高い優先度で展開/圧縮するラベルの「コンテンツハグと圧縮耐性の優先度」を調整します。
tableView: heightForRow
データソースを実装しなくても機能します。
tableView: heightForRow
ます。(IOSのため10- tableView: heightForRow
必要)
tableView: heightForRow
..if (indexPath.row == 0) { return 100} else { return UITableView.automaticDimension }
同様ボブ-Spryn @私は答えとしてこれを掲示していていることが重要十分な落とし穴に走りました。
@smileyborgの回答にしばらく苦労しました。あなたは追加要素(とIBであなたのプロトタイプのセルを定義した場合、私はに走ったことを知っておかなけれはUILabels
、UIButtons
あなたが[とセルインスタンス化するときIBで、など)[YourTableViewCellClass alloc] init]
あなたがいない限り、それはそのセル内の他のすべての要素をインスタンス化しませんがそれを行うために書かれたコード。(私も同様の経験をしましたinitWithStyle
。)
ストーリーボードに追加の要素をすべてインスタンス化させると、セルが取得されます[tableView dequeueReusableCellWithIdentifier:@"DoseNeeded"]
([tableView dequeueReusableCellWithIdentifier:forIndexPath:]
これは興味深い問題を引き起こすわけではありません)。これを行うと、IBで定義したすべての要素がインスタンス化されます。
ストーリーボードの自動レイアウトで問題を解決する良い方法:
- (CGFloat)heightForImageCellAtIndexPath:(NSIndexPath *)indexPath {
static RWImageCell *sizingCell = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sizingCell = [self.tableView dequeueReusableCellWithIdentifier:RWImageCellIdentifier];
});
[sizingCell setNeedsLayout];
[sizingCell layoutIfNeeded];
CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height;
}
別の「解決策」:このフラストレーションをすべてスキップし、代わりにUIScrollViewを使用して、UITableViewと同じ外観と感じの結果を取得します。
それは、スマイリーボーが提案したようなものを構築するために文字通り合計で20以上の非常に苛立たしい時間を費やし、何ヶ月にもわたって失敗し、3バージョンのApp Storeリリースで失敗した後の、私にとって苦痛な「解決策」でした。
私の見解では、もしあなたが本当にiOS 7のサポートを必要とするなら(私たちにとって、それは不可欠です)、そのテクノロジーはあまりにも壊れやすく、あなたはあなたの髪を引っ張って試すでしょう。そして、そのUITableViewは、高度な行編集機能の一部を使用していないか、実際に1000以上の「行」をサポートする必要がない限り、完全に過剰です(実際には、アプリでは20行を超えることはありません)。
追加のボーナスは、UITableViewに付属しているすべてのデリゲートのがらくたに対して、コードが非常に単純になることです。これは、見栄えがよく、管理が簡単なviewOnLoadのコードの単一ループにすぎません。
これを行う方法に関するいくつかのヒントを次に示します。
Storyboardまたはnibファイルを使用して、ViewControllerおよび関連するルートビューを作成します。
UIScrollViewをルートビューにドラッグします。
UIScrollViewがルートビュー全体を埋めるように、トップレベルビューに上、下、左、右の制約を追加します。
UIScrollView内にUIViewを追加し、「コンテナー」と呼びます。UIScrollView(その親)に上、下、左、右の制約を追加します。キートリック:また、UIScrollViewとUIViewをリンクする「等しい幅」制約を追加します。
注:「スクロールビューにはスクロール可能なコンテンツの高さがあいまいです」というエラーが表示され、コンテナーのUIViewの高さが0ピクセルである必要があります。アプリを実行しているときは、どちらのエラーも問題ではないようです。
「セル」ごとにnibファイルとコントローラーを作成します。UITableViewCellではなくUIViewを使用します。
ルートViewControllerで、本質的にすべての「行」をコンテナーのUIViewに追加し、プログラムでそれらの左端と右端をコンテナービューにリンクし、上端をコンテナービューの上部(最初の項目)または前のいずれかにリンクします。細胞。次に、最後のセルをコンテナの下部にリンクします。
私たちにとって、各「行」はnibファイルにあります。したがって、コードは次のようになります。
class YourRootViewController {
@IBOutlet var container: UIView! //container mentioned in step 4
override func viewDidLoad() {
super.viewDidLoad()
var lastView: UIView?
for data in yourDataSource {
var cell = YourCellController(nibName: "YourCellNibName", bundle: nil)
UITools.addViewToTop(container, child: cell.view, sibling: lastView)
lastView = cell.view
//Insert code here to populate your cell
}
if(lastView != nil) {
container.addConstraint(NSLayoutConstraint(
item: lastView!,
attribute: NSLayoutAttribute.Bottom,
relatedBy: NSLayoutRelation.Equal,
toItem: container,
attribute: NSLayoutAttribute.Bottom,
multiplier: 1,
constant: 0))
}
///Add a refresh control, if you want - it seems to work fine in our app:
var refreshControl = UIRefreshControl()
container.addSubview(refreshControl!)
}
}
そして、UITools.addViewToTopのコードは次のとおりです。
class UITools {
///Add child to container, full width of the container and directly under sibling (or container if sibling nil):
class func addViewToTop(container: UIView, child: UIView, sibling: UIView? = nil)
{
child.setTranslatesAutoresizingMaskIntoConstraints(false)
container.addSubview(child)
//Set left and right constraints so fills full horz width:
container.addConstraint(NSLayoutConstraint(
item: child,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: container,
attribute: NSLayoutAttribute.Left,
multiplier: 1,
constant: 0))
container.addConstraint(NSLayoutConstraint(
item: child,
attribute: NSLayoutAttribute.Trailing,
relatedBy: NSLayoutRelation.Equal,
toItem: container,
attribute: NSLayoutAttribute.Right,
multiplier: 1,
constant: 0))
//Set vertical position from last item (or for first, from the superview):
container.addConstraint(NSLayoutConstraint(
item: child,
attribute: NSLayoutAttribute.Top,
relatedBy: NSLayoutRelation.Equal,
toItem: sibling == nil ? container : sibling,
attribute: sibling == nil ? NSLayoutAttribute.Top : NSLayoutAttribute.Bottom,
multiplier: 1,
constant: 0))
}
}
これまでのところこのアプローチで見つけた唯一の「落とし穴」は、UITableViewがスクロール時にビューの上部に「フローティング」セクションヘッダーの優れた機能を備えていることです。上記の解決策では、プログラミングを追加しない限りそれはできませんが、私たちの特定のケースでは、この機能は100%必須ではなかったため、廃止されたときに誰も気づきませんでした。
セル間に仕切りが必要な場合は、仕切りのように見えるカスタム「セル」の下部に高さ1ピクセルのUIViewを追加するだけです。
リフレッシュコントロールが機能するように「バウンス」と「垂直方向にバウンス」をオンにしてください。そうすると、テーブルビューのように見えます。
TableViewは、コンテンツの下に空の行とディバイダーをいくつか表示します。ただし、このソリューションが全画面を満たしていない場合。しかし、個人的には、これらの空の行がそこにない方がいいと思います-セルの高さが可変であるため、空の行がそこにあると、常に「バギー」に見えました。
他のプログラマーが自分のアプリのテーブルビューでそれを理解しようとする20時間以上を無駄にする前に私の投稿を読んでくれることを期待しています。:)
サブビューを持つセルがあり、セルの高さをサブビュー+パディングを囲むのに十分な高さにしたいとします。
1)サブビューの下部制約をcell.contentViewから必要なパディングを引いた値に設定します。セルまたはcell.contentView自体に制約を設定しないでください。
2)tableViewのrowHeight
プロパティまたはtableView:heightForRowAtIndexPath:
に設定しますUITableViewAutomaticDimension
。
3)tableViewのestimatedRowHeight
プロパティまたはtableView:estimatedHeightForRowAtIndexPath:
高さの最適な推定値のいずれかに設定します。
それでおしまい。
プログラムでレイアウトする場合、iOS 10でSwiftのアンカーを使用する際の考慮事項を次に示します。
3つのルール/ステップがあります
NUMBER 1:viewDidLoadでtableviewのこの2つのプロパティを設定します。1つ目は、セルの動的サイズを期待する必要があるtableviewに伝えます。2つ目は、アプリにスクロールバーインジケーターのサイズを計算させるだけなので、パフォーマンス。
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
番号2:これは、ビューではなくセルのcontentViewにサブビューを追加する必要があり、またそのlayoutsmarginguideを使用してサブビューを上部と下部にアンカーする必要があることが重要です。これは、これを行う方法の実用的な例です。
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUpViews()
}
private func setUpViews() {
contentView.addSubview(movieImageView)
contentView.addSubview(descriptionLabel)
let marginGuide = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
movieImageView.heightAnchor.constraint(equalToConstant: 80),
movieImageView.widthAnchor.constraint(equalToConstant: 80),
movieImageView.leftAnchor.constraint(equalTo: marginGuide.leftAnchor),
movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 20),
descriptionLabel.leftAnchor.constraint(equalTo: movieImageView.rightAnchor, constant: 15),
descriptionLabel.rightAnchor.constraint(equalTo: marginGuide.rightAnchor),
descriptionLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -15),
descriptionLabel.topAnchor.constraint(equalTo: movieImageView.topAnchor)
])
}
サブビューを追加してレイアウトを実行するメソッドを作成し、initメソッドで呼び出します。
番号3:メソッドを呼び出さない:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
}
これを行うと、実装がオーバーライドされます。
テーブルビューの動的セルについては、この3つのルールに従います。
これは動作中の実装です https://github.com/jamesrochabrun/MinimalViewController
長い文字列がある場合。たとえば、改行がないもの。次に、いくつかの問題が発生する可能性があります。
「疑わしい」修正は、受け入れられた回答と他のいくつかの回答で言及されています。追加するだけです
cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width
私はスラーの答えが最も完全で簡潔なので、混乱しないでください。
これらの変更が必要な理由は説明していませんが。そうしよう。
次のコードをプロジェクトにドロップします。
import UIKit
class ViewController: UIViewController {
lazy var label : UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.backgroundColor = .red
lbl.textColor = .black
return lbl
}()
override func viewDidLoad() {
super.viewDidLoad()
// step0: (0.0, 0.0)
print("empty Text intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step1: (29.0, 20.5)
label.text = "hiiiii"
print("hiiiii intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step2: (328.0, 20.5)
label.text = "translatesAutoresizingMaskIntoConstraints"
print("1 translate intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step3: (992.0, 20.5)
label.text = "translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints"
print("3 translate intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step4: (328.0, 20.5)
label.text = "translatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints"
print("3 translate w/ line breaks (but the line breaks get ignored, because numberOfLines is defaulted to `1` and it will force it all to fit into one line! intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step5: (328.0, 61.0)
label.numberOfLines = 0
print("3 translate w/ line breaks and '0' numberOfLines intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step6: (98.5, 243.5)
label.preferredMaxLayoutWidth = 100
print("3 translate w/ line breaks | '0' numberOfLines | preferredMaxLayoutWidth: 100 intrinsicContentSize: \(label.intrinsicContentSize)")
setupLayout()
}
func setupLayout(){
view.addSubview(label)
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
サイズの制約を追加していないことに注意してください。centerX、centerYの制約のみを追加しました。しかし、それでもラベルのサイズは正しくなります。なぜですか?
のためですcontentSize
。
これをより適切に処理するには、最初にstep0を保持し、次にステップ1〜6をコメントアウトします。setupLayout()
とどまりましょう。動作を観察します。
次に、step1のコメントを外して観察します。
次に、step2のコメントを外して観察します。
6つのステップすべてのコメントを外して、それらの動作を観察するまで、これを繰り返します。
contenSize
ますか?\n
場合、intrinsicContentSizeの幅はすべての行の最大幅になります。1行が25文字、別の行が2文字、別の行が21文字の場合、幅は25文字に基づいて計算されますnumberOfLines
し0
そうでなければ、複数行を持っていません。あなたnumberOfLines
はintrinsicContentSizeの高さを調整します調整を行う:テキストに基づいて、intrinsicContentSizeの幅200
と高さはだったが100
、幅をラベルのコンテナーに制限したいとしたらどうしますか?解決策は、それを望ましい幅に設定することです。これをに設定preferredMaxLayoutWidth
することで130
、新しい組み込みコンテンツサイズはおおよその幅になります130
。100
より多くの行が必要になるため、高さは明らかに高くなります。制約が正しく設定されていれば、これを使用する必要はまったくありません!詳細については、この回答とそのコメントを参照してください。preferredMaxLayoutWidth
幅/高さを制限する制約がない場合にのみ使用する必要があります。たとえば、「テキストを超えない限り、テキストを折り返さないでください。preferredMaxLayoutWidth
」。しかし、あなたがリード/末尾と設定した場合、100%確実にnumberOfLines
にに0
あなたがしている良い!ロングストーリーが短い、それを使用することをお勧めしますここで最も答えは間違っている!あなたはそれを必要としない。それを必要とする、あなたの制約というサインです正しく設定されていない、または制約がないこと
フォントサイズ: fontSizeを大きくすると、intrinsicContentSizeの高さが大きくなることにも注意してください。私のコードではそれを示しませんでした。自分で試すことができます。
したがって、tableViewCellの例に戻ります。
あなたがする必要があるのは:
numberOfLines
には0
preferredMaxLayoutWidth
。私の場合、サーバーから取得された画像を使用してカスタムのセルを作成する必要があり、幅と高さは任意です。そして、動的サイズ(幅と高さの両方)を持つ2つのUILabels
ここで私のautolayoutとプログラムで同じ答えを達成しました:
基本的には@smileyBorgの回答は役に立ちましたが、systemLayoutSizeFittingSizeは私にとってはうまくいきませんでした。私のアプローチでは:
1.自動行高さ計算プロパティを使用しません。2.推定された高さの使用なし3.不要なupdateConstraintsの必要なし。4.自動優先最大レイアウト幅は使用しません。5. systemLayoutSizeFittingSizeは使用しません(使用する必要がありますが、機能しません。内部で何が行われているのかわかりません)。代わりに、メソッド-(float)getViewHeightが機能し、内部で何が行われているのかを確認します。
私は2つの値を使ってちょっとした試行錯誤をしただけでrowHeight
、estimatedRowHeight
デバッグの洞察が得られるかもしれないと思いました。
両方を設定するか、設定するだけのestimatedRowHeight
場合は、目的の動作が得られます。
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1.00001 // MUST be greater than 1
正確な見積もりを取得するために最善を尽くすことをお勧めしますが、最終結果に違いはありません。パフォーマンスに影響するだけです。
rowHeightのみを設定する場合、つまり次のようにします。
tableView.rowHeight = UITableViewAutomaticDimension
あなたの最終的な結果は期待どおりではありません:
estimatedRowHeight
を1以下に設定すると、に関係なくクラッシュしますrowHeight
。
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1
次のエラーメッセージが表示されてクラッシュしました。
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'table view row height
must not be negative - provided height for index path (<NSIndexPath:
0xc000000000000016> {length = 2, path = 0 - 0}) is -1.000000'
...some other lines...
libc++abi.dylib: terminating with uncaught exception of type
NSException
@smileyborgの承認された回答に関して、私は見つけました
[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]
制約があいまいな場合に、信頼性が低くなります。以下のUIViewのヘルパーカテゴリを使用して、レイアウトエンジンに強制的に一方向の高さを計算させることをお勧めします。
-(CGFloat)systemLayoutHeightForWidth:(CGFloat)w{
[self setNeedsLayout];
[self layoutIfNeeded];
CGSize size = [self systemLayoutSizeFittingSize:CGSizeMake(w, 1) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
CGFloat h = size.height;
return h;
}
ここで、w:はテーブルビューの幅です。
これらの2つの関数をビューコントローラーに追加するだけで、問題が解決します。ここで、listはすべての行の文字列を含む文字列配列です。
func tableView(_ tableView: UITableView,
estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
tableView.rowHeight = self.calculateHeight(inString: list[indexPath.row])
return (tableView.rowHeight)
}
func calculateHeight(inString:String) -> CGFloat
{
let messageString = input.text
let attributes : [NSAttributedStringKey : Any] = [NSAttributedStringKey(rawValue: NSAttributedStringKey.font.rawValue) : UIFont.systemFont(ofSize: 15.0)]
let attributedString : NSAttributedString = NSAttributedString(string: messageString!, attributes: attributes)
let rect : CGRect = attributedString.boundingRect(with: CGSize(width: 222.0, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)
let requredSize:CGRect = rect
return requredSize.height
}
swift 4
@IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var tableView: UITableView!
private var context = 1
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.addObserver(self, forKeyPath: "contentSize", options: [.new,.prior], context: &context)
}
// Added observer to adjust tableview height based on the content
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &self.context{
if let size = change?[NSKeyValueChangeKey.newKey] as? CGSize{
print("-----")
print(size.height)
tableViewHeightConstraint.constant = size.height + 50
}
}
}
//Remove observer
deinit {
NotificationCenter.default.removeObserver(self)
}
セルの高さがコンテンツによって動的である場合は、正確に数え、セルがレンダリングされる前に高さの値を返す必要があります。簡単な方法は、テーブルセルの高さのデリゲートメソッドで呼び出すコントローラーのテーブルビューのセルコードでカウント方法を定義することです。高さがテーブルまたは画面の幅に依存している場合は、実際のセルフレームの幅(デフォルトは320)をカウントすることを忘れないでください。つまり、テーブルセルの高さのデリゲートメソッドで、cell.frameを使用してセルの幅を修正してから、セルで定義されている高さのカウントメソッドを呼び出して適切な値を取得し、それを返します。
PS。セルオブジェクトを生成するためのコードは、別のテーブルビューのセルデリゲートメソッドが呼び出す別のメソッドで定義できます。
Swiftのさらに別のiOs7 + iOs8ソリューション
var cell2height:CGFloat=44
override func viewDidLoad() {
super.viewDidLoad()
theTable.rowHeight = UITableViewAutomaticDimension
theTable.estimatedRowHeight = 44.0;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myTableViewCell", forIndexPath: indexPath) as! myTableViewCell
cell2height=cell.contentView.height
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if #available(iOS 8.0, *) {
return UITableViewAutomaticDimension
} else {
return cell2height
}
}
cellForRowAtIndexPath
。この時点ではセルはまだレイアウトされていません。