使用 Firebase firestore 进行分页 - swift 4

Pagination with Firebase firestore - swift 4

我正在尝试使用 firestore 对数据进行分页(无限滚动我的 table 视图)。我已经整合了代码 google 尽我所能提供分页,但我仍然无法正确加载数据。

根据需要将初始数据集加载到 table 视图中。然后,当用户点击屏幕底部时,下一个 "x" 数量的项目被加载。但是当用户第二次点击屏幕底部时,相同的 "x" 项目只是附加到 table 视图。相同的项目会无限期地添加。

所以它是最初的 3 个 "ride" 个对象,然后是接下来的 4 个 "ride" 个对象,永远重复。

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: [=11=].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: [=11=].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
            }

        })
    }
}

这就是我想出的解决方案!此解决方案很可能多次调用 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: [=11=].data())})
            self.rides.append(contentsOf: newRides)

            //
            DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
                self.tableView.reloadData()
                self.fetchingMore = false
            })

            self.lastDocumentSnapshot = snapshot!.documents.last
        }
    }
}

我的解决方案与@yambo 类似,但是,我尽量避免对数据库进行额外调用。第一次调用数据库后,我得到了 10 个对象,当需要加载新页面时,我保留了多少个对象的引用,并检查计数 + 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()
            }
        }
    }
}

游戏有点晚了,但我想分享一下我是如何做到的,使用 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.

简单、快速、容易的方法是...

class 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: [=10=].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")
    }
}

}