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()
}
}
Ответы
Ответ 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 состоит в том, чтобы предотвратить просмотр прокрутки из большего количества списков при одном перетаскивании в нижней части представления таблицы.
Ответ 4
Теперь это стало немного проще с добавлением нового протокола в iOS10: UITableViewDataSourcePrefetching
https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching
Ответ 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
}
}