解雇随后的分页电话

时间:2017-09-26 17:13:36

标签: ios swift firebase pagination

好的,我真的需要你的帮助。我从firebase加载了一些元素让我们说4.这些元素被加载到collectionView中。一旦我到达集合视图的底部,它应该触发另一个分页请求或获取更多数据,但它永远不会。它总是出现在我的其他陈述中,这让我知道分页没有发生。

包含我的主视图控制器的代码

import UIKit
import UIKit
import Alamofire
import AlamofireNetworkActivityIndicator
import SwiftLocation
import CoreLocation
import AMScrollingNavbar

class NewHomeFeedControllerViewController: UIViewController {
    let detailView = EventDetailViewController()
    var allEvents = [Event]()
    let customCellIdentifier1 = "customCellIdentifier1"
    var grideLayout = GridLayout(numberOfColumns: 2)
    let refreshControl = UIRefreshControl()
    var newHomeFeed: NewHomeFeedControllerViewController?
      let paginationHelper = PaginationHelper<Event>(serviceMethod: PostService.showEvent)
    lazy var dropDownLauncer : DropDownLauncher = {
        let launcer = DropDownLauncher()
        launcer.newHomeFeed = self
        return launcer
    }()

    // 1 IGListKit uses IGListCollectionView, which is a subclass of UICollectionView, which patches some functionality and prevents others.
    let collectionView: UICollectionView = {
        // 2 This starts with a zero-sized rect since the view isn’t created yet. It uses the UICollectionViewFlowLayout just as the ClassicFeedViewController did.
        let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
        // 3 The background color is set to white
        view.backgroundColor = UIColor.white
        return view
    }()
    func handleDropDownMenu(){
        dropDownLauncer.showDropDown()
    }
    func configureCollectionView() {
        // add pull to refresh
        refreshControl.addTarget(self, action: #selector(reloadHomeFeed), for: .valueChanged)
        collectionView.addSubview(refreshControl)
    }
    func reloadHomeFeed() {
        self.paginationHelper.reloadData(completion: { [unowned self] (events) in
            self.allEvents = events

            if self.refreshControl.isRefreshing {
                self.refreshControl.endRefreshing()
            }

            DispatchQueue.main.async {
                self.collectionView.reloadData()
            }
        })
    }

    func categoryFetch(dropDown: DropDown){
        navigationItem.title = dropDown.name
        paginationHelper.category = dropDown.name
        configureCollectionView()
        reloadHomeFeed()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
        collectionView.contentInset = UIEdgeInsetsMake(15, 0, 0, 0)
        navigationItem.title = "Home"
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.collectionViewLayout = grideLayout
        collectionView.reloadData()
        collectionView.register(CustomCell.self, forCellWithReuseIdentifier: customCellIdentifier1)
        //  self.navigationItem.hidesBackButton = true
        let backButton = UIBarButtonItem(image: UIImage(named: "menu"), style: .plain, target: self, action: #selector(handleDropDownMenu))
        self.navigationItem.leftBarButtonItem = backButton
        configureCollectionView()
        reloadHomeFeed()
        // Do any additional setup after loading the view.
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if let navigationController = self.navigationController as? ScrollingNavigationController {
            navigationController.followScrollView(self.collectionView, delay: 50.0)
        }
    }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        if let navigationController = navigationController as? ScrollingNavigationController {
            navigationController.stopFollowingScrollView()
        }
    }

     func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
        if let navigationController = navigationController as? ScrollingNavigationController {
            navigationController.showNavbar(animated: true)
        }
        return true
    }
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        grideLayout.invalidateLayout()
    }
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        collectionView.frame = view.bounds
    }
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

extension NewHomeFeedControllerViewController: UICollectionViewDelegate {
     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        //let selectedEvent = self.imageArray[indexPath.row]
        //let eventDetailVC
        if let cell = collectionView.cellForItem(at: indexPath){
            //  print("Look here for event name")
            // print(detailView.eventName)
            detailView.eventKey = allEvents[indexPath.row].key!
            detailView.eventPromo = allEvents[indexPath.row].currentEventPromo!
            detailView.currentEvent = allEvents[indexPath.row]
            present(detailView, animated: true, completion: nil)
            //self.navigationController?.pushViewController(detailView, animated: true)

        }
        print("Cell \(indexPath.row) selected")
    }
}

