在iPhone和iPad上具有自动布局左边距的UITableViewCell不同

时间:2014-12-11 10:42:46

标签: ios uitableview autolayout margin

我正在使用带有静态单元格的分组UITableView作为选项屏幕/场景。一切都在使用Autolayout在Xcode 6.1 / iOS 8.1.x / Storyboard中完成。在表格组中有混合类型的单元格,有两种类型会导致我出现问题:

  1. 具有自定义样式和
  2. 的单元格
  3. 样式为“右侧细节”的单元格
  4. 在单元格#1上,我可以为标签和前导容器之间的左边距设置约束。在单元#2上,据我所知,我无法在Interface Builder中设置任何约束。我在单元格#1中的标签上设置了左边距,因此它与单元格#2中的标签对齐。 iPhone上的一切看起来都不错,但是如果我在iPad上显示相同的表格,其中表格视图的容器大小是屏幕大小的一半,则单元格#2获得更多边距(动态?),而单元格#1保持绝对边距I在约束中设置。我还尝试使用属性“相对于保证金”更改单元格#1中的左边距,但无效。

    iPhone:

    Table on iPhone

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

    Table on iPad

    所以问题是:如何在单元格#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也可能有所不同,因为现在一切都应该是自适应的。这就是我决定发布这个问题的原因。

5 个答案:

答案 0 :(得分:147)

如何解决

在与苹果bug报告团队进行了多次示例项目和截图并解析that answer之后,我发现让自定义样式单元格在其边距上表现一致的解决方案就像默认情况一样UITableViewCells,您必须执行以下操作(主要基于Becky's answer,我突出显示了哪些不同以及它对我有用的原因):

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

    Checking preserve superview margins

  5. (这里是密钥)对单元格本身执行相同的 (内容视图的父级,如果你愿意的话)

    The cell and not the content view this time

  6. 按如下方式设置约束:Label.Leading = Superview.Leading Margin(常量为0)

    Setting the constraint for the custom cell's label

  7. 现在所有单元格的标签都与默认单元格一致!这适用于我在Xcode 7及更高版本中,它包含我所提到的线程中提到的修复。 IB和模拟器现在应该显示正确对齐的标签。

    Final result in the simulator

    您也可以通过编程方式执行此操作,例如在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时,在布置内容时也会考虑超级视图的边距。此边距会影响视图边缘与其超视图之间的距离小于相应边距的布局。例如,您可能有一个内容视图,其框架精确匹配其超级视图的边界。当superview的任何边距位于内容视图所代表的区域内以及它自己的边距时,UIKit会调整内容视图的布局以尊重superview的边距。调整量是确保内容也在超视图边距内所需的最小量。

    因此,如果您希望单元格的内容与 TableView的边距对齐(如果您愿意,那么它是非常祖父的),您需要拥有内容的两个优势,内容视图和表单元格本身,保留他们自己的超级视图的边缘。

    为什么这不是默认行为让我感到惊讶:我觉得大多数不想自定义所有内容的开发人员都会默认这种“继承”。

答案 1 :(得分:6)

我遇到了和你一样的问题并找到了解决方案。

首先,一点背景:从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;
}

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

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

干杯

答案 2 :(得分:4)

在阅读了现有的答案而没有找到明显的程序化解决方案之后,我做了一些挖掘工作,现在对于遇到这个问题的任何人都有一个很好的答案。

首先,没有必要将false设置为单元格的视图或内容视图,如other answers所示。虽然默认值为true,但将其更改为layoutMarginsGuide并没有明显的效果,我可以看到。

实现此功能的关键是UIView上的leadingAnchor属性。使用此值,我们可以轻松地将任何子视图的leadingAnchor固定到指南的UITableViewCell。以下是它在代码中的外观(很可能就像在Jonas's answer中一样,IB在幕后工作)。

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() } 子类中,您可以执行以下操作:

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

Swift 4.1更新

layoutMargins

这就是全部!如果您在iOS 9之前的iOS版本中进行开发,则需要替换布局锚点并改为使用SuperLayout插件。

注意:如果您更喜欢更清晰的语法,我会编写一个库来使锚点更加漂亮。它被称为SuperLayout,可在Cocoapods上找到。在源文件的顶部,导入import SuperLayout

~~

然后在您的布局块中,使用≤≤≥≥override func updateConstraints() { let margins = contentView.layoutMarginsGuide subview1.leadingAnchor ~~ margins.leadingAnchor subview2.leadingAnchor ~~ margins.leadingAnchor super.updateConstraints() } 来限制约束:

#pragma comment(lib, "user32")
#include <windows.h>

int main() {
    MessageBox(NULL, "hello world", "", 0);
    return 0;
}

答案 3 :(得分:1)

通过执行以下操作,我能够将自定义样式的单元格与标准单元格对齐:

  1. 在文档大纲中,选择&#34;内容视图&#34;用于细胞 自定义样式。
  2. 转到尺寸检查器。
  3. 在&#34;布局边距&#34;下拉,点击&#34;保留Superview边距旁边的小加号。&#34;
  4. 选择iPad尺寸等级,即&#34;常规宽度x常规高度。&#34;
  5. 选中&#34;保留Superview边距。&#34;
  6. 旁边的复选框
  7. 通过更新框架解决所有自动布局警告。
  8. 这在Xcode 7中对我有用;我希望它也适用于Xcode 6。

答案 4 :(得分:0)

在iPad Air,OS 10.1.1上进行测试时出现此问题。表格标题的缩进程度远远超过它们应有的程度,而且在横向方向上更糟糕。但是在操作系统11之前,它们在iphone上运行良好。

令人惊讶的解决方案是在创建表之后的以下代码行(对不起,我只使用C#,但它很容易找出Obj-C和Swift的等价物):

myTableView.SeparatorInset = myTableView.SeparatorInset;

然后一切都缩进了!