ios UICollectionView 水平分页不居中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29658328/
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
UICollectionView Horizontal Paging not centered
提问by mlevi
I have a horizontal scrolling collectionView with each cell the size of the view. When I page through the collectionView it doesn't page by cell. The cells aren't in the center of the screen. I've tried a bunch of things to try to fix it and haven't had any luck. Here's a video of the problem: https://www.youtube.com/watch?v=tXsxWelk16wAny ideas?
我有一个水平滚动的 collectionView,每个单元格都是视图的大小。当我翻阅 collectionView 时,它不会按单元翻页。单元格不在屏幕中央。我已经尝试了很多方法来尝试修复它,但没有任何运气。这是问题的视频:https: //www.youtube.com/watch? v=tXsxWelk16w 有什么想法吗?
回答by Vlad
Remove spaces between items. For horizontal scrolling collection view set minimum line spacing to 0. You can do this with interface builder or with method of UICollectionViewDelegateFlowLayout
protocol:
删除项目之间的空格。对于水平滚动集合视图,将最小行距设置为 0。您可以使用界面构建器或UICollectionViewDelegateFlowLayout
协议方法执行此操作:
- (CGFloat)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 0;
}
Another way is making your cell's width less than collectionView's width for a value of horizontal space between items. Then add section insets with left and right insets that equal a half of horizontal space between items. For example, your minimum line spacing is 10:
另一种方法是使单元格的宽度小于 collectionView 的宽度,以获得项目之间的水平空间值。然后添加部分插入,左右插入等于项目之间水平空间的一半。例如,您的最小行距为 10:
- (CGFloat)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 10;
}
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(collectionView.frame.size.width - 10, collectionView.frame.size.height);
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(0, 5, 0, 5);
}
And third way: manipulate collectionView scroll in scrollViewDidEndDecelerating:
method:
第三种方式:在scrollViewDidEndDecelerating:
方法中操作 collectionView 滚动:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.collectionView) {
CGPoint currentCellOffset = self.collectionView.contentOffset;
currentCellOffset.x += self.collectionView.frame.size.width / 2;
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:currentCellOffset];
[self.collectionView scrollToItemAtIndexPath:indexPath
atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
animated:YES];
}
}
回答by Paolo Musolino
Demo here in Swift 3: https://github.com/damienromito/CollectionViewCustom
在Swift 3 中进行演示:https: //github.com/damienromito/CollectionViewCustom
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let pageWidth = Float(itemWidth + itemSpacing)
let targetXContentOffset = Float(targetContentOffset.pointee.x)
let contentWidth = Float(collectionView!.contentSize.width )
var newPage = Float(self.pageControl.currentPage)
if velocity.x == 0 {
newPage = floor( (targetXContentOffset - Float(pageWidth) / 2) / Float(pageWidth)) + 1.0
} else {
newPage = Float(velocity.x > 0 ? self.pageControl.currentPage + 1 : self.pageControl.currentPage - 1)
if newPage < 0 {
newPage = 0
}
if (newPage > contentWidth / pageWidth) {
newPage = ceil(contentWidth / pageWidth) - 1.0
}
}
self.pageControl.currentPage = Int(newPage)
let point = CGPoint (x: CGFloat(newPage * pageWidth), y: targetContentOffset.pointee.y)
targetContentOffset.pointee = point
}
Swift 4:
斯威夫特 4:
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let pageWidth = Float(itemWidth + itemSpacing)
let targetXContentOffset = Float(targetContentOffset.pointee.x)
let contentWidth = Float(collectionView!.contentSize.width )
var newPage = Float(self.pageControl.currentPage)
if velocity.x == 0 {
newPage = floor( (targetXContentOffset - Float(pageWidth) / 2) / Float(pageWidth)) + 1.0
} else {
newPage = Float(velocity.x > 0 ? self.pageControl.currentPage + 1 : self.pageControl.currentPage - 1)
if newPage < 0 {
newPage = 0
}
if (newPage > contentWidth / pageWidth) {
newPage = ceil(contentWidth / pageWidth) - 1.0
}
}
self.pageControl.currentPage = Int(newPage)
let point = CGPoint (x: CGFloat(newPage * pageWidth), y: targetContentOffset.pointee.y)
targetContentOffset.pointee = point
}
回答by Aleksey Shevchenko
Swift version of @vlad-che accepted answer:
@vlad-che 的 Swift 版本接受了答案:
extension GoodsViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let frameSize = collectionView.frame.size
return CGSize(width: frameSize.width - 10, height: frameSize.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
}
}
回答by TejAces
Swift 4 solution to remove line spacing to keep cells centered:
删除行间距以保持单元格居中的 Swift 4 解决方案:
public func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
回答by Dallas Johnson
Being able to have cells that are smaller the collectionView
frame with space between the cells allows for hinting to the user that there other cells either side to scroll to which is a big win for UX. But for the centering of the pages doesn't work as expected with each cell progressively becoming more offset as the user scrolls. I've found the following to work well. The centering/snapping animation on each cell is almost invisible to user since it is only tweaking where the collectionView
scrolling would end naturally rather than jerking the collectionView
to quickly scroll to another indexPath
. It's still important to to have the sectionInset
property set large enough to allow cell not to stick to the containing frame edges. Also since there are spaces between the cells the target could land on an indexPath
of nil which would cause the collectionView
to scroll back to the start. I've fixed this offsetting a little and then trying again but different approaches could be taken here.
能够拥有更小的collectionView
单元格和单元格之间的空间允许向用户暗示任何一侧还有其他单元格可以滚动到这对用户体验来说是一个巨大的胜利。但是对于页面的居中并没有按预期工作,随着用户滚动,每个单元格逐渐变得更多偏移。我发现以下方法运行良好。每个单元格上的居中/对齐动画对用户几乎不可见,因为它只是调整collectionView
滚动自然结束的位置,而不是collectionView
快速滚动到另一个indexPath
. 将sectionInset
属性设置得足够大以允许单元格不粘到包含框架的边缘仍然很重要。此外,由于单元格之间存在空间,因此目标可能会落在indexPath
的 nil 这将导致collectionView
滚动回开始。我稍微修正了这个偏移,然后再试一次,但这里可以采取不同的方法。
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
{
//Ensure the scrollview is the collectionview we care about
if (scrollView == self.collectionView) {
// Find cell closest to the frame centre with reference from the targetContentOffset.
CGPoint frameCentre = self.collectionView.center;
CGPoint targetOffsetToCentre = CGPointMake((* targetContentOffset).x + frameCentre.x, (* targetContentOffset).y + frameCentre.y);
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:targetOffsetToCentre];
//Check for "edgecase" that the target will land between cells and then find a close neighbour to prevent scrolling to index {0,0}.
while (!indexPath) {
targetOffsetToCentre.x += ((UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout).minimumInteritemSpacing;
indexPath = [self.collectionView indexPathForItemAtPoint:targetOffsetToCentre];
}
// Find the centre of the target cell
CGPoint centreCellPoint = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath].center;
// Calculate the desired scrollview offset with reference to desired target cell centre.
CGPoint desiredOffset = CGPointMake(centreCellPoint.x - frameCentre.x, centreCellPoint.y - frameCentre.y);
*targetContentOffset = desiredOffset;
}
}
回答by Umesh Verma
Swift 3
斯威夫特 3
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if scrollView == self.collectionView {
var currentCellOffset = self.collectionView.contentOffset
currentCellOffset.x += self.collectionView.frame.width / 2
if let indexPath = self.collectionView.indexPathForItem(at: currentCellOffset) {
self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
}
}
回答by lastcc
The code I just saw from Apple Official Guides and Sample Code:
我刚刚从苹果官方指南和示例代码中看到的代码:
AssetViewController.swift:
AssetViewController.swift:
self.collectionView?.isPagingEnabled = true
self.collectionView?.frame = view.frame.insetBy(dx: -20.0, dy: 0.0)
this code enlarges the collection view so that it extends out of the screen, while the content is just within the screen edges
这段代码放大了集合视图,使其延伸到屏幕之外,而内容就在屏幕边缘内
回答by Natalia
Swift 3 solutionbased on @Santos's answer, for use if if you have a regular horizontally paging collection view withouta page control like Paolo was using in his Swift 3 example.
基于@Santos 的答案的Swift 3 解决方案,如果您有一个常规的水平分页集合视图,而没有像 Paolo 在他的 Swift 3 示例中使用的页面控件,则可以使用该解决方案。
I used this to solve an issue where a horizontally paging cell full screen cells with a custom UICollectionViewFlowLayout animator didn't finish rotating AND ended up offset so that the the edges of a full screen cell frame were increasingly horizontally off set from the collection view's bounds as you scrolled (like in the video OP shared).
我用它来解决一个问题,即带有自定义 UICollectionViewFlowLayout 动画器的水平分页单元全屏单元没有完成旋转并最终偏移,因此全屏单元框架的边缘越来越水平偏离集合视图的边界当您滚动时(就像在共享的视频 OP 中一样)。
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
// Ensure the scrollview is the one on the collectionView we care are working with
if (scrollView == self.collectionView) {
// Find cell closest to the frame centre with reference from the targetContentOffset.
let frameCenter: CGPoint = self.collectionView.center
var targetOffsetToCenter: CGPoint = CGPoint(x: targetContentOffset.pointee.x + frameCenter.x, y: targetContentOffset.pointee.y + frameCenter.y)
var indexPath: IndexPath? = self.collectionView.indexPathForItem(at: targetOffsetToCenter)
// Check for "edge case" where the target will land right between cells and then next neighbor to prevent scrolling to index {0,0}.
while indexPath == nil {
targetOffsetToCenter.x += 10
indexPath = self.collectionView.indexPathForItem(at: targetOffsetToCenter)
}
// safe unwrap to make sure we found a valid index path
if let index = indexPath {
// Find the centre of the target cell
if let centerCellPoint: CGPoint = collectionView.layoutAttributesForItem(at: index)?.center {
// Calculate the desired scrollview offset with reference to desired target cell centre.
let desiredOffset: CGPoint = CGPoint(x: centerCellPoint.x - frameCenter.x, y: centerCellPoint.y - frameCenter.y)
targetContentOffset.pointee = desiredOffset
}
}
}
}
回答by santos
Swift 3.0 set your own UICollectionViewFlowLayout
Swift 3.0 设置你自己的 UICollectionViewFlowLayout
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
let width = UIScreen.main.bounds.width
layout.itemSize = CGSize(width: width, height: 154)
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
layout.scrollDirection = .horizontal
collectionView?.collectionViewLayout = layout
回答by user6520705
After having a similar issue, I fixed mine by realizing that when using horizontal scrolling the height is now the width and the width is now the height because the default is set for vertical scrolling. Try switching the values and see if that helps. https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html
遇到类似问题后,我意识到使用水平滚动时,高度现在是宽度,宽度现在是高度,因为默认设置为垂直滚动,从而修复了我的问题。尝试切换值,看看是否有帮助。 https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html