I have to create Grid view like Appstore iOS app. I want to do this with UICollectionView paging. I have also implemented the code but not able to scroll like that.
Wha
I took shtefane's answer and improved on it. Enter your own cellWidth
and cellPadding
values.
- (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;
}
Here's a working swift4 version of Rickster answer:
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)
}
full source is here
this supports RTL (Right to Left)
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
}
}
Have you tried setting the scroll direction of your UICollectionViewFlowLayout to horizontal?
[yourFlowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
You'll need to enable paging on your collection view like so:
[yourCollectionView setPagingEnabled:YES];
If you use pagining in collectionView it will scroll by one page Not one cell. You can disable pagining and implement 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);
}
Only one different from standart paging: If you drag fast Collection will scroll more than one cell. And don't forget to add UIScrollViewDelegate
Ref to Rickster's answer and I rewrite with 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)
}
}