Ios 快速表视图分页

Ios 快速表视图分页,ios,swift,uitableview,pagination,Ios,Swift,Uitableview,Pagination,我成功地使用json解析代码处理tableview。但可能还有1000个项目,所以在滚动底部时需要分页。我不知道如何才能做到这一点,我的代码下面。对于objective-c,我有很多例子,但对于swift,我没有找到有效的例子。我在等你的帮助。我认为这会帮助太多人。谢谢大家! import UIKit class ViewController: UIViewController, UITableViewDataSource,UITableViewDelegate { let kSuc

我成功地使用json解析代码处理tableview。但可能还有1000个项目,所以在滚动底部时需要分页。我不知道如何才能做到这一点,我的代码下面。对于objective-c,我有很多例子,但对于swift,我没有找到有效的例子。我在等你的帮助。我认为这会帮助太多人。谢谢大家!

import UIKit

class ViewController: UIViewController, UITableViewDataSource,UITableViewDelegate {

    let kSuccessTitle = "Congratulations"
    let kErrorTitle = "Connection error"
    let kNoticeTitle = "Notice"
    let kWarningTitle = "Warning"
    let kInfoTitle = "Info"
    let kSubtitle = "You've just displayed this awesome Pop Up View"


    @IBOutlet weak var myTableView: UITableView!
    @IBOutlet weak var myActivityIndicator: UIActivityIndicatorView!

    var privateList = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        loadItems()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    internal func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return privateList.count
    }




    internal func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {

       let cell:myCell = tableView.dequeueReusableCellWithIdentifier("myCell") as! myCell

        cell.titleLabel.text = privateList[indexPath.row]


        return cell
    }


    func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {

        if (editingStyle == UITableViewCellEditingStyle.Delete){

         print(indexPath.row)


            let alert = SCLAlertView()
            alert.addButton("Hayır"){ }
            alert.addButton("Evet") {

                self.myTableView.beginUpdates()

                 self.privateList.removeAtIndex(indexPath.row)
                tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Left)
                print("Silindi")

                self.myTableView.endUpdates()

                  self.loadItems()

            }
            alert.showSuccess(kSuccessTitle, subTitle: kSubtitle)

        }


    }





    func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // the cells you would like the actions to appear needs to be editable
        return true
    }



    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {


        if(segue.identifier == "Detail") {

            let destinationView = segue.destinationViewController as! DetailViewController

            if let indexPath = myTableView.indexPathForCell(sender as! UITableViewCell) {

                destinationView.privateLista = privateList[indexPath.row]

            }
        }
    }



    internal func tableView(tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat
    {
        return 0.0
    }


    func loadItems()
    {
     loadItemsNow("privateList")

    }

    func loadItemsNow(listType:String){
        myActivityIndicator.startAnimating()
        let listUrlString =  "http://bla.com/json2.php?listType=" + listType + "&t=" + NSUUID().UUIDString
        let myUrl = NSURL(string: listUrlString);
        let request = NSMutableURLRequest(URL:myUrl!);
        request.HTTPMethod = "GET";

        let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
            data, response, error in

            if error != nil {
                print(error!.localizedDescription)
                dispatch_async(dispatch_get_main_queue(),{
                    self.myActivityIndicator.stopAnimating()
                })

                return
            }


            do {

                let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray

                if let parseJSON = json {


                        self.privateList = parseJSON as! [String]

                }

            } catch {
                print(error)

            }

            dispatch_async(dispatch_get_main_queue(),{
                self.myActivityIndicator.stopAnimating()
                self.myTableView.reloadData()
            })


        }

        task.resume()
    }


}

另一种方法是:您可以在每次发送请求时设置获取元素的阈值:

假设您第一次获取20个元素。您将保存最后获取的记录id或编号,以获取接下来20个元素的列表

let lastFetchedIndex = 20;
我假设您已经在myArray中添加了这些记录。MyArray是tableView的数据源。现在myArray包含40个对象。现在,我将列出需要插入tableView的行的IndExpath

var indexPathsArray = [NSIndexPath]()


for index in lastFetchedIndex..<myArray.count{
    let indexPath = NSIndexPath(forRow: index, inSection: 0)
    indexPathsArray.append(indexPath)

}

