UITableView load more when scrolling to bottom like Facebook application

后端 未结 18 2052

I am developing an application that uses SQLite. I want to show a list of users (UITableView) using a paginating mechanism. Could any one please tell me how to load more dat

相关标签:
18条回答
  • 2020-11-27 09:33

    Swift

    Method 1: Did scroll to bottom

    Here is the Swift version of Pedro Romão's answer. When the user stops scrolling it checks if it has reached the bottom.

    func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    
        // UITableView only moves in one direction, y axis
        let currentOffset = scrollView.contentOffset.y
        let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
    
        // Change 10.0 to adjust the distance from bottom
        if maximumOffset - currentOffset <= 10.0 {
            self.loadMore()
        }
    }
    

    Method 2: Reached last row

    And here is the Swift version of shinyuX's answer. It checks if the user has reached the last row.

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    
        // set up cell
        // ...
    
        // Check if the last row number is the same as the last current data element
        if indexPath.row == self.dataArray.count - 1 {
            self.loadMore()
        }
    
    }
    

    Example of a loadMore() method

    I set up these three class variables for fetching batches of data.

    // number of items to be fetched each time (i.e., database LIMIT)
    let itemsPerBatch = 50
    
    // Where to start fetching items (database OFFSET)
    var offset = 0
    
    // a flag for when all database items have already been loaded
    var reachedEndOfItems = false
    

    This is the function to load more items from the database into the table view.

    func loadMore() {
    
        // don't bother doing another db query if already have everything
        guard !self.reachedEndOfItems else {
            return
        }
    
        // query the db on a background thread
        DispatchQueue.global(qos: .background).async {
    
            // determine the range of data items to fetch
            var thisBatchOfItems: [MyObjects]?
            let start = self.offset
            let end = self.offset + self.itemsPerBatch
    
            // query the database
            do {
                // SQLite.swift wrapper
                thisBatchOfItems = try MyDataHelper.findRange(start..<end)
            } catch _ {
                print("query failed")
            }
    
            // update UITableView with new batch of items on main thread after query finishes
            DispatchQueue.main.async {
    
                if let newItems = thisBatchOfItems {
    
                    // append the new items to the data source for the table view
                    self.myObjectArray.appendContentsOf(newItems)
    
                    // reload the table view
                    self.tableView.reloadData()
    
                    // check if this was the last of the data
                    if newItems.count < self.itemsPerBatch {
                        self.reachedEndOfItems = true
                        print("reached end of data. Batch count: \(newItems.count)")
                    }
    
                    // reset the offset for the next data query
                    self.offset += self.itemsPerBatch
                }
    
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 09:37
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
        if (news.count == 0) {
            return 0;
        } else {
            return news.count +  1 ;
        }
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        @try {
    
            uint position = (uint) (indexPath.row);
            NSUInteger row = [indexPath row];
            NSUInteger count = [news count];
    
            //show Load More
            if (row == count) {
                UITableViewCell *cell = nil;
    
                static NSString *LoadMoreId = @"LoadMore";
                cell = [tableView dequeueReusableCellWithIdentifier:LoadMoreId];
                if (cell == nil) {
                    cell = [[UITableViewCell alloc]
                            initWithStyle:UITableViewCellStyleDefault
                          reuseIdentifier:LoadMoreId];
                }
                if (!hasMoreLoad) {
                    cell.hidden = true;
                } else {
    
                    cell.textLabel.text = @"Load more items...";
                    cell.textLabel.textColor = [UIColor blueColor];
                    cell.textLabel.font = [UIFont boldSystemFontOfSize:14];
                    NSLog(@"Load more");
                    if (!isMoreLoaded) {
                        isMoreLoaded = true;
                        [self performSelector:@selector(loadMoreNews) withObject:nil afterDelay:0.1];
                    }
                }
    
                return cell;
    
            } else {
                NewsRow *cell = nil;
    
                NewsObject *newsObject = news[position];
                static NSString *CellIdentifier = @"NewsRow";
                cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    
                if (cell == nil) {
                    // Load the top-level objects from the custom cell XIB.
                    NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
                    // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
                    cell = topLevelObjects[0];
                    // Configure the cell...
    
                }
    
                cell.title.text = newsObject.title;             
                return cell;
            }
    
        }
        @catch (NSException *exception) {
            NSLog(@"Exception occurred: %@, %@", exception, [exception userInfo]);
        }
        return nil;
    }
    

    very good explanation on this post.

    http://useyourloaf.com/blog/2010/10/02/dynamically-loading-new-rows-into-a-table.html

    simple you have to add last row and hide it and when table row hit last row than show the row and load more items.

    0 讨论(0)
  • 2020-11-27 09:40

    Use limit and offset in your queries and fill your tableview with that content. When the user scrolls down, load the next offset.

    Implement the tableView:willDisplayCell:forRowAtIndexPath: method in your UITableViewDelegate and check to see if it's the last row

    0 讨论(0)
  • 2020-11-27 09:40
    let threshold = 100.0 // threshold from bottom of tableView
    var isLoadingMore = false // flag
    
    
    func scrollViewDidScroll(scrollView: UIScrollView) {
        let contentOffset = scrollView.contentOffset.y
        let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height;
    
        if !isLoadingMore && (maximumOffset - contentOffset <= threshold) {
            // Get more data - API call
            self.isLoadingMore = true
    
            // Update UI
            dispatch_async(dispatch_get_main_queue()) {
                tableView.reloadData()
                self.isLoadingMore = false
            }
        }
      }
    
    0 讨论(0)
  • 2020-11-27 09:42

    I have implemented one solution that i found in stackoverflow, and it works fine, but i think the shinyuX's solution it's very easy to implement and works fine for my propose. If someone wants a different solution can use this one below.

    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    
       // UITableView only moves in one direction, y axis
        CGFloat currentOffset = scrollView.contentOffset.y;
        CGFloat maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height;
    
        //NSInteger result = maximumOffset - currentOffset;
    
        // Change 10.0 to adjust the distance from bottom
        if (maximumOffset - currentOffset <= 10.0) {
            [self loadOneMorePage];
            //[self methodThatAddsDataAndReloadsTableView];
        }
    }
    
    0 讨论(0)
  • 2020-11-27 09:43

    for loading from an API, It works for me, Xcode10 , swift 4.2 :

    1- create New Swift file and do like this:

    //
    //  apiTVCController.swift
    //  ApiTestingTableView
    //
    //  Created by Hooma7n on 4/7/19.
    //  Copyright © 2019 Hooma7n. All rights reserved.
    //
    
    import Foundation
    import Alamofire
    
    class apiget {
    
        var tableData : [Datum] = []
        var loadin : [Datum] = []
        var testfortotal : Int?
    
    
        func getfromapi(completionHandler : ((_ isSucess : Bool) -> Void)?) {
            let url = "https://reqres.in/api/users?page=1"
            Alamofire.request(url, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil)
                .responseJSON(completionHandler : { response in
                    switch response.result {
                    case .success(let data):
                        guard let jsonData = try? JSONSerialization.data(withJSONObject: data, options: JSONSerialization.WritingOptions.prettyPrinted) else {return}
                        let decoder = JSONDecoder()
                        guard let result = try? decoder.decode(Welcome.self, from: jsonData) else {return}
                        self.tableData = result.data ?? []
                        self.testfortotal = result.total ?? 0
                        completionHandler?(true)
    
                    //                    print(result)
                    case .failure(let error):
                        print(error)
                    }
                })
        }
    
        var pagecounter : Int = 2
    
    
        func loadmore(completionHandler : ((_ isSucess : Bool) -> Void)?){
    
            let url = "https://reqres.in/api/users?page=\(pagecounter)"
            Alamofire.request(url, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil)
                .responseJSON(completionHandler : { response in
                    switch response.result {
                    case .success(let data):
                        guard let jsonData = try? JSONSerialization.data(withJSONObject: data, options: JSONSerialization.WritingOptions.prettyPrinted) else {return}
                        let decoder = JSONDecoder()
                        guard let myresult = try? decoder.decode(Welcome.self, from: jsonData) else {return}
                        self.loadin = myresult.data ?? []
                        self.tableData.append(contentsOf: myresult.data ?? [])
                        completionHandler?(true)
                        print(self.pagecounter)
                        self.pagecounter += 1
    
                    //                    print(myresult)
                    case .failure(let error):
                        print(error)
                    }
                })
    
        }
    
    }
    
    extension apiget {
    
        struct Welcome: Codable {
            let page, perPage, total, totalPages: Int?
            var data: [Datum]?
    
            enum CodingKeys: String, CodingKey {
                case page
                case perPage = "per_page"
                case total
                case totalPages = "total_pages"
                case data
            }
        }
    
        struct Datum: Codable {
            let id: Int?
            let firstName, lastName: String?
            let avatar: String?
    
            enum CodingKeys: String, CodingKey {
                case id
                case firstName = "first_name"
                case lastName = "last_name"
                case avatar
            }
        }
    
    
    }
    

    2- in Your ViewController file (tableView Controller) :

    //
    //  apiTVC.swift
    //  ApiTestingTableView
    //
    //  Created by Hooma7n on 4/7/19.
    //  Copyright © 2019 Hooma7n. All rights reserved.
    //
    
    import UIKit
    import Alamofire
    
    class apiTVC: UITableViewController {
    
        var datamodel = apiget()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            datamodel.getfromapi(completionHandler: {finish in
                if finish {self.tableView.reloadData()
                }
    
            })
    
        }
    
    
        override func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
    
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return datamodel.tableData.count
        }
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
            let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! apiTableViewCell
            cell.firstNameLabel.text = datamodel.tableData[indexPath.row].firstName
            cell.lastNameLabel.text = datamodel.tableData[indexPath.row].lastName
            cell.dateLabel.text = "\(datamodel.tableData[indexPath.row].id ?? 0)"
            cell.profileImageView.loadImage(fromURL: datamodel.tableData[indexPath.row].avatar ?? "")
    
            return cell
    
        }
    
        override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
            let lastElement = datamodel.tableData.count - 1
            let total = datamodel.testfortotal ?? 12
            if indexPath.row == lastElement && datamodel.tableData.count < total{
    
                datamodel.loadmore(completionHandler: {finish in
                    if finish {
    
                        self.tableView.reloadData()
    
                    }})
            }
        }
    }
    

    if using tableView in Your viewController set delegate,datasource self in viewDidLoad.

    0 讨论(0)
提交回复
热议问题