iPhone X横屏下的UICollectionView

45

iPhone X 横屏时,需要检查 safeAreaInsets,以便在左右两侧留出适当的间距。UITableView 有一个新的 insetsContentViewsToSafeArea 属性(默认为 true),可以自动将单元格内容保留在安全区域。

我很惊讶 UICollectionView 似乎没有类似的东西。我期望对于垂直滚动的集合视图,在横向时左右两侧会被插入到安全区域中(反之,在纵向时如果需要,水平滚动的集合视图将被插入到安全区域)。

确保此行为最简单的方法似乎是添加到集合视图控制器中:

- (void)viewSafeAreaInsetsDidChange {
    [super viewSafeAreaInsetsDidChange];
    UIEdgeInsets contentInset = self.collectionView.contentInset;
    contentInset.left = self.view.safeAreaInsets.left;
    contentInset.right = self.view.safeAreaInsets.right;
    self.collectionView.contentInset = contentInset;
}

假设contentInset.left/right通常为零。

(注:对于UICollectionViewController,需要使用self.view.safeAreaInsets;在调用此代码时,对safeAreaInsets的更改尚未奇怪地传播到self.collectionView)。

我错过了什么吗?那个样板文件足够简单,但现在它实际上对于每个接触屏幕边缘的集合视图都是必需的。看起来很奇怪,苹果没有提供默认启用此功能的东西。


1
请注意,不建议插入整个集合视图内容。例如,部分标题应该拉伸到边缘。这可以通过在布局上使用部分插图而不是在集合视图上使用内容插图来实现。 - Jordan H
啊,是的,好发现。我的初始测试设置是一个没有标题的单个部分,所以我没有考虑到这一点。 - Wes Campaigne
@Joey 为什么节标题不应缩进到安全区域?在横向模式下,节标题中的文本会被凹口遮挡。 - Jonny
2
@Jonny 头部内容应该正确地嵌入,但头部视图本身不应该嵌入,从而允许其背景边缘拉伸。 - Jordan H
@Joey 好的,对我来说内容没有正确插入,我需要再仔细检查一下。 - Jonny
@Joey 谢谢你的帮助,让我解决了这个问题。我一直在使用uitableviewdelegate方法viewForHeaderInSection来实现自己的布局。我需要将包含UILabel的前导锚点约束到mycustomheaderview.layoutMarginsGuide.leadingAnchor。这有点棘手,以前从未在代码中做过这样的事情。 - Jonny
6个回答

72

我遇到了同样的问题。这个方法对我有效:

override func viewDidLoad() {
    if #available(iOS 11.0, *) {
        collectionView?.contentInsetAdjustmentBehavior = .always
    }
}

.always 枚举常量的 documentation 表示:

始终将安全区域内的插图包括在内容调整中。

即使手机旋转,此解决方案也能正常工作。


@Jonny 在我的情况下,这个解决方案比被接受的答案更好,因为它在手机旋转时也能按预期工作。 - petrsyn
1
是的,我现在也用了这个。它很好,因为设置可以在viewDidLoad中设置,这感觉更方便,作为一次性解决方案。 - Jonny
这对我有效,但当我以集合视图进入VC时,它会将所有单元格对齐到左侧,我需要旋转到纵向然后再旋转回来。尝试了无效布局等方法,但都没有起作用。你遇到过这样的行为吗? - Łukasz Przytuła
@ŁukaszPrzytuła 我没有遇到这个问题。当您设置 collectionView?.contentInsetAdjustmentBehavior = .always 时,您是否遇到过这种情况,并且除此之外它是否按预期工作? - petrsyn
已经成功修复了。发现一些代码滚动到底部,将contentOffset硬编码为0。 - Łukasz Przytuła
contentInsetAdjustmentBehavior属性被设置为考虑安全区域的值时,您可以通过检查UICollectionView的adjustedContentInset属性来找到这些插入已经调整到了什么值。FYI。https://developer.apple.com/documentation/uikit/uiscrollview/2902259-adjustedcontentinset - Derek Lee

42

尽管Nathan正确指出了UICollectionView在各种布局方面的多功能性,但我主要关心的是使用UICollectionViewFlowLayout的“默认”情况。

结果,iOS 11为UICollectionViewFlowLayout添加了一个sectionInsetReference属性。官方文档目前缺乏描述,但头文件将其描述为:

