Ios 在UICollectionView(或UITableView)swift中循环数据
我正在尝试创建一个将无限滚动的Ios 在UICollectionView(或UITableView)swift中循环数据,ios,swift,uitableview,uicollectionview,Ios,Swift,Uitableview,Uicollectionview,我正在尝试创建一个将无限滚动的UICollectionView。这个想法是,当您到达数据数组的底部时,它会重新开始 我是通过为numberOfItemsInSection返回一个更大的数字,然后执行%来从数组中获取数据 这很好,我理解: override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 500 } o
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
) 有趣的问题!这是一个不错的方法,但缺点是滚动条指示器的大小将非常小
您可以将项目数设置为实际项目数的两倍,然后在用户滚动到下半部分(滚动停止)后,重新调整数据偏移量和滚动位置,然后重新加载。用户将看不到任何变化,但一旦他们再次滚动,滚动条的位置似乎已经在顶部附近跳了起来。我喜欢这一点,因为滚动指示器的大小将保持合理,用户实际上会得到一些视觉反馈,他们滚动到最后,现在正在重复。您的代码是最简单的解决方案。而且在大多数情况下,它将非常适合。如果您想实现诚实的无限滚动,您应该创建自己的布局和单元格缓存。您可以找到更多详细信息 资料来源:
你所拥有的一切都很好。另一个选项是构建一个集合,该集合包装数据源阵列(
照片
),并提供对其内容的循环访问:
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)]
}
}
然后,您可以最终将集合视图方法转换为更通用的循环集合,或直接使用循环集合:
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]
}
你需要的是两件事:
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,但可以根据需要随意调整
现在,在我们的cellforrowatinexpath
中,我们可以从技术上调用函数来确定是否可以附加数据集
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中的
LoadNextPageiFneed()
函数调用它,您的意思是当我到达列表末尾(而不是滚动)时重置contentOffset
有效地将它们移回列表顶部。正确吗?假设一个数据集的高度为1000。当用户在1200停止滚动时,然后在没有动画的情况下将contentOffset静默重置为200。视图应与之前完全相同,只是当用户再次滚动时,他将看到滚动指示器已“向上”移动。根据您表格的大小,您可以进行2x、4x、10x等操作。用户当然可以通过持续不断地向上滑动来“愚弄”这个算法,从而将scrollview强制到底部单元格,但我怀疑这并不是什么现实问题。顺便说一句,您可以隐藏scroller旋钮。@DavidH,你能帮我解决这个问题吗?我去年尝试过这个方法,但我所能实现的大部分都是由于重新绘制表格而导致的视觉故障,我最终使用了一个类似这样的第三方库:我和你的代码完全一样。这是最简单的解决方案,视觉效果也很好。这是一个有趣的想法。但我想它仍然不能解决如何提供无限卷轴的问题。可能需要监视滚动,然后在用户接近底部时插入行…这正是我所需要的。你真棒@DanielGalasko更重要的是,您需要在循环集合的“中间”启动collectionview,然后在用户滚动太远时重置回该起始点。例如,对于UIPicker来说,这并不太糟糕,但是集合视图要复杂得多-您需要处理许多不同的状态。如果它是循环集合,模运算不起作用吗?那么他们所需要做的就是跟踪整体尺寸?@NateCook,兄弟,你能帮我解决这个问题吗?这是一个很好的答案,但问题是即使在加载了第一批完整的数据之后,内存占用也会持续增长。你是否重复添加self.pictures。在我们继续向下滚动时,将batches的内容添加到self.pictures
。一旦我们加载了所有图片并将它们添加到self.pictures
中,我们就不想再添加了。作为一个切线,你在这里的回答是史诗般的,拯救了我的屁股:不@random,我们只在达到页面70%时才添加到数组中。该代码片段反映了我在生产应用程序中使用的内容。调用PerformBatchUpdate后,内容大小会增加。我认为这是将循环列表与分页数据的自动加载混为一谈。两者都可以让你永远滚动,但OP寻找的是一组循环回到顶部的有限数据,而不是一种滚动(实际上是)无限数据集的方式。这是一个极好的答案和很棒的帖子,但@natecooks answer more有助于解决我的问题。@MeghsDhameliya,兄弟,你能帮我解决问题吗?@MayPhyu结帐回答并让我知道是的兄弟@MeghsDhameliya。谢谢兄弟。我会试试你的建议。@MeghsDhameliya,兄弟,我还有一个问题。你能看看那个问题吗?
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 {
}