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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27420888/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 03:59:11  来源:igfitidea点击:

UITableViewCell with autolayout left margin different on iPhone and iPad

iosuitableviewautolayoutmargin

提问by Georg

I am using a grouped UITableViewwith static cells for an options screen/scene. Everything is done in Xcode 6.1 / iOS 8.1.x / Storyboard using Autolayout. Within the table groups there are mixed types of cells and there are two types that cause me problems:

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

  1. Cells with custom style and
  2. Cells with style "Right Detail"
  1. 具有自定义样式的单元格和
  2. 具有“正确细节”样式的单元格

On cell #1 I can set a constraint for the left margin between the label and the leading container. On cell #2 I cannot set any constraints in Interface Builder as far as I know. I have set the left margin on the label in cell #1 so it aligns with the label in cell #2. Everything looks fine on an iPhone, but if I show the same table on an iPad where the size of the table view's container is half the screen size, cell #2 gets more margin (dynamically?) while cell #1 maintains the absolute margin I set in the constraints. I also tried to change the left margin in cell #1 with the attribute "relative to margin" but to no avail.

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

iPhone:

苹果手机:

Table on iPhone

iPhone 上的桌子

iPad (with tableview width = 1/2 screen size)

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

Table on iPad

iPad 上的桌子

So the question is: How do I set the constraints for the label in cell #1 so that it aligns like cell #2.

所以问题是:如何为单元格 #1 中的标签设置约束,使其像单元格 #2 一样对齐。

Here is also a link to a Xcode 6.1 sample project demonstrating the problem. Run on iPhone and iPad to see the difference:

这里还有一个链接,指向演示该问题的 Xcode 6.1 示例项目。在 iPhone 和 iPad 上运行以查看差异:

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

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

This question might be related to Layout static table cell for iPhone and iPad, but it might also differ for iOS 8 since everything is supposed to be adaptive now. That's why I decided to post this question anyway.

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

回答by Jonas Zaugg

?How to fix it

?如何解决

After fighting with the apple bug reporting team with many sample projects and screenshots and dissecting that answer, I've found that the solution to have your custom-style cells behave consistently regarding their margins and be just like the default UITableViewCells, you have to do the following (mostly based on Becky's answer, I've highlighted what's different and what made it work for me) :

在与许多示例项目和屏幕截图的苹果错误报告团队进行了斗争并剖析了该答案之后,我发现让您的自定义样式单元格在其边距方面表现一致并且就像默认的 UITableViewCells 一样的解决方案,您必须这样做以下(主要基于贝基的回答,我强调了不同之处以及它对我有用的原因):

  1. Select your cell's content viewin IB
  2. Go to the Size Inspector
  3. In the Layout Margins section, check Preserve Superview Margins(do not click the plus-sign)

    Checking preserve superview margins

  4. (And here's the key) Do the same for the cell itself(the content view's parent if you will)

    The cell and not the content view this time

  5. Setup your constraints as follows : Label.Leading = Superview.Leading Margin (with a constant of 0)

    Setting the constraint for the custom cell's label

  1. 在 IB 中选择单元格的内容视图
  2. 转到尺寸检查器
  3. 在 Layout Margins 部分,选中 Preserve Superview Margins(不要单击加号)

    检查保留超级视图边距

  4. (这是关键)对单元格本身做同样的事情(内容视图的父级,如果你愿意的话)

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

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

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

Now all your cells will have their label consistent with the default cells! This works for me in Xcode 7 and up and it includes the fix mentioned in the thread I referred to. IB and the simulator should now show properly aligned labels.

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

Final result in the simulator

模拟器中的最终结果

You could also do some of this programmatically, for example in the View Controller's class :

您也可以以编程方式执行其中的一些操作,例如在视图控制器的类中:

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

Or you could have it set up by calling UIAppearance once at startup (I only know Swift, sorry) :

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

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


How and why it works

它如何以及为什么起作用

