UIRefreshControl on viewDidLoad

I’m using the following code to create a UIRefreshControl:

- (void) viewDidLoad
{
    [super viewDidLoad];

    UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
    [refreshControl addTarget:self action:@selector(doLoad) forControlEvents:UIControlEventValueChanged];
    self.refreshControl = refreshControl;
}

- (void) doLoad
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
            // Instead of sleeping, I do a webrequest here.
            [NSThread sleepForTimeInterval: 5];

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

It works great. If I navigate to my view, drag the table, the code runs and the data displays.

  • Adding sections, separated by dates, to UITableView in Swift
  • Display Separator only for the Available CellForRow in UITableView
  • iPhone TableView with TextFields - next, previous button issue
  • add entires from textfield to UITableView to automatically populate
  • NSFetchedResultsController attempting to insert nil object
  • TableView not reloading with new data from AFNetworking Response Object
  • However, what I would like to do is have the view in the ‘loading’ state as soon as it appears (that way the user knows something is going on). I have tried adding the following:

    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        [self.refreshControl beginRefreshing];
    }
    

    But it does not seem to work. When I navigate to the view, it looks like a regular view (refresh control is not visible), plus when I try to pull the refresh control, it never finished loading.

    Obviously I’m going about this the wrong way. Any suggestions on how I should handle this?

    4 Solutions Collect From Internet About “UIRefreshControl on viewDidLoad”

    Try this:

    - (void) viewWillAppear: (BOOL) animated
    {
        [super viewWillAppear: animated];
    
        self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
        [self.refreshControl beginRefreshing];    
    
        // kick off your async refresh!
        [self doLoad];
    }
    

    Remember to call endRefreshing at some point!

    EDIT to add full working sample:

    This sample view controller, built and run in iOS6.1 as the root viewcontroller starts with the UIRefreshControl already visible and animating when the app launches.

    TSTableViewController.h

    @interface TSTableViewController : UITableViewController
    @end
    

    TSTableViewController.m

    #import "TSTableViewController.h"
    
    @implementation TSTableViewController
    {
        NSMutableArray*    _dataItems;
    }
    
    - (void) viewDidLoad
    {
        [super viewDidLoad];
    
        self.refreshControl = [UIRefreshControl new];
    
        [self.refreshControl addTarget: self
                                action: @selector( onRefresh: )
                      forControlEvents: UIControlEventValueChanged];
    }
    
    - (void) viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear: animated];
    
        self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
    
        [self.refreshControl beginRefreshing];
        [self onRefresh: nil];
    }
    
    - (void) onRefresh: (id) sender
    {
        double delayInSeconds = 2.0;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    
            _dataItems = [NSMutableArray new];
    
            for ( int i = 0 ; i < arc4random() % 100 ; i++ )
            {
                CFUUIDRef uuid = CFUUIDCreate( NULL );
    
                [_dataItems addObject: CFBridgingRelease(CFUUIDCreateString( NULL, uuid)) ];
    
                CFRelease( uuid );
            }
    
            [self.refreshControl endRefreshing];
    
            [self.tableView reloadData];
        });
    }
    
    #pragma mark - Table view data source
    
    - (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView
    {
        return 1;
    }
    
    - (NSInteger) tableView:(UITableView *) tableView numberOfRowsInSection: (NSInteger) section
    {
        return _dataItems.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
                                                       reuseIdentifier: nil];
    
        cell.textLabel.text = [_dataItems objectAtIndex: indexPath.row];
    
        return cell;
    }
    
    @end
    

    Manually modifying the contentOffset is insecure and wrong and can lead to unexpected behavior in some cases. This solution works without touching the contentOffset at all:

    func showRefreshControl(show: Bool) {
        if show {
            refreshControl?.beginRefreshing()
            tableView.scrollRectToVisible(CGRectMake(0, 0, 1, 1), animated: true)
        } else {
            refreshControl?.endRefreshing()
        }
    }
    

    Another option is fire a UIControlEventValueChanged in your viewDidAppear: to trigger an initial refresh.

       - (void) viewDidAppear: (BOOL) animated
       {
              [super viewDidAppear: animated];
              UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
              [refreshControl addTarget:self action:@selector(doLoad) forControlEvents:UIControlEventValueChanged];
              self.refreshControl = refreshControl;
              [self.refreshControl beginRefreshing];    
    
       }
    

    You never set self.refreshControl