我正在将带有静态单元格的分组 UITableView
用于选项屏幕/场景。一切都在 Xcode 6.1 / iOS 8.1.x / Storyboard 中使用 Autolayout 完成。在表格组中,有混合类型的单元格,有两种类型会导致我出现问题:
具有自定义样式的单元格和具有“正确细节”样式的单元格
在单元格 #1 上,我可以为标签和前导容器之间的左边距设置一个约束。据我所知,在单元格 #2 上,我无法在 Interface Builder 中设置任何约束。我在单元格#1 中的标签上设置了左边距,使其与单元格#2 中的标签对齐。在 iPhone 上一切看起来都很好,但是如果我在 iPad 上显示同一个表格,其中表格视图容器的大小是屏幕大小的一半,单元格 #2 会获得更多边距(动态?),而单元格 #1 保持绝对边距我在约束中设置。我还尝试使用“相对于边距”属性更改单元格 #1 中的左边距,但无济于事。
苹果手机:
https://i.stack.imgur.com/FygPX.png
iPad(tableview 宽度 = 1/2 屏幕尺寸)
https://i.stack.imgur.com/u8Q5x.png
所以问题是:如何为单元格#1 中的标签设置约束,使其像单元格#2 一样对齐。
这里还有一个指向演示该问题的 Xcode 6.1 示例项目的链接。在 iPhone 和 iPad 上运行以查看差异:
https://dl.dropboxusercontent.com/u/5252156/Code/tableViewTest.zip
这个问题可能与 Layout static table cell for iPhone and iPad 有关,但对于 iOS 8 也可能有所不同,因为现在一切都应该是自适应的。这就是为什么我决定发布这个问题。
如何修复它
在与苹果错误报告团队进行了许多示例项目和屏幕截图并剖析 that answer 之后,我发现让您的自定义样式单元格在其边距方面表现一致并且就像默认 UITableViewCells 一样的解决方案,您必须执行以下操作(主要基于 Becky's answer,我强调了不同之处以及它对我有用的原因):
在 IB 中选择单元格的内容视图 转到大小检查器 在 Layout Margins 部分中,选中 Preserve Superview Margins (不要单击加号)(这是关键)对单元格本身执行相同操作(内容视图的父级,如果你会)设置你的约束如下:Label.Leading = Superview.Leading Margin(常数为0)
现在您的所有单元格的标签都将与默认单元格一致!这在 Xcode 7 及更高版本中对我有用,它包括我提到的线程中提到的修复。 IB 和模拟器现在应该显示正确对齐的标签。
https://i.stack.imgur.com/d5GcCm.png
您也可以以编程方式执行其中的一些操作,例如在 View Controller 的类中:
cell.preservesSuperviewLayoutMargins = true
cell.contentView.preservesSuperviewLayoutMargins = true
或者你可以通过在启动时调用 UIAppearance 来设置它(我只知道 Swift,抱歉):
UITableViewCell.appearance().preservesSuperviewLayoutMargins = true
UITableViewCell.appearance().contentView.preservesSuperviewLayoutMargins = true
它如何以及为何起作用
正如 Ethan 所指出的,Apple 自己的 UIView 文档对 preservesSuperviewLayoutMargins
的描述如下:
当此属性的值为 true 时,在布局内容时也会考虑超级视图的边距。此边距影响视图边缘与其父视图之间的距离小于相应边距的布局。例如,您可能有一个内容视图,其框架与其父视图的边界精确匹配。当任何超级视图的边距位于内容视图及其自身边距所表示的区域内时,UIKit 会调整内容视图的布局以尊重超级视图的边距。调整量是确保内容也在超级视图的边距内所需的最小量。
因此,如果您希望单元格的内容与 TableView 的边距对齐(如果您愿意,它是曾祖父),您需要让内容的两个上级内容视图和表格单元格本身保留它们自己的超级视图的边距。
为什么这不是默认行为让我感到惊讶:我觉得大多数不想自定义所有内容的开发人员都会默认这种“继承”。
我遇到了和你一样的问题并想出了一个解决方案。
首先,一点背景知识:从 iOS 8 开始,默认表格视图单元格尊重单元格的 layoutMargins
以适应不同的特征(又名屏幕又名设备)。例如,所有 iPhone(在表单中显示的 iPhone 6 Plus 除外)上的布局边距都是 {8, 16, 8, 16}
。在 iPad 上,它们是 {8, 20, 8, 20}
。所以现在我们知道有 4 个像素的差异,这很可能是您的自定义表格视图单元格不尊重。
当 layoutMargins 改变时,您的表格视图单元子类需要适应左边距约束。
这是相关的代码片段:
- (void)layoutMarginsDidChange
{
[super layoutMarginsDidChange];
self.leftLayoutMarginConstraint.constant = self.layoutMargins.left;
self.rightLayoutMarginConstraint.constant = self.layoutMargins.right;
}
适应代码中的布局边距使您始终为标题标签获得正确的填充。
您还可以查看我的 UITableViewCell 子类之一,它已经尊重 layoutMargins:https://github.com/bhr/BHRExtensions/blob/master/BHRExtensions/Utilities/BHRTitleAndValueTableCell.m
干杯
在阅读了现有的答案并且没有找到明显的编程解决方案之后,我做了一些更多的挖掘,现在对于其他面临这个问题的人来说有一个很好的答案。
首先,没有必要像 other answers 所暗示的那样将 preservesSuperviewLayoutMargins
设置为单元格的视图或内容视图。虽然默认值为 false
,但将其更改为 true
并没有我看到的明显效果。
使它真正起作用的关键是 UIView
上的 layoutMarginsGuide
属性。使用这个值,我们可以轻松地将任何子视图的 leadingAnchor
固定到指南的 leadingAnchor
。以下是它在代码中的样子(很可能是 IB 在幕后所做的,如 Jonas's answer)。
在 UITableViewCell
子类中,您将执行以下操作:
override func updateConstraints() {
let margins = contentView.layoutMarginsGuide
let leading = margins.leadingAnchor
subview1.leadingAnchor.constraintEqualToAnchor(leading).active = true
subview2.leadingAnchor.constraintEqualToAnchor(leading).active = true
super.updateConstraints()
}
斯威夫特 4.1 更新
override func updateConstraints() {
let margins = contentView.layoutMarginsGuide
let leading = margins.leadingAnchor
subview1.leadingAnchor.constraint(equalTo: leading).isActive = true
subview2.leadingAnchor.constraint(equalTo: leading).isActive = true
super.updateConstraints()
}
就这样!如果您正在为 iOS 9 之前的 iOS 版本进行开发,则需要替换布局锚点并改用 layoutMargins
插图。
注意:如果您希望使用更简洁的语法,我编写了一个库来使锚定更漂亮。它称为 SuperLayout,可在 Cocoapods 上使用。在源文件的顶部,导入 SuperLayout
:
import SuperLayout
然后在您的布局块中,使用 ~~
、≤≤
和 ≥≥
固定约束:
override func updateConstraints() {
let margins = contentView.layoutMarginsGuide
subview1.leadingAnchor ~~ margins.leadingAnchor
subview2.leadingAnchor ~~ margins.leadingAnchor
super.updateConstraints()
}
ios 11+:让 margins = contentView.directionalLayoutMargins ... 以防您需要开箱即用地适应 LTR 和 RTL。我想大多数人确实需要这个。
preservesSuperviewLayoutMargins
属性设置为 true 对我不起作用,无论是通过编程方式还是通过界面构建器。我正在使用布局约束。感谢@Dan Loewenherz 省去了很多麻烦!
通过执行以下操作,我能够获得具有与标准单元格对齐的自定义样式的单元格:
在文档大纲中,为具有自定义样式的单元格选择“内容视图”。转到尺寸检查器。在“布局边距”下拉菜单下,点击“保留超级视图边距”旁边的小加号。选择 iPad 尺寸等级,即“常规宽度 x 常规高度”。选中“保留 Superview 边距”旁边的复选框。通过更新框架解决任何自动布局警告。
这在 Xcode 7 中对我有用;我希望它也能在 Xcode 6 中工作。
我在 iPad Air、OS 10.1.1 上测试时遇到了这个问题。表头的缩进比他们应该的要远得多,横向方向的情况更糟。但它们在 OS 11 之前的 iPhone 上都很好。
令人惊讶的解决方案是以下代码行,就在创建表之后(对不起,我只在 C# 中工作,但很容易计算出 Obj-C 和 Swift 等价物):
myTableView.SeparatorInset = myTableView.SeparatorInset;
然后一切都缩进了它应该的!
UITableViewCell
,preservesSuperviewLayoutMargins
默认为true
。