A bug with UIRefreshControl + UISearchController?

When adding UISearchController to a UITableViewController's navigation item, refresh control becomes invisible.


I could not find any official info on whether iOS 13's behavior has changed in regards to search bars or refresh controls, and/or what I have to change to make it work, so I assume this is a bug.


The bug is easily reproducible with Xcode 11 Beta on both iOS 13 Beta 1 and Beta 2, as well as when running apps built with Xcode 10 on iOS 13 Betas. The same code works perfectly fine on iOS 12 and older: refresh control gets embedded in a navigation item and is visible and animated as expected.


1. Create a Storyboard with a simple navigation stack: UITableViewController embedded in a UINavigationController

2. Enable refreshing for UITableViewController

3. in viewDidLoad method, create UISearchController and assign it to the self.navigationItem.searchController


var searchController: UISearchController!

override func viewDidLoad() {
    super.viewDidLoad()
    
    searchController = UISearchController(searchResultsController: nil)
    navigationItem.searchController = searchController
    
    // This doesn't change anything, but I added it to be consistent with official code for UISearchController
    definesPresentationContext = true
}


Without search controller embedded in a navigation item, refresh control is visible and working as expected. With search controller, refresh control still seems to be functioning and sending control events, but it's invisible on the screen.


Can someone confirm if this is a known issue or if I'm doing something wrong here?

Replies

Digging a little deeper, it seems like refresh control's UI is not present at all in the view hierarchy. I can add it manually in hacky way by going through the UIRefreshControl's view heirarchy:


for subview in refreshControl?.subviews {
    view.addSubview(subview)
}


That does make it appear when doing pull-to-refresh, but it (obviously) buggy because table view doesn't apply proper insets for the content in this case.

I'm having the same issue. I don't suppose you ever resolved this?

in my case the following helped


navigationItem.hidesSearchBarWhenScrolling = false
Code Block
navigationItem.hidesSearchBarWhenScrolling = false


this worked for me too!

Setting navigationItem.hidesSearchBarWhenScrolling = false will enforce search bar visibility.

I found a working solution which lets you keep the default search bar behavior. Just recreate and assign the refresh control every time the table view controller appears on screen.

If you subclassed UITableViewController:

class MyTableViewController: UITableViewController {

    ...

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        refreshControl = UIRefreshControl(...)
    }
}

If you use view controller containment:

class MyContainerViewController: UITableViewController {

    private let tableViewController = UITableViewController(style: .plain)

    ...

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        tableViewController.refreshControl = UIRefreshControl(...)
    }
}