![](/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.