extension NewHomeFeedControllerViewController: UICollectionViewDataSource {
     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return allEvents.count
    }
    // The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        let customCell = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier1, for: indexPath) as! CustomCell
        let imageURL = URL(string: allEvents[indexPath.item].currentEventImage)
        print(imageURL ?? "")
        customCell.sampleImage.af_setImage(withURL: imageURL!)
        return customCell
    }



    func collectionView(_ collectionView: UICollectionView, willDisplay cell: CustomCell, forItemAt indexPath: NSIndexPath) {
        if indexPath.section >= allEvents.count - 1 {
            // print("paginating for post")
            paginationHelper.paginate(completion: { [unowned self] (events) in
                self.allEvents.append(contentsOf: events)

                DispatchQueue.main.async {
                    self.collectionView.reloadData()
                }
            })
        }else{
            print("Not paginating")
        }
    }

}


extension NewHomeFeedControllerViewController: UICollectionViewDelegateFlowLayout{
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if indexPath.item == 0 || indexPath.item == 1 {
            return CGSize(width: view.frame.width, height: grideLayout.itemSize.height)
        }else{
            return grideLayout.itemSize
        }
    }
}

我的分页是通过这个辅助方法完成的

protocol Keyed {
    var key: String? { get set }
}

// Create a new instance using a genetic type
//let paginationHelper = MGPaginationHelper<Post>()

// Generic class type



//   1. initial - no data has been loaded yet
//   2.  ready - ready and waiting for next request to paginate and load the next page
//   3.  loading - currently paginating and waiting for data from Firebase
//   4.  end - all data has been paginated

enum PaginationState
{
    case initial
    case ready
    case loading
    case end
}

class PaginationHelper<T : Keyed>
{
    // MARK: - Properties

    // 1. page size - Determines the number of posts that will be on each page
    // 2. serviceMethod - The service method that will return paginated data
    // 3. state - The current pagination state of the helper
    // 4. lastobjectKey - Firebase uses object keys to determine the last position of the page. We'lll need to use this as an offset for paginating.
    let pageSize: UInt
    let serviceMethod: (UInt, String?,String?, @escaping (([T]) -> Void)) -> Void
    var state: PaginationState = .initial
    var lastObjectKey: String?
    var category: String?

    // MARK: - Init
    //    Can change the default page size for our helper
    //    Set the service method that will be paginated and return data
    init(pageSize: UInt = 4, serviceMethod: @escaping (UInt, String?,String?, @escaping (([T]) -> Void)) -> Void) {
        self.pageSize = pageSize
        self.serviceMethod = serviceMethod
    }


    // 1 Notice our completion parameter type. We use our generic type to enforce that we return type T.
    func paginate(completion: @escaping([T]) -> Void)
    {
        // 2 We switch on our helper's state to determine the behavior of our helper when paginate(completion:) is called
        switch state
        {
        // 3 For our initial state, we make sure that the lastObjectKey is nil use the fallthrough keyword to execute the ready case below.
        case .initial:
            lastObjectKey = nil
            fallthrough
        //4 For our ready state, we make sure to change the state to loading and execute our service method to return the paginated data.
        case .ready:
            state = .loading
          //  print(lastObjectKey)
            serviceMethod(pageSize, lastObjectKey, category) { [unowned self] (objects: [T]) in
                //5 We use the defer keyword to make sure the following code is executed whenever the closure returns. This is helpful for removing duplicate code.
                defer {
                    //6 If the returned last returned object has a key value, we store that in lastObjectKey to use as a future offset for paginating. Right now the compiler will throw an error because it cannot infer that T has a property of key. We'll fix that next.
                    if let lastObjectKey = objects.last?.key {
                        self.lastObjectKey = lastObjectKey
                      //  print(self.lastObjectKey)
                      //  print(lastObjectKey)
                    }
                    // 7 We determine if we've paginated through all content because if the number of objects returned is less than the page size, we know that we're only the last page of objects.
                    self.state = objects.count < Int(self.pageSize) ? .end : .ready
                }

                // 8 If lastObjectKey of the helper doesn't exist, we know that it's the first page of data so we return the data as is.
                guard let _ = self.lastObjectKey else {
                   // print(self.lastObjectKey)
                    return completion(objects)
                }

                // 9 Due to implementation details of Firebase, whenever we page with the lastObjectKey, the previous object from the last page is returned. Here we need to drop the first object which will be a duplicate post in our timeline. This happens whenever we're no longer on the first page.
              //  print(objects.last?.key)
              //  let newObjects = Array(objects.dropLast())
              //  print(newObjects)
                print("\n")
              //  print(objects)
                print("\n")
                completion(objects)

            }

        //10 If the helper is currently paginating or has no more content, the helper returns and doesn't do anything.
        case . loading, .end:
            return
        }
    }

