Mar*_*ard 7 uitableview ios autolayout
我试图在tableView中显示自定义视图,并让iOS计算每行的高度UITableViewAutomaticDimension.不幸的是,我的牢房尺寸不合适(高度不好).
我的自定义视图定义如下(由于多种原因,我没有在此部分使用AutoLayout,我不想使用它):
class MyView : UIView {
var label: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
func setupView() {
label = UILabel()
label.numberOfLines = 0
addSubview(label)
}
func setText(text: String) {
label.text = text
setNeedsLayout()
}
override func layoutSubviews() {
super.layoutSubviews()
label.frame = bounds
invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
guard let text = label.text else { return .zero }
let width = bounds.width
let height = text.heightWithConstrainedWidth(width: width, font: label.font)
return CGSize(width: width, height: height)
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我将这个自定义视图包装在一个中以便在一个UITableViewCell中使用它UITableView.我在这里使用AutoLayout:
class MyCell : UITableViewCell {
var myView: MyView!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupCell()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupCell()
}
func setupCell() {
myView = MyView()
contentView.addSubview(myView)
myView.snp.makeConstraints { make in
make.edges.equalTo(contentView)
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,使用以下viewController,我显示一个带有大文本的单元格,以查看它的高度是否自动计算:
class ViewController: UIViewController {
var tableView: UITableView!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableView = UITableView()
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.edges.equalTo(view)
}
tableView.register(MyCell.self, forCellReuseIdentifier: "MyCell")
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 30
tableView.dataSource = self
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell") as! MyCell
cell.myView.setText(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur")
return cell
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,单元格的高度等于21.而且,即使最后一次intrinsicContentSize调用返回的高度MyView为143.这是结果的屏幕截图:

如果单元格被回收,则单元格大小正确(我在屏幕外滚动单元格):

我知道intrinsictContentSize的MyView使用它的框架,它不应该,但我对如何设计出具有给定一个固定的宽度灵活的高度,一个UIView子类不知道.鉴于我invalidateIntrinsicContentSize()在MyView中调用的事实,我认为它会起作用layoutSubviews().
如何从开始更改此代码以获得良好的单元格高度?
该文档表明,覆盖intrinsicContentSize可能不是实现您想要实现的最佳方法。如您所见,在UIView类参考中:
设置此属性将允许自定义视图根据其内容与布局系统通信所需的大小。此固有大小必须独立于内容框架,例如,因为无法根据更改的高度将更改的宽度动态传达给布局系统。
在请求MyView bounds之前,不会设置MyView intrinsicContentSize,并且在自动布局上下文中,不能保证在layoutSubviews()调用MyView 之前的任何时间都设置了MyView 。有一种方法可以解决此问题,并将宽度传递给您的自定义视图以用于中intrinsicContentSize,但是,我已将其作为此答案的第二个选项。
选项1:通过覆盖进行手动布局 sizeThatFits(_:)
首先,在没有自动布局的情况下,配置MyView及其标签子视图以调整大小并适当调整其大小:
// MyView.swift
func setupView() {
label = UILabel()
label.numberOfLines = 0
label.autoresizingMask = [.flexibleWidth] // New
addSubview(label)
}
override func layoutSubviews() {
super.layoutSubviews()
label.sizeToFit() // New
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
return label.sizeThatFits(size) // New
}
Run Code Online (Sandbox Code Playgroud)
MyView现在将根据其标签子视图的大小调整自身大小,并根据.flexibleWidth其父视图的宽度(由于其自动调整大小的掩码)及其文本内容(由于numberOfLines将其设置为0)来调整标签的大小。如果MyView还有其他子视图,则需要计算并返回中的总大小sizeThatFits(_:)。
其次,我们希望UITableView能够根据上面的手动布局计算其单元格和自定义子视图的高度。自定义单元格大小时,UITableView会systemLayoutSizeFitting(_:withHorizontalFittingPriority:verticalFittingPriority)在每个单元格上调用,然后依次调用sizeThatFits(_:)该单元格。(请参阅WWDC 2014 Session226。)
传递给这些方法的目标大小或配件大小是表格视图的宽度,高度为零。水平拟合优先级为UILayoutPriorityRequired。您可以通过覆盖自定义单元格子类中的方法之一(前者用于自动布局,后者用于手动布局)并使用传入的大小宽度来计算并返回高度的高度,从而控制单元大小的过程细胞。
在这种情况下,单元格的大小为myView:
// MyCell.swift
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
return myView.sizeThatFits(targetSize)
}
Run Code Online (Sandbox Code Playgroud)
这样就完成了。
选项2:通过覆盖自动布局 intrinsicContentSize
由于MyView的大小取决于其标签的大小,因此第一步是确保标签的位置和大小正确。您已经做到了:
// MyView.swift
override func layoutSubviews() {
super.layoutSubviews()
label.frame = bounds
}
Run Code Online (Sandbox Code Playgroud)
第二步是定义MyView的固有内容大小。在这种情况下,没有固有宽度(因为尺寸将完全由单元格或其他超级视图确定),固有高度是标签的固有高度:
// MyView.swift
override var intrinsicContentSize: CGSize {
return CGSize(width: UIViewNoIntrinsicMetric, height: label.intrinsicContentSize.height)
}
Run Code Online (Sandbox Code Playgroud)
由于标签是多行标签,因此如果没有最大宽度,则无法确定其固有高度。默认情况下,在没有最大宽度的情况下,UILabel将返回适合单个行标签的固有内容大小,这不是我们想要的。
为计算多行标签的固有内容大小而为其提供最大宽度的唯一记录方法是该preferredMaxLayoutWidth属性。UILabel的头文件为该属性提供以下注释:
//如果非零,则在确定多行标签的-intrinsicContentSize时使用
所以第三步是确保preferredMaxLayoutWidth设置为适当的宽度。由于该intrinsicContentSize属性是自动布局过程的一部分,并且仅与该元素有关,并且您的自定义单元格子类是执行自动布局的代码的唯一部分,因此在该类中设置布局首选项的适当位置:
// MyCell.swift
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
myView.label.preferredMaxLayoutWidth = targetSize.width
myView.invalidateIntrinsicContentSize()
return super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority)
}
Run Code Online (Sandbox Code Playgroud)
如果MyView具有一个以上的多行标签,或者您希望保留MyView的子视图层次结构从其超级视图中隐藏,则可以同样preferredMaxLayoutWidth在MyView上创建自己的属性并进行后续操作。
上面的代码将确保MyView根据intrinsicContentSize其多行标签的内容进行计算并返回适当的值,并且旋转时大小将无效。
| 归档时间: |
|
| 查看次数: |
2974 次 |
| 最近记录: |