-systemLayoutSizeFittingSize:在 iOS 8 下为 tableHeaderView 返回不正确的高度
Posted
技术标签:
【中文标题】-systemLayoutSizeFittingSize:在 iOS 8 下为 tableHeaderView 返回不正确的高度【英文标题】:-systemLayoutSizeFittingSize: returning incorrect height for tableHeaderView under iOS 8 【发布时间】:2014-10-15 20:57:37 【问题描述】:有很多关于使用自动布局 (one such thread) 正确调整 tableHeaderView 大小的线程,但它们往往早于 ios 8。
我有很多表格视图的情况,所有表格视图都带有标题,在 iOS 7 下大小正确,但在 iOS 8 下使用上述大多数线程支持的代码不正确。在表格的控制器中,我有以下方法:
- (void)rejiggerTableHeaderView
self.tableView.tableHeaderView = nil;
UIView *header = self.headerView;
[header setNeedsLayout];
[header layoutIfNeeded];
CGFloat height = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect headerFrame = header.frame;
headerFrame.size.height = height;
header.frame = headerFrame;
self.tableView.tableHeaderView = header;
在 iOS 7 下使用多行标签,这样可以正确调整表格视图的标题,如下所示:
但在 iOS 8 下运行的相同代码会产生以下结果:
获取 -systemLayoutSizeFittingSize: 在 iOS 8 下返回正确大小的技巧是什么? Here's a sample project 证明了这个问题。
【问题讨论】:
【参考方案1】:将您的 headerview 功能更改为以下对我有用:
- (void)rejiggerTableHeaderView
self.tableView.tableHeaderView = nil;
UIView *header = self.headerView;
CGRect frame = header.frame;
frame.size.width = self.tableView.frame.size.width;
header.frame = frame;
[header setNeedsLayout];
[header layoutIfNeeded];
CGFloat height = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect headerFrame = header.frame;
headerFrame.size.height = height;
header.frame = headerFrame;
self.tableView.tableHeaderView = header;
【讨论】:
是的,确实可以解决问题。非常感谢你。值得一提的是,iOS 8.1 修复了一些问题,因此它的行为与 7.1 中的一样(上面的屏幕截图来自 8.0.2)。但是在我的示例项目中进行额外的戳戳后,我意识到上面的两个屏幕截图都是不正确的。如果旋转到横向模式,视图的高度将被设置为正确包含字符串,因此您明确说明视图的实际宽度的修复方法可以修复它。 这对我不起作用。我在标题中有两个标签,其中一个没有正确调整大小。只显示了三行。 我发现罪魁祸首是缺少约束:从UILabel
在所有 4 个方向上对超级视图都有一个支撑是至关重要的。一个缺失的约束将阻止systemLayoutSizeFittingSize:UILayoutFittingCompressedSize
正常工作。
此答案已过时,无法正确处理 Size 类。我已经为现在正在查看的任何人发布了一个更新的答案。【参考方案2】:
问题可能在未设置的preferredMaxLayoutWidth
中。
如果将其设置为正确的 UILabel 宽度,它将正确确定约束。
你可以遍历所有UILabel
在header中,设置preferredMaxLayoutWidth
为标签宽度。
Swift 3 示例:
extension UITableView
public func relayoutTableHeaderView()
if let tableHeaderView = tableHeaderView
let labels = tableHeaderView.findViewsOfClass(viewClass: UILabel.self)
for label in labels
label.preferredMaxLayoutWidth = label.frame.width
tableHeaderView.setNeedsLayout()
tableHeaderView.layoutIfNeeded()
tableHeaderView.frame.height = tableHeaderView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
self.tableHeaderView = tableHeaderView
public func findViewsOfClass<T:UIView>(viewClass: T.Type) -> [T]
var views: [T] = []
for subview in subviews
if subview is T
views.append(subview as! T)
views.append(contentsOf: subview.findViewsOfClass(viewClass: T.self))
return views
更新 2:
如果您的子视图具有纵横比约束并且同时与父视图宽度约束成正比,那么您可能会遇到不正确的高度计算问题
【讨论】:
【参考方案3】:由于这个问题已经有一年半的历史了,这里有一个更新的完整版本,用 Swift 编写。已接受答案中的某些代码是错误的或过时的。
func fixHeaderHeight()
// Set your label text if needed
// ...
//
guard let header = tableView.tableHeaderView else
return
let height = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
header.frame.height = height
tableView.tableHeaderView = header
您需要在代码中的其他位置设置标头中标签的preferredMaxLayoutWidth
。这应该等于 tableView(或屏幕宽度)减去任何填充。你的标签出口的didSet
方法是个好地方:
@IBOutlet weak var headerMessageLabel: UILabel!
didSet
headerMessageLabel.preferredMaxLayoutWidth = UIScreen.mainScreen().bounds.width - headerMessageLabelPadding
注意:如果接受的答案对您有用,则说明您没有正确使用尺码等级。
【讨论】:
【参考方案4】:如果标题只包含一个标签,那么我使用UILabel
扩展来查找给定宽度的多行标签高度:
public extension UILabel
public class func size(withText text: String, forWidth width: CGFloat) -> CGSize
let measurementLabel = UILabel()
measurementLabel.text = text
measurementLabel.numberOfLines = 0
measurementLabel.lineBreakMode = .byWordWrapping
measurementLabel.translatesAutoresizingMaskIntoConstraints = false
measurementLabel.widthAnchor.constraint(equalToConstant: width).isActive = true
let size = measurementLabel.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
return size
注意:以上是 Swift 3 语法。
使用上面计算的size
,您可以在UITableViewDelegate
方法中返回正确的高度:
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
【讨论】:
【参考方案5】:在 Swift 3 中这个问题的正确解决方案是使用这个类而不是标准的 UILabel:
class UILabelPreferedWidth : UILabel
override var bounds: CGRect
didSet
if (bounds.size.width != oldValue.size.width)
self.setNeedsUpdateConstraints()
override func updateConstraints()
if(preferredMaxLayoutWidth != bounds.size.width)
preferredMaxLayoutWidth = bounds.size.width
super.updateConstraints()
还要确保您没有在 Cell 类中禁用这两个:
translatesAutoresizingMaskIntoConstraints = false
contentView.translatesAutoresizingMaskIntoConstraints = false
【讨论】:
【参考方案6】:我遇到了同样的问题。 这对我在 iOS 8.4 上很有效。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
[self.myLabel sizeToFit];
[self.tableView.tableHeaderView layoutIfNeeded];
return UITableViewAutomaticDimension;
【讨论】:
以上是关于-systemLayoutSizeFittingSize:在 iOS 8 下为 tableHeaderView 返回不正确的高度的主要内容,如果未能解决你的问题,请参考以下文章