    //  resets the pagination helper to it's initial state
    func reloadData(completion: @escaping ([T]) -> Void)
    {
        state = .initial
        paginate(completion: completion)
    }


}

调用此功能

import Foundation
import  UIKit
import Firebase

struct PostService {
    static func create(for event: String?,for vidURL: String) {
        // 1
        guard let key = event else {
            return 
        }
        let storyUrl = vidURL
        // 2
        guard let uid = Auth.auth().currentUser?.uid else{
            return
        }
        let story = Story(url: storyUrl)
        let dict = story.dictValue
       let postRef = Database.database().reference().child("Stories").child(key).childByAutoId()
        let userRef = Database.database().reference().child("users").child(uid).child("Stories").child(key).childByAutoId()
        postRef.updateChildValues(dict)
        userRef.updateChildValues(dict)
    }
    static func showEvent(pageSize: UInt, lastPostKey: String? = nil, category: String? = nil,completion: @escaping ([Event]) -> Void) {
        //getting firebase root directory
       // print(lastPostKey)
      //  print("came here")
        var currentEvents = [Event]()
        let eventsByLocationRef = Database.database().reference().child("eventsbylocation").child(User.current.location!)
        //let ref = Database.database().reference().child("events")
        var query = eventsByLocationRef.queryOrderedByKey().queryLimited(toFirst: pageSize)
        if let lastPostKey = lastPostKey {
          //  print(lastPostKey)
            query = query.queryEnding(atValue: lastPostKey)
        }
        query.observeSingleEvent(of: .value, with: { (snapshot) in
         //   print(snapshot)
           // print(snapshot.value)
            guard let allObjects = snapshot.children.allObjects as? [DataSnapshot] else{
                return
            }
            allObjects.forEach({ (snapshot) in
            // print(snapshot.value ?? "")
                print(category)
                EventService.show(forEventKey: snapshot.value as! String,eventCategory: category, completion: { (event) in
                    currentEvents.append(event!)
                   // print("\n\n\n\n\n\n")
                   // print("Finished an event")
                   // print(currentEvents.count)
                    completion(currentEvents)
                })

            })


        })

    }



}

前面的代码使用此函数最终通过密钥

从firebase中提取数据
import Foundation
import Firebase
import FirebaseAuth


struct EventService {

    static func show(forEventKey eventKey: String, eventCategory: String? = nil, completion: @escaping (Event?) -> Void) {
       // print(eventKey)
        let ref = Database.database().reference().child("events").child(eventKey)
       // print(eventKey)
                //pull everything
                ref.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { (snapshot,eventKey) in
                    print(snapshot.value ?? "")
                    guard let event = Event(snapshot: snapshot) else {
                        return completion(nil)
                    }
                    if event.category == eventCategory{
                        completion(event)
                    }
                    if eventCategory == nil || eventCategory == "" || eventCategory == "Home" {
                        completion(event)
                    }
                })




    }
}

我包含所有这些方法的唯一原因是为了更好地追踪正在发生的事情。我上下看了这些功能,但我似乎无法弄清楚是什么。任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:2)

问题中有很多代码,所以我可能没有走上正轨,但我确实想要帮助,而且我有一个理论。现在,您正在检查collectionView中的最后一项是否像这样:

if indexPath.section >= allEvents.count - 1

如果你打印indexPath.section,我敢打赌它将永远是相同的。可能总是0,因为您的collectionView只有一个部分。而是尝试:

if indexPath.item >= allEvents.count - 1

这应该返回项目的编号而不是项目所在的部分。

像这样:

func collectionView(_ collectionView: UICollectionView, willDisplay cell: CustomCell, forItemAt indexPath: NSIndexPath) {
    if indexPath.item >= allEvents.count - 1 {
        // print("paginating for post")
        paginationHelper.paginate(completion: { [unowned self] (events) in
            self.allEvents.append(contentsOf: events)

            DispatchQueue.main.async {
                self.collectionView.reloadData()
            }
        })
    } else {
        print("Not paginating")
    }
}