13

I've seen quite a few problems with UIRefreshControl, and I'm having a problem as well with my UITableViewController. The problem occurs so randomly and henceforth I cannot figure out why or how it happens.

The problem is that sometimes when you scroll down on the tableView, the UIRefreshControl appears in the wrong place, and what seems like above/on top of the tableView itself. I'm attaching a screenshot of what the problem looks like, and also my code used to add the UIRefreshControl and it's refreshing method as well.

I appreciate any help offered!

enter image description here

- (void)viewDidLoad
{
    self.refreshControl = [[UIRefreshControl alloc] init];

    [self.refreshControl addTarget:self action:@selector(refreshing:) forControlEvents:UIControlEventValueChanged];

    [self.tableView addSubview:self.refreshControl];

    self.tableView.tableFooterView = [[UIView alloc] init];
}

- (void)refreshing:(UIRefreshControl*)refreshControl
{
    [refreshControl beginRefreshing];

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    [refreshControl endRefreshing];

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
 }
klcjr89
  • 5,862
  • 10
  • 58
  • 91

3 Answers3

27

This is a known bug with iOS7; sometimes the refresh control is put incorrectly in the front of the view hierarchy instead of back. You can counter part of the problem by sending it to back after layout:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    [self.refreshControl.superview sendSubviewToBack:self.refreshControl];
}

Animation will still be imperfect, but at least it will still be below the table view. Please open a bug report with Apple for this issue.

Also, as stated in another answer, you should not add the refresh control to the view hierarchy yourself. The table view controller will do that for you. But that is not the issue here.

Swift version

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    refreshControl?.superview?.sendSubview(toBack: refreshControl!)
}
Voyteck
  • 354
  • 2
  • 13
Léo Natan
  • 56,823
  • 9
  • 150
  • 195
  • How do I add it to the tableView then? Just call alloc init on the refresh control? – klcjr89 Jan 24 '14 at 17:08
  • @troop231 You create it, add target/action and assign it to the table view controller. It will take care of things for you. – Léo Natan Jan 24 '14 at 17:09
  • I just removed the addSubview part, and already have the target/action, now how do i assign it? As the table view is already showing it – klcjr89 Jan 24 '14 at 17:11
  • @troop231 `self.refreshControl = [[UIRefreshControl alloc] init];` is enough to make it display. – Léo Natan Jan 24 '14 at 17:11
  • @troop231 you already added to tableView with the first line of code in viewDidLoad, no need of 3rd line . – santhu Jan 24 '14 at 17:12
  • I got it now, thanks. I was confused when I saw a refreshControl property in the tableView delegate, but you can't access it for some reason – klcjr89 Jan 24 '14 at 17:15
  • And to prevent refresh control being hidden by table view background, do immediately after: `[self.tableView.backgroundView.superview sendSubviewToBack:self.tableView.backgroundView];` – Andrew Morris Dec 14 '18 at 14:37
18

I had the same problem and fixed it with this:

override func viewDidLoad() {
    let refreshControl = UIRefreshControl()
    refreshControl.addTarget(self, action: "refresh:", forControlEvents: .ValueChanged)
    tableView.backgroundView = refreshControl // <- THIS!!!
}

Instead of adding a subview assign the refreshControl as backgroundView

Andres
  • 11,439
  • 12
  • 48
  • 87
  • This only works if you only want 1 thing in your view, but if you have things like an infinite scroll or other things this will break that – Tomer Shemesh Jun 09 '16 at 17:00
0

override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() refreshControl?.superview?.sendSubview(toBack: refreshControl!) }