如何在表视图中的UITableViewCells中使用“自动布局”,让每个单元格的内容和子视图确定行高度(自身/自动),同时保持平滑的滚动性能?
当前回答
另一个“解决方案”:跳过所有这些挫折,改用UIScrollView来获得与UITableView看起来和感觉相同的结果。
对我来说,这是一个痛苦的“解决方案”,因为我花了20多个令人沮丧的小时试图构建类似smileyborg建议的东西,但在几个月和三个版本的应用商店发布中失败了。
我的看法是,如果你真的需要iOS 7支持(对我们来说,这是必不可少的),那么这项技术太脆弱了,你会竭尽全力。UITableView通常是完全过度的,除非您使用一些高级行编辑功能和/或确实需要支持1000+“行”(在我们的应用程序中,它实际上永远不会超过20行)。
额外的好处是,与UITableView附带的所有委托垃圾相比,代码变得异常简单。它只是viewOnLoad中的一个代码循环,看起来很优雅,易于管理。
以下是一些关于如何做到这一点的提示:
使用Storyboard或nib文件,创建ViewController和关联的根视图。将UIScrollView拖到根视图上。向顶层视图添加顶部、底部、左侧和右侧约束,以便UIScrollView填充整个根视图。在UIScrollView中添加一个UIView,并将其称为“容器”。向UIScrollView(其父视图)添加顶部、底部、左侧和右侧约束。关键技巧:还要添加一个“等宽”约束来链接UIScrollView和UIView。注意:您将收到一个错误“滚动视图的可滚动内容高度不明确”,并且您的容器UIView的高度应该为0像素。当应用程序运行时,这两个错误似乎都不重要。为每个“单元”创建笔尖文件和控制器。使用UIView而不是UITableViewCell。在您的根ViewController中,您实际上将所有“行”添加到容器UIView中,并以编程方式添加约束,将它们的左边缘和右边缘链接到容器视图,将其顶部边缘链接到(第一个项目的)容器视图顶部或上一个单元格。然后将最终单元格链接到容器底部。
对我们来说,每一行都在一个笔尖文件中。所以代码看起来像这样:
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多个小时试图在自己的应用程序中使用表视图解决问题之前阅读我的帖子
其他回答
可变高度UITableViewCell的快速示例
为Swift 3更新
胡威廉(William Hu)的快速回答很好,但在我第一次学习做某事时,它能帮助我掌握一些简单而详细的步骤。下面的示例是我在学习使用可变单元格高度制作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.0tableView.rowHeight=UITableViewAutomaticDimension
设置情节提要
将表视图添加到视图控制器中,并使用自动布局将其固定到四边。然后将表视图单元格拖到表视图上。然后将标签拖到原型单元格上。使用自动布局将标签固定到表视图单元的内容视图的四个边缘。
重要说明:
自动布局与我上面提到的两行重要代码一起工作。如果不使用自动布局,它将无法工作。
其他IB设置
自定义类名和标识符
选择TableViewCell并将自定义类设置为MyCustomCell(我们添加的Swift文件中的类名称)。还将Identifier设置为cell(与上面代码中用于cellReuseIdentifier的字符串相同)。
标签的零行
将标签中的行数设置为0。这意味着多行,并允许标签根据其内容调整自身大小。
连接插座
控件从情节提要中的表视图拖动到ViewController代码中的tableView变量。对Prototype单元格中的Label和MyCustomCell类中的myCellLabel变量执行同样的操作。
完成了
您现在应该能够运行项目并获得高度可变的单元格。
笔记
此示例仅适用于iOS 8及更高版本。如果您仍然需要支持iOS 7,那么这将不适用于您。未来项目中您自己的自定义单元格可能不止一个标签。确保将所有内容固定正确,以便自动布局可以确定要使用的正确高度。你可能还需要使用垂直抗压和拥抱。有关这方面的更多信息,请参阅本文。如果不固定前缘和后缘(左侧和右侧),则可能还需要设置标签的preferredMaxLayoutWidth,以便它知道何时换行。例如,如果在上面的项目中向标签添加了“水平居中”约束,而不是固定前缘和后缘,则需要将此行添加到tableView:cellForRowAtIndexPath方法:cell.myCellLabel.referencedMaxLayoutWidth=表格视图边界宽度
另请参见
了解iOS 8中的自调整单元格和动态类型具有不同行高的表视图单元格Swift的UITableView示例
在我的例子中,我必须创建一个自定义单元格,其中包含来自服务器的图像,可以是任何宽度和高度。以及两个具有动态大小(宽度和高度)的UILabels
我在回答自动布局和编程实现了同样的效果:
基本上在@smileyBorg以上的回答有所帮助,但systemLayoutSizeFittingSize从未对我奏效,在我的方法中:
1.不使用自动行高计算属性。2.不使用估计高度3.不需要不必要的更新约束。4.不使用自动首选最大布局宽度。5.不使用systemLayoutSizeFittingSize(应该使用但不适用于我,我不知道它在内部做什么),而是使用我的方法-(float)getViewHeight,我知道它在在内部做些什么。
当我使用几种不同的方式显示单元格时,UITableView单元格中的高度是否可能不同?
TL;DR:不喜欢读书吗?直接跳转到GitHub上的示例项目:
iOS 8示例项目-需要iOS 8iOS 7示例项目-适用于iOS 7+
概念性描述
无论您正在为哪个iOS版本开发,下面的前两个步骤都适用。
1.设置和添加约束
在UITableViewCell子类中,添加约束,使单元格的子视图的边缘固定到单元格的contentView的边缘(最重要的是顶部和底部边缘)。注意:不要将子视图固定到单元格本身;仅限于单元格的contentView!通过确保每个子视图的垂直维度中的内容压缩阻力和内容拥抱约束不会被添加的更高优先级约束覆盖,让这些子视图的固有内容大小驱动表视图单元格内容视图的高度。(嗯?点击这里。)
记住,这样做的目的是使单元格的子视图垂直连接到单元格的内容视图,以便它们可以“施加压力”,并使内容视图扩展以适应它们。使用带有几个子视图的示例单元格,下面是一些(不是所有!)约束所需的外观的直观图示:
您可以想象,当更多的文本添加到上面示例单元格中的多行正文标签时,它需要垂直增长以适应文本,这将有效地迫使单元格高度增长。(当然,您需要正确设置约束条件,才能正确工作!)
在使用Auto Layout获得动态单元格高度的过程中,正确设置约束无疑是最困难也是最重要的部分。如果你在这里犯了一个错误,它可能会阻止其他一切工作——所以慢慢来吧!我建议您在代码中设置约束,因为您确切地知道哪些约束被添加到哪里,并且在出现问题时更容易调试。在代码中添加约束可能与使用布局锚或GitHub上提供的一个很棒的开源API的Interface Builder一样简单,也比它强大得多。
如果要在代码中添加约束,应该在UITableViewCell子类的updateConstraints方法中执行一次。请注意,updateConstraints可能被多次调用,因此为了避免多次添加相同的约束,请确保在检查诸如didSetupConstraints之类的布尔属性(在运行一次约束添加代码后将其设置为YES)时,将约束添加代码包装在updateConstraints中。另一方面,如果您有更新现有约束的代码(例如调整某些约束的常量属性),请将其放在updateConstraints中,但不要检查didSetupConstraints,以便每次调用方法时都可以运行。
2.确定唯一的表视图单元重用标识符
对于单元中的每个唯一约束集,使用唯一的单元重用标识符。换句话说,如果您的单元格有多个唯一的布局,那么每个唯一的布局都应该接收自己的重用标识符。(当单元变量具有不同数量的子视图,或者子视图以不同的方式排列时,需要使用新的重用标识符。)
例如,如果您在每个单元格中显示一封电子邮件,则可能有4种独特的布局:仅包含主题的邮件、包含主题和正文的邮件、带有主题和照片附件的邮件,以及包含主题、正文和图片附件的邮件。每个布局都有实现它所需的完全不同的约束,因此,一旦初始化了单元并为这些单元类型之一添加了约束,单元就应该获得特定于该单元类型的唯一重用标识符。这意味着当您将一个单元出列以供重用时,约束已经被添加,并准备好用于该单元类型。
请注意,由于固有内容大小的差异,具有相同约束(类型)的单元格可能仍然具有不同的高度!不要因为内容大小不同而将根本不同的布局(不同的约束)与不同的计算视图框架(根据相同的约束解决)混淆。
不要将具有完全不同约束集的单元格添加到同一个重用池(即使用相同的重用标识符),然后在每次出队后尝试删除旧约束并从头开始设置新约束。内部自动布局引擎的设计不是为了处理约束的大规模变化,您会看到大量的性能问题。
对于iOS 8-自动调整单元格大小
3.启用行高度估计
要启用自调整表视图单元格大小,必须设置表视图的rowHeight属性设置为UITableViewAutomaticDimension。您还必须为estimatedRowHeight属性赋值。一旦设置这些财产后,系统使用自动布局计算行的实际高度Apple:使用自调整表视图单元格
在iOS 8中,苹果已经将之前必须由您在iOS 8之前完成的大部分工作内部化。为了允许自动调整单元格大小机制工作,必须首先将表视图上的rowHeight属性设置为常量UITableView.automaticDimension。然后,只需通过将表视图的estimatedRowHeight属性设为非零值来启用行高度估计,例如:
self.tableView.rowHeight = UITableView.automaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to whatever your "average" cell height is
这将为表格视图提供尚未显示在屏幕上的单元格行高度的临时估计值/占位符。然后,当这些单元格即将在屏幕上滚动时,将计算实际的行高度。为了确定每行的实际高度,表视图会根据内容视图的已知固定宽度(该宽度基于表视图的宽度,减去任何其他内容(如分区索引或附件视图))以及添加到单元格内容视图和子视图的自动布局约束,自动询问每个单元格的contentView需要的高度。一旦确定了此实际单元格高度,将使用新的实际高度更新行的旧估计高度(并根据需要对表视图的contentSize/contentOffset进行任何调整)。
一般来说,您提供的估计值不必非常准确——它只用于在表格视图中正确调整滚动指示器的大小,而表格视图在您滚动屏幕上的单元格时,可以很好地调整滚动指示器以显示不正确的估计值。您应该将表视图(在viewDidLoad或类似视图中)的estimatedRowHeight属性设置为一个常量值,即“平均”行高度。只有当您的行高度具有极大的可变性(例如,相差一个数量级),并且您在滚动时注意到滚动指示器“跳跃”时,才需要麻烦地实现tableView:estimatedHeightForRowAtIndexPath:以进行所需的最小计算,从而为每行返回更准确的估计值。
对于iOS 7支持(自己实现自动调整单元格大小)
3.通过布局并获取单元格高度
首先,实例化一个表视图单元的屏幕外实例,每个重用标识符对应一个实例,该实例严格用于高度计算。(屏幕外意味着单元格引用存储在视图控制器上的属性/ivar中,并且从未从tableView:cellForRowAtIndexPath:返回,以使表视图在屏幕上实际呈现。)接下来,必须使用如果要在表视图中显示,它将保存的确切内容(例如文本、图像等)配置单元格。
然后,强制单元格立即布局其子视图,然后使用UITableViewCell的contentView上的systemLayoutSizeFittingSize:方法来确定单元格所需的高度。使用UILayoutFittingCompressedSize获取适合单元格所有内容所需的最小大小。然后可以从tableView:heightForRowAtIndexPath:delegate方法返回高度。
4.使用估计的行高度
如果您的表视图中有十几行以上的行,那么您会发现,在首次加载表视图时,执行自动布局约束解决会很快使主线程陷入停滞,因为在首次加载时,会对每一行调用tableView:heightForRowAtIndexPath:(以便计算滚动指示器的大小)。
从iOS 7开始,您可以(而且绝对应该)在表视图上使用estimatedRowHeight属性。这将为表格视图提供尚未显示在屏幕上的单元格行高度的临时估计值/占位符。然后,当这些单元格即将在屏幕上滚动时,将计算实际的行高度(通过调用tableView:heightForRowAtIndexPath:),并用实际高度更新估计的高度。
一般来说,您提供的估计值不必非常准确——它只用于在表格视图中正确调整滚动指示器的大小,而表格视图在您滚动屏幕上的单元格时,可以很好地调整滚动指示器以显示不正确的估计值。您应该将表视图(在viewDidLoad或类似视图中)的estimatedRowHeight属性设置为一个常量值,即“平均”行高度。只有当您的行高度具有极大的可变性(例如,相差一个数量级),并且您在滚动时注意到滚动指示器“跳跃”时,才需要麻烦地实现tableView:estimatedHeightForRowAtIndexPath:以进行所需的最小计算,从而为每行返回更准确的估计值。
5.(如果需要)添加行高缓存
如果您已经完成了以上所有操作,但仍然发现在tableView:heightForRowAtIndexPath:中执行约束求解时性能慢得令人无法接受,那么很遗憾,您需要为单元格高度实现一些缓存。(这是苹果工程师建议的方法。)总的想法是让自动布局引擎第一次解决约束,然后缓存该单元格的计算高度,并将缓存的值用于该单元格高度的所有未来请求。当然,诀窍是确保在发生任何可能导致单元格高度发生变化的情况时,清除单元格的缓存高度——主要是当该单元格的内容发生变化或发生其他重要事件时(如用户调整动态类型文本大小滑块)。
iOS 7通用示例代码(有很多有趣的评论)
- (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;
}
}
示例项目
iOS 8示例项目-需要iOS 8iOS 7示例项目-适用于iOS 7+
由于表视图单元格包含UILabels中的动态内容,这些项目是具有可变行高度的表视图的完整工作示例。
Xamarin(C#/.NET)
如果您正在使用Xamarin,请查看@KentBoogaart整理的这个示例项目。
@smileyborg提出的解决方案几乎是完美的。如果您有一个自定义单元格,并且需要一个或多个具有动态高度的UILabel,则systemLayoutSizeFittingSize方法与启用自动布局相结合将返回一个CGSizeZero,除非您将所有单元格约束从单元格移到其contentView(如@TomSwift在这里建议的那样,如何调整超视图的大小以适应具有自动布局的所有子视图?)。
为此,您需要在自定义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答案与此混合应该会奏效。
我必须使用动态视图(通过代码设置视图和约束),当我想设置preferredMaxLayoutWidth标签的宽度为0时。所以我有错误的细胞高度。
然后我添加了
[cell layoutSubviews];
执行之前
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
之后,标签的宽度符合预期,动态高度计算正确。
推荐文章
- iPhone上UIView和UILabels的渐变
- keychain上的分发证书中缺少私钥
- 在实现API时,我如何避免在块中捕获自我?
- 如何创建一个Swift Date对象?
- Xcode 4在目标设备上说“finished running <my app>”——什么都没有发生
- 从另一个应用程序打开设置应用程序
- 快速提取正则表达式匹配
- 如何应用梯度的背景视图的iOS Swift应用程序
- 图书馆吗?静态的?动态吗?或框架?另一个项目中的项目
- 如何用SwiftUI调整图像大小?
- Xcode 6 gitignore文件应该包括什么?
- 如何在iPhone/iOS上删除电话号码的蓝色样式?
- 检测视网膜显示
- 如何在UIImageView中动画图像的变化?
- 如何从iPhone访问SOAP服务