As Ethankindly pointed out, Apple's own documentation on UIView describes preservesSuperviewLayoutMarginsas follows :

正如Ethan亲切地指出的那样,Apple 自己的 UIView 文档描述preservesSuperviewLayoutMargins如下:

When the value of this property is true, the superview's margins are also considered when laying out content. This margin affects layouts where the distance between the edge of a view and its superview is smaller than the corresponding margin. For example, you might have a content view whose frame precisely matches the bounds of its superview. When any of the superview's margins is inside the area represented by the content view and its own margins, UIKit adjusts the content view's layout to respect the superview's margins. The amount of the adjustment is the smallest amount needed to ensure that content is also inside the superview's margins.

当该属性的值为 时true,在布局内容时也会考虑superview的边距。此边距会影响视图边缘与其父视图之间的距离小于相应边距的布局。例如,您可能有一个内容视图,其框架与其父视图的边界精确匹配。当父视图的任何边距位于内容视图及其自己的边距所代表的区域内时,UIKit 会调整内容视图的布局以尊重父视图的边距。调整量是确保内容也在父视图的边距内所需的最小量。

Therefore, if you want your cell's content to align with the TableView'smargins (it's great-grandparent if you will), you need to have your content's two ascendants, Content View and the Table Cell itself, preserve the margins of their own superview.

因此,如果您希望您的单元格的内容与TableView 的边距对齐(如果您愿意,它是曾祖父母),您需要让您的内容的两个祖先,内容视图和表格单元格本身,保留它们自己的超级视图的边距。

Why this isn't default behavior surprises me : I feel like most developers who don't want to customize everything would expect this "inheritance" by default.

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

回答by tubtub

I ran in the same problem as you did and came up with a solution.

我遇到了和你一样的问题,并想出了一个解决方案。

First, a little background: Since iOS 8, default table view cells respect the cell's layoutMarginsto adapt for different traits (aka screens aka devices). For instance, layout margins on all iPhones (except iPhone 6 Plus when shown in a form sheet) are {8, 16, 8, 16}. On iPad they're {8, 20, 8, 20}. So now we know that there 4 pixels difference, which most likely your custom table view cell doesn't respect.

首先,一点背景知识:从 iOS 8 开始,默认的表格视图单元格尊重单元格的layoutMargins以适应不同的特征(又名屏幕又名设备)。例如,所有 iPhone 上的布局边距(iPhone 6 Plus 除外,当显示在表单中时)都是{8, 16, 8, 16}. 在 iPad 上,它们是{8, 20, 8, 20}. 所以现在我们知道有 4 个像素差异,这很可能是您的自定义表格视图单元格不尊重的。

Your table view cell subclass needs to adapt the left margin constraint when layoutMargins change.

当 layoutMargins 更改时,您的表格视图单元格子类需要调整左边距约束。

Here's the relevant code snippet:

这是相关的代码片段:

 - (void)layoutMarginsDidChange
{
    [super layoutMarginsDidChange];

    self.leftLayoutMarginConstraint.constant = self.layoutMargins.left;
    self.rightLayoutMarginConstraint.constant = self.layoutMargins.right;
}

Adapting to the layout margins in code enables you getting always the right padding for your title label.

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

You may also take a look at one of my UITableViewCell subclasses that already respect layoutMargins: https://github.com/bhr/BHRExtensions/blob/master/BHRExtensions/Utilities/BHRTitleAndValueTableCell.m

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

Cheers

干杯

回答by Dan Loewenherz

After reading through the existing answers and not finding an obvious programmatic solution, I did some more digging and now have a good answer for anyone else facing this issue.

在通读现有答案并没有找到明显的程序化解决方案后,我进行了更多的挖掘,现在为其他面临此问题的人提供了一个很好的答案。

First off, it's not necessary to set preservesSuperviewLayoutMarginsto the cell's view or content view as otheranswersimply. While the default value is false, changing it to truehad no noticeable effect that I could see.

首先,没有必要preservesSuperviewLayoutMargins其他答案暗示的那样设置单元格的视图或内容视图。虽然默认值为false,但将其更改为true我看不到的明显效果。

The key for making this actually work is the layoutMarginsGuideproperty on UIView. Using this value, we can just easily pin the leadingAnchorof any subview to the leadingAnchorof the guide. Here's how it looks in code (and may very well be what IB is doing behind the scenes as in Jonas's answer).

