要获得适用于UITableView.rowHeight的自动尺寸,需要在UITableViewCell类中使用重复约束。
我正在以编程方式创建UITableView(SwiftUI,没有情节提要),并且具有不同高度的单元格。我已将表格的rowHeight设置为UITableView.automaticDimension,但找不到正确的约束组合,无法在不添加重复约束的情况下使表格实际计算出正确的单元格高度。
我希望添加宽度,高度,顶部和前导约束,以使事情正常运行。但是,除非我也添加了底部约束,否则该表无法正确调整行的大小。自然会产生警告:
2019-10-23 18:06:53.515025-0700 Test[15858:7764405] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x600001651c20 V:|-(0)-[UIView:0x7f8fc5a08890] (active, names: '|':Aries.TestTableViewCell:0x7f8fc5a084e0'TestTableViewCell' )>",
"<NSLayoutConstraint:0x600001651e50 UIView:0x7f8fc5a08890.bottom == Aries.TestTableViewCell:0x7f8fc5a084e0'TestTableViewCell'.bottom (active)>",
"<NSLayoutConstraint:0x600001651ea0 UIView:0x7f8fc5a08890.height == 40 (active)>",
"<NSLayoutConstraint:0x600001652120 'UIView-Encapsulated-Layout-Height' Aries.TestTableViewCell:0x7f8fc5a084e0'TestTableViewCell'.height == 40.3333 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600001651ea0 UIView:0x7f8fc5a08890.height == 40 (active)>
如果删除高度约束或底部锚约束,则重复约束消失,警告消失。但是,该表将无法正确调整行的大小。
视图:
import SwiftUI
struct TableViewTest: View {
var body: some View {
TestTableView().frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
}
}
TableView:
import SwiftUI
struct TestTableView: UIViewRepresentable {
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeUIView(context: UIViewRepresentableContext<TestTableView>) -> UITableView {
let tableView = UITableView(frame: .zero)
tableView.register(TestTableViewCell.self, forCellReuseIdentifier: "TestTableViewCell")
tableView.rowHeight = UITableView.automaticDimension
let dataSource = UITableViewDiffableDataSource<Section, TestData>(tableView: tableView) { tableView, indexPath, data in
let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableViewCell", for: indexPath) as! TestTableViewCell
cell.data = data
return cell
}
populate(dataSource: dataSource)
context.coordinator.dataSource = dataSource
return tableView
}
func populate(dataSource: UITableViewDiffableDataSource<Section, TestData>) {
let items = [
TestData(color: .red, size: CGSize(width: 40, height: 20)),
TestData(color: .blue, size: CGSize(width: 40, height: 40)),
TestData(color: .green, size: CGSize(width: 40, height: 80))
]
var snapshot = NSDiffableDataSourceSnapshot<Section, TestData>()
snapshot.appendSections([.main])
snapshot.appendItems(items)
dataSource.apply(snapshot)
}
func updateUIView(_ tableView: UITableView, context: UIViewRepresentableContext<TestTableView>) {
guard let dataSource = context.coordinator.dataSource else {
return
}
populate(dataSource: dataSource)
}
class Coordinator {
var dataSource: UITableViewDiffableDataSource<Section, TestData>?
}
enum Section {
case main
}
struct TestData: Hashable {
var id = UUID()
var color: UIColor
var size: CGSize
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
}
[表格视图单元格:
import UIKit
class TestTableViewCell: UITableViewCell {
var data: TestTableView.TestData? {
didSet {
if let data = data {
let view = UIView()
view.backgroundColor = data.color
addSubview(view)
// !!! SETTING THE BOTTOM ANCHOR TO NIL OR HEIGHT TO 0 PREVENTS THE TABLE FROM SIZING THE ROWS CORRECTLY
view.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: data.size.width, height: data.size.height, enableInsets: false)
}
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
UIView锚扩展名:
func anchor (top: NSLayoutYAxisAnchor?, left: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, right: NSLayoutXAxisAnchor?, paddingTop: CGFloat, paddingLeft: CGFloat, paddingBottom: CGFloat, paddingRight: CGFloat, width: CGFloat, height: CGFloat, enableInsets: Bool) {
var topInset = CGFloat(0)
var bottomInset = CGFloat(0)
if #available(iOS 11, *), enableInsets {
let insets = self.safeAreaInsets
topInset = insets.top
bottomInset = insets.bottom
}
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
self.topAnchor.constraint(equalTo: top, constant: paddingTop+topInset).isActive = true
}
if let left = left {
self.leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
}
if let right = right {
rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
}
if let bottom = bottom {
bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom-bottomInset).isActive = true
}
if height != 0 {
heightAnchor.constraint(equalToConstant: height).isActive = true
}
if width != 0 {
widthAnchor.constraint(equalToConstant: width).isActive = true
}
}
如果仅锚定左上角并在表格视图单元格中指定宽度和高度,则表格视图将无法正确计算高度。但是,如果我还指定了高度,则将正确调整行的大小,但是会生成有关重复约束的警告。是否有一种魔术组合可以允许正确的布局但不会产生警告?
错误(但没有重复约束):
良好(有重复约束):
要解决此问题,您需要更改底部约束的优先级,然后警告将消失。
if let bottom = bottom {
let bottomConstraint = bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom-bottomInset)
bottomConstraint.priority = UILayoutPriority(750)
bottomConstraint.isActive = true
}