内容简介:在大部分 App 中,在有 feeds 流之类列表的地方,由于后端数据一般采用分页加载,为了用户体验需要做预加载。最简单的加载方式,就是当列表显示的内容达到一定的数量时候,自动请求下一个分页。而这其实就是根据总行数,列表总高度,列表当前偏移值这三个数字决定是否要加载的关系式 fx。这里判断加载的策略,是需要自定义的,所以可以定义这样一个 Protocol。下面给出几种简单的加载策略。
在大部分 App 中,在有 feeds 流之类列表的地方,由于后端数据一般采用分页加载,为了用户体验需要做预加载。最简单的加载方式,就是当列表显示的内容达到一定的数量时候,自动请求下一个分页。
加载策略
而这其实就是根据总行数,列表总高度,列表当前偏移值这三个数字决定是否要加载的关系式 fx。这里判断加载的策略,是需要自定义的,所以可以定义这样一个 Protocol。
protocol ListPrefetcherStrategy {
var totalRowsCount:Int { get set }
func shouldFetch(_ totalHeight:CGFloat, _ offsetY:CGFloat) -> Bool
}
复制代码
下面给出几种简单的加载策略。
阈值策略
设定一个阈值,比如 70%,显示内容达到阈值时进行加载。这种比较时候每一页的数量一致的情况。
同时要注意的是,这里的阈值应该是每个分页的阈值,总的阈值会随着列表长度增长。比如设置阈值为 70%,每页加载 10 个,第一页在加载到 7 个时进行预加载,第二页在第 17 个时进行预加载,此时阈值为 85%,而如果还是 70%,则会在第 14 个时进行预加载。所以这里的阈值需要动态增长。
假设我们已知目前列表的数据量和目前页数,根据每一页的阈值就可以动态计算总阈值:
// 数据总数除以当前页数,算出每一页的数量 let perPageCount = Double(totalRowsCount) / Double(currentPageIndex + 1) // 每页数量乘以页数加上每一页的阈值的和,就是总共需要的数量 let needRowsCount = perPageCount * (Double(currentPageIndex) + threshold) // 算出动态的阈值 let actalThreshold = needRowsCount / Double(totalRowsCount) 复制代码
这里需要记录当前的页数,笔者这里用了一个比较 trick 的做法,当行数增长时,则认为页数 +1,行数减少时,则认为页数归 0,适用于下拉刷新整个列表清空的情况。可以用属性观察 willSet 来改变页数。
struct ThresholdStrategy: ListPrefetcherStrategy{
func shouldFetch(_ totalHeight: CGFloat, _ offsetY: CGFloat) -> Bool {
let viewRatio = Double(offsetY / totalHeight)
let perPageCount = Double(totalRowsCount) / Double(currentPageIndex + 1)
let needRowsCount = perPageCount * (Double(currentPageIndex) + threshold)
let actalThreshold = needRowsCount / Double(totalRowsCount)
if viewRatio >= actalThreshold {
return true
} else {
return false
}
}
var totalRowsCount: Int{
willSet{
if newValue > totalRowsCount {
currentPageIndex += 1
} else if newValue < totalRowsCount {
currentPageIndex = 0
}
}
}
let threshold: Double
var currentPageIndex = 0
public init(threshold:Double = 0.7) {
self.threshold = threshold
totalRowsCount = 0
}
}
复制代码
剩余策略
也可以设定当列表剩余未展示行数即将少于某个值时,进行加载。这种适合每次分页数量不一定一致的情况。
struct RemainStrategy: ListPrefetcherStrategy{
func shouldFetch(_ totalHeight: CGFloat, _ offsetY: CGFloat) -> Bool {
let rowHeight = totalHeight / CGFloat(totalRowsCount)
let needOffsetY = rowHeight * CGFloat(totalRowsCount - remainRowsCount)
if offsetY > needOffsetY {
return true
} else {
return false
}
}
var totalRowsCount: Int
let remainRowsCount: Int
init(remainRowsCount:Int = 1) {
self.remainRowsCount = remainRowsCount
totalRowsCount = 0
}
}
复制代码
除法策略
还可以自己定义除数和余数,当达到余数时,进行加载。当然还要考虑一下实际余数比指定余数小的情况,这里笔者简单的往前面偏移一个除数的量进行判断。
struct OffsetStrategy: ListPrefetcherStrategy {
func shouldFetch(_ totalHeight: CGFloat, _ offsetY: CGFloat) -> Bool {
let rowHeight = totalHeight / CGFloat(totalRowsCount)
let actalOffset = totalRowsCount % gap
let needOffsetY = actalOffset > offset ? totalHeight - CGFloat(actalOffset - offset) * rowHeight : totalHeight - CGFloat(2 * gap + offset) * rowHeight
if offsetY > needOffsetY {
return true
} else {
return false
}
}
var totalRowsCount: Int
let gap: Int
let offset: Int
init(gap:Int, offset:Int) {
self.gap = gap
self.offset = offset
totalRowsCount = 0
}
}
复制代码
预加载组件
组件需要的信息有,scrollView,总行数,以及加载时候的通知外界。
定义一个 delegate 让外界遵循。
protocol ListPrefetcherDelegate:AnyObject {
var totalRowsCount:Int { get }
func startFetch()
}
复制代码
然后用 KVO 监听 scrollView 的 contentSize,当发生变化是,就认为总行数发生改变,就可以将总行数设置给策略。监听 scrollView 的 contentOffset,变化时就是列表滚动,就可以用策略进行判断。
class ListPrefetcher:NSObject{
@objc let scrollView:UIScrollView
var contentSizeObserver:NSKeyValueObservation?
var contentOffsetObserver:NSKeyValueObservation?
weak var delegate: ListPrefetcherDelegate?
var strategy: ListPrefetcherStrategy
public func start() {
contentSizeObserver = observe(\.scrollView.contentSize) { (_, _) in
guard let delegate = self.delegate else { return }
self.strategy.totalRowsCount = delegate.totalRowsCount
}
contentOffsetObserver = observe(\.scrollView.contentOffset){ (_, _) in
let offsetY = self.scrollView.contentOffset.y + self.scrollView.frame.height
let totalHeight = self.scrollView.contentSize.height
guard offsetY < totalHeight else { return }
if self.strategy.shouldFetch(totalHeight, offsetY) {
self.delegate?.startFetch()
}
}
}
public func stop() {
contentSizeObserver?.invalidate()
contentOffsetObserver?.invalidate()
}
public init(strategy:ListPrefetcherStrategy, scrollView:UIScrollView) {
self.strategy = strategy
self.scrollView = scrollView
}
}
复制代码
这样外界使用起来只需要提供策略和 scrollView,实现 delegate 的方法,然后在需要的时候 start 和 stop,就可以自动完成预加载的工作了。
以上所述就是小编给大家介绍的《iOS 实现简单的列表预加载》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 手把手实现图片懒加载+封装vue懒加载组件
- 如何实现 Bitmap 的高效加载?
- 【译】预加载视频实现快速播放
- [回炉计划]-实现一个图片预加载
- 原生js如何实现懒加载?
- 使用IntersectionObserver实现图片懒加载
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PHP高级程序设计
Kevin McArthur / 汪泳 等 / 人民邮电出版社出版 / 2009.7 / 45.00元
今天,PHP已经是无可争议的Web开发主流语言。PHP 5以后,它的面向对象特性也足以与Java和C#相抗衡。然而,讲述PHP高级特性的资料一直缺乏,大大影响了PHP语言的深入应用。 本书填补了这一空白。它专门针对有一定经验的PHP程序员,详细讲解了对他们最为重要的主题:高级面向对象、设计模式、文档、测试和标准PHP库等内容。同时,为适应目前Web开发的新趋势,作者还全面探讨了MVC架构和Z......一起来看看 《PHP高级程序设计》 这本书的介绍吧!
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
Markdown 在线编辑器
Markdown 在线编辑器