ios swift uiscrollview uicollectionview uiscrollviewdelegate

ios - ¿Cómo implementar UICollectionView de desplazamiento infinito horizontalmente?



swift uiscrollview (7)

  1. Para aplicar esta funcionalidad de bucle infinito, debe tener el diseño adecuado de collectionView

  2. Debe agregar el primer elemento de la matriz al último y el último elemento de la matriz al principio
    Ej: - array = [1,2,3,4]
    presentando matriz = [4,1,2,3,4,1]

    func infinateLoop(scrollView: UIScrollView) { var index = Int((scrollView.contentOffset.x)/(scrollView.frame.width)) guard currentIndex != index else { return } currentIndex = index if index <= 0 { index = images.count - 1 scrollView.setContentOffset(CGPoint(x: (scrollView.frame.width+60) * CGFloat(images.count), y: 0), animated: false) } else if index >= images.count + 1 { index = 0 scrollView.setContentOffset(CGPoint(x: (scrollView.frame.width), y: 0), animated: false) } else { index -= 1 } pageController.currentPage = index } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { infinateLoop(scrollView: scrollView) } func scrollViewDidScroll(_ scrollView: UIScrollView) { infinateLoop(scrollView: scrollView) }

¿Quiero implementar UICollectionView que se UICollectionView horizontal e infinitamente?