部分插图相对于其定义的参考边界。默认为.fromContentInset

注意:内容插图始终会被最小限度地尊重。例如,如果sectionInsetReference等于.fromSafeArea,但调整后的内容插图大于安全区域和部分插图的组合,则部分内容将与内容插图对齐。

可能的值为:

@available(iOS 11.0, *)
public enum UICollectionViewFlowLayoutSectionInsetReference : Int {
    case fromContentInset
    case fromSafeArea
    case fromLayoutMargins
}

将其设置为.fromSafeArea可产生期望的结果,即在初始纵向方向时:

initial portrait layout

当旋转到横屏时,单元格会被插入,以便它们完全位于安全区域内:

iPhone X landscape collection view layout

...然而,目前存在一个bug,当从横屏旋转回竖屏之后,视图仍会像左/右safeAreaInsets设置为横屏值一样运作:

portrait layout following rotation from landscape

关于这个问题,我已经提交了一份雷达报告(rdar://34491993)。


太好了,我很惊讶更多的人没有遇到这个问题。然而,我无法重现你提到的错误。当我从横屏旋转回纵屏后,我的插图是正确的。 - mluisbrown
哦,有趣。我用于截图的测试项目(并在我的错误报告中提交)非常简单,我认为会反映出最基本的默认行为... 我还没有收到苹果的回复。iPhone X模拟器肯定存在许多其他错误行为... 无论如何,我期望到 iPhone X 实际发布时,我们将拥有 iOS 11.1,并希望这些问题已经被解决了。 - Wes Campaigne
图片对我来说还能正常工作吗?它们是使用堆栈溢出的集成图像托管服务。 - Wes Campaigne
你是如何让你的标题拥有全宽背景的? - David Beck
集合视图本身占据整个屏幕宽度,不受安全区域指南的限制。同时,我将标题标签的前沿锚定到父视图的前沿边缘;边距会自动适应安全区域,因此在需要时标签会适当地缩进。 - Wes Campaigne
显示剩余6条评论

12

感谢上面的帮助。对于我的UICollectionViewController子类,我在viewDidLoad()中添加了以下内容(Swift 3):

if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
    if #available(iOS 11.0, *) {
        flowLayout.sectionInsetReference = .fromSafeArea
    }
}

1
请注意,以上解决方案并不能解决这样一种情况:您的集合视图单元格视图(以及约束到其单元格前缘或后缘的子视图)与集合视图边界具有相同的宽度,并且没有设置为遵守安全区域布局指南。
注意:@petrsyn的答案使标题从两侧缩进,这可能不是大多数人想要的,而@Wes Campaigne的答案在完整宽度单元格上并不真正起作用,其中一个子视图附加到单元格的前沿或后沿。
特别是对于那些来自旧项目的人来说,将您的Xib和Storyboard文件设置为使用安全区域布局指南,然后使用自动布局放置相应的约束以保持安全区域或在代码中执行类似操作至关重要。

1
如果您想以编程方式将集合视图添加为主视图的子视图,则可以在viewDidLoad方法中执行以下操作:
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)

NSLayoutConstraint.activate([
        collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        collectionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
        collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
        collectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor)
])

1
为什么Kirill的答案被downvote了?有什么需要注意的地方吗?在我的情况下,他的答案帮助我意识到,我可以在viewDidLoad中将collectionView限制在安全区域(而不是在UICollectionViewCell中)。这解决了我的问题,尽管我的问题与主题发起者的问题略有不同。除非有人能向我展示他的答案存在的问题,否则我会upvote Kirill的答案。 - andreas1724
@andreas1724 他的解决方案存在问题,即集合视图被剪裁到安全区域。集合视图内容应该能够滚动到安全区域之外并进入视图的边界(包含屏幕上所有像素的矩形)。正确的方法是插入内容。 - Teng L

0

UICollectionView旨在为各种布局提供灵活性。最常见的布局是具有多行和列的网格,但也可以使用UICollectionView创建非网格布局。

另一方面,UITableView设计用于全宽度单元格。

因此,UITableView具有处理安全区域插图的内置支持是有意义的,因为表视图布局将始终受到其影响。由于UICollectionView使用自定义布局,因此解决这些问题的最佳方法是根据每个实现进行而不是尝试提供一种适合所有情况的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接