在Apple的Photo's App上,如果在滚动到任意偏移的同时旋转设备,则预先在中心的相同单元将在旋转后终止于中心。
我试图用UICollectionView实现相同的行为。 'indexPathsForVisibleItems'似乎是这样做的方式....
以下代码提供了旋转之间的平滑操作,但中心项计算似乎是关闭的:
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
})
}
而不是使用'indexPathsForVisibleItems',只使用'contentOffset'?计算偏移后的偏移?但是,当旋转后的contentSize可能与预期的不同单元尺寸,单元间距等不同时,这怎么可能呢?
你可以通过实现UICollectionViewDelegate方法collectionView(_:targetContentOffsetForProposedContentOffset:)
来做到这一点:
在布局更新期间,或在布局之间转换时,集合视图调用此方法,使您有机会更改要在动画结束时使用的建议内容偏移。如果布局或动画可能导致项目以不适合您的设计的方式定位,则可能会返回新值。
或者,如果您已经将UICollectionViewLayout子类化,则可以在布局子类中实现targetContentOffset(forProposedContentOffset:)
,这可能更方便。
在您的方法实现中,计算并返回内容偏移量,该偏移量将导致中心单元格位于集合视图的中心。如果您的单元格大小是固定的,则应该简单地撤消由其他UI元素(例如状态栏和/或导航栏的消失帧)导致的内容偏移的更改。
如果您的单元格大小因设备方向而异:
indexPathForItem(at:)
来获取并保存中心单元的索引路径,将内容偏移量(加上集合视图的可见边界的高度的一半)作为点。targetContentOffset(forProposedContentOffset:)
方法。通过使用保存的索引路径调用layoutAttributesForItem(at:)
来检索中心单元格的布局属性。目标内容偏移量是该项目框架的中间位置(减少了集合视图可见边界高度的一半)。第一步可以在viewWillTransition(to:with:)
或scrollViewDidScroll()
中的视图控制器中实现。它也可以在prepareForAnimatedBoundsChange()
中的布局对象中实现,或者在检查由设备方向更改引起的边界更改后可能在invalidateLayout(with:)
中实现。 (在这种情况下,您还需要确保shouldInvalidateLayout(forBoundsChange:)
返回true
。)
您可能需要针对内容插入,单元格间距或应用程序特定的其他事项进行调整。
这是qazxsw poi中jamesk解决方案的实现:
在您的ViewController中:
swift
在UICollectionViewDelegateFlowLayout中:
fileprivate var prevIndexPathAtCenter: IndexPath?
fileprivate var currentIndexPath: IndexPath? {
let center = view.convert(collectionView.center, to: collectionView)
return collectionView.indexPathForItem(at: center)
}
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to: newCollection, with: coordinator)
if let indexAtCenter = currentIndexPath {
prevIndexPathAtCenter = indexAtCenter
}
collectionView.collectionViewLayout.invalidateLayout()
}