水平および無限にスクロールするUICollectionView
を実装したいですか?
データが静的で、一種の循環動作が必要な場合は、次のようなことができます。
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
}
基本的に、膨大な数のセルがある(Int.maxは無限ではありませんが、トリックを行う可能性がある)とコレクションビューに言い、%演算子を使用してデータソースにアクセスします。私の例では、「アイテム0」、「アイテム1」、「アイテム2」、「アイテム0」、「アイテム1」、「アイテム2」...になります。
これが役立つことを願っています:)
Manikanta Adimulamにより、最も良い解決策が提案されたようです。最もクリーンなソリューションは、データリストの先頭に最後の要素を追加し、最後のデータリストの位置に最初の要素を追加することです(例:[4] [0] [1] [2] [3] [4] [ 0])。したがって、最後のリスト項目をトリガーするときに最初の配列項目までスクロールします。逆の場合も同様です。これは、1つの表示項目があるコレクションビューで機能します。
UICollectionViewDelegateをオーバーライドし、次のメソッドをオーバーライドします。
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)
}
}
UICollectionView内のlayoutSubviews()メソッドをオーバーライドして、常に最初のアイテムの正しいオフセットを作成します。
override func layoutSubviews() {
super.layoutSubviews()
let numberOfCells = items.count
if numberOfCells > 1 {
if contentOffset.x == 0.0 {
contentOffset = CGPoint(x: cellWidth, y: 0.0)
}
}
}
Initメソッドをオーバーライドし、セルの寸法を計算します。
let layout = self.collectionViewLayout as! UICollectionViewFlowLayout
cellPadding = layout.minimumInteritemSpacing
cellWidth = layout.itemSize.width
チャームのように機能します!複数の表示項目があるコレクションビューでこの効果を達成したい場合は、 here に投稿されたソリューションを使用してください。
UICollectionView
に無限スクロールを実装しました。 githubでコードを利用できるようにしました。試してみてください。そのSwift 3.0。
ポッドを使用して追加できます。使い方はとても簡単です。以下のようにInfiniteScrollingBehaviour
を初期化します。
infiniteScrollingBehaviour = InfiniteScrollingBehaviour(withCollectionView: collectionView, andData: Card.dummyCards, delegate: self)
構成されたUICollectionViewCell
を返すために必要なデリゲートメソッドを実装します。実装例は次のようになります。
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
}
元のデータセットに適切な先頭および末尾の境界要素を追加し、collectionViewのcontentOffset
を調整します。
コールバックメソッドでは、元のデータセット内のアイテムのインデックスが提供されます。
この無限ループ機能を適用するには、適切なcollectionViewレイアウトが必要です。
配列の最初の要素を最後に追加し、配列の最後の要素を最初に追加する必要があります
ex:-array = [1,2,3,4]
presenting array = [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)
}
テスト済みコード
これを達成するために、単純にxセルをx回繰り返します。次のように、
ループの数を宣言します
let x = 50
numberOfItems
を実装する
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return myArray.count*x // large scrolling: lets see who can reach the end :p
}
このユーティリティ関数を追加して、indexPath行でarrayIndex
を計算します
func arrayIndexForRow(_ row : Int) {
return row % myArray.count
}
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
}
指定されたインデックスでcollectionView
の中央にスクロールするユーティリティ関数を追加します
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)
}
データソースが最後に追加される無限および水平方向にスクロールするコレクションビューを探している場合-scrollViewDidScroll
でデータソースに追加し、コレクションビューでreloadData()
を呼び出します。スクロールオフセットを維持します。
以下のサンプルコード。ページ分割された日付ピッカーにコレクションビューを使用します。ユーザーが右端(2番目から最後)に向かっているときに(月全体の)より多くのページを読み込みます。
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 [〜#〜]:-データソースの先頭に挿入する場合、これは機能しないようです。
ここで提供される回答は、機能を実装するのに適しています。しかし、私の意見では、それらには回避できる低レベルの更新(コンテンツオフセットの設定、データソースの操作など)が含まれています。まだ満足しておらず、別のアプローチを探している場合は、私がやったことです。
主なアイデアは、最後のセルの前にセルに到達するたびにセルの数を更新することです。アイテムの数を1つ増やすたびに、無限スクロールのように見えます。これを行うには、scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
関数を使用して、ユーザーがスクロールを終了したことを検出し、コレクションビューのアイテムの数を更新します。これを実現するコードスニペットを次に示します。
class InfiniteCarouselView: UICollectionView {
var data: [Any] = []
private var currentIndex: Int?
private var currentMaxItemsCount: Int = 0
// Set up data source and delegate
}
extension InfiniteCarouselView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// Set the current maximum to a number above the maximum count by 1
currentMaxItemsCount = max(((currentIndex ?? 0) + 1), data.count) + 1
return currentMaxItemsCount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
let row = indexPath.row % data.count
let item = data[row]
// Setup cell
return cell
}
}
extension InfiniteCarouselView: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
}
// Detect when the collection view has finished scrolling to increase the number of items in the collection view
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
// Get the current index. Note that the current index calculation will keep changing because the collection view is expanding its content size based on the number of items (currentMaxItemsCount)
currentIndex = Int(scrollView.contentOffset.x/scrollView.contentSize.width * CGFloat(currentMaxItemsCount))
// Reload the collection view to get the new number of items
reloadData()
}
}
また、データが静的であり、すべてのUICollectionView
セルが同じサイズでなければならないことを意味します この有望なソリューション 。
サンプルプロジェクトを github からダウンロードして、自分でプロジェクトを実行する必要があります。 UICollectionView
を作成するViewControllerのコードは非常に単純で使いやすいです。
基本的には次の手順に従います:
InfiniteCollectionView
を作成しますinfiniteDataSource
およびinfiniteDelegate
を設定します