使这一实际工作的关键是layoutMarginsGuide物业UIView。使用这个值,我们可以轻松地将leadingAnchor任何子视图的 固定到leadingAnchor指南的 。这是它在代码中的样子(很可能是 IB 在幕后所做的事情,如乔纳斯的回答)。

In a UITableViewCellsubclass, you would do something like this:

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

Swift 4.1 update

斯威夫特 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()
}

That's all! If you're developing for iOS versions pre-iOS 9, you'll need to substitute out the layout anchors and use the layoutMarginsinset instead.

就这样!如果您正在为 iOS 9 之前的 iOS 版本进行开发,则需要替换布局锚点并使用layoutMargins插图代替。



Note: I wrote a library to make the anchor pinning a little prettier, if you'd prefer a cleaner syntax. It's called SuperLayoutand is available on Cocoapods. At the top of your source file, import SuperLayout:

注意:我写了一个库来让锚钉更漂亮一些,如果你更喜欢更简洁的语法。它称为SuperLayout,可在 Cocoapods 上使用。在源文件的顶部,导入SuperLayout

import SuperLayout

And then in your layout block, use ~~, ≤≤, and ≥≥to pin constraints:

然后在你的布局块,使用~~≤≤≥≥对销约束:

override func updateConstraints() {
    let margins = contentView.layoutMarginsGuide

    subview1.leadingAnchor ~~ margins.leadingAnchor
    subview2.leadingAnchor ~~ margins.leadingAnchor

    super.updateConstraints()
}

ios 11+: let margins = contentView.directionalLayoutMargins ... in case you need to adapt to LTR and RTL out of the box. I assume most folks do need that.

ios 11+: let margins = contentView.directionalLayoutMargins ... 以防你需要适应 LTR 和 RTL 开箱即用。我想大多数人确实需要那个。

回答by Becky Hansmeyer

I was able to get cells with custom styles aligned with the standard cells by doing the following:

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

  1. In the Document Outline, select the "Content View" for the cell with the custom style.
  2. Go to the Size Inspector.
  3. Under the "Layout Margins" dropdown, hit the little plus symbol next to "Preserve Superview Margins."
  4. Select the iPad size class, which is "Regular Width x Regular Height."
  5. Check the checkbox next to "Preserve Superview Margins."
  6. Resolve any Auto Layout warnings by updating the frames.
  1. 在文档大纲中,为具有自定义样式的单元格选择“内容视图”。
  2. 转到尺寸检查器。
  3. 在“布局边距”下拉菜单下,点击“保留超级视图边距”旁边的小加号。
  4. 选择 iPad 尺寸等级,即“常规宽度 x 常规高度”。
  5. 选中“保留超级视图边距”旁边的复选框。
  6. 通过更新框架解决任何自动布局警告。

This worked for me in Xcode 7; I'm hoping it will work in Xcode 6 as well.

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

回答by diyaddict

I had this problem when testing on a iPad Air, OS 10.1.1. The table headers were indented much further than they should have been, and it was even worse in landscape orientation. But they were fine on iphones up to OS 11.

我在 iPad Air、OS 10.1.1 上测试时遇到了这个问题。表头的缩进比应有的更深,横向更糟。但它们在操作系统 11 之前的 iPhone 上都很好。

The surprising solution was the following line of code, just after the table was created (Sorry, I only work in C#, but it's easy to work out Obj-C and Swift equivalents):

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

myTableView.SeparatorInset = myTableView.SeparatorInset;

Then everything was indented as it should be!

然后一切都按原样缩进了!