Swift tableView分页

时间:2016-02-12 16:26:01

标签: ios swift uitableview pagination

我使用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()
    }


}

14 个答案:

答案 0 :(得分:50)

为此,您还需要更改服务器端。

  1. 服务器将接受fromIndex网址中的batchSizeAPI作为查询参数。

    let listUrlString =  "http://bla.com/json2.php?listType=" + listType + "&t=" + NSUUID().UUIDString + "&batchSize=" + batchSize + "&fromIndex=" + fromIndex
    
  2. 在服务器响应中,将有一个额外的密钥totalItems。这将用于识别是否收到所有项目。一个或多个项目fromIndexbatchSize个项目。

  3. 在app方面

    1. 首先loadItem()将使用fromIndex = 0batchSize = 20进行调用(例如viewDidLoad()viewWillAppear)。在首次致电privateList之前删除loadItem()数组中的所有项目

    2. 服务器返回服务器中前20项和totalItems项目总数的数组。

    3. privateList数组中附加20个项目并重新加载tableView

    4. tableView:cellForRowAtIndexPath方法中检查单元格是否是最后一个单元格。并检查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
          }
      }
      
    5. 问题: where is refresh ? will be scrolling ?

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

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

      添加了示例项目:
      https://github.com/rishi420/TableViewPaging

答案 1 :(得分:10)

这样做的好方法是在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)

                }
            }


    }

答案 2 :(得分:9)

SWIFT 3.0 - 4 ...

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

1)声明具有初始值0的变量current Page和bool,以检查是否正在加载任何列表,初始值为false

var currentPage : Int = 0
var isLoadingList : Bool = false

2)这是获取列表示例的函数:

 func getListFromServer(_ pageNumber: Int){
 self.isloadingList = false
 self.table.reloadData()
 } 

3)这是增加页码并调用API函数的函数

 func loadMoreItemsForList(){
 currentPage += 1
 getListFromServer(currentPage)
 }

4)这是在scrollView滚动

时调用的方法
func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if (((scrollView.contentOffset.y + scrollView.frame.size.height) > scrollView.contentSize.height ) && ! isLoadingList){
        self. isLoadingList = true
        self. loadMoreItemsForList()
    }
}

P.S。 bool isLoadingList角色是为了防止滚动视图在一次拖动中获取更多列表到表视图的底部。

答案 3 :(得分:9)

现在,在iOS10中添加新协议会更容易:UITableViewDataSourcePrefetching

https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching

答案 4 :(得分:2)

在您的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()
    }
}

答案 5 :(得分:1)

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

让我们说你第一次拿到20个元素。您将保存上次获取的记录ID或数字,以获取下20个元素的列表。

let lastFetchedIndex = 20;

我假设您已经在myArray中添加了这些记录。 MyArray是tableView的dataSource。现在myArray包含40个对象。我将创建一个需要在tableView中插入的行的indexPaths列表。

var indexPathsArray = [NSIndexPath]()


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

}

这里我正在更新我的tableView。确保您的dataSource我的意思是您的myArray已经更新。这样它就可以正确插入行。

self.tableView.beginUpdates()
tableView!.insertRowsAtIndexPaths(indexPathsArray, withRowAnimation: .Fade)
self.tableView.endUpdates()

答案 6 :(得分:1)

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

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

2 - 每次点击“显示更多”时,创建一个子阵列,从privateList数组中添加一些对象

    let subArray = privateList?.subarrayWithRange(NSMakeRange(0, numberOfObjectsInSubArray))

并在

上使用它
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()
}

我希望它有所帮助

答案 7 :(得分:1)

这是集合视图的示例代码:

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
   }
}

答案 8 :(得分:1)

我已经尝试了willDisplayCell的方法。但是它在滚动过程中会产生不必要的停止,这会使用户体验不佳。 我认为更好的方法是在scrollViewDidEndDecelerating委托方法中执行此操作。滚动完成时它将调用,然后才有新数据出现。用户看到有新内容,并根据需要再次滚动。我已经回答了here,但不是使用scrollViewDidEndDragging,而是使用scrollViewDidEndDecelerating。就我而言,看起来更好。这是我项目中的一些代码。

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())
}

答案 9 :(得分:1)

制定了通用的目的分页框架:?

https://github.com/eonist/PaginationTable

event.preventDefault();

答案 10 :(得分:1)

//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()
        }
    }
}`

答案 11 :(得分:1)

API处理程序是仅用于POST和GET调用的网络调用的api处理程序。 getNotifications基本上只是一个带有params(offset和pageSize)的post调用,作为响应,存在list。 主要逻辑是根据 willDisplay collectionView委托中的单元格更改偏移量。如果您有任何问题请发表评论,乐于帮助。

df = pd.DataFrame (['aaa','bba','baa','cba', 'xbb'], columns = ['name2'])
df_small = df[(df.name2.str.endswith ('aa'))|(df.name2.str.endswith ('ba'))]
>>>
  name2
0  aaa
1  bba
2  baa
3  cba

答案 12 :(得分:1)

Swift 5(全面的分页解决方案)

UI代码: https://github.com/eonist/PaginationTable

数据模型代码: https://github.com/eonist/PaginationService

核心组件:

  • rowData:此数组将在每个滚动结束事件上增长,直到从backend-API加载了所有项目为止
  • paginationAmount:每个分页周期要获取的金额
  • paginationIndex:当前的单元格数量(随着您加载更多数据而增加
  • isFetching:一个布尔值,使代码知道数据是否已经加载,以避免重复获取等。 fetchData:模拟从remote-api获取数据 陷阱:

该示例代码不依赖于后端。它仅使用文件中的数据进行测试,并通过休眠几秒钟来模拟网络调用 该示例使用一些依赖项以加快创建此示例的速度。但它的基本内容包括AFNetwork,Json解析,Autollayout。所有这些都可以轻松替换 要求:

可以提供项目计数的后端API 可以返回某个范围(startIndex,endIndex)项目的Backend-API

答案 13 :(得分:0)

通过使用 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
        }
    }
}