1

Here is my code for the Refresh Control. ( Code has been updated with the entire ViewController Code for better understanding )

import UIKit

class AirportTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, AirportRequestDelegate {


    @IBOutlet weak var airportTable: UITableView!
    var airportRequest = AirportRequest()
    var airportList = [AirportDetail]()
    var refreshControl = UIRefreshControl()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "Airport List"
        airportTable.delegate = self
        airportRequest.delegate = self
        airportRequest.fetchAirports()
        airportTable.dataSource = self
        refreshControl.addTarget(self, action: #selector(refresh), for: UIControl.Event.valueChanged)
        airportTable.addSubview(refreshControl)

    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return airportList.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let myCell = airportTable.dequeueReusableCell(withIdentifier: "airportTableCell", for: indexPath)
        myCell.textLabel?.text = self.airportList[indexPath.row].AirportName
        myCell.detailTextLabel?.text = self.airportList[indexPath.row].StationName
        myCell.accessoryType = .disclosureIndicator
        return myCell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        self.airportTable.deselectRow(at: indexPath, animated: true)
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if  editingStyle == .delete {
            self.airportList.remove(at: indexPath.row)
            airportTable.deleteRows(at: [indexPath], with: .top)
        }
    }


// The AirportRequestDelegate adds this function and is called once airport list is fetched
    func didUpdateAirports(_ airportRequest: AirportRequest, airports: [AirportDetail]) {

// copies the airport list to a local variable so that it can be used with the tableView delegate functions
        self.airportList = airports

// updating the UI
        DispatchQueue.main.async {
            self.airportTable.reloadData()
            self.refreshControl.endRefreshing()
        }
    }

    @objc func refresh(sender: UIRefreshControl) {
        airportRequest.fetchAirports()
    }


}

In the Image below you can see that the animation doesnt work as expected. how do i fix this. Preferably I would like the animation to continue until the tableView has been updated.

enter image description here

asreerama
  • 144
  • 1
  • 9
  • 1
    Could you add the function where you perform the request and then call the delegate `didUpdateAirports`? – IloneSP Feb 04 '20 at 16:46
  • Here is a [link](https://github.com/asreerama/JBeta.git) to a public github repository with the entire project (instead of cluttering the question with lots of code). I'm sure you will be able to reproduce the issue. Thank you for the assistance. – asreerama Feb 04 '20 at 17:50

5 Answers5

5

Adding refresh control as a subview could be a problem. UITableView now have property for the refresh control. Here you have description from apple documentation how you should implement that: https://developer.apple.com/documentation/uikit/uirefreshcontrol

  • I don't think this is causing the issue depicted in the question. While it is true that using refreshControl property of the tableView is better practice, I've been using addSubview in my projects without any issue (but I'll start using that property for now on). – IloneSP Feb 04 '20 at 17:08
  • Thank you for the update, but as Roberto said , I changed my code to use the property but that did not affect the issue. – asreerama Feb 04 '20 at 17:17
  • 1
    I also found similar issue: https://stackoverflow.com/questions/35391236/refreshcontrol-bugs-first-cell-of-tableview-after-using-back-on-navbar – Łukasz Łabuński Feb 04 '20 at 17:41
  • Thanks a lot for pointing me towards this, It solved my Issue. @Roberto thank you for your help and patience as well. – asreerama Feb 04 '20 at 18:18
  • can you please check this question --> https://stackoverflow.com/q/67056902/15466427 – coceki Apr 13 '21 at 05:34
2

You're instantly ending the animation after calling the fetchAirports(), which I assume is an async network request or something that has a delay to complete.

If you want to wait until the airports are fetched and the table is updated, add a completion closure to that function. Something like that:

func fetchAirports(@escaping completion: (() -> Void) {
    // Perform the network request and once it finishes, call completion()
    networkRequest() {
        completion()
    }
}

And then in your refresh method:

@objc func refresh(sender: UIRefreshControl) {
    airportRequest.fetchAirports(completion: { [weak self] in
        self?.sender.endRefreshing()
    })
}
IloneSP
  • 429
  • 2
  • 13
  • I have a delegate function that is called after the fetchAirports() function is done. The delegate function updates the tableView after which I added the endRefreshing() function but this too did not solve the problem. – asreerama Feb 04 '20 at 15:08
  • Are you sure the `endRefreshing()` function gets called after and only after the tableView is reloaded? – IloneSP Feb 04 '20 at 15:09
  • yes Ive made sure of that. In any case, I will update my question with the entire code so that you have a better idea. – asreerama Feb 04 '20 at 15:27
1

Try it ..

@objc func refresh(sender: UIRefreshControl) {
        refreshControl.beginRefreshing()
        airportRequest.fetchAirports(completion: { [weak self] in
            self?.tableView?.reloadData()
            refreshControl?.endRefreshing()
     })
  } 
Jayesh Patel
  • 938
  • 5
  • 16
0

You can call this method after insert new data into table.

refreshControl.endRefreshing()
deepak
  • 78
  • 4
0

Put a breakpoint in didUpdateAirports and see where (and when) it's being called from. The animation should only stop once endRefreshing is called.

Perhaps there is no change in the data, so maybe you're getting a cached response which would lead to a very quick endRefreshing call.

You can also put in a simple print ("ending animation now") statement to see it happening in real time.

EarlGrey
  • 2,514
  • 4
  • 34
  • 63