设备旋转后的 UICollectionView contentOffset [英] UICollectionView contentOffset after device rotation

查看:17
本文介绍了设备旋转后的 UICollectionView contentOffset的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Apple 的 Photo 应用程序中,如果您在滚动到任意偏移量的同时旋转设备,则之前位于中心的相同单元格将在旋转后最终位于中心.

On Apple's Photo's App, if you rotate device while scroll to an arbitrary offset, same cell that was in the center beforehand would end up in the center after the rotation.

我正在尝试使用 UICollectionView 实现相同的行为.'indexPathsForVisibleItems' 似乎是这样做的方法......

I am trying to achieve the same behavior with UICollectionView. 'indexPathsForVisibleItems' seem to be the way to do it....

以下代码提供了旋转之间的平滑操作,但中心项目的计算似乎已关闭:

Following code provides smooth operation between rotates, yet the center item calculation seem to be off:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    self.collectionView?.collectionViewLayout.invalidateLayout()

    let middleItem = (self.collectionView?.indexPathsForVisibleItems.count / 2) - 1
    let indexPath = self.collectionView?.indexPathsForVisibleItems[middleItem]

    coordinator.animate(alongsideTransition: { ctx in

        self.collectionView?.layoutIfNeeded()
        self.collectionView?.scrollToItem(at: indexPath!, at: .centeredVertically, animated: false)

    }, completion: { _ in


    })
}

上面的代码有什么问题:

  1. 纵向 -> 横向:与最终位于中心的单元格有些偏离.
  2. 人像 -> 风景 -> 人像:与最初在人像中的偏移完全不同.
  3. 纵向 -> 横向 -> 纵向 -> 横向:离得远!
  1. Portrait -> Landscape: Somewhat off from what cell should end up in the center.
  2. Portrait -> Landscape -> Portrait: Completely off from what offset was in Portrait in the first place.
  3. Portrait -> Landscape -> Portrait -> Landscape: Way off!

注意事项:

  • 我必须在旋转时使集合视图布局无效,因为必须重新计算像元大小和间距.
  • self.collectionView?.layoutIfNeeded() 在动画中
    块,使过渡平滑
  • 可能是 scrollToItem 在集合视图之前被调用布局未最终确定,导致滚动偏移不正确?
  • Notes:

    • I have to invalidate the collection view layout upon rotation due to cell sizes and spacing having to be recalculated.
    • self.collectionView?.layoutIfNeeded() being inside the animation
      block, make the transition smooth
    • May be scrollToItem is being called before the collection view layout is not finalized, leading to incorrect scroll offset?
    • 不使用indexPathsForVisibleItems",而只使用contentOffset"?计算旋转后偏移量?但是,当旋转后的 contentSize 可能与对不同单元格大小、单元格间距等的预期不同时,这怎么可能呢?

      Instead of using 'indexPathsForVisibleItems', use just 'contentOffset'? Calculate the offset after-rotation offset? But how would this be possible when after-rotation contentSize could be different than what's expected do to different cell sizes, cell spacing etc?

      推荐答案

      您可以通过实现 UICollectionViewDelegate 方法 collectionView(_:targetContentOffsetForProposedContentOffset:):

      You can do this by implementing the UICollectionViewDelegate method collectionView(_:targetContentOffsetForProposedContentOffset:):

      在布局更新期间,或在布局之间转换时,集合视图会调用此方法,让您有机会更改提议的内容偏移以在动画结束时使用.如果布局或动画可能导致项目以不适合您的设计的方式定位,您可能会返回一个新值.

      During layout updates, or when transitioning between layouts, the collection view calls this method to give you the opportunity to change the proposed content offset to use at the end of the animation. You might return a new value if the layout or animations might cause items to be positioned in a way that is not optimal for your design.

      或者,如果你已经继承了 UICollectionViewLayout,你可以实现 targetContentOffset(forProposedContentOffset:) 在你的布局子类中,这样可能更方便.

      Alternatively, if you've already subclassed UICollectionViewLayout, you can implement targetContentOffset(forProposedContentOffset:) in your layout subclass, which may be more convenient.

      在该方法的实现中,计算并返回将导致中心单元格位于集合视图中心的内容偏移量.如果您的单元格大小是固定的,那么撤消由其他 UI 元素(例如状态栏和/或导航栏的消失框架)引起的内容偏移更改应该是一件简单的事情.

      In your implementation of that method, compute and return the content offset that would cause the center cell to be positioned in the center of the collection view. If your cell sizes are fixed, it should be a simple matter of undoing the change to the content offset caused by other UI elements (such as the disappearing frames of the status bar and/or navigation bar).

      如果您的单元格大小因设备方向而异:

      If your cell sizes vary with device orientation:

      1. 在设备旋转之前,通过调用 indexPathForItem(at:) 在您的集合视图上,将内容偏移量(加上集合视图可见边界高度的一半)作为点.
      2. 实现 targetContentOffset(forProposedContentOffset:) 方法之一.通过使用保存的索引路径调用 layoutAttributesForItem(at:) 来检索中心单元格的布局属性.目标内容偏移量是该项目的框架的中间(小于集合视图可见边界高度的一半).
      1. Prior to device rotation, fetch and save the index path of the center cell by calling indexPathForItem(at:) on your collection view, passing the content offset (plus half of the height of the collection view's visible bounds) as the point.
      2. Implement one of the targetContentOffset(forProposedContentOffset:) methods. Retrieve the layout attributes for the center cell by calling layoutAttributesForItem(at:) with the saved index path. The target content offset is the middle of the frame for that item (less half of the height of the collection view's visible bounds).

      第一步可以在 viewWillTransition(to:with:)scrollViewDidScroll() 中的视图控制器中实现.它也可以在 prepareForAnimatedBoundsChange() 中的布局对象中实现,或者在检查由设备方向变化引起的边界变化后,也可以在 invalidateLayout(with:) 中实现.(您还需要确保 shouldInvalidateLayout(forBoundsChange:) 在这些情况下返回 true.)

      The first step could be implemented in your view controller in viewWillTransition(to:with:) or in scrollViewDidScroll(). It could also be implemented in your layout object in prepareForAnimatedBoundsChange(), or perhaps in invalidateLayout(with:) after checking for a bounds change caused by a change in device orientation. (You would also need to ensure that shouldInvalidateLayout(forBoundsChange:) returns true in those circumstances.)

      您可能需要对内容插入、单元格间距或其他特定于您的应用的事项进行调整.

      You may need to make adjustments for content insets, cell spacing, or other matters specific to your app.

      这篇关于设备旋转后的 UICollectionView contentOffset的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