23

How can I reveal the UIRefreshControl when I update the table programmatically? Using [self.refreshControl beginRefreshing] make the spinner animate but does not reveal it.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Dave Ronsom
  • 253
  • 3
  • 5
  • 9

4 Answers4

20

You'll have to manually change the contentOffset of your UITableView yourself. Be sure to account for the contentInset.top. It should be something as simple as:

CGPoint newOffset = CGPointMake(0, -[myTableView contentInset].top);
[myTableView setContentOffset:newOffset animated:YES];
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • 3
    I've noticed this tends to cause problems with cell drawing if you don't reset the contentOffset as well after you call `endRefreshing`: `[myTableView setContentOffset:CGPointZero animated:YES];` – Alan Zeino Jan 16 '13 at 05:18
  • 2
    To fix that in iOS 8 or iOS 9, just call `[self.refreshControl beginRefreshing]` from `viewWillAppear:` method, not from `viewDidLoad:`. – Alexander Perechnev Mar 01 '16 at 15:09
  • 2
    Anybody else who has the problem that the color of the spinning wheel is wrong when triggering the refresh by with beginRefresh()? In my case with a dark background it is black, but it is white when triggering the refresh with a pull. Setting the tint color does not help. – sofacoder Jul 14 '16 at 14:48
  • 1
    @AlexanderPerechnev it worked for me in ios 9 in viewDidLoad but now in iOS 10 it doesnt even work in viewWillAppear – Bushra Shahid Sep 21 '16 at 13:03
  • @sofacoder Yep, I'm having the same issue. Tint color is black when beginRefreshing is called from viewWillAppear:, and nothing I do will change that. Changing the tintColor works from then on, but not on the first display. – itnAAnti Nov 11 '16 at 16:04
18

This will do the trick

- (void)beginRefreshingTableView {

    [self.refreshControl beginRefreshing];

    // check if contentOffset is zero
    if (fabsf(self.tableView.contentOffset.y) < FLT_EPSILON) {

        [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){

            self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);

        } completion:^(BOOL finished){

        }];

    }
}
leviathan
  • 11,080
  • 5
  • 42
  • 40
Peter Lapisu
  • 19,915
  • 16
  • 123
  • 179
  • 1
    Apparently you need to call `beginRefreshing` inside the completion block of the tableview animation, or after the animation call, otherwise the tintColor of the refreshControl set by you will be ignored. http://stackoverflow.com/a/20383030/903544 – René Nov 08 '15 at 14:43
9

For Swift 3, this is what I have based on Peter Lapisu's answer:

override func viewDidLoad() {
    super.viewDidLoad()
    self.refreshControl?.addTarget(self, action: #selector(refresh), forControlEvents: UIControlEvents.ValueChanged)
    // ...
}

func refresh(sender:AnyObject) {
    self.refreshControl?.beginRefreshing()

    if let yOffsetTable = self.tableView?.contentOffset.y {
        if yOffsetTable < CGFloat(Float.ulpOfOne) {
            UIView.animate(withDuration: 0.25, delay: 0, options: UIViewAnimationOptions.beginFromCurrentState, animations: {
                if let refreshControlHeight = self.refreshControl?.frame.height {
                    self.tableView?.contentOffset = CGPoint(x: 0, y: -refreshControlHeight)
                }
            }, completion: nil)
        }
    }
}
Gobe
  • 2,559
  • 1
  • 25
  • 24
7

For Swift 5, this is the only working version for me.

extension UIRefreshControl {

    func beginRefreshingManually() {
        if let scrollView = superview as? UIScrollView {
            scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - frame.height), animated: false)
        }
        beginRefreshing()
        sendActions(for: .valueChanged)
    }

}
LukasHromadnik
  • 387
  • 4
  • 8
  • In the end, it turned out this is not what I needed, but your version works pretty well for sure! – choofie May 16 '22 at 16:46