UICollectionView:像 Safari 选项卡或 App Store 搜索一样分页
Posted
技术标签:
【中文标题】UICollectionView:像 Safari 选项卡或 App Store 搜索一样分页【英文标题】:UICollectionView: paging like Safari tabs or App Store search 【发布时间】:2013-04-10 07:22:58 【问题描述】:我想在我的应用中实现“卡片”,例如 Safari 选项卡或 App Store 搜索。
我将在屏幕中心向用户显示一张卡片,并在左右两侧显示上一张和下一张卡片的一部分。 (例如,请参阅 App Store 搜索或 Safari 选项卡)
我决定使用UICollectionView
,我需要改变页面大小(没找到怎么做)或者实现自己的布局子类(不知道怎么做)?
有什么帮助吗?
【问题讨论】:
【参考方案1】:以下是我找到的获得此效果的最简单方法。它涉及您的收藏视图和一个额外的秘密滚动视图。
设置您的收藏视图
设置您的集合视图及其所有数据源方法。 框出集合视图;它应该跨越您希望可见的整个宽度。设置集合视图的contentInset
:
_collectionView.contentInset = UIEdgeInsetsMake(0, (self.view.frame.size.width-pageSize)/2, 0, (self.view.frame.size.width-pageSize)/2);
这有助于正确偏移单元格。
设置您的秘密滚动视图
创建一个滚动视图,将其放置在您喜欢的任何位置。如果您愿意,可以将其设置为hidden
。
将滚动视图的边界大小设置为页面所需的大小。
将自己设置为滚动视图的代表。
将其contentSize
设置为集合视图的预期内容大小。
移动你的手势识别器
将秘密滚动视图的手势识别器添加到集合视图中,并禁用集合视图的手势识别器:
[_collectionView addGestureRecognizer:_secretScrollView.panGestureRecognizer];
_collectionView.panGestureRecognizer.enabled = NO;
委托
- (void) scrollViewDidScroll:(UIScrollView *)scrollView
CGPoint contentOffset = scrollView.contentOffset;
contentOffset.x = contentOffset.x - _collectionView.contentInset.left;
_collectionView.contentOffset = contentOffset;
随着滚动视图的移动,获取它的偏移量并将其设置为集合视图的偏移量。
我在这里写了一篇博客,所以请查看此链接以获取更新:http://khanlou.com/2013/04/paging-a-overflowing-collection-view/
【讨论】:
然而,这并没有将选择和点击事件正确地传递到 ios7 的底层集合视图。有什么想法吗? 在 iOS7 上(至少——在此之前可能是真的)你不需要滚动视图。只需检测左右滑动手势并使用它们来设置集合视图 1 页向前/向后的偏移量。 我一辈子都无法让它在 iOS7 上运行。scrollViewDidScroll
的“秘密”滚动视图永远不会交付。
确保您的视图控制器符合 UIScrollView 委托协议并将其设置为“秘密”滚动视图的委托。
最后。我重新审视了这个解决方案,结果发现问题出在动态内容上。集合视图contentSize
在reloadData
之后没有正确更新,因此当视图最初加载时宽度始终为 0。作为一种解决方法(我还不完全理解),我使用_collectionView.collectionViewLayout.collectionViewContentSize
而不是_collectionView.contentSize
来更新秘密滚动视图的内容大小。 ***.com/q/19103333/23643【参考方案2】:
您可以继承 UICollectionViewFlowLayout 并像这样覆盖:
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)contentOffset
withScrollingVelocity:(CGPoint)velocity
NSArray* layoutAttributesArray =
[self layoutAttributesForElementsInRect:self.collectionView.bounds];
if(layoutAttributesArray.count == 0)
return contentOffset;
UICollectionViewLayoutAttributes* candidate =
layoutAttributesArray.firstObject;
for (UICollectionViewLayoutAttributes* layoutAttributes in layoutAttributesArray)
if (layoutAttributes.representedElementCategory != UICollectionElementCategoryCell)
continue;
if((velocity.x > 0.0 && layoutAttributes.center.x > candidate.center.x) ||
(velocity.x <= 0.0 && layoutAttributes.center.x < candidate.center.x))
candidate = layoutAttributes;
return CGPointMake(candidate.center.x - self.collectionView.bounds.size.width * 0.5f, contentOffset.y);
这将根据速度获得下一个或上一个单元格......但是它不会捕捉到当前单元格。
【讨论】:
通过此委托方法为偏移返回不同的CGPoint
效果很好。停止在单元格上的过渡感觉非常顺利。
此外,在转换时将滚动视图设置为 snap 的技巧似乎是将集合视图的 decelerationRate
设置为 UIScrollViewDecelerationRateFast
。为了更快地捕捉,如果您将 decelerationRate
设置为小于 0.990000 的值(这似乎是当前的 UIScrollViewDecelerationRateFast
),它似乎也有点工作。【参考方案3】:
@Mike M's answer 在 Swift 中……
class CenteringFlowLayout: UICollectionViewFlowLayout
override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint
guard let collectionView = collectionView,
let layoutAttributesArray = layoutAttributesForElementsInRect(collectionView.bounds),
var candidate = layoutAttributesArray.first else return proposedContentOffset
layoutAttributesArray.filter($0.representedElementCategory == .Cell ).forEach layoutAttributes in
if (velocity.x > 0 && layoutAttributes.center.x > candidate.center.x) ||
(velocity.x <= 0 && layoutAttributes.center.x < candidate.center.x)
candidate = layoutAttributes
return CGPoint(x: candidate.center.x - collectionView.bounds.width / 2, y: proposedContentOffset.y)
【讨论】:
【参考方案4】:对 Soroush 的回答稍作修改,这对我有用。 我所做的唯一编辑而不是禁用手势:
[_collectionView addGestureRecognizer:_secretScrollView.panGestureRecognizer];
_collectionView.panGestureRecognizer.enabled = NO;
我在 collectionview 上禁用了滚动:
_collectionView.scrollEnabled = NO;
由于禁用手势也禁用了秘密滚动视图手势。
【讨论】:
【参考方案5】:我将添加另一个解决方案。就地捕捉并不完美(不如设置启用分页时那么好,但效果很好)。
我已尝试实施 Soroush 的解决方案,但它对我不起作用。
因为UICollectionView
是UIScrollView
的子类,它会响应一个重要的UIScrollViewDelegate
方法,即:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
targetContentOffset
(inout 指针)可让您重新定义集合视图的停止点(如果您水平滑动,则在本例中为 x
)。
关于以下几个变量的简要说明:
self.cellWidth
– 这是您的集合视图单元格的宽度(如果需要,您甚至可以在那里硬编码)
self.minimumLineSpacing
- 这是您在单元格之间设置的最小行距
self.scrollingObjects
是集合视图中包含的对象数组(我需要这个主要用于计数,以了解何时停止滚动)
因此,想法是在集合视图的委托中实现此方法,如下所示:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
if (self.currentIndex == 0 && velocity.x < 0)
// we have reached the first user and we're trying to go back
return;
if (self.currentIndex == (self.scrollingObjects.count - 1) && velocity.x > 0)
// we have reached the last user and we're trying to go forward
return;
if (velocity.x < 0)
// swiping from left to right (going left; going back)
self.currentIndex--;
else
// swiping from right to left (going right; going forward)
self.currentIndex++;
float xPositionToStop = 0;
if (self.currentIndex == 0)
// first row
xPositionToStop = 0;
else
// one of the next ones
xPositionToStop = self.currentIndex * self.cellWidth + (self.currentIndex + 1) * self.minimumLineSpacing - ((scrollView.bounds.size.width - 2*self.minimumLineSpacing - self.cellWidth)/2);
targetContentOffset->x = xPositionToStop;
NSLog(@"Point of stopping: %@", NSStringFromCGPoint(*targetContentOffset));
期待您提出的任何反馈,这些反馈可以让卡扣效果更好。我也会继续寻找更好的解决方案...
【讨论】:
这似乎可行...但不能精确复制“启用分页”,感觉有点尴尬。【参考方案6】:前面比较复杂,UICollectionView 是 UIScrollView 的子类,所以这样做:
[self.collectionView setPagingEnabled:YES];
你们都准备好了。
看到这个detailed tutorial。
【讨论】:
这不能解决分页时左右显示内容的问题,例如 safari 选项卡或应用商店搜索。以上是关于UICollectionView:像 Safari 选项卡或 App Store 搜索一样分页的主要内容,如果未能解决你的问题,请参考以下文章
tvOS:当它像这样实现时无法滚动 UICollectionView 项目:UITableView 内的 UICollectionView
UICollectionView 像 iOS 主屏幕 [关闭]