我必须创建像 Appstore iOS 应用程序一样的网格视图。我想用 UICollectionView 分页来做到这一点。我也实现了代码,但无法像那样滚动。
我想要做的是在中心和两侧(左侧和右侧)都有一张图像,它应该显示上一张和下一张图像的某些部分。我已将 UICollectionView 的 Frame 设置为 320*320。单元格大小为290*320。(单元格最小间距为10)1
下面是两个描述我的要求的链接。预先感谢。
(这就是我想要的)2
您是否尝试过将 UICollectionViewFlowLayout 的滚动方向设置为水平?
[yourFlowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
您需要在集合视图上启用分页,如下所示:
[yourCollectionView setPagingEnabled:YES];
我采纳了shtefane的答案并对其进行了改进。 输入您自己的
cellWidth
和 cellPadding
值。
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
{
CGFloat cellWidth = self.cellWidth;
CGFloat cellPadding = 9;
NSInteger page = (scrollView.contentOffset.x - cellWidth / 2) / (cellWidth + cellPadding) + 1;
if (velocity.x > 0) page++;
if (velocity.x < 0) page--;
page = MAX(page,0);
CGFloat newOffset = page * (cellWidth + cellPadding);
targetContentOffset->x = newOffset;
}
如果您在 collectionView 中使用分页,它将滚动一页而不是一个单元格。您可以禁用分页并实现 ScrollViewDelegate
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
CGFloat pageW = 300;
int page = scrollView.contentOffset.x / pageW;
CGFloat newOffset =(page + ((velocity.x > 0)? 1 : -1)) * (pageW - 20);
CGPoint target = CGPointMake(newOffset, 0);
targetContentOffset = ⌖
NSLog(@"end Drag at %f /%i /%f",scrollView.contentOffset.x, page, velocity.x);
}
与标准分页只有一处不同:如果快速拖动,集合将滚动多个单元格。 并且不要忘记添加 UIScrollViewDelegate
只需使用collectionView.isPagingEnabled = true;
这是 Rickster 答案的 swift4 版本:
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let cellWidth = 174 as CGFloat
let cellPadding = 10 as CGFloat
var page = (scrollView.contentOffset.x - cellWidth / 2) / (cellWidth + cellPadding) + 1
if (velocity.x > 0) { page += 1 }
if (velocity.x < 0) { page -= 1 }
page = max(page,0)
targetContentOffset.pointee.x = page * (cellWidth + cellPadding)
}
参考 Rickster 的答案,我用 Swift 4 重写:
/* paging */
extension AlbumViewController {
/* In case the user scrolls for a long swipe, the scroll view should animate to the nearest page when the scrollview decelerated. */
override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
scrollToPage(scrollView, withVelocity: CGPoint(x:0, y:0))
}
override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
scrollToPage(scrollView, withVelocity: velocity)
}
func scrollToPage(_ scrollView: UIScrollView, withVelocity velocity: CGPoint) {
let cellWidth: CGFloat = cellSize
let cellPadding: CGFloat = 10
var page: Int = Int((scrollView.contentOffset.x - cellWidth / 2) / (cellWidth + cellPadding) + 1)
if velocity.x > 0 {
page += 1
}
if velocity.x < 0 {
page -= 1
}
page = max(page, 0)
let newOffset: CGFloat = CGFloat(page) * (cellWidth + cellPadding)
scrollView.setContentOffset(CGPoint(x:newOffset, y:0), animated: true)
}
}
完整源代码在这里
这支持 RTL(从右到左)
collectionView.decelerationRate = .fast
// paging
extension ViewController: UIScrollViewDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.dragStartPoint = scrollView.contentOffset
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let isRTL = UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft
let pageWidth = UIScreen.main.bounds.size.width - ViewController.left - ViewController.right + ViewController.lineSpacing
if scrollView.contentOffset.x == targetContentOffset.pointee.x { // no decelerate
if fabsf(Float(self.dragStartPoint.x - scrollView.contentOffset.x)) > 40 { // min move distance = 40
let dragLeft = self.dragStartPoint.x < scrollView.contentOffset.x
if dragLeft {
self.currentPage = isRTL ? self.currentPage - 1 : self.currentPage + 1
} else {
self.currentPage = isRTL ? self.currentPage + 1 : self.currentPage - 1
}
}
} else if scrollView.contentOffset.x > targetContentOffset.pointee.x {
let maxRight = scrollView.contentSize.width - UIScreen.main.bounds.size.width
if scrollView.contentOffset.x <= maxRight { // not right bounce
self.currentPage = isRTL ? self.currentPage + 1 : self.currentPage - 1
}
} else {
if scrollView.contentOffset.x >= 0 { // not left bounce
self.currentPage = isRTL ? self.currentPage - 1 : self.currentPage + 1
}
}
self.currentPage = max(0, self.currentPage)
self.currentPage = min(self.numberOfPages - 1, self.currentPage)
var offset = targetContentOffset.pointee
if isRTL {
offset.x = CGFloat(self.numberOfPages - self.currentPage - 1) * pageWidth
} else {
offset.x = CGFloat(self.currentPage) * pageWidth
}
targetContentOffset.pointee = offset
}
}