Mircea Dragota
Mircea Dragota

Reputation: 754

UIRefreshControl is not displayed on iOS 17 (received offscreen beginRefreshing warning)

I tried to run a simple code which is running perfectly on iOS 14/15/16, but now while using Xcode 15 on iOS 17 this is not working anymore. I will attach the code below displaying a table view with a UIRefreshControl which will run beginRefreshing() before a fetch request, but the refresh control is not showing anymore on iOS 17 and I'm getting this warning which is saying that the refresh control received offscreen beginRefreshing.

enter image description here

Here is the code with a simulated fetch request using DispatchQueue.main.asyncAfter displaying a simple table view. If you try to run it on iOS 17, the refresh control will not be displayed before the simulated fetch request.

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    private let tableView = UITableView()
    private let cellIdentifier = "cellIdentifier"
    private let data = ["Cell 1", "Cell 2"]
    private let refreshControl = UIRefreshControl()

    override func viewDidLoad() {
        super.viewDidLoad()
        configureTableView()
        
        refreshControl.beginRefreshing()
        
        // Simulate fetch request
        DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
            self.setDataSource()
        })
    }
    
    private func configureTableView() {
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
        refreshControl.addTarget(self, action: #selector(refreshData), for: .valueChanged)
        tableView.refreshControl = refreshControl
        
        tableView.frame = view.bounds
        tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(tableView)
    }
    
    private func setDataSource() {
        tableView.dataSource = self
        tableView.reloadData()
        refreshControl.endRefreshing()
    }
    
    // MARK: - UITableViewDataSource
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
        cell.textLabel?.text = data[indexPath.row]
        return cell
    }
    
    // MARK: - Refresh Control Action
    
    @objc private func refreshData() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            self.refreshControl.endRefreshing()
            self.tableView.reloadData()
        }
    }
}

Do you know any fix for this in order to run the same way even on iOS 17, without using viewDidAppear or viewWillAppear methods, because I want the fetch to be called only once per view life cycle?

Upvotes: 4

Views: 1500

Answers (1)

rozeri dilar
rozeri dilar

Reputation: 381

With iOS 17, apple introduced a new lifecycle method: viewisappearing, it also has iOS 13+ backwards compatibility.

refreshControl.beginRefreshing() should be called in viewIsAppearing https://developer.apple.com/documentation/uikit/uiviewcontroller/4195485-viewisappearing

Upvotes: 8

Related Questions