使用 Firebase Firestore 进行分页 - swift 4
Posted
技术标签:
【中文标题】使用 Firebase Firestore 进行分页 - swift 4【英文标题】:Pagination with Firebase firestore - swift 4 【发布时间】:2019-02-11 00:12:04 【问题描述】:我正在尝试使用 Firestore 对数据进行分页(无限滚动我的表格视图)。我已尽我所能集成了谷歌为分页提供的代码,但我仍然无法正确加载数据。
初始数据集根据需要加载到 tableview 中。然后,当用户点击屏幕底部时,下一个“x”数量的项目被加载。但是当用户第二次点击屏幕底部时,相同的“x”项目被简单地附加到表格中看法。相同的项目会无限期地添加。
所以它最初的 3 个“骑行”对象,接下来的 4 个“骑行”对象永远重复。
123 4567 4567 4567 4567...
如何让数据正确加载?
func scrollViewDidScroll(_ scrollView: UIScrollView)
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
if offsetY > contentHeight - scrollView.frame.height
// Bottom of the screen is reached
if !fetchingMore
beginBatchFetch()
func beginBatchFetch()
// Array containing "Ride" objcets is "rides"
fetchingMore = true
// Database reference to "rides" collection
let ridesRef = db.collection("rides")
let first = ridesRef.limit(to: 3)
first.addSnapshotListener (snapshot, err) in
if let snapshot = snapshot
// Snapshot isn't nil
if self.rides.isEmpty
// rides array is empty (initial data needs to be loaded in).
let initialRides = snapshot.documents.compactMap(Ride(dictionary: $0.data()))
self.rides.append(contentsOf: initialRides)
self.fetchingMore = false
DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute:
self.tableView.reloadData()
)
print("first rides loaded in")
else
// Error
print("Error retreiving rides: \(err.debugDescription)")
return
// reference to lastSnapshot
guard let lastSnapshot = snapshot!.documents.last else
// The collection is empty
return
let next = ridesRef.limit(to: 4).start(afterDocument: lastSnapshot)
next.addSnapshotListener( (snapshot, err) in
if let snapshot = snapshot
if !self.rides.isEmpty
let newRides = snapshot.documents.compactMap(Ride(dictionary: $0.data()))
self.rides.append(contentsOf: newRides)
self.fetchingMore = false
DispatchQueue.main.asyncAfter(deadline: .now() + 7, execute:
self.tableView.reloadData()
)
print("new items")
return
else
print("Error retreiving rides: \(err.debugDescription)")
return
)
【问题讨论】:
【参考方案1】:这就是我想出的解决方案!该解决方案很可能会多次调用 firestore,从而为任何实际项目创建大笔账单,但我猜你可以说它是一个概念证明。
如果您有任何建议或修改,请随时分享!
所有变量的初始化方式如下:
var rides = [Ride]()
var lastDocumentSnapshot: DocumentSnapshot!
var fetchingMore = false
如果您有任何建议或修改,请随时分享!
func scrollViewDidScroll(_ scrollView: UIScrollView)
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
//print("offsetY: \(offsetY) | contHeight-scrollViewHeight: \(contentHeight-scrollView.frame.height)")
if offsetY > contentHeight - scrollView.frame.height - 50
// Bottom of the screen is reached
if !fetchingMore
paginateData()
// Paginates data
func paginateData()
fetchingMore = true
var query: Query!
if rides.isEmpty
query = db.collection("rides").order(by: "price").limit(to: 6)
print("First 6 rides loaded")
else
query = db.collection("rides").order(by: "price").start(afterDocument: lastDocumentSnapshot).limit(to: 4)
print("Next 4 rides loaded")
query.getDocuments (snapshot, err) in
if let err = err
print("\(err.localizedDescription)")
else if snapshot!.isEmpty
self.fetchingMore = false
return
else
let newRides = snapshot!.documents.compactMap(Ride(dictionary: $0.data()))
self.rides.append(contentsOf: newRides)
//
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute:
self.tableView.reloadData()
self.fetchingMore = false
)
self.lastDocumentSnapshot = snapshot!.documents.last
【讨论】:
【参考方案2】:我的解决方案类似于@yambo,但是,我尽量避免对数据库进行额外调用。在第一次调用数据库后,我得到了 10 个对象,当该加载新页面时,我保留了多少对象的引用,并检查了 count + 9 是否在我的新计数范围内。
@objc func LoadMore()
let oldCount = self.uploads.count
guard shouldLoadMore else return
self.db.getNextPage (result) in
switch result
case .failure(let err):
print(err)
case .success(let newPosts):
self.uploads.insert(contentsOf: newPosts, at: self.uploads.count)
if oldCount...oldCount+9 ~= self.uploads.count
self.shouldLoadMore = false
DispatchQueue.main.async
self.uploadsView.collectionView.reloadData()
【讨论】:
【参考方案3】:游戏有点晚了,但我想分享一下我是如何做到的,使用 query.start(afterDocument:)
方法。
class PostsController: UITableViewController
let db = Firestore.firestore()
var query: Query!
var documents = [QueryDocumentSnapshot]()
var postArray = [Post]()
override func viewDidLoad()
super.viewDidLoad()
query = db.collection("myCollection")
.order(by: "post", descending: false)
.limit(to: 15)
getData()
func getData()
query.getDocuments() (querySnapshot, err) in
if let err = err
print("Error getting documents: \(err)")
else
querySnapshot!.documents.forEach( (document) in
let data = document.data() as [String: AnyObject]
//Setup your data model
let postItem = Post(post: post, id: id)
self.postArray += [postItem]
self.documents += [document]
)
self.tableView.reloadData()
func paginate()
//This line is the main pagination code.
//Firestore allows you to fetch document from the last queryDocument
query = query.start(afterDocument: documents.last!)
getData()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return postArray.count
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
// Trigger pagination when scrolled to last cell
// Feel free to adjust when you want pagination to be triggered
if (indexPath.row == postArray.count - 1)
paginate()
结果如下:
这是reference。
【讨论】:
我见过的手动 UITableView 分页的最佳答案。 这是一个很好的答案。我遇到了此处使用的 WillDisplay 的问题,因此我选择使用 scrollViewDidScroll(例如:***.com/a/41255637/9339880),并添加一个小延迟以避免底部检测重复,但这是一个很好的基础。【参考方案4】:简单、快速、简单的方法是......
类 FeedViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, FeedCellDelegate
private var quotes = [Quote]()
didSet tbl_Feed.reloadData()
var quote: Quote?
var fetchCount = 10
@IBOutlet weak var tbl_Feed: UITableView!
override func viewDidLoad()
super.viewDidLoad()
fetchPost()
// MARK: - API
func fetchPost()
reference(.Quotes).limit(to: getResultCount).getDocuments (snapshot, error) in
guard let documents = snapshot?.documents else return
documents.forEach (doc) in
let quotes = documents.map (Quote(dictionary: $0.data()))
self.quotes = quotes
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool)
let currentOffset = scrollView.contentOffset.y
let maxxOffset = scrollView.contentSize.height - scrollView.frame.size.height
if maxxOffset - currentOffset <= 300 // Your cell size 300 is example
fetchCount += 5
fetchPost()
print("DEBUG: Fetching new Data")
【讨论】:
以上是关于使用 Firebase Firestore 进行分页 - swift 4的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ReactJs 对 Cloud Firestore 数据进行分页
使用 Swift 进行 Firebase Firestore 分页
Firestore 分页 - 是不是有任何与 firebase 的 limitToLast 兼容的查询?