UITableViewCell与iPhone和iPad上的自动布局左边距不同

我正在使用静态单元格的分组UITableView的选项屏幕/场景。 一切都在Xcode 6.1 / iOS 8.1.x / Storyboard使用Autolayout完成。 在表格组里有混合types的单元格,有两种types会引起我的问​​题:

  1. 单元格与自定义样式和
  2. 具有样式“右侧细节”

在单元格1上,我可以为标签和前导容器之间的左边距设置一个约束。 就我所知,在单元#2上,我无法在Interface Builder中设置任何约束。 我已经在单元格#1中的标签上设置了左边距,使其与单元格#2中的标签alignment。 一切都在iPhone上看起来不错,但如果我在iPad上显示表视图的容器的大小是屏幕大小的一半的同一张表,单元格#2获得更多的余量(dynamic?),而单元格1保持绝对余量我在约束中设置。 我也尝试改变单元格#1的左边距的属性“相对于边距”,但无济于事。

苹果手机:

在iPhone上的表

iPad(桌面宽度= 1/2屏幕尺寸)

在iPad上的表

所以问题是:我如何设置单元格#1中的标签的约束,以便它像单元格#2一样alignment。

这里也是一个Xcode 6.1示例项目的链接,演示了这个问题。 在iPhone和iPad上运行以查看区别:

https://dl.dropboxusercontent.com/u/5252156/Code/tableViewTest.zip

这个问题可能与iPhone和iPad的Layout静态表格单元格有关 ,但是对于iOS 8来说也可能不同,因为现在一切都应该是自适应的。 这就是为什么我决定发表这个问题。

如何解决它

在与许多示例项目和屏幕截图的苹果bug报告团队作斗争之后,我已经发现解决scheme让您的自定义样式单元格对于他们的边缘行为一致,就像默认的UITableViewCells,你必须做的以下(主要基于贝基的回答 ,我已经强调了什么是不同的,什么使它为我工作):

  1. 在IB中select您的单元格的内容视图
  2. 转到尺寸检查器
  3. 在“版面边距”部分中, 选中“保留超视图边距”(不要单击加号)

    检查保留superview边缘

  4. (这里是关键) 对单元格本身也是一样 (如果你愿意的话,内容视图的父类)

    单元格而不是这次的内容视图

  5. 设置你的约束如下:Label.Leading = Superview.Leading Margin(常量为0)

    为自定义单元格的标签设置约束

现在,您的所有单元格都将具有与默认单元格一致的标签! 这对我来说在Xcode 7中起作用,它包括我提到的线程中提到的修复。 IB和模拟器现在应该显示正确alignment的标签。

模拟器的最终结果

你也可以用编程的方式做一些这样的事情,例如在视图控制器的类中:

 cell.preservesSuperviewLayoutMargins = true cell.contentView.preservesSuperviewLayoutMargins = true 

或者你可以通过在启动时调用UIAppearance来设置它(我只知道Swift,对不起):

 UITableViewCell.appearance().preservesSuperviewLayoutMargins = true UITableViewCell.appearance().contentView.preservesSuperviewLayoutMargins = true 

如何和为什么它的作品

正如Ethan所指出的那样,Apple自己的UIView文档描述了如下所示的preservesSuperviewLayoutMargins了SuperviewLayoutMargins:

当这个属性的值为true ,在布置内容时也会考虑超视图的边距。 此边距会影响视图边缘与其俯视图之间的距离小于相应边距的布局。 例如,您可能有一个内容视图,其框架与其超级视图的边界精确匹配。 当任何超视图的边距都在内容视图和自己的边距表示的区域内时,UIKit会调整内容视图的布局以尊重超视图的边距。 调整的金额是确保内容也在监视范围内所需的最小金额。

因此,如果你希望你的单元格的内容与TableView的边距一致(如果你愿意的话,它是一个很大的祖父母),你需要让你的内容的两个方面,内容视图和表格单元本身,保留自己的超级视angular的边缘。

为什么这不是默认行为让我感到惊讶:我觉得像大多数不想自定义所有东西的开发人员默认会希望这种“inheritance”。

我遇到了同样的问题,并提出了一个解决scheme。

首先,有一点背景:自iOS 8以来,默认的表格视图单元格尊重单元格的layoutMargins以适应不同的特性(又名屏幕aka设备)。 例如,所有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; } 

适应代码中的布局边距可以使您始终为标题标签填充正确的内边距。

你也可以看看我已经尊重layoutMargins的一个UITableViewCell子类: https : //github.com/bhr/BHRExtensions/blob/master/BHRExtensions/Utilities/BHRTitleAndValueTableCell.m

干杯

在阅读了现有的答案之后,并没有find一个明显的程序化解决scheme,我做了一些更深入的挖掘,现在对于面临这个问题的任何其他人都有一个很好的答案。

首先,没有必要像其他 答案所暗示的那样将preservesSuperviewLayoutMargins的SuperviewLayoutMargins设置为单元格的视图或内容视图。 虽然默认值为false ,但将其更改为true并没有显而易见的效果。

使这实际工作的关键是UIView上的layoutMarginsGuide属性。 使用这个值,我们可以很容易地将leadingAnchor视图的leadingAnchor到指南的leadingAnchor 。 下面是它在代码中的外观(在Jonas的回答中,很可能是IB在做幕后工作)。

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() } 

就这样! 如果您正在开发iOS版本之前的iOS版本,则需要replace布局锚点,然后使用layoutMargins插入。


注:我写了一个库,使锚定更漂亮,如果你更喜欢一个更清晰的语法。 这叫做SuperLayout ,可以在Cocoapods上使用。 在源文件的顶部,导入SuperLayout

 import SuperLayout 

然后在你的布局块中,使用~~≤≤≥≥引脚约束:

 override func updateConstraints() { let margins = contentView.layoutMarginsGuide subview1.leadingAnchor ~~ margins.leadingAnchor subview2.leadingAnchor ~~ margins.leadingAnchor super.updateConstraints() } 

我能够通过执行以下操作来获取具有与标准单元格alignment的自定义样式的单元格:

  1. 在“文档大纲”中,为自定义样式的单元格select“内容视图”。
  2. 转到尺寸检查器。
  3. 在“Layout Margins”下拉菜单中,点击“Preview Superview Margins”旁边的小加号。
  4. select“常规宽度x常规高度”的iPad大小类别。
  5. 选中“保留Superview边距”旁边的checkbox。
  6. 通过更新帧来解决任何自动布局警告。

这在Xcode 7中适用于我; 我希望它也能在Xcode 6中工作。

在iPad Air 10.1.1上testing时遇到了这个问题。 表头比预想的要进一步缩进,在横向上更糟糕。 但是,他们在OS 11以上的手机都很好。

令人惊讶的解决scheme是在创build表之后的下面一行代码(对不起,我只用C#工作,但是很容易计算出Obj-C和Swift等价物):

 myTableView.SeparatorInset = myTableView.SeparatorInset; 

然后,一切都缩进,因为它应该是!