Paging UICollectionView por celdas, no pantalla

Tengo UICollectionView con desplazamiento horizontal y siempre hay 2 celdas lado a lado por toda la pantalla. Necesito que el desplazamiento se detenga al principio de una celda. Con la pagination habilitada, la vista de colección desplaza toda la página, que es de 2 celdas a la vez, y luego se detiene.

Necesito habilitar el desplazamiento por una sola celda, o desplazarme por varias celdas con detención en el borde de la celda.

Intenté subclass UICollectionViewFlowLayout e implementé el método targetContentOffsetForProposedContentOffset , pero hasta ahora solo pude romper mi vista de colección y dejó de desplazarse. ¿Hay alguna manera más fácil de lograr esto y cómo, o realmente necesito implementar todos los methods de la subclass UICollectionViewFlowLayout ? Gracias.

OK, entonces encontré la solución aquí: targetContentOffsetForProposedContentOffset: withScrollingVelocity sin subclasss UICollectionViewFlowLayout

Debería haber buscado targetContentOffsetForProposedContentOffset al principio.

solo reemplaza el método:

 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { *targetContentOffset = scrollView.contentOffset; // set acceleration to 0.0 float pageWidth = (float)self.articlesCollectionView.bounds.size.width; int minSpace = 10; int cellToSwipe = (scrollView.contentOffset.x)/(pageWidth + minSpace) + 0.5; // cell width + min spacing for lines if (cellToSwipe < 0) { cellToSwipe = 0; } else if (cellToSwipe >= self.articles.count) { cellToSwipe = self.articles.count - 1; } [self.articlesCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:cellToSwipe inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:YES]; } 

Enfoque 1: Vista de colección

flowLayout es propiedad UICollectionViewFlowLayout

 override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { if let collectionView = collectionView { targetContentOffset.memory = scrollView.contentOffset let pageWidth = CGRectGetWidth(scrollView.frame) + flowLayout.minimumInteritemSpacing var assistanceOffset : CGFloat = pageWidth / 3.0 if velocity.x < 0 { assistanceOffset = -assistanceOffset } let assistedScrollPosition = (scrollView.contentOffset.x + assistanceOffset) / pageWidth var targetIndex = Int(round(assistedScrollPosition)) if targetIndex < 0 { targetIndex = 0 } else if targetIndex >= collectionView.numberOfItemsInSection(0) { targetIndex = collectionView.numberOfItemsInSection(0) - 1 } print("targetIndex = \(targetIndex)") let indexPath = NSIndexPath(forItem: targetIndex, inSection: 0) collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .Left, animated: true) } } 

Método 2: controller de vista de página

Podría usar UIPageViewController si cumple con sus requisitos, cada página tendría un controller de vista independiente.

Versión Swift 3 de la respuesta de Evya:

 func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { targetContentOffset.pointee = scrollView.contentOffset let pageWidth:Float = Float(self.view.bounds.width) let minSpace:Float = 10.0 var cellToSwipe:Double = Double(Float((scrollView.contentOffset.x))/Float((pageWidth+minSpace))) + Double(0.5) if cellToSwipe < 0 { cellToSwipe = 0 } else if cellToSwipe >= Double(self.articles.count) { cellToSwipe = Double(self.articles.count) - Double(1) } let indexPath:IndexPath = IndexPath(row: Int(cellToSwipe), section:0) self.collectionView.scrollToItem(at:indexPath, at: UICollectionViewScrollPosition.left, animated: true) } 

Parcialmente basado en la respuesta de Steven Ojo. He probado esto con un desplazamiento horizontal y sin Bounce UICollectionView. cellSize es el tamaño CollectionViewCell. Puede modificar el factor para modificar la sensibilidad de desplazamiento.

 override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { targetContentOffset.pointee = scrollView.contentOffset var factor: CGFloat = 0.5 if velocity.x < 0 { factor = -factor } let indexPath = IndexPath(row: (scrollView.contentOffset.x/cellSize.width + factor).int, section: 0) collectionView?.scrollToItem(at: indexPath, at: .left, animated: true) } 

Aquí está mi versión de Swift 3. Calcula el desplazamiento después de que finalizó el desplazamiento y ajusta el desplazamiento con animation.

collectionLayout es un UICollectionViewFlowLayout()

 func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { let index = scrollView.contentOffset.x / collectionLayout.itemSize.width let fracPart = index.truncatingRemainder(dividingBy: 1) let item= Int(fracPart >= 0.5 ? ceil(index) : floor(index)) let indexPath = IndexPath(item: item, section: 0) collectionView.scrollToItem(at: indexPath, at: .left, animated: true) } 

Algo así como la respuesta de evya, pero un poco más suave porque no establece el targetContentOffset en cero.

 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { if ([scrollView isKindOfClass:[UICollectionView class]]) { UICollectionView* collectionView = (UICollectionView*)scrollView; if ([collectionView.collectionViewLayout isKindOfClass:[UICollectionViewFlowLayout class]]) { UICollectionViewFlowLayout* layout = (UICollectionViewFlowLayout*)collectionView.collectionViewLayout; CGFloat pageWidth = layout.itemSize.width + layout.minimumInteritemSpacing; CGFloat usualSideOverhang = (scrollView.bounds.size.width - pageWidth)/2.0; // k*pageWidth - usualSideOverhang = contentOffset for page at index k if k >= 1, 0 if k = 0 // -> (contentOffset + usualSideOverhang)/pageWidth = k at page stops NSInteger targetPage = 0; CGFloat currentOffsetInPages = (scrollView.contentOffset.x + usualSideOverhang)/pageWidth; targetPage = velocity.x < 0 ? floor(currentOffsetInPages) : ceil(currentOffsetInPages); targetPage = MAX(0,MIN(self.projects.count - 1,targetPage)); *targetContentOffset = CGPointMake(MAX(targetPage*pageWidth - usualSideOverhang,0), 0); } } }