UIRefreshControl endRefreshing is not smooth

2019-04-04 02:48发布

问题:

When my data has finished loading and my tableView has been reloaded, I call endRefreshing on my refresh control, it then 'jumps' from its loading state and disappears - how can I implement a smooth animation that slides the refresh control away when it is complete?

回答1:

I'm adding a new answer since the only one is not very precise in explanation. And this would probably be too much for comments.

The solution by Halpo is correct. But the reason mentioned is wrong.

Calling -[NSObject (NSDelayedPerforming) performSelector:withObject:afterDelay:] guarantees the call to be performed in the next runloop iteration.

So this does not work, because there is a small delay, but you're moving it to the next loop.

Actually you sh/could decrease the delay to 0.0 and it would still work. Also, a more correct (in terms of using the object meant to achieve something, not better in outcome) implementation is possible.

Here are the various code snippets for that:

// SOLUTION I

[[self tableView] reloadData];
[[self refreshControl] performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.0];

// SOLUTION II (semantically correct)

[[self tableView] reloadData];
[[NSOperationQueue currentQueue] addOperationWithBlock:^{
    [[self refreshControl] endRefreshing];
}];

// SOLUTION III (GCD)

dispatch_async(dispatch_get_main_queue(), ^{
    [[self refreshControl] endRefreshing];
}];

EDIT

As johann-fradj pointed out, you could also use GCD. I do like GCD a lot, but I think, solution II is most descriptive about whats going on. Also, GCD is not very Objective-C-ish, so I'd personally rather stick to my solution.



回答2:

Fixed the problem - I added a tiny delay to endRefresh after reloading data:

[self.tableView reloadData];
[self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.05];


回答3:

Swift 4 solution

    tableView.reloadData()
    if refreshControl?.isRefreshing == true {
        DispatchQueue.main.async {
            self.refreshControl?.endRefreshing()
        }
    }

Other solutions (delay, using CATransaction, ...) are more complex and don't give any visible advantage



回答4:

Try wrapping the call to endRefreshing() in a UIView animation block:

I've run into this issue as well. For endRefreshing() The documentation states:

UIView.animateWithDuration(0.5) {
    self.refreshControl.endRefreshing()
}

The documentation states:

If animations are also enabled, the control is hidden using an animation.



回答5:

One of the reasons can be that you refresh your table view after the endRefresh is called. For example, my calls used to look like this:

[self.refreshControl beginRefreshing];
[self.network getThings:^(id things, NSError *error) {
    [self.refreshControl endRefreshing];

    if (error) {
        ...
    } else {
        self.things = things;
        [self.tableView reloadData];
    }
}];

Which is bad, because the table updates itself while the endRefresh animation runs. So the code done right looks like this:

[self.refreshControl beginRefreshing];
[self.network getThings:^(id things, NSError *error) {
    if (error) {
        [self.refreshControl endRefreshing];
        ...
    } else {
        self.things = things;
        [self.tableView reloadData];
        [self.refreshControl endRefreshing];
    }
}];