使用堆栈视图和自动布局创建自定义 UITableViewCell
Create custom UITableViewCell with using stack view and auto layout
我有 2 个关于约束和自动布局的问题:
- 如何以编程方式创建下图中的类单元格?我不明白如何以编程方式为我的单元格添加自动布局和约束。
- 如何为单元格分配默认的 Apple 布局边距? (例如,默认单元格中的左插图对于大屏幕等于 20 pt,对于小屏幕等于 16 pt)。
我当前的代码:
class cellWithTitleAndDetail: UITableViewCell {
// MARK: - Properties
let title = UILabel()
let detail = UILabel()
let stackView = UIStackView()
// MARK: - Override init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
title.translatesAutoresizingMaskIntoConstraints = false
detail.translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fillProportionally
// Set color
title.textColor = .white
detail.textColor = .white
// Highlight StackView
stackView.addBackground(color: .blue)
stackView.addArrangedSubview(title)
stackView.addArrangedSubview(detail)
stackView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
stackView.isLayoutMarginsRelativeArrangement = true
self.contentView.addSubview(stackView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
结果:
更新:
下面添加了我的新代码和 DonMag 的回答中的代码。
新问题:“LayoutMarginsGuide”在屏幕宽度等于 375 pt 的 iPhone 上完美运行(图像 375-1)。但在大尺寸屏幕上,分隔符出现的时间早于单元格(图像 414-2)。我该如何解决这个问题?
新代码:
// MARK: - Override init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Off translatesAutoresizingMaskIntoConstraints
title.translatesAutoresizingMaskIntoConstraints = false
detail.translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
// Setup stackView
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fill
// Hugging
title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
// Resistance
title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
// Set textAlignment
title.textAlignment = .left
detail.textAlignment = .right
// Set numberOfLines
title.numberOfLines = 0
// Highlight stackView and set colors
stackView.addBackground(color: .blue)
title.textColor = .white
detail.textColor = .white
// Add title and detail
stackView.addArrangedSubview(title)
stackView.addArrangedSubview(detail)
// Add to subview
self.contentView.addSubview(stackView)
// Get layoutMarginsGuide
let layoutMarginsGuide = contentView.layoutMarginsGuide
// Set constraints
NSLayoutConstraint.activate([
// constrain all 4 sides of the stack view to the content view's layoutMarginsGuide
stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor, constant: 0.0),
])
}
您可以使用 内容视图的 layoutMarginsGuide
:
// only if you want different margins than the content view's margins
//stackView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
//stackView.isLayoutMarginsRelativeArrangement = true
self.contentView.addSubview(stackView)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
// constrain all 4 sides of the stack view to the
// content view's layoutMarginsGuide
stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
附带说明一下,您真正想要什么还不清楚(如果这不能提供您想要的布局,这将是一个单独的问题)...
- 你想让你的单元格看起来像“列”吗
- 您希望“细节”右对齐吗?
- 详情标签可以是多行的吗?
编辑
使用更新后的代码 - 我所做的唯一更改是为标签添加背景色,因为您没有显示 stackView.addBackground(color: .blue)
代码:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Off translatesAutoresizingMaskIntoConstraints
title.translatesAutoresizingMaskIntoConstraints = false
detail.translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
// Setup stackView
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fill
// Hugging
title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
// Resistance
title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
// Set textAlignment
title.textAlignment = .left
detail.textAlignment = .right
// Set numberOfLines
title.numberOfLines = 0
// Highlight stackView and set colors
//stackView.addBackground(color: .blue)
title.backgroundColor = .blue
detail.backgroundColor = .red
title.textColor = .white
detail.textColor = .white
// Add title and detail
stackView.addArrangedSubview(title)
stackView.addArrangedSubview(detail)
// Add to subview
self.contentView.addSubview(stackView)
// Get layoutMarginsGuide
let layoutMarginsGuide = contentView.layoutMarginsGuide
// Set constraints
NSLayoutConstraint.activate([
// constrain all 4 sides of the stack view to the content view's layoutMarginsGuide
stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor, constant: 0.0),
])
}
这是我得到的:
编辑 2
Table 视图单元格分隔符可以根据设备、iOS 版本、table 视图样式等进行更改。
为获得最可靠的一致性,请自行设置。
这是一个例子...
- 我们将单元格的 stackView 约束设置为
contentView
而不是 到内容视图的布局边距指南。
- 我们设置 table 视图的
separatorInset
以便左侧插图与堆栈视图的前导锚点匹配。
- 我们还需要将每个单元格的
.separatorInset
设置为等于我们 table 视图的自定义 .separatorInset
完整代码如下:
class MyTestTableViewController: UITableViewController {
let testTitles: [String] = [
"Yesterday all my troubles seemed so far away, Now it looks as though they're here to stay.",
"She packed my bags last night pre-flight, Zero hour nine AM.",
"When you're weary, feeling small, When tears are in your eyes, I will dry them all."
]
let testDetails: [String] = [
"The Beatles",
"Elton John",
"Simon & Garfunkel",
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(MyCellWithTitleAndDetail.self, forCellReuseIdentifier: "myCell")
// our custom separatorInset
// left matches cell's stackView leading anchor
tableView.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyCellWithTitleAndDetail
// set cell separatorInset equal to tableView separatorInset
cell.separatorInset = tableView.separatorInset
cell.title.text = testTitles[indexPath.row]
cell.detail.text = testDetails[indexPath.row]
return cell
}
}
class MyCellWithTitleAndDetail: UITableViewCell {
// MARK: - Properties
let title = UILabel()
let detail = UILabel()
let stackView = UIStackView()
// MARK: - Override init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Off translatesAutoresizingMaskIntoConstraints
title.translatesAutoresizingMaskIntoConstraints = false
detail.translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
// Setup stackView
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fillEqually
// if we want the labels to be 50% of the width,
// set stackView.distribution = .fillEqually
// then we don't need any Content Hugging or Compression Resistance priority changes
// // Hugging
// title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
// detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
//
// // Resistance
// title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
// detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
// Set textAlignment
title.textAlignment = .left
detail.textAlignment = .right
// Set numberOfLines
title.numberOfLines = 0
// Highlight stackView and set colors
title.backgroundColor = .blue
detail.backgroundColor = .red
//stackView.addBackground(color: .blue)
title.textColor = .white
detail.textColor = .white
// Add title and detail
stackView.addArrangedSubview(title)
stackView.addArrangedSubview(detail)
// Add to subview
self.contentView.addSubview(stackView)
// Set constraints
NSLayoutConstraint.activate([
// constrain all 4 sides of the stack view to the content view
// with your own "margins"
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15.0),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15.0),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12.0),
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
结果:
我有 2 个关于约束和自动布局的问题:
- 如何以编程方式创建下图中的类单元格?我不明白如何以编程方式为我的单元格添加自动布局和约束。
- 如何为单元格分配默认的 Apple 布局边距? (例如,默认单元格中的左插图对于大屏幕等于 20 pt,对于小屏幕等于 16 pt)。
我当前的代码:
class cellWithTitleAndDetail: UITableViewCell {
// MARK: - Properties
let title = UILabel()
let detail = UILabel()
let stackView = UIStackView()
// MARK: - Override init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
title.translatesAutoresizingMaskIntoConstraints = false
detail.translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fillProportionally
// Set color
title.textColor = .white
detail.textColor = .white
// Highlight StackView
stackView.addBackground(color: .blue)
stackView.addArrangedSubview(title)
stackView.addArrangedSubview(detail)
stackView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
stackView.isLayoutMarginsRelativeArrangement = true
self.contentView.addSubview(stackView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
结果:
更新:
下面添加了我的新代码和 DonMag 的回答中的代码。
新问题:“LayoutMarginsGuide”在屏幕宽度等于 375 pt 的 iPhone 上完美运行(图像 375-1)。但在大尺寸屏幕上,分隔符出现的时间早于单元格(图像 414-2)。我该如何解决这个问题?
新代码:
// MARK: - Override init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Off translatesAutoresizingMaskIntoConstraints
title.translatesAutoresizingMaskIntoConstraints = false
detail.translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
// Setup stackView
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fill
// Hugging
title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
// Resistance
title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
// Set textAlignment
title.textAlignment = .left
detail.textAlignment = .right
// Set numberOfLines
title.numberOfLines = 0
// Highlight stackView and set colors
stackView.addBackground(color: .blue)
title.textColor = .white
detail.textColor = .white
// Add title and detail
stackView.addArrangedSubview(title)
stackView.addArrangedSubview(detail)
// Add to subview
self.contentView.addSubview(stackView)
// Get layoutMarginsGuide
let layoutMarginsGuide = contentView.layoutMarginsGuide
// Set constraints
NSLayoutConstraint.activate([
// constrain all 4 sides of the stack view to the content view's layoutMarginsGuide
stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor, constant: 0.0),
])
}
您可以使用 内容视图的 layoutMarginsGuide
:
// only if you want different margins than the content view's margins
//stackView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
//stackView.isLayoutMarginsRelativeArrangement = true
self.contentView.addSubview(stackView)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
// constrain all 4 sides of the stack view to the
// content view's layoutMarginsGuide
stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
附带说明一下,您真正想要什么还不清楚(如果这不能提供您想要的布局,这将是一个单独的问题)...
- 你想让你的单元格看起来像“列”吗
- 您希望“细节”右对齐吗?
- 详情标签可以是多行的吗?
编辑
使用更新后的代码 - 我所做的唯一更改是为标签添加背景色,因为您没有显示 stackView.addBackground(color: .blue)
代码:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Off translatesAutoresizingMaskIntoConstraints
title.translatesAutoresizingMaskIntoConstraints = false
detail.translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
// Setup stackView
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fill
// Hugging
title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
// Resistance
title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
// Set textAlignment
title.textAlignment = .left
detail.textAlignment = .right
// Set numberOfLines
title.numberOfLines = 0
// Highlight stackView and set colors
//stackView.addBackground(color: .blue)
title.backgroundColor = .blue
detail.backgroundColor = .red
title.textColor = .white
detail.textColor = .white
// Add title and detail
stackView.addArrangedSubview(title)
stackView.addArrangedSubview(detail)
// Add to subview
self.contentView.addSubview(stackView)
// Get layoutMarginsGuide
let layoutMarginsGuide = contentView.layoutMarginsGuide
// Set constraints
NSLayoutConstraint.activate([
// constrain all 4 sides of the stack view to the content view's layoutMarginsGuide
stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor, constant: 0.0),
])
}
这是我得到的:
编辑 2
Table 视图单元格分隔符可以根据设备、iOS 版本、table 视图样式等进行更改。
为获得最可靠的一致性,请自行设置。
这是一个例子...
- 我们将单元格的 stackView 约束设置为
contentView
而不是 到内容视图的布局边距指南。 - 我们设置 table 视图的
separatorInset
以便左侧插图与堆栈视图的前导锚点匹配。 - 我们还需要将每个单元格的
.separatorInset
设置为等于我们 table 视图的自定义.separatorInset
完整代码如下:
class MyTestTableViewController: UITableViewController {
let testTitles: [String] = [
"Yesterday all my troubles seemed so far away, Now it looks as though they're here to stay.",
"She packed my bags last night pre-flight, Zero hour nine AM.",
"When you're weary, feeling small, When tears are in your eyes, I will dry them all."
]
let testDetails: [String] = [
"The Beatles",
"Elton John",
"Simon & Garfunkel",
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(MyCellWithTitleAndDetail.self, forCellReuseIdentifier: "myCell")
// our custom separatorInset
// left matches cell's stackView leading anchor
tableView.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyCellWithTitleAndDetail
// set cell separatorInset equal to tableView separatorInset
cell.separatorInset = tableView.separatorInset
cell.title.text = testTitles[indexPath.row]
cell.detail.text = testDetails[indexPath.row]
return cell
}
}
class MyCellWithTitleAndDetail: UITableViewCell {
// MARK: - Properties
let title = UILabel()
let detail = UILabel()
let stackView = UIStackView()
// MARK: - Override init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Off translatesAutoresizingMaskIntoConstraints
title.translatesAutoresizingMaskIntoConstraints = false
detail.translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
// Setup stackView
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fillEqually
// if we want the labels to be 50% of the width,
// set stackView.distribution = .fillEqually
// then we don't need any Content Hugging or Compression Resistance priority changes
// // Hugging
// title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
// detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
//
// // Resistance
// title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
// detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
// Set textAlignment
title.textAlignment = .left
detail.textAlignment = .right
// Set numberOfLines
title.numberOfLines = 0
// Highlight stackView and set colors
title.backgroundColor = .blue
detail.backgroundColor = .red
//stackView.addBackground(color: .blue)
title.textColor = .white
detail.textColor = .white
// Add title and detail
stackView.addArrangedSubview(title)
stackView.addArrangedSubview(detail)
// Add to subview
self.contentView.addSubview(stackView)
// Set constraints
NSLayoutConstraint.activate([
// constrain all 4 sides of the stack view to the content view
// with your own "margins"
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15.0),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15.0),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12.0),
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
结果: