Подтвердить что ты не робот

Swift tableView Pagination

У меня есть успешный рабочий стол с 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()
    }


}
4b9b3361

Ответ 1

Для этого вам также необходимо изменить серверную сторону.

  • Сервер примет fromIndex и batchSize в URL API как параметр запроса.

    let listUrlString =  "http://bla.com/json2.php?listType=" + listType + "&t=" + NSUUID().UUIDString + "&batchSize=" + batchSize + "&fromIndex=" + fromIndex
    
  • В ответе сервера появится дополнительный ключ totalItems. Это будет использоваться для идентификации всех элементов, полученных или нет. Массив или элементы fromIndex - batchSize количество элементов.

В стороне приложения

  • Сначала loadItem() будет вызываться с fromIndex = 0 и batchSize = 20 (например, в viewDidLoad() или viewWillAppear). removeAll из массива privateList перед вызовом loadItem() в первый раз

  • Сервер возвращает массив из первых 20 элементов и totalItems общее количество элементов на сервере.

  • Добавьте 20 элементов в массив privateList и перезагрузите tableView

  • В методе 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

Ответ 2

Хороший и эффективный способ сделать это - использовать scrollviewDelegate в tableview Просто добавьте 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)

                }
            }


    }

Ответ 3

SWIFT 3.0 - 4...

Если вы отправляете номер страницы в запросе API, это идеальный способ реализации разбивки на страницы в приложении.

1) объявите переменную текущую страницу с начальным значением 0 и 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 состоит в том, чтобы предотвратить просмотр прокрутки из большего количества списков при одном перетаскивании в нижней части представления таблицы.

Ответ 5

Мне нужно что-то подобное в проекте, и мое решение было:

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

Я надеюсь, что это поможет

Ответ 6

Добавьте другой раздел в таблицу, пусть в этом разделе будет только одна строка, которая будет ячейкой, содержащей индикатор активности, для обозначения загрузки.

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

Ответ 7

Другой способ сделать это: вы можете установить порог для получения элементов при отправке запроса каждый раз:

Предположим, вы впервые забираете 20 элементов. Вы будете сохранять последний выбранный идентификатор записи или номер для получения списка из следующих 20 элементов.

let lastFetchedIndex = 20;

Я предполагаю, что вы уже добавили эти записи в свой myArray. MyArray - это dataSource таблицыView. Теперь myArray содержит 40 объектов. Я собираюсь сделать список indexPaths строк, которые нужно вставить в tableView сейчас.

var indexPathsArray = [NSIndexPath]()


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

}

Здесь я обновляю свой tableView. Убедитесь, что ваш источник данных я означает, что ваш myArray уже обновлен. Чтобы он мог правильно вставлять строки.

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

Ответ 8

Вот пример кода для коллекции:

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

Ответ 9

Я попробовал подход с willDisplayCell. Но он производит нежелательные остановки во время прокрутки, что делает работу пользователя не очень приятной. Я думаю, что лучший способ сделать это в методе делегата scrollViewDidEndDecelerating. Он вызывает, когда прокрутка заканчивается, и только тогда приходят новые данные. Пользователь видит, что есть новый контент и прокручивает снова, если он хочет. Я взял ответ здесь, но вместо 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())
}

Ответ 10

Сделал каркас страницы с общей форс-мажором: a

https://github.com/eonist/PaginationTable

let table = Table(rowData: [], frame: .zero, style: .plain)
  view = table
  table.isFetching = true
  Table.fetchData(range: table.paginationRange) { rowItem in
     DispatchQueue.main.async { [weak table] in
        table?.rowData += rowItem
        table?.reloadData()
        table?.paginationIndex += Table.paginationAmount // set the new pagination index
        table?.isFetching = false
     }
  }