为此,还需要对服务器端进行更改

  • 服务器将接受
    API
    url中的
    fromIndex
    batchSize
    作为查询参数

    let listUrlString =  "http://bla.com/json2.php?listType=" + listType + "&t=" + NSUUID().UUIDString + "&batchSize=" + batchSize + "&fromIndex=" + fromIndex
    
  • 在服务器响应中,将有一个额外的键
    totalItems
    。这将用于确定是否收到所有项目。从索引到批大小的一个或多个项的数组

  • 在应用程序端

  • 第一个
    loadItem()
    将使用
    fromIndex=0
    batchSize=20
    调用(例如在
    viewDidLoad()
    视图中将出现
    )。在第一次调用
    loadItem()
    之前,从
    privateList
    数组中删除所有项

  • 服务器返回包含前20个项目和服务器中项目总数的数组

  • privateList
    数组中追加20项,然后重新加载
    tableView

  • tableView:cellforrowatinexpath方法中,检查该单元格是否为最后一个单元格。并检查
    totalItems
    (表单服务器)是否大于
    privateList.count
    。这意味着服务器中有更多的项要加载

    if indexPath.row == privateList.count - 1 { // last cell
        if totalItems > privateList.count { // more items to fetch
            loadItem() // increment `fromIndex` by 20 before server call
        }
    }
    


  • 问题:
    刷新在哪里?是否将滚动?

    收到服务器响应时,在数组中追加新项后刷新。(步骤3)

    当用户滚动时,滚动将为每个单元格触发
    tableView:cellforrowatinexpath
    。代码正在检查它是否是最后一个单元格,并获取剩余的项。(步骤4)


    示例项目添加:

    我需要一个项目上类似的东西,我的解决方案是:

    1-创建变量numberOfObjectsInSubArray(初始值30或任何您想要的值)

    2-每次我点击“显示更多”时,创建一个子阵列以添加privateList阵列中的多个对象

    然后用它

    internal func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return subArray.count
    }
    
    3-每当需要显示更多对象时,请执行以下操作:

    func addMoreObjectsOnTableView () {
    
        numberOfObjectsInSubArray += 30
    
        if (numberOfObjectsInSubArray < privateList.count) {
    
            subArray = privateList?.subarrayWithRange(NSMakeRange(0, numberOfObjectsInSubArray))  
    
        } else {
    
            subArray = privateList?.subarrayWithRange(NSMakeRange(0, privateList.count))  
        }
    
        tableView.reloadData()
    }
    
    func addMoreObjectsOnTableView(){
    子阵列中的对象数+=30
    if(numberOfObjectsInSubArray

    我希望它有助于在tableview中添加另一个部分,让这个部分只有一行,这将是一个包含活动指示器的单元格,以表示加载

    internal func numberOfSectionsInTableView(tableView: UITableView) -> Int
    {
        return 2;
    }
    
    internal func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
        {
            if section == 0 {
                return privateList.count
            } else if section == 1 {    // this is going to be the last section with just 1 cell which will show the loading indicator
                return 1
            }
        }
    
    internal func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
       if section == 0 {
           let cell:myCell = tableView.dequeueReusableCellWithIdentifier("myCell") as! myCell
    
            cell.titleLabel.text = privateList[indexPath.row]
    
    
            return cell
        } else if section == 1 { 
            //create the cell to show loading indicator
            ...
    
            //here we call loadItems so that there is an indication that something is loading and once loaded we relaod the tableview
            self.loadItems()
        }
    }
    

    在tableview中使用
    scrollviewDelegate
    是一种有效的方法 只需将
    UIScrollViewDelegate
    添加到
    viewController
    视图控制器

    //For Pagination
    var isDataLoading:Bool=false
    var pageNo:Int=0
    var limit:Int=20
    var offset:Int=0 //pageNo*limit
    var didEndReached:Bool=false
    viewDidLoad(_){
    tableview.delegate=self //To enable scrollviewdelegate
    }
    
    重写此委托中的两个方法

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    
            print("scrollViewWillBeginDragging")
            isDataLoading = false
        }
    
    
    
        func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
            print("scrollViewDidEndDecelerating")
        }
        //Pagination
        func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    
                print("scrollViewDidEndDragging")
                if ((tableView.contentOffset.y + tableView.frame.size.height) >= tableView.contentSize.height)
                {
                    if !isDataLoading{
                        isDataLoading = true
                        self.pageNo=self.pageNo+1
                        self.limit=self.limit+10
                        self.offset=self.limit * self.pageNo
                        loadCallLogData(offset: self.offset, limit: self.limit)
    
                    }
                }
    
    
        }
    

    以下是集合视图的示例代码:

    var page = 0
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
        print("page Num:\(page)")
    }
    
    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath){
         if arrImagesData.count-1 == indexPath.row && arrImagesData.count%10 == 0{
            getMoreImages(page)
         }
    }
    
    func getMoreImages(page:Int){ 
       //hit api
       if api_success == true {
           if self.page == 0 {
              self.arrImagesData.removeAll()
           }
       self.arrImagesData.appendContentsOf(api_data)
       self.collectionImages.reloadData()
       self.page = self.page + 1
       }
    }
    

    SWIFT 3.0和4.0

    如果您在API请求中发送页码,那么这是在应用程序中实现分页的理想方式

  • 使用初始值0和bool声明变量current Page,以检查是否加载了初始值为false的列表
  • 这是获取列表的函数示例:
  • 这是一个增加页码并调用API函数的函数
  • 这是scrollView滚动时将调用的方法

  • 另外,bool isLoadingList的作用是防止滚动视图在一次拖动到表视图底部的过程中获得更多列表。

    通过在iOS10中添加一个新的协议,现在这变得更容易了:
    uitableViewDataSourceRefetch


    我尝试了一种使用willDisplayCell的方法。但它在滚动过程中会产生不必要的停止,这使得用户体验不佳。 我认为更好的方法是使用ScrollViewDiEndDecreating委托方法。当滚动结束并且只有新数据出现时,它才会调用。用户看到有新内容,如果需要,可以再次滚动。我接受了答案,但我使用ScrollViewDiEndDraging代替ScrollViewDiEndDraging。我的情况看起来更好。下面是我的项目中的一些代码

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        guard scrollView == tableView,
            (scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height,
            !viewModel.isLastPeriodicsPage else { return }
    
        viewModel.paginatePeriodics(tableView.getLastIndexPath())
    }
    
    创建了一个通用的分页框架:通过使用UITableViewDelegate,您可以调用该函数

       func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        let lastItem = self.mes.count - 1
        if indexPath.row == lastItem {
            print("IndexRow\(indexPath.row)")
            if currentPage < totalPage {
                currentPage += 1
               //Get data from Server
            }
        }
    }
    
    func tableView(tableView:UITableView,willDisplay单元格:UITableView单元格,forRowAt indexath:indexPath){
    让lastItem=self.mes.count-1
    如果indexPath.row==lastItem{
    打印(“IndexRow\(indexPath.row)”)
    如果currentPage
    //工作正常
    func getPageCount(TotalCount:Int)->Int{
    var num=总计数
    让提醒=num%50
    打印(提醒)
    如果提示!=0{
    num=总计数/50
    num=num+1
    }否则{
    num=总计数/50
    }
    返回数
    }
    func tableView(tableView:UITableView,willDisplay单元格:UITableView单元格,forRowAt indexath:indexath){
    让TotalPage=self.getPageCount(TotalCount:Int(Datacount)!)
    
        var currentPage : Int = 0
        var isLoadingList : Bool = false
    
        func getListFromServer(_ pageNumber: Int){
            self.isLoadingList = false
            self.table.reloadData()
        }
    
       func loadMoreItemsForList(){
           currentPage += 1
           getListFromServer(currentPage)
       }
       
    
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            if (((scrollView.contentOffset.y + scrollView.frame.size.height) > scrollView.contentSize.height ) && !isLoadingList){
                self.isLoadingList = true
                self.loadMoreItemsForList()
            }
        }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        guard scrollView == tableView,
            (scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height,
            !viewModel.isLastPeriodicsPage else { return }
    
        viewModel.paginatePeriodics(tableView.getLastIndexPath())
    }
    
       func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        let lastItem = self.mes.count - 1
        if indexPath.row == lastItem {
            print("IndexRow\(indexPath.row)")
            if currentPage < totalPage {
                currentPage += 1
               //Get data from Server
            }
        }
    }
    
    //It works fine 
    func getPageCount(TotalCount : Int) -> Int{
        var num = TotalCount
        let reminder = num % 50
        print(reminder)
        if reminder != 0{
            num = TotalCount/50
            num = num + 1
    
        }else{
            num = TotalCount/50
        }
        return num
    }
    
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        let TotalPage =  self.getPageCount(TotalCount: Int(Datacount)!)
        let lastItem = self.mainArr.count - 1
        if indexPath.row == lastItem {
            print("IndexRow\(indexPath.row)")
            if self.page < TotalPage-1 {
                self.view_Loader.isHidden = false
                self.view_LoaderHeight.constant = 50
            self.page += 1
            self.YourAPI()
            }
        }
    }`
    
    var isFetching: Bool = false
    var offset = 0
    var totalListOnServerCount = 20 // it must be returned from server
    var pageSize = 10 // get 10 objects for instance
    // MARK: - API Handler
    private func fetchNotifications(){
        // return from function if already fetching list
        guard !isFetching else {return}
            if offset == 0{
                // empty list for first call i.e offset = 0
                self.anyList.removeAll()
                self.collectionView.reloadData()
            }
            isFetching = true
            // API call to fetch notifications with given offset or page number depends on server logic just simple POST Call
            APIHandler.shared.getNotifications(offset: offset) {[weak self] (response, error) in
                if let response = response {
                    self?.isFetching = false
                    if self?.offset == 0{
                        // fetch response from server for first fetch
                        self?.notificationsResponse = response
                        if self?.refreshControl.isRefreshing ?? false {
                            self?.refreshControl.endRefreshing()
                        }
                    }else{
                        // append if already exist ( pagination )
                        self?.notificationsResponse?.notifications.append(contentsOf: response.notifications)
                    }
                    self?.collectionView.reloadData()
    
                }
    
            }
    }
    
    
    // MARK: - Collection View Delegate
    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    
        guard let anyList = responseFromServer else { return }
        // check if scroll reach last index available and keep fetching till our model list has all entries from server
        if indexPath.item == anyList.count - 1 && anyList.count  < totalListOnServerCount{
    
            offset += pageSize
            fetchNotifications()
    
        }
    }