启用分页时将水平滚动集合视图的单元格居中,并使其他单元格部分可见
Centring a cell of a horizontally scrollable collection view when paging is enabled and making other cells partly visible, too
我有一个可水平滚动的集合视图。它上面有几个细胞。为该集合视图启用了分页。
我想让它显示居中单元格左侧和右侧的部分单元格(当它不是第一个或最后一个时),如下所示:
这里是UICollectionViewDelegateFlowLayout
的代码:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width - 50, height: collectionView.frame.height - 16)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return sectionInsets
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
和sectionInsets
如下:
private var sectionInsets = UIEdgeInsetsMake(8, 20, 8, 20)
目前,当我向右滚动时,右边的单元格越来越明显(单元格之间的间距线向左边缘移动)
我尝试根据答案使用不同的值 and ,但我无法达到我想要的效果。
如果您对此有任何建议,我将不胜感激。
NSInteger lastOffset, currentIndex;
-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:currentIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
lastOffset = scrollView.contentOffset.x;
}
-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
CGFloat newOffset = scrollView.contentOffset.x;
if (currentIndex > 0 && lastOffset > 0 && (newOffset < lastOffset - (collectionViewCellwidth / 2.0) || velocity.x < -0.5)) {
currentIndex -= 1;
}
if (currentIndex < (totalNumberOfRows - 1) && lastOffset < ((totalNumberOfRows - 1) * collectionViewCellwidth) && (newOffset > (lastOffset + (collectionViewCellwidth / 2.0)) || velocity.x > 0.5)) {
currentIndex += 1 ;
}
targetContentOffset->x = currentIndex * collectionViewCellwidth;
}
在您的情况下,collectionViewCellwidth 将为 "collectionView.frame.width - 50"
实际上,解决方案并不难。感谢 this 文章展示了实现此功能的正确方法,我终于实现了它。
要了解的主要事情是,在集合视图中构建的分页在这种情况下不起作用,因为单元格的大小小于屏幕的大小。所以,我们需要实现我们的自定义分页技术。
首先,在自定义帮助程序 struct
中,我们定义了一个 属性,它将在拖动之前保存单元格的索引:
var indexOfCellBeforeDragging: Int = 0
然后,我们创建一个计算单元格截面插入的方法:
//Setting the inset
func calculateSectionInset(forCollectionViewLayout collectionViewLayout: UICollectionViewFlowLayout, numberOfCells: Int) -> CGFloat {
let inset = (collectionViewLayout.collectionView!.frame.width) / CGFloat(numberOfCells)
return inset
}
现在我们可以使用上面的方法,来设置collection view的items的大小了。
//Setting the item size of the collection view
func configureCollectionViewLayoutItemSize(forCollectionViewLayout collectionViewLayout: UICollectionViewFlowLayout) {
let inset: CGFloat = calculateSectionInset(forCollectionViewLayout: collectionViewLayout, numberOfCells: 5)
collectionViewLayout.sectionInset = UIEdgeInsets(top: 0, left: inset/4, bottom: 0, right: inset/4)
collectionViewLayout.itemSize = CGSize(width: collectionViewLayout.collectionView!.frame.size.width - inset / 2, height: collectionViewLayout.collectionView!.frame.size.height)
collectionViewLayout.collectionView?.reloadData()
}
我们需要获取主单元格(当前最大的单元格)的索引。为此,我们在 x
轴上使用有关项目宽度和集合视图的 contentOffset
的信息:
//Getting the index of the major cell
func indexOfMajorCell(in collectionViewLayout: UICollectionViewFlowLayout) -> Int {
let itemWidth = collectionViewLayout.itemSize.width
let proportionalLayout = collectionViewLayout.collectionView!.contentOffset.x / itemWidth
return Int(round(proportionalLayout))
}
接下来,我们在开始拖动之前设置cell的索引,使用上面的方法:
//Setting the index of cell before starting dragging
mutating func setIndexOfCellBeforeStartingDragging(indexOfMajorCell: Int) {
indexOfCellBeforeDragging = indexOfMajorCell
}
现在我们应该处理拖动的结束。此外,我们还处理了单元格上可能的捕捉手势。这里我们为滑动速度使用一些自定义阈值。
//Handling dragging end of a scroll view
func handleDraggingWillEndForScrollView(_ scrollView: UIScrollView, inside collectionViewLayout: UICollectionViewFlowLayout, withVelocity velocity: CGPoint, usingIndexOfMajorCell indexOfMajorCell: Int) {
//Calculating where scroll view should snap
let indexOfMajorCell = indexOfMajorCell
let swipeVelocityThreshold: CGFloat = 0.5
let majorCellIsTheCellBeforeDragging = indexOfMajorCell == indexOfCellBeforeDragging
let hasEnoughVelocityToSlideToTheNextCell = indexOfCellBeforeDragging + 1 < 5 && velocity.x > swipeVelocityThreshold
let hasEnoughVelocityToSlideToThePreviousCell = ((indexOfCellBeforeDragging - 1) >= 0) && (velocity.x < -swipeVelocityThreshold)
let didUseSwipeToSkipCell = majorCellIsTheCellBeforeDragging && (hasEnoughVelocityToSlideToTheNextCell || hasEnoughVelocityToSlideToThePreviousCell)
if didUseSwipeToSkipCell {
let snapToIndex = indexOfCellBeforeDragging + (hasEnoughVelocityToSlideToTheNextCell ? 1 : -1)
let toValue = collectionViewLayout.itemSize.width * CGFloat(snapToIndex)
// Damping equal 1 => no oscillations => decay animation
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: velocity.x, options: .allowUserInteraction, animations: {
scrollView.contentOffset = CGPoint(x: toValue, y: 0)
scrollView.layoutIfNeeded()
}, completion: nil)
} else {
let indexPath = IndexPath(row: indexOfMajorCell, section: 0)
collectionViewLayout.collectionView!.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
}
现在,在 scrollViewWillBeginDragging(_:)
方法中的视图控制器中,我们需要获取主要单元格的索引并在开始拖动之前将其设置为索引(commentsPagingHelper
是辅助结构的一个实例):
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
let indexOfMajorCell = commentsPagingHelper.indexOfMajorCell(in: collectionViewLayout)
commentsPagingHelper.setIndexOfCellBeforeStartingDragging(indexOfMajorCell: indexOfMajorCell)
}
最后,我们在scrollViewWillEndDragging(_:, withVelocity:, targetContentOffset:)
方法中处理主单元格索引的变化:
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
//Stop scrollView sliding:
targetContentOffset.pointee = scrollView.contentOffset
let indexOfMajorCell = commentsPagingHelper.indexOfMajorCell(in: collectionViewLayout)
commentsPagingHelper.handleDraggingWillEndForScrollView(scrollView, inside: collectionViewLayout, withVelocity: velocity, usingIndexOfMajorCell: indexOfMajorCell)
}
就是这样。希望,这会帮助有类似问题的人。
我有一个可水平滚动的集合视图。它上面有几个细胞。为该集合视图启用了分页。
我想让它显示居中单元格左侧和右侧的部分单元格(当它不是第一个或最后一个时),如下所示:
这里是UICollectionViewDelegateFlowLayout
的代码:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width - 50, height: collectionView.frame.height - 16)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return sectionInsets
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
和sectionInsets
如下:
private var sectionInsets = UIEdgeInsetsMake(8, 20, 8, 20)
目前,当我向右滚动时,右边的单元格越来越明显(单元格之间的间距线向左边缘移动)
我尝试根据答案使用不同的值
如果您对此有任何建议,我将不胜感激。
NSInteger lastOffset, currentIndex;
-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:currentIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
lastOffset = scrollView.contentOffset.x;
}
-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
CGFloat newOffset = scrollView.contentOffset.x;
if (currentIndex > 0 && lastOffset > 0 && (newOffset < lastOffset - (collectionViewCellwidth / 2.0) || velocity.x < -0.5)) {
currentIndex -= 1;
}
if (currentIndex < (totalNumberOfRows - 1) && lastOffset < ((totalNumberOfRows - 1) * collectionViewCellwidth) && (newOffset > (lastOffset + (collectionViewCellwidth / 2.0)) || velocity.x > 0.5)) {
currentIndex += 1 ;
}
targetContentOffset->x = currentIndex * collectionViewCellwidth;
}
在您的情况下,collectionViewCellwidth 将为 "collectionView.frame.width - 50"
实际上,解决方案并不难。感谢 this 文章展示了实现此功能的正确方法,我终于实现了它。
要了解的主要事情是,在集合视图中构建的分页在这种情况下不起作用,因为单元格的大小小于屏幕的大小。所以,我们需要实现我们的自定义分页技术。
首先,在自定义帮助程序 struct
中,我们定义了一个 属性,它将在拖动之前保存单元格的索引:
var indexOfCellBeforeDragging: Int = 0
然后,我们创建一个计算单元格截面插入的方法:
//Setting the inset
func calculateSectionInset(forCollectionViewLayout collectionViewLayout: UICollectionViewFlowLayout, numberOfCells: Int) -> CGFloat {
let inset = (collectionViewLayout.collectionView!.frame.width) / CGFloat(numberOfCells)
return inset
}
现在我们可以使用上面的方法,来设置collection view的items的大小了。
//Setting the item size of the collection view
func configureCollectionViewLayoutItemSize(forCollectionViewLayout collectionViewLayout: UICollectionViewFlowLayout) {
let inset: CGFloat = calculateSectionInset(forCollectionViewLayout: collectionViewLayout, numberOfCells: 5)
collectionViewLayout.sectionInset = UIEdgeInsets(top: 0, left: inset/4, bottom: 0, right: inset/4)
collectionViewLayout.itemSize = CGSize(width: collectionViewLayout.collectionView!.frame.size.width - inset / 2, height: collectionViewLayout.collectionView!.frame.size.height)
collectionViewLayout.collectionView?.reloadData()
}
我们需要获取主单元格(当前最大的单元格)的索引。为此,我们在 x
轴上使用有关项目宽度和集合视图的 contentOffset
的信息:
//Getting the index of the major cell
func indexOfMajorCell(in collectionViewLayout: UICollectionViewFlowLayout) -> Int {
let itemWidth = collectionViewLayout.itemSize.width
let proportionalLayout = collectionViewLayout.collectionView!.contentOffset.x / itemWidth
return Int(round(proportionalLayout))
}
接下来,我们在开始拖动之前设置cell的索引,使用上面的方法:
//Setting the index of cell before starting dragging
mutating func setIndexOfCellBeforeStartingDragging(indexOfMajorCell: Int) {
indexOfCellBeforeDragging = indexOfMajorCell
}
现在我们应该处理拖动的结束。此外,我们还处理了单元格上可能的捕捉手势。这里我们为滑动速度使用一些自定义阈值。
//Handling dragging end of a scroll view
func handleDraggingWillEndForScrollView(_ scrollView: UIScrollView, inside collectionViewLayout: UICollectionViewFlowLayout, withVelocity velocity: CGPoint, usingIndexOfMajorCell indexOfMajorCell: Int) {
//Calculating where scroll view should snap
let indexOfMajorCell = indexOfMajorCell
let swipeVelocityThreshold: CGFloat = 0.5
let majorCellIsTheCellBeforeDragging = indexOfMajorCell == indexOfCellBeforeDragging
let hasEnoughVelocityToSlideToTheNextCell = indexOfCellBeforeDragging + 1 < 5 && velocity.x > swipeVelocityThreshold
let hasEnoughVelocityToSlideToThePreviousCell = ((indexOfCellBeforeDragging - 1) >= 0) && (velocity.x < -swipeVelocityThreshold)
let didUseSwipeToSkipCell = majorCellIsTheCellBeforeDragging && (hasEnoughVelocityToSlideToTheNextCell || hasEnoughVelocityToSlideToThePreviousCell)
if didUseSwipeToSkipCell {
let snapToIndex = indexOfCellBeforeDragging + (hasEnoughVelocityToSlideToTheNextCell ? 1 : -1)
let toValue = collectionViewLayout.itemSize.width * CGFloat(snapToIndex)
// Damping equal 1 => no oscillations => decay animation
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: velocity.x, options: .allowUserInteraction, animations: {
scrollView.contentOffset = CGPoint(x: toValue, y: 0)
scrollView.layoutIfNeeded()
}, completion: nil)
} else {
let indexPath = IndexPath(row: indexOfMajorCell, section: 0)
collectionViewLayout.collectionView!.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
}
现在,在 scrollViewWillBeginDragging(_:)
方法中的视图控制器中,我们需要获取主要单元格的索引并在开始拖动之前将其设置为索引(commentsPagingHelper
是辅助结构的一个实例):
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
let indexOfMajorCell = commentsPagingHelper.indexOfMajorCell(in: collectionViewLayout)
commentsPagingHelper.setIndexOfCellBeforeStartingDragging(indexOfMajorCell: indexOfMajorCell)
}
最后,我们在scrollViewWillEndDragging(_:, withVelocity:, targetContentOffset:)
方法中处理主单元格索引的变化:
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
//Stop scrollView sliding:
targetContentOffset.pointee = scrollView.contentOffset
let indexOfMajorCell = commentsPagingHelper.indexOfMajorCell(in: collectionViewLayout)
commentsPagingHelper.handleDraggingWillEndForScrollView(scrollView, inside: collectionViewLayout, withVelocity: velocity, usingIndexOfMajorCell: indexOfMajorCell)
}
就是这样。希望,这会帮助有类似问题的人。