【发布时间】:2021-10-31 14:54:31
【问题描述】:
十年后,我怀疑没有人真正直接问过这个问题。例如,有很多问题询问如何解决由旋转引起的 tableHeaderView 布局问题。但真正的问题是,Apple 打算如何让它发挥作用?
自动布局似乎无法与 tableHeaderView 一起玩,正如您在这篇将近 9 年的帖子中看到的那样
Is it possible to use AutoLayout with UITableView's tableHeaderView?
自 2011 年以来,我每天都在进行 iOS 开发,我从未遇到过文档记录如此糟糕的 API。
考虑到在安装 tableHeaderView 时自动布局是一个很棘手的问题,我上周决定使用老派的自动调整蒙版大小的方法。已经整整4天了,它仍然对我不起作用。这很令人谦卑,我想与你们联系,问这个简单的问题。
如何使用自动调整大小掩码(无自动布局)正确安装 tableHeaderView?
我的失败尝试
final class EventDetailTableHeaderView: UIView {
private let titleContainer: TitleContainerView
private let subtitleContainer: SubtitleContainerView
init(_ width: CGFloat, event: CloudEvent) {
let size = CGSize(width: width, height: 0)
let frame = CGRect(origin: .zero, size: size)
titleContainer = TitleContainerView(frame: frame, text: event.title)
subtitleContainer = SubtitleContainerView(frame: frame, text: event.displayString)
super.init(frame: frame)
backgroundColor = StyleKit.wDOWhite
autoresizingMask = [.flexibleWidth]
setupSubviews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupSubviews() {
setupTitleContiner()
setupSubtitleContainer()
}
private func setupTitleContiner() {
addSubview(titleContainer)
titleContainer.autoresizingMask = [.flexibleWidth]
titleContainer.backgroundColor = StyleKit.wDOWhite
}
private func setupSubtitleContainer() {
addSubview(subtitleContainer)
subtitleContainer.autoresizingMask = [.flexibleWidth]
subtitleContainer.backgroundColor = StyleKit.wDOBlue
}
override func layoutSubviews() {
super.layoutSubviews()
positionSubtitleContainer()
frame = CGRect(
origin: .zero,
size: calculateSize()
)
}
private func positionSubtitleContainer() {
subtitleContainer.frame.origin.y = titleContainer.frame.height
}
private func calculateSize() -> CGSize {
CGSize(
width: frame.width,
height: calculateHeightOfSubviews()
)
}
private func calculateHeightOfSubviews() -> CGFloat {
let titleContainerHeight = titleContainer.frame.height
let subtitleContainerHeight = subtitleContainer.frame.height
return titleContainerHeight + subtitleContainerHeight
}
}
final class TitleContainerView: UIView {
private static let font = FontManagement.fontWithStyle(.heavy, withSize: 32.0)
private let label: UILabel = {
let label = UILabel()
label.autoresizingMask = [.flexibleWidth]
label.numberOfLines = 0
label.backgroundColor = StyleKit.wDOWhite
label.font = TitleContainerView.font
label.textColor = StyleKit.wDOBlue
return label
}()
convenience init(frame: CGRect, text: String) {
let font = TitleContainerView.font
let labelFrame = TitleContainerView.establishLabelFrame(frame, text, font)
var frame = frame
frame.size.height = TitleContainerView.establishHeight(labelFrame)
self.init(frame: frame)
label.text = text
label.frame = labelFrame
}
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(label)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private static let insets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
override func layoutSubviews() {
super.layoutSubviews()
let font = label.font!
let text = label.text ?? ""
label.frame = Self.establishLabelFrame(frame, text, font)
frame.size.height = Self.establishHeight(label.frame)
}
private static func establishLabelFrame(_ frame: CGRect, _ text: String, _ font: UIFont) -> CGRect {
let size = establishLabelSize(frame, text, font)
let origin = establishLabelOrigin(frame, size)
return CGRect(origin: origin, size: size)
}
private static func establishLabelSize(_ frame: CGRect, _ text: String, _ font: UIFont) -> CGSize {
let width = frame.width - TitleContainerView.insets.left - TitleContainerView.insets.right
let height = text.height(withConstrainedWidth: width, font: font)
return CGSize(
width: width,
height: height
)
}
private static func establishLabelOrigin(_ frame: CGRect, _ size: CGSize) -> CGPoint {
CGPoint(
x: (frame.width - size.width) / 2.0,
y: (frame.height - size.height) / 2.0
)
}
private static func establishHeight(_ labelFrame: CGRect) -> CGFloat {
labelFrame.size.height + TitleContainerView.insets.top + TitleContainerView.insets.bottom
}
}
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
return ceil(boundingBox.height)
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView = EventDetailTableView(frame: .zero, style: .plain)
tableView?.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView!)
let width = view.bounds.width
let tableHeaderView = EventDetailTableHeaderView(width, event: event)
tableHeaderView.layoutIfNeeded()
tableView?.tableHeaderView = tableHeaderView
NSLayoutConstraint.activate([
view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: tableView!.topAnchor),
view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: tableView!.trailingAnchor),
view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: tableView!.leadingAnchor),
view.bottomAnchor.constraint(equalTo: tableView!.bottomAnchor)
])
}
【问题讨论】:
-
自从我与
.autoresizingMask合作以来已经很长时间了......我想回到过去,拥有动态高度表标题视图并不常见。但是,使用自动布局并不难。您是否只是要求尝试找到解释?或者,您是否有不想使用自动布局的原因? -
我不想使用自动布局,因为它的每个实现(对于 tableHeaderView)看起来都很老套 - 请参阅上述链接。
-
我假设“纸飞机”是一个按钮……那是
SubtitleContainerView的一部分吗?还是您的 headerViewTitleContainerView+SubtitleContainerView+button?还是表格第一行的按钮? -
按钮在表 @DonMag 的第一行。如果您决定试一试,我期待您的任何反馈。
标签: ios uitableview