web-dev-qa-db-ja.com

Swift tableViewページネーション

Json解析コードでテーブルビューを正常に動作させることができますが、さらに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()
    }


}
42
SwiftDeveloper

そのためには、サーバー側の変更も必要です。

  1. サーバーは、fromIndex URLのbatchSizeおよびAPIをクエリパラメーターとして受け入れます。

    let listUrlString =  "http://bla.com/json2.php?listType=" + listType + "&t=" + NSUUID().UUIDString + "&batchSize=" + batchSize + "&fromIndex=" + fromIndex
    
  2. サーバーの応答には、追加のキーtotalItemsがあります。これは、すべてのアイテムが受信されたかどうかを識別するために使用されます。配列またはアイテムfromIndexからbatchSizeのアイテム数。

アプリ側で

  1. 最初のloadItem()は、fromIndex = 0およびbatchSize = 20で呼び出されます(たとえば、viewDidLoad()またはviewWillAppearで)。 loadItem()を初めて呼び出す前にprivateList配列からremoveAllアイテム

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

質問:where is refresh ? will be scrolling ?

サーバーの応答を受信したときに配列に新しいアイテムを追加した後に更新します。 (ステップ3)

スクロールすると、ユーザーがスクロールするとすべてのセルに対してtableView:cellForRowAtIndexPathがトリガーされます。コードは、最後のセルであるかどうかを確認し、残りのアイテムを取得します。 (ステップ4)

追加されたサンプルプロジェクト:
https://github.com/rishi420/TableViewPaging

53

Swift 3.0-4 ...

APIリクエストでページ番号を送信する場合、これはアプリにページネーションを実装するための理想的な方法です。

1)初期値0とboolで変数current Pageを宣言し、初期値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()
    }
}

追伸bool isLoadingListロールは、スクロールビューがテーブルビューの下部への1回のドラッグでさらにリストを取得しないようにすることです。

11
MhmdRizk

TableViewでscrollviewDelegateを使用すると、UIScrollViewDelegate In View Controllerに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
}

このデリゲートから2つのメソッドをオーバーライドします

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)

                }
            }


    }
11
Mahesh Giri

IOS10に新しいプロトコルUITableViewDataSourcePrefetchingが追加されたことで、これが少し簡単になりました。

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

10
Paul Robinson

テーブルビューに別のセクションを追加します。このセクションには、ロードを示すアクティビティインジケータを含むセルとなる1行のみが含まれるようにします。

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

プロジェクトで似たようなものが必要で、私の解決策は次のとおりです。

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

私はそれが役立つことを願っています

3
scollaco

コレクションビューのサンプルコードを次に示します。

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

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

これを行う別の方法は次のとおりです。リクエストを送信するたびに要素を取得するためのしきい値を設定できます。

初めて20個の要素を取得するとします。次の20要素のリストを取得するために、最後に取得したレコードIDまたは番号を保存します。

let lastFetchedIndex = 20;

MyArrayにこれらのレコードが既に追加されていると仮定しています。 MyArrayはtableViewのdataSourceです。現在、myArrayには40個のオブジェクトが含まれています。 tableViewに挿入する必要がある行のindexPathのリストを作成します。

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()
2
Shehzad Ali