Je veux implémenter UICollectionView
qui défile horizontalement et infiniment?
Si vos données sont statiques et que vous souhaitez un comportement circulaire, vous pouvez procéder comme suit:
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
}
En gros, vous dites à votre vue Collection que vous avez un grand nombre de cellules (Int.max ne sera pas infini, mais pourrait faire l'affaire) et vous accédez à votre source de données à l'aide de l'opérateur%. Dans mon exemple, nous allons nous retrouver avec "item 0", "item 1", "item 2", "item 0", "item 1", "item 2" ....
J'espère que ça aide :)
Apparemment, la solution la plus proche de la bonne solution avait été proposée par le Manikanta Adimulam. La solution la plus propre consisterait à ajouter le dernier élément au début de la liste de données et le premier à la dernière position de la liste de données (ex: [4] [0] [1] [2] [3] [4] [ 0]), nous passons donc au premier élément du tableau lorsque nous déclenchons le dernier élément de la liste et inversement. Cela fonctionnera pour les vues de collection avec un élément visible:
Remplacez UICollectionViewDelegate et substituez les méthodes suivantes:
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)
}
}
Remplacez la méthode layoutSubviews () dans votre UICollectionView afin de toujours effectuer un décalage correct pour le premier élément:
override func layoutSubviews() {
super.layoutSubviews()
let numberOfCells = items.count
if numberOfCells > 1 {
if contentOffset.x == 0.0 {
contentOffset = CGPoint(x: cellWidth, y: 0.0)
}
}
}
Ignorez la méthode init et calculez les dimensions de votre cellule:
let layout = self.collectionViewLayout as! UICollectionViewFlowLayout
cellPadding = layout.minimumInteritemSpacing
cellWidth = layout.itemSize.width
Fonctionne comme un charme! Si vous souhaitez obtenir cet effet avec une vue de collection ayant plusieurs éléments visibles, utilisez la solution publiée ici .
J'ai implémenté le défilement infini dans UICollectionView
. Fait le code disponible dans github. Vous pouvez essayer. C'est dans Swift 3.0.
Vous pouvez l'ajouter en utilisant pod. L'utilisation est assez simple. Intialise simplement la InfiniteScrollingBehaviour
comme ci-dessous.
infiniteScrollingBehaviour = InfiniteScrollingBehaviour(withCollectionView: collectionView, andData: Card.dummyCards, delegate: self)
et implémentez la méthode déléguée requise pour renvoyer une UICollectionViewCell
configurée. Un exemple d'implémentation ressemblera à:
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
}
Il ajoutera les éléments de limite de début et de fin appropriés dans votre jeu de données d'origine et ajustera la variable contentOffset
de collectionView.
Dans les méthodes de rappel, cela vous donnera l'index d'un élément dans le jeu de données d'origine.
Pour appliquer cette fonctionnalité de boucle infinie Vous devez disposer d'une disposition collectionView appropriée
Vous devez ajouter le premier élément du tableau au dernier et le dernier élément du tableau au premier.
ex: - array = [1,2,3,4]
tableau de présentation = [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)
}
Pour ceux qui recherchent des vues de collection avec défilement infini et horizontal dont les sources de données sont ajoutées à la fin, ajoutez-la à votre source de données dans scrollViewDidScroll
et appelez reloadData()
sur votre vue de collection. Il maintiendra le décalage de défilement.
Exemple de code ci-dessous. J'utilise ma vue Collection pour un sélecteur de date paginé, dans lequel je charge plus de pages (de mois entiers) lorsque l'utilisateur se trouve à l'extrémité droite (de la seconde à la dernière):
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()
// }
}
EDIT: - Cela ne semble pas fonctionner lorsque vous insérez au début de la source de données.
Code testé
J'ai réalisé cela simplement en répétant cellule x fois. Comme suit,
Déclarez combien de boucles aimeriez-vous avoir
let x = 50
Implémenter numberOfItems
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return myArray.count*x // large scrolling: lets see who can reach the end :p
}
Ajoutez cette fonction utilitaire pour calculer arrayIndex
à partir d'une ligne indexPath
func arrayIndexForRow(_ row : Int) {
return row % myArray.count
}
Implémenter 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
}
Ajouter une fonction utilitaire pour faire défiler jusqu'au milieu de collectionView
à un index donné
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)
}
Cela implique également que vos données sont statiques et que toutes vos cellules UICollectionView
doivent avoir la même taille. J'ai trouvé cette solution prometteuse .
Vous devriez peut-être simplement télécharger l'exemple de projet sur github et l'exécuter vous-même. Le code dans ViewController qui crée la UICollectionView
est assez simple et facile à utiliser.
Vous suivez essentiellement ces étapes:
InfiniteCollectionView
dans le StoryboardinfiniteDataSource
et infiniteDelegate