![](/img/trans.png)
[英]Same data is repeating in sections in UITableView and UICollectionView in swift 4
[英]Looping data in UICollectionView (or UITableView) swift
我正在嘗試創建一個無限滾動的UICollectionView
。 想法是,當你到達底部的數據陣列時,它重新開始。
我這樣做是通過為numberOfItemsInSection
返回一個更大的數字,然后執行%
來從數組中獲取數據。
這很好,我理解:
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 500
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath) as! PhotoCell
let index = indexPath.item % photos.count
let url = photos[index]
}
我的問題是,這是實現此功能的最佳方式嗎? 我一直在網上無休止地環顧四周,找不到任何關於如何做的其他建議(使用UICollectionView
)。
你有什么是完美的。 另一個選擇是構建一個包裝數據源數組( photos
)的集合,並提供對其內容的循環訪問:
struct LoopedCollection<Element>: CollectionType {
let _base: AnyRandomAccessCollection<Element>
/// Creates a new LoopedCollection that wraps the given collection.
init<Base: CollectionType where Base.Index: RandomAccessIndexType, Base.Generator.Element == Element>(_ base: Base, withLength length: Int = Int.max) {
self._base = AnyRandomAccessCollection(base)
self.endIndex = length
}
/// The midpoint of this LoopedCollection, adjusted to match up with
/// the start of the base collection.
var startAlignedMidpoint: Int {
let mid = endIndex / 2
return mid - mid % numericCast(_base.count)
}
// MARK: CollectionType
let startIndex: Int = 0
let endIndex: Int
subscript(index: Int) -> Element {
precondition(index >= 0, "Index must not be negative.")
let adjustedIndex = numericCast(index) % _base.count
return _base[_base.startIndex.advancedBy(adjustedIndex)]
}
}
您可以在photos
數組旁邊聲明此循環集合:
let photos: [NSURL] = ...
lazy var loopedPhotos: LoopedCollection<NSURL> = LoopedCollection(self.photos)
然后,您最終可以將集合視圖方法轉換為在循環集合上更通用,或直接使用循環集合:
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return loopedPhotos.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath) as! PhotoCell
let url = loopedPhotos[index]
}
有趣的問題! 不是壞方法,但缺點是滾動條指示器的大小將非常小。
您可以將項目數設置為實際項目數的兩倍,然后一旦用戶滾動到后半部分(並且滾動停止),重新調整數據偏移和滾動位置,然后重新加載。 用戶看不到任何變化,但一旦他們再次滾動,滾動器位置似乎已經跳到頂部附近。 我喜歡這個,因為滾動指示器的大小將保持合理,並且用戶實際上會得到一些視覺反饋,他們滾動到結束並且現在正在重復。
您的代碼是最簡單的解決方案。 在大多數情況下,它完全適合。 如果你想實現誠實的無限滾動,你應該創建自己的布局和單元格緩存。
你需要的是兩件事:
要做1是相當簡單的,我們可以簡單地擴展UIScrollView:
extension UIScrollView {
/**
A convenience method that returns true when the scrollView is near to the bottom
- returns: true when the current contentOffset is close enough to the bottom to merit initiating a load for the next page
*/
func canStartLoadingNextPage() -> Bool {
//make sure that we have content and the scrollable area is at least larger than the scroll views bounds.
if contentOffset.y > 0 && contentSize.height > 0 && (contentOffset.y + CGRectGetHeight(bounds))/contentSize.height > 0.7
return true
}
return false
}
}
當您達到當前內容大小的70%時,此函數將返回true,但可以根據需要隨意調整。
現在在我們的cellForRowAtIndexPath
我們可以從技術上調用我們的函數來確定我們是否可以追加我們的數據集。
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("niceReuseIdentifierName", forIndexPath: indexPath)
if collectionView.canStartLoadingNextPage() {
//now we can append our data
self.loadNextPage()
}
return cell
}
func loadNextPage() {
self.collectionView.performBatchUpdates({ () -> Void in
let nextBatch = self.pictures//or however you get the next batch
self.pictures.appendContentsOf(nextBatch)
self.collectionView.reloadSections(NSIndexSet(index: 0))
}, completion: nil)
}
而瞧,你現在應該有無限卷軸。
要改進此代碼,您可以擁有一個可以促進任何UIScrollView子類的預加載的對象。 這也可以更容易地轉換為網絡調用和更復雜的邏輯:
class ScrollViewPreloader {
enum View {
case TableView(tableView: UITableView)
case CollectionView(collectionView: UICollectionView)
}
private (set) var view: View
private (set) var pictures: [Picture] = []
init(view: View) {
self.view = view
}
func loadNextPageIfNeeded() {
func shouldLoadNextPage(scrollView: UIScrollView) -> Bool {
return scrollView.canStartLoadingNextPage()
}
switch self.view {
case .TableView(tableView: let tableView):
if shouldLoadNextPage(tableView) {
loadNextPageForTableView(tableView)
}
break
case .CollectionView(collectionView: let collectionView):
if shouldLoadNextPage(collectionView) {
loadNextPageForCollectionView(collectionView)
}
break
}
}
private func loadNextBatchOfPictures() -> [Picture] {
let nextBatch = self.pictures//or however you get the next batch
self.pictures.appendContentsOf(nextBatch)
return self.pictures
}
private func loadNextPageForTableView(tableView: UITableView) {
tableView.beginUpdates()
loadNextBatchOfPictures()
tableView.reloadData()//or you could call insert functions etc
tableView.endUpdates()
}
private func loadNextPageForCollectionView(collectionView: UICollectionView) {
collectionView.performBatchUpdates({ () -> Void in
self.loadNextBatchOfPictures()
collectionView.reloadSections(NSIndexSet(index: 0))
}, completion: nil)
}
}
struct Picture {
}
你可以使用cellForRow中的loadNextPageIfNeeded()
函數調用它
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.