29

My UIRefreshController is doing something odd. When I pull-down refresh, the tableView headers are displaced.

If I pull-down it looks fine, but if I scroll down the table while the refresher is still working, the headers are offset by the height of the refresh control while the UITableCells are fine and scroll behind the header.

http://oi49.tinypic.com/2a8hqti.jpg

http://oi46.tinypic.com/acssrb.jpg

I want to avoid creating a tableViewController, and so I am doing the following in viewDidLoad:

_refreshControl = [[UIRefreshControl alloc] init];
[_refreshControl addTarget:self action:@selector(refresh) forControlEvents:UIControlEventValueChanged];
[_tableView addSubview:_refreshControl];

I have a lot of tables in different view controllers that require this functionality. Is there any way I can avoid making a UITableViewController for each one?

Thanks a ton!

iDev
  • 23,310
  • 7
  • 60
  • 85
Dave
  • 534
  • 1
  • 5
  • 14
  • 1
    Post the image to [some third party image uploading sites](http://tinypic.com/) and share the links here. We can help you to post the images here. – iDev Mar 05 '13 at 20:18
  • possible duplicate of [UIRefreshControl without UITableViewController](http://stackoverflow.com/questions/12497940/uirefreshcontrol-without-uitableviewcontroller). Instead of focusing on not using UITableViewController, you should focus on creating an abstract superclass you easily extend to add specific functionalities to each table. Controllers are cheap, I'm surprised you're averse to them. – CodaFi Mar 05 '13 at 20:59
  • 1
    Good idea, thanks! When I first refresh it looks like : http://tinypic.com/view.php?pic=2a8hqti&s=6 then when I scroll down it looks like: http://tinypic.com/view.php?pic=acssrb&s=6 – Dave Mar 05 '13 at 21:02
  • 3
    I do not believe it is a duplicate of that post, his code is actually the same as the answer to the question from the post you are linking to. – JonathanC Mar 05 '13 at 21:02
  • @JonathanC he's asking *why* it doesn't work (that is answered in the other question's answer (it's an implementation detail and a hack that it worked in the other post)) How is this not a dupe? – CodaFi Mar 05 '13 at 21:05
  • The reason I have been using UIViewControllers instead of UITableViewControllers is [here](http://stackoverflow.com/questions/11507921/objective-c-ios-subclassing-uitableviewcontroller-for-a-custom-view). I wonder how I could avoid the issues brought up in that post if I build UITableViewControllers. Thanks for your help @CodaFi – Dave Mar 05 '13 at 21:30
  • @Dave for all the problems layed out in that post, you have to admit, the accepted answer shows just how much they handle for us automatically. Plus, with access to the internals of UITableView, they are always aware of the unnecessary implementation details that are causing you headaches. I wouldn't really suggest recreating them, rather, subclass them and use that subclass as that "abstract superclass" I keep bringing up. – CodaFi Mar 05 '13 at 21:36
  • I ran into the same issue where the header would quickly move down then jump up again when the tableview is bounced at the bottom. I fixed this by switching from using my own tableview to using the UITableViewController instead. – sudoExclaimationExclaimation Jan 28 '16 at 20:51
  • iOS 10.0+ has refreshControl property for UITableView. Using it solves this problem. more info: [link](https://cocoacasts.com/how-to-add-pull-to-refresh-to-a-table-view-or-collection-view) – Okhan Okbay Feb 11 '18 at 21:29

5 Answers5

18

This could be an issue due to the fact that you are adding _refreshControl as a subview which is not supposed to be done. However you can create a UITableViewController object add it as the child view controller of your current viewcontroller class.

For eg:-

UITableViewController *tableViewController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
[self addChildViewController:tableViewController];

tableViewController.refreshControl = [[UIRefreshControl alloc] init];
[tableViewController.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
tableViewController.tableView.frame = CGRectMake(...);//set the frame here
[self.view addSubview:tableViewController.tableView];
iDev
  • 23,310
  • 7
  • 60
  • 85
  • 2
    What if I'm not using a `UITableViewController` but instead using a `UITableView`. – Hemang Jun 10 '15 at 10:11
  • View hierarchy for my controller is like this : `view` -> `scrollview` -> - `TableView_1` & `TableView_2`. – Hemang Jun 10 '15 at 10:12
  • 1
    I actually managed to get it working by calling: `[self.tableView setContentOffset:CGPointMake(0, -1) animated:YES];` after the refresh control was added to the table view. – Pontus Jan 13 '16 at 09:34
  • @Pontus i have uitablview than how can i handle ? – ramesh bhuja Jun 24 '16 at 07:27
  • @rameshbhuja just add: `[self.tableView setContentOffset:CGPointMake(0, -1) animated:YES];` in the `- (void)viewDidLoad` method AFTER you add the UIRefreshControl like this: `tableViewController.refreshControl = [[UIRefreshControl alloc] init];` – Pontus Jun 25 '16 at 09:14
  • 1
    @rameshbhuja There is no difference. In the view controller just add the lines above connected to your table view outlet. – Pontus Jun 25 '16 at 10:07
17

a quick fix to this is to go like this

Objective-C

//header
@property UITableViewController *tableController;

//.m (right at the beginning of viewDidLoad for example)
self.tableController = [[UITableViewController alloc] init];
[self addChildViewController:self.tableController];
self.tableController.tableView = self.tableView;

...

//then create the refresh control and assign it to the UITableViewController
self.tableController.refreshControl = refreshControl;

Swift 2.1

//Create an instance of a UITableViewController. This will host your UITableView.
private let tableController = UITableViewController()

//Add tableController as a childViewController and set its tableView property to your UITableView.
self.addChildViewController(self.tableController)
self.tableController.tableView = self.tableView
self.refreshControl.addTarget(self, action: "refreshData:", forControlEvents: .ValueChanged)
self.tableController.refreshControl = self.refreshControl

this helps if you have your table hooked up to an IBOutlet and have other things linked into the storyboard you dont feel like messing with.

Leverin
  • 739
  • 8
  • 24
Fonix
  • 11,447
  • 3
  • 45
  • 74
1

UIRefreshControl's aren't meant to be subviews, they're meant to (literally) be the table's refresh control. UITableViewController has an outlet specifically for them (again, literally called refreshControl) that you should be using. As a subview of the table, you may be causing the table to assume it's a cell, rather than just a subview, which forces a recalculation around it. There will be cases where you do get lucky and the control may set itself in the right place, but this is, again, the result of undefined behavior.

UITableViewController is not meant to be a limiting class, and it certainly should not keep you from implementing "multiple table views" (which sound context-specific enough that they'd warrant a new view controller presented anyhow). If you are worried about having to write boilerplate for each class, write an abstract superclass controller for every table view you want to implement, and subclass it as necessary.

CodaFi
  • 43,043
  • 8
  • 107
  • 153
1

@available(iOS 10.0, *)

tableView.refreshControl = refreshControl

baluhman
  • 25
  • 9
0

Try this way to add push view controller.

Create a table view controller and add it as the sub view of existing view controller. Then assign your table view and refresh controllers to tableview controller's properties.

UITableViewController *newTableViewController = [[UITableViewController alloc] init];
newTableViewControler.tableView = <yourTableView>;

<yourRefreshController> = [[UIRefreshControl alloc] init];
[<yourRefreshController> addTarget:self
                            action:@selector(refreshTableView:)
                  forControlEvents:UIControlEventValueChanged];

newTableViewController.refreshControl = _chatListRefreshControl;
[self addChildViewController:newTableViewController];
Anand Prem
  • 397
  • 5
  • 15