如何UITableViewCell
在表格视图的s中使用“自动布局”,以使每个单元格的内容和子视图确定行高(自身/自动),同时保持流畅的滚动性能?
如何UITableViewCell
在表格视图的s中使用“自动布局”,以使每个单元格的内容和子视图确定行高(自身/自动),同时保持流畅的滚动性能?
Answers:
TL; DR:不喜欢阅读?直接跳转到GitHub上的示例项目:
无论您针对哪个iOS版本开发,以下前两个步骤均适用。
在UITableViewCell
子类中,添加约束,以使单元格的子视图的边缘固定到单元格的contentView的边缘(最重要的是固定到顶部和底部边缘)。注意:不要将子视图固定到单元格本身;只对细胞的contentView
!让这些子视图的固有内容大小驱动表格视图单元格的内容视图的高度,方法是确保每个子视图的垂直方向的内容压缩阻力和内容拥抱约束都不会被添加的更高优先级约束所覆盖。(嗯?点击这里。)
请记住,其想法是使单元的子视图垂直连接到单元的内容视图,以便它们可以“施加压力”并使内容视图扩展以适合它们。使用带有几个子视图的示例单元格,这是一些 (不是全部!)约束看起来像的视觉插图:
您可以想象,随着在上面的示例单元格中将更多文本添加到多行主体标签中,它将需要垂直增长以适合文本,这将有效地迫使单元格高度增加。(当然,您需要正确设置约束条件才能使其正常工作!)
正确使用约束绝对是使用自动版式获得动态像元高度时最困难也是最重要的部分。如果您在此处输入错误,则可能会阻止其他所有功能运行-因此,请花点时间!我建议您在代码中设置约束,因为您确切知道要在哪里添加约束,并且在出现问题时调试起来要容易得多。与使用布局锚点或GitHub上一种出色的开源API之一的Interface Builder相比,在代码中添加约束既简单又功能强大。
updateConstraints
UITableViewCell子类的方法中执行一次。请注意,它updateConstraints
可能会被多次调用,因此为避免多次添加相同的约束,请确保将添加约束的代码包装updateConstraints
在检查boolean属性中,例如didSetupConstraints
(在运行约束后将其设置为YES) -一次添加代码)。另一方面,如果您有更新现有约束的代码(例如constant
在某些约束上调整属性),请将其放在updateConstraints
检查对象的外部,didSetupConstraints
以便每次调用该方法时都可以运行。对于单元中的每个唯一约束集,请使用唯一的单元重用标识符。换句话说,如果您的单元具有多个唯一的布局,则每个唯一的布局应收到其自己的重用标识符。(当单元格变体具有不同数量的子视图,或者子视图以不同的方式排列时,就需要使用新的重用标识符。)
例如,如果您在每个单元格中显示一封电子邮件,则可能有4种独特的布局:仅包含主题的消息,包含主题和正文的消息,具有主题和照片附件的消息以及具有主题的消息,机身和照片附件。每个布局具有实现它所需的完全不同的约束,因此,一旦初始化了单元并为这些单元类型之一添加了约束,该单元就应获得特定于该单元类型的唯一重用标识符。这意味着当您使单元出队以供重用时,约束已被添加并且可以用于该单元类型。
请注意,由于内部内容大小的不同,具有相同约束(类型)的单元格可能仍具有不同的高度!不要将根本不同的布局(不同的约束)与由于内容大小不同而计算出的不同视图框架(由相同约束解决)混淆。
要启用自动调整大小的表格视图单元格,必须将表格视图的rowHeight属性设置为UITableViewAutomaticDimension。您还必须将一个值分配给estimatedRowHeight属性。一旦设置了这两个属性,系统就会使用“自动布局”来计算行的实际高度
在iOS 8中,Apple内部化了以前必须由iOS 8之前的用户执行的大部分工作。为了使自动调整单元格功能起作用,必须首先将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
类似方式)设置为恒定值,即“平均”行高。仅当您的行高具有极大的可变性(例如,相差一个数量级)并且在滚动时注意到滚动指示器“跳跃”时,您tableView:estimatedHeightForRowAtIndexPath:
才需要执行最小化计算来为每行返回更准确的估计值。
首先,实例化一个表格视图单元格的屏幕外实例,每个重用标识符一个实例,该实例严格用于高度计算。(屏幕外表示单元格引用存储在视图控制器上的属性/ ivar中,从不返回tableView:cellForRowAtIndexPath:
以使表格视图实际在屏幕上呈现。)接下来,必须为单元格配置准确的内容(例如,文本,图像等)如果要在表格视图中显示,它将保留。
然后,迫使细胞立即布局其子视图,然后使用systemLayoutSizeFittingSize:
该方法UITableViewCell
的contentView
找出电池的必要高度是什么。使用UILayoutFittingCompressedSize
获得的最小尺寸必须适应单元格中的所有内容。然后可以从tableView:heightForRowAtIndexPath:
委托方法中返回高度。
如果您的表格视图中包含多于几十行,您会发现执行“自动布局”约束求解可以在首次加载表格视图时迅速使主线程陷入困境,就像tableView:heightForRowAtIndexPath:
在第一次加载时在每一行上调用的那样(以计算滚动指示器的大小)。
从iOS 7开始,您可以(绝对应该)estimatedRowHeight
在表格视图上使用属性。这是为表格视图提供临时估计/占位符,用于尚未显示在屏幕上的单元格的行高。然后,当这些单元格即将在屏幕上滚动时,将计算实际行高(通过调用tableView:heightForRowAtIndexPath:
),并将估计的高度更新为实际行高。
一般而言,您提供的估算值不一定非常准确-仅用于在表格视图中正确调整滚动指示器的大小,而表格视图在您调整滚动指示器以适应不正确的估算值方面做得很好滚动屏幕上的单元格。您应该将estimatedRowHeight
表视图上的属性(以viewDidLoad
类似方式)设置为恒定值,即“平均”行高。仅当您的行高具有极大的可变性(例如,相差一个数量级)并且在滚动时注意到滚动指示器“跳跃”时,您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更新
William Hu的Swift回答很好,但是当我第一次学习做某事时,它可以帮助我采取一些简单而详细的步骤。下面的示例是我的学习项目,同时学习了如何制作UITableView
具有可变单元格高度的。我基于Swift的基本UITableView示例。
完成的项目应如下所示:
它可以只是一个单视图应用程序。
将新的Swift文件添加到您的项目。将其命名为MyCustomCell。此类将容纳您添加到情节提要中单元格中的视图的出口。在此基本示例中,每个单元格中只有一个标签。
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).")
}
}
重要的提示:
以下两行代码(以及自动布局)使可变单元格高度成为可能:
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
将表视图添加到视图控制器,并使用自动布局将其固定到四个侧面。然后将一个表视图单元格拖到该表视图上。并将其拖到“原型”单元格上。使用自动布局将标签固定到“表格视图”单元格内容视图的四个边缘。
重要的提示:
自定义类名称和标识符
选择Table View Cell并将自定义类设置为MyCustomCell
(我们添加的Swift文件中该类的名称)。还要将标识符设置为cell
(与cellReuseIdentifier
上面代码中用于的字符串相同)。
标签零线
0
在“标签”中将行数设置为。这意味着多行,并允许标签根据其内容调整自身大小。
连接插座
tableView
变量ViewController
。myCellLabel
对该MyCustomCell
类中的变量执行相同的操作。您应该现在就可以运行您的项目并获得高度可变的单元格。
如果不固定前缘和后缘(左右),则可能还需要设置标签的标签,preferredMaxLayoutWidth
以便知道何时换行。例如,如果您在上面的项目中向标签添加了“水平居中”约束,而不是固定前缘和后缘,则需要将此行添加到tableView:cellForRowAtIndexPath
方法中:
cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width
preferredMaxLayoutWidth
对于单元格设置它实际上不是更好 contentSize
吗?这样,如果您有一个AccessoriesView或已对其进行刷卡进行编辑,那么仍将其考虑在内?
我决定将@smileyborg的巧妙解决方案包装到一个UICollectionViewCell+AutoLayoutDynamicHeightCalculation
类别中。
该类别还纠正了@wildmonkey的答案中概述的问题(从笔尖加载细胞并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
它加载视图之前。否则它将无法像预期的那样运行。
目标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
具有动态高度,则systemLayoutSizeFittingSize方法与启用了AutoLayout的结合将返回a,CGSizeZero
除非您将所有单元格约束从该单元格移至其contentView(如@TomSwift所建议,此处如何将 Superview 调整为使所有子视图都适合自动布局吗?)。
为此,您需要在自定义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);
(我碰巧要缓存此特定单元以供重复使用,但这无关紧要)。
如果人们仍然对此感到麻烦。我写了一篇简短的博客文章,内容涉及将自动布局与UITableViews结合使用,以利用动态布局高度的自动布局,以及一个开源组件来帮助使这一过程更加抽象和易于实现。 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显示红色的自动布局错误图标,并建议您修改拥抱/压缩优先级。
您应该忽略这些警告。您可以*做的是手动更改行的高度(选择“单元格”>“大小检查器”>“行高”)。
我一次(使用向上/向下步进器)一次更改此高度,直到红色箭头错误消失!(实际上,您会收到黄色警告,然后继续执行“更新框架”,一切都应该可以)。
*请注意,您实际上不必解决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
设置为required(1000),Interface Builder 也会拉伸或剪切标签以适合单元格(而不是调整单元格高度以适合标签周围)。在这种情况下,它甚至可能不会显示任何AutoLayout警告或错误。或者有时它确实可以完成Xcode 7.x的操作,如上所述。
要为行高和估计的行高设置自动尺寸,请确保执行以下步骤,自动尺寸对单元格/行高布局有效。
UITableViewAutomaticDimension
给rowHeight和估计的RowHeightheightForRowAt
返回值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 }
像@ Bob-Spryn一样,我遇到了一个非常重要的陷阱,因此我将其发布为答案。
我在@smileyborg的答案中挣扎了一段时间。我遇到的问题是,如果您在IB中实例化单元时在IB中用其他元素(UILabels
,UIButtons
等)定义了原型单元,[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多个非常令人沮丧的小时来尝试构建类似Smileyborg所建议的内容,并且使App Store发行了三个月的多个版本而失败。
我的观点是,如果您确实需要iOS 7支持(对我们来说,这是必不可少的),那么该技术太脆弱了,您将无法尝试。而且,除非您正在使用某些高级行编辑功能和/或确实需要支持1000多个“行”(在我们的应用程序中,实际上不超过20行),否则UITableView完全是矫kill过正。
额外的好处是,与UITableView附带的所有委托来回相比,代码变得异常简单。这只是viewOnLoad中的一个代码循环,看起来很优雅并且易于管理。
以下是有关操作方法的提示:
使用Storyboard或nib文件,创建一个ViewController和关联的根视图。
将UIScrollView拖到根视图上。
将约束顶部,底部,左侧和右侧的约束添加到顶层视图,以便UIScrollView填充整个根视图。
在UIScrollView内添加一个UIView并将其称为“容器”。向UIScrollView(其父级)添加顶部,底部,左侧和右侧约束。关键技巧:还添加“等宽”约束以链接UIScrollView和UIView。
注意:您将收到一个错误“滚动视图具有可滚动的内容高度不明确的高度”,并且您的容器UIView的高度应为0像素。应用程序运行时,这两个错误似乎都无关紧要。
为每个“单元”创建nib文件和控制器。使用UIView而不是UITableViewCell。
在您的根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多个小时才能尝试在自己的应用程序中使用Table View弄清楚之前阅读我的文章。:)
假设您有一个带有子视图的单元格,并且希望该单元格的高度足够高以包含子视图+填充。
1)设置子视图的底部约束等于cell.contentView减去所需的填充。不要在单元格或cell.contentView本身上设置约束。
2)将tableView的rowHeight
属性设置tableView:heightForRowAtIndexPath:
为UITableViewAutomaticDimension
。
3)设置tableView的estimatedRowHeight
属性或tableView:estimatedHeightForRowAtIndexPath:
对高度的最佳猜测。
而已。
如果您以编程方式进行布局,则以下是在Swift中使用锚点的iOS 10的考虑因素。
共有三个规则/步骤
NUMBER 1:在viewDidLoad上设置tableview的这两个属性,第一个告诉tableview应该期望其单元格上的动态大小,第二个只是让应用程序计算滚动条指示器的大小,因此有助于性能。
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
我认为苏拉(Suragh)的答案最完整,最简洁,因此不会造成混淆。
虽然没有解释为什么需要这些更改。来做吧。
将以下代码放入项目中。
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()
时光。观察行为。
然后取消注释步骤1,并观察。
然后取消注释步骤2并观察。
这样做直到您取消注释所有6个步骤并观察它们的行为。
contenSize
?\n
则internalContentSize的宽度将是所有行的最大宽度。如果一行包含25个字符,另一行包含2个字符,另一行包含21个字符,则您的宽度将基于25个字符进行计算numberOfLines
,0
否则您将没有多行。您numberOfLines
将调整您的internalContentSize的高度进行调整:想象一下,根据您的文本,您的internalContentSize的宽度为200
and height为100
,但是您想将宽度限制为标签容器的宽度,您将要做什么?解决方案是将其设置为所需的宽度。您可以通过设置preferredMaxLayoutWidth
为来做到这一点,130
然后新的internalContentSize的宽度大约为130
。高度显然不止100
是因为您需要更多的行。话虽这么说,如果您的约束设置正确,那么您根本就不需要使用它!有关更多信息,请参见此答案及其评论。您只需要使用“即可。但是如果您设置领先/追踪,则可以100%确定,那么您就很好了!preferredMaxLayoutWidth
当您没有限制宽度/高度的约束时,因为您可能会说:“除非文字超出限制,否则不要包装文字”preferredMaxLayoutWidth
numberOfLines
0
长话短说,这里推荐使用它的大多数答案都是错误的!您不需要它。需要它是您约束的信号。设置不正确或您没有约束
字体大小:另外请注意,如果您增加fontSize,那么internalContentSize的高度将增加。我没有在代码中显示。您可以自己尝试。
回到您的tableViewCell示例:
您需要做的只是:
numberOfLines
为0
preferredMaxLayoutWidth
。在我的情况下,我必须使用来自服务器的图像创建一个自定义单元,该图像可以是任何宽度和高度。和两个具有动态尺寸(宽度和高度)的UILabel
我已经在我的答案中通过自动布局和以编程方式实现了相同的目的:
基本上在@smileyBorg答案上面有帮助,但systemLayoutSizeFittingSize从来没有为我工作,在我的方法中:
1.不使用自动行高计算属性。2.不使用估计的高度。3.不需要不必要的更新约束。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:是表格视图的宽度
只需在您的视图控制器中添加这两个功能,即可解决您的问题。在这里,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)
}
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
,此时尚未设置单元格。