Aparentemente, el Manikanta Adimulam propuso la solución más cercana a la buena. La solución más limpia sería agregar el último elemento al principio de la lista de datos y el primero a la última posición de la lista de datos (por ejemplo: [4] [0] [1] [2] [3] [4] [ 0]), por lo que nos desplazamos al primer elemento de la matriz cuando activamos el último elemento de la lista y viceversa. Esto funcionará para vistas de colección con un elemento visible:

  1. Subclase UICollectionView.
  2. Reemplace UICollectionViewDelegate y anule los siguientes métodos:

    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { let numberOfCells = items.count let page = Int(scrollView.contentOffset.x) / Int(cellWidth) if page == 0 { // we are within the fake last, so delegate real last currentPage = numberOfCells - 1 } else if page == numberOfCells - 1 { // we are within the fake first, so delegate the real first currentPage = 0 } else { // real page is always fake minus one currentPage = page - 1 } // if you need to know changed position, you can delegate it customDelegate?.pageChanged(currentPage) } public func scrollViewDidScroll(_ scrollView: UIScrollView) { let numberOfCells = items.count if numberOfCells == 1 { return } let regularContentOffset = cellWidth * CGFloat(numberOfCells - 2) if (scrollView.contentOffset.x >= cellWidth * CGFloat(numberOfCells - 1)) { scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x - regularContentOffset, y: 0.0) } else if (scrollView.contentOffset.x < cellWidth) { scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x + regularContentOffset, y: 0.0) } }

  3. Reemplace el método layoutSubviews () dentro de su UICollectionView para poder realizar siempre un desplazamiento correcto para el primer elemento:

    override func layoutSubviews() { super.layoutSubviews() let numberOfCells = items.count if numberOfCells > 1 { if contentOffset.x == 0.0 { contentOffset = CGPoint(x: cellWidth, y: 0.0) } } }

  4. Reemplace el método init y calcule las dimensiones de su celda:

    let layout = self.collectionViewLayout as! UICollectionViewFlowLayout cellPadding = layout.minimumInteritemSpacing cellWidth = layout.itemSize.width

¡Funciona de maravilla! Si desea lograr este efecto con la vista de colección con varios elementos visibles, utilice la solución publicada here .


He implementado el desplazamiento infinito en UICollectionView . Hecho el código disponible en github. Puedes darle una oportunidad. Está en veloz 3.0.

InfiniteScrolling

Puedes agregarlo usando pod. El uso es bastante simple. Solo inicie el InfiniteScrollingBehaviour como se muestra abajo.

infiniteScrollingBehaviour = InfiniteScrollingBehaviour(withCollectionView: collectionView, andData: Card.dummyCards, delegate: self)

e implemente el método de delegado requerido para devolver un UICollectionViewCell configurado. Una implementación de ejemplo se verá así:

func configuredCell(forItemAtIndexPath indexPath: IndexPath, originalIndex: Int, andData data: InfiniteScollingData, forInfiniteScrollingBehaviour behaviour: InfiniteScrollingBehaviour) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CellID", for: indexPath) if let collectionCell = cell as? CollectionViewCell, let card = data as? Card { collectionCell.titleLabel.text = card.name } return cell }

Agregará elementos de límite contentOffset y finales apropiados en su conjunto de datos original y ajustará el contentOffset de contentOffset .

En los métodos de devolución de llamada, le dará un índice de un elemento en el conjunto de datos original.


Para aquellos que buscan vistas de colección con desplazamiento infinito y horizontal cuyos orígenes de datos se agregan al final, agregue a su fuente de datos en scrollViewDidScroll y llame a reloadData() en su vista de colección. Se mantendrá el desplazamiento de desplazamiento.

Código de muestra a continuación. Utilizo mi vista de colección para un selector de fecha paginado, donde cargo más páginas (de meses completos) cuando el usuario está en el extremo derecho (segundo al último):

func scrollViewDidScroll(scrollView: UIScrollView) { let currentPage = self.customView.collectionView.contentOffset.x / self.customView.collectionView.bounds.size.width if currentPage > CGFloat(self.months.count - 2) { let nextMonths = self.generateMonthsFromDate(self.months[self.months.count - 1], forPageDirection: .Next) self.months.appendContentsOf(nextMonths) self.customView.collectionView.reloadData() } // DOESN''T WORK - adding more months to the left // if currentPage < 2 { // let previousMonths = self.generateMonthsFromDate(self.months[0], forPageDirection: .Previous) // self.months.insertContentsOf(previousMonths, at: 0) // self.customView.collectionView.reloadData() // } }

EDITAR : - Esto no parece funcionar cuando se inserta al principio de la fuente de datos.


Si sus datos son estáticos y desea un tipo de comportamiento circular, puede hacer algo como esto:

var dataSource = ["item 0", "item 1", "item 2"] func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return Int.max // instead of returnin dataSource.count } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let itemToShow = dataSource[indexPath.row % dataSource.count] let cell = UICollectionViewCell() // setup cell with your item and return return cell }

Básicamente, usted le dice a su vista de colección que tiene una gran cantidad de celdas (Int.max no será infinito, pero podría hacer el truco), y accede a su fuente de datos usando el operador%. En mi ejemplo, terminaremos con "artículo 0", "artículo 1", "artículo 2", "artículo 0", "artículo 1", "artículo 2" ....

Espero que esto ayude :)


También implicando que sus datos son estáticos y que todas sus celdas UICollectionView deben tener el mismo tamaño, encontré here .

Tal vez debería simplemente descargar el proyecto de ejemplo en github y ejecutar el proyecto usted mismo. El código en el ViewController que crea el UICollectionView es bastante sencillo y fácil de usar.

Básicamente sigues estos pasos:

  • Crear un InfiniteCollectionView en Storyboard
  • Establecer infiniteDataSource y infiniteDelegate
  • Implementa las funciones necesarias que crean tus celdas de desplazamiento infinito.

Código probado

Logré esto simplemente repitiendo la celda por x cantidad de veces. Como siguiendo,

Declara cuántos bucles te gustaría tener

let x = 50

Implementar numberOfItems

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return myArray.count*x // large scrolling: lets see who can reach the end :p }

Agregue esta función de utilidad para calcular arrayIndex dada una fila indexPath

func arrayIndexForRow(_ row : Int) { return row % myArray.count }

Implementar cellForItem

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myIdentifier", for: indexPath) as! MyCustomCell let arrayIndex = arrayIndexForRow(indexPath.row) let modelObject = myArray[arrayIndex] // configure cell return cell }

Agregue la función de utilidad para desplazarse hasta el centro de collectionView en el índice dado

func scrollToMiddle(atIndex: Int, animated: Bool = true) { let middleIndex = atIndex + x*yourArray.count/2 collectionView.scrollToItem(at: IndexPath(item: middleIndex, section: 0), at: .centeredHorizontally, animated: animated) }