Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIAlertController sometimes prevents UIRefreshControl to hide

I'm using UIRefreshControl on my tableview to update items. And the end, I show a UIAlertController to inform the user that the update was complete, and how many items were updated. Pretty straightforward, and it works fine for one thing. If I pull to refresh several times in a row, sometimes the refresh control is not dismissed, even after dismissing the alert. I need to swipe up the table to make it go away.

This is the code I use, all UI stuff is nicely done on the main thread:

    if(refreshControl.refreshing) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [self refreshItems];

            dispatch_async(dispatch_get_main_queue(), ^{
                [refreshControl endRefreshing];
                [self.tableView reload];
                [self showUpdateInfo];                     
            });
        });
    }

Any idea what could cause this?

EDIT: This is how I create the refresh control in code (in viewDidLoad):

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
refreshControl.attributedTitle   = [[NSAttributedString alloc] initWithString: @"Checking for updates…"];
[refreshControl addTarget: self
                   action: @selector(refreshOutdatedItems)
         forControlEvents: UIControlEventValueChanged];

self.refreshControl = refreshControl;
like image 855
koen Avatar asked Jan 08 '17 15:01

koen


2 Answers

Delaying by an arbitrary time is not a very clean solution. Implementing the delay by counting up a double to 10 million also is a bit of a hack in my opinion.

What I ended up doing instead is similar to this pseudo-Swift:

let alert = UIAlertController(...)
present(alert, animated: true) {
    refreshControl.endRefreshing()
}

It delays the call to endRefreshing() until the alert is presented.

My actual code using Swift 3 and PromiseKit looks more like this:

@IBAction func onRefresh() {
    Repository.getNewData().then { data in
        self.data = data
        self.tableView.reloadData()
    }.recover { error in
        // Return a promise to make sure endRefreshing isn't called until the alert is presented.
        return self.presentErrorAlert(message: "Some user-friendly message")
    }.always {
        self.refreshControl?.endRefreshing()
    }
}

private func presentErrorAlert(message: String) -> Promise<Void> {
    return Promise<Void> { fulfill, _ in
        let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        self.present(alert, animated: true) {
            fulfill()
        }
    }
}
like image 106
Johan Levin Avatar answered Oct 30 '22 06:10

Johan Levin


Those works for me:

    refreshControl.endRefreshing()
    tableView.setContentOffset(CGPoint.zero, animated: true)
    -- show alert than --
like image 34
Dren Avatar answered Oct 30 '22 06:10

Dren