ios Swift tableView 分页
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35367496/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Swift tableView Pagination
提问by SwiftDeveloper
I have success working tableview with json parsing codes.But may have 1000 more item so need pagination when scrolling bottom side. I dont know how can i do this my codes under below. For objective-c have a lot of example but for swift i didnt find working example. Im waiting your helps. I think will be help too many people. Thank you !
我使用 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()
}
}
回答by Warif Akhand Rishi
For that you need to have server side change also.
为此,您还需要更改服务器端。
Server will accept
fromIndex
andbatchSize
in theAPI
url as query param.let listUrlString = "http://bla.com/json2.php?listType=" + listType + "&t=" + NSUUID().UUIDString + "&batchSize=" + batchSize + "&fromIndex=" + fromIndex
In the server response, there will be an extra key
totalItems
. This will be used to identify all items are received or not. An array or itemsfromIndex
tobatchSize
number of items.
服务器将接受
fromIndex
并batchSize
在API
url 中作为查询参数。let listUrlString = "http://bla.com/json2.php?listType=" + listType + "&t=" + NSUUID().UUIDString + "&batchSize=" + batchSize + "&fromIndex=" + fromIndex
在服务器响应中,会有一个额外的 key
totalItems
。这将用于识别是否收到所有项目。一个数组或项目fromIndex
到项目batchSize
数。
In the app side
在应用端
First
loadItem()
will be called withfromIndex = 0
andbatchSize = 20
(for example inviewDidLoad()
orviewWillAppear
). removeAll items fromprivateList
array before callingloadItem()
for the first timeServer returns an array of first 20 items and
totalItems
total number of items in the server.Append the 20 items in
privateList
array and reloadtableView
In
tableView:cellForRowAtIndexPath
method check if the cell is the last cell. And check iftotalItems
(form server) is greater thanprivateList.count
. That means there are more items in the server to loadif indexPath.row == privateList.count - 1 { // last cell if totalItems > privateList.count { // more items to fetch loadItem() // increment `fromIndex` by 20 before server call } }
First
loadItem()
将使用fromIndex = 0
and调用batchSize = 20
(例如 inviewDidLoad()
orviewWillAppear
)。在第一次privateList
调用之前从数组中删除所有项目loadItem()
服务器返回一个包含前 20 个项目和
totalItems
服务器中项目总数的数组。追加
privateList
数组中的 20 个项目并重新加载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 } }
Question:where is refresh ? will be scrolling ?
题:where is refresh ? will be scrolling ?
Refresh after appending new items in the array when server response received. (step 3)
收到服务器响应时在数组中添加新项目后刷新。(第 3 步)
Scrolling will trigger tableView:cellForRowAtIndexPath
for every cell when user scrolls. Code is checking if it is the last cell and fetch remaining items. (step 4)
tableView:cellForRowAtIndexPath
当用户滚动时,每个单元格都会触发滚动。代码正在检查它是否是最后一个单元格并获取剩余的项目。(第四步)
Sample project added:
https://github.com/rishi420/TableViewPaging
回答by Mahesh Giri
The good and efficient way to do it is by using scrollviewDelegate
in tableview
Just add UIScrollViewDelegate
in your viewController
In view controller
这样做的好方法是scrollviewDelegate
在 tableview 中使用只需添加UIScrollViewDelegate
到您的viewController
In 视图控制器中
//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
}
Override two methods from this delegate
覆盖此委托的两个方法
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)
}
}
}
回答by MhmdRizk
SWIFT 3.0 - 4...
SWIFT 3.0 - 4...
If you're sending the page number in the API request then this is the ideal way for implementing pagination in your app.
如果您在 API 请求中发送页码,那么这是在您的应用中实现分页的理想方式。
1) declare the variable current Page with initial Value 0 and a bool to check if any list is being loaded with initial value false
1) 用初始值 0 和一个 bool 声明变量 current Page 以检查是否有任何列表正在加载初始值 false
var currentPage : Int = 0
var isLoadingList : Bool = false
2) This is the function that gets the list example:
2)这是获取列表示例的函数:
func getListFromServer(_ pageNumber: Int){
self.isloadingList = false
self.table.reloadData()
}
3) This is the function that increments page number and calls the API function
3)这是增加页码和调用API函数的函数
func loadMoreItemsForList(){
currentPage += 1
getListFromServer(currentPage)
}
4) this is the method that will be called when the scrollView scrolls
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. the bool isLoadingList role is to prevent the scroll view from getting more lists in one drag to the bottom of the table view.
PS bool isLoadingList 作用是防止滚动视图在一次拖拽到表格视图底部时获取更多列表。
回答by Paul Robinson
This is now a little bit easier with the addition of a new protocol in iOS10: UITableViewDataSourcePrefetching
通过在 iOS10 中添加新协议,这现在变得更容易了: UITableViewDataSourcePrefetching
https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching
https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching
回答by Satyendra Singh Bhati
//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()
}
}
}`
回答by Андр?й Погор?лко
I've tried an approach with willDisplayCell. But it produces unwanted stops during scrolling which makes the user experience not good. I think a better way is to do it in scrollViewDidEndDecelerating delegate method. It calls when the scroll finishes and only then new data comes. User sees that there is new content and scroll again if he wants. I've taken the answer herebut instead of scrollViewDidEndDragging I use scrollViewDidEndDecelerating. It looks just better in my case. Here is some code from my project.
我已经尝试了一种使用 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())
}
回答by scollaco
I needed something similar on a project and my solution was:
我在一个项目上需要类似的东西,我的解决方案是:
1 - create a variable numberOfObjectsInSubArray (initial value 30 or whatever you want)
1 - 创建一个变量 numberOfObjectsInSubArray(初始值 30 或任何你想要的)
2 - create a subarray to add a number of objects from your privateList array every time i tap "show more"
2 - 每次点击“显示更多”时,创建一个子数组以从您的 privateList 数组中添加多个对象
let subArray = privateList?.subarrayWithRange(NSMakeRange(0, numberOfObjectsInSubArray))
And use it on
并将其用于
internal func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return subArray.count
}
3- Whenever you need to show more objects, do:
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()
}
I hope it helps
我希望它有帮助
回答by Sumeet
Add another section to your tableview, let this section have only 1 row which will be a cell containing an activity indicator, to denote loading.
将另一个部分添加到您的 tableview,让该部分只有 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()
}
}
回答by Shehzad Ali
Another way of doing this is: You may set a threshold for getting elements while sending request each time:
另一种方法是:您可以在每次发送请求时设置获取元素的阈值:
Lets say you you are fetching 20 elements first time. You will be saving last fetched record id or number for getting list of next 20 elements.
假设您第一次获取 20 个元素。您将保存上次获取的记录 ID 或编号,以获取接下来 20 个元素的列表。
let lastFetchedIndex = 20;
I am assuming that you have already added these records in your myArray. MyArray is the dataSource of tableView. Now myArray is containing 40 objects. I am going to make a list of indexPaths of rows that needs to be inserted in tableView now.
我假设您已经在 myArray 中添加了这些记录。MyArray 是 tableView 的数据源。现在 myArray 包含 40 个对象。我现在要制作一个需要插入 tableView 的行的 indexPaths 列表。
var indexPathsArray = [NSIndexPath]()
for index in lastFetchedIndex..<myArray.count{
let indexPath = NSIndexPath(forRow: index, inSection: 0)
indexPathsArray.append(indexPath)
}
Here I am updating my tableView. Make sure your dataSource i mean your myArray has already been updated. So that it may insert rows properly.
在这里,我正在更新我的 tableView。确保您的数据源我的意思是您的 myArray 已经更新。以便它可以正确插入行。
self.tableView.beginUpdates()
tableView!.insertRowsAtIndexPaths(indexPathsArray, withRowAnimation: .Fade)
self.tableView.endUpdates()
回答by SwiftyIso
here is a sample code for collection view :
这是集合视图的示例代码:
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
}
}