35

Occasionally my table view won't be connected to a service to refresh, and in that case, I don't want the UIRefreshControl to be present.

After I add it in viewDidLoad, I've tried hiding it under certain circumstances with setEnabled: and setHidden: but neither seems to work.

Doug Smith
  • 29,668
  • 57
  • 204
  • 388
  • 1
    You may find this funny. I tried sub classing both the control and tableview. I intercepted messages looking for something to leverage, no luck. Finally with the tableview I overrode -subviews so as to hide the control, no luck. In the end if all you do is just add the control using addSubview, the tableview holds a strong reference to it. No amount of hiding, fixing frames, would work. – David H Oct 31 '13 at 12:31

13 Answers13

41

Try setting your table view controller's refreshControl property to nil.

Jonathan Arbogast
  • 9,620
  • 4
  • 35
  • 47
  • 1
    If I do a check in viewDidAppear (setting it if there is a service to connect to, setting to nil if there isn't) I get the following warning when I reload the view with the refresh control in it after I remove the service: "Attempting to change the refresh control while it is not idle is strongly discouraged and probably won't work properly." (Though it does visually seem to work.) – Doug Smith Oct 20 '13 at 21:26
  • 4
    Ok, I would suggest calling `endRefreshing` on the refresh control before removing it. You could also try doing this in `viewWillAppear` instead of `viewDidAppear`. – Jonathan Arbogast Oct 21 '13 at 00:47
  • `[self.refreshControl endRefreshing];` still triggers that warning if called before `self.refreshControl = nil;`. – Doug Smith Oct 22 '13 at 21:55
  • I just tried this out and `self.refreshControl = nil;` works perfectly for me as long as outside the `UIControlEventValueChanged` fired method and `endRefreshing` method has been called. My only suggestion would would be to insert logging the start/end of your refresh call and `nil` assignment call to make sure the assignment isn't happening while a refresh event is occurring. – DBD Oct 29 '13 at 13:30
  • This was my first attempt as well; came to SO to look for answers as first calling `endRefreshing` before setting the control to `nil` still gives me the warning (and yes, I've logged the calls and made sure `endRefreshing` is called _before_ setting the control to `nil`). Any other ideas? -- EDIT: As an added measure; I also added a `dispatch_after` after `endRefreshing` so the control will be set to `nil` after 10 seconds. Sadly I _still_ get the warning. -- YET ANOTHER EDIT: In the `dispatch_after` logged control's `isRefreshing` which returned `NO`, but **still** received the message – Gerald Eersteling Oct 07 '15 at 09:50
  • @DougSmith Just use [self.refreshControl removeFromSuperview]; – priwiljay Jan 18 '18 at 07:32
10

You have several ways to do this. I think the best way is to do a check in the viewDidLoad method with:

if (condition){
 //attach refreshControl
}

if this isn't possible the best way is put this code where you want to hide the refresh (I think in viewWillAppear method in if condition)

//End refresh control
[self.refreshControl endRefreshing];
//Remove refresh control to superview
[self.refreshControl removeFromSuperview];
Serluca
  • 2,102
  • 2
  • 19
  • 31
9

Try this:

[self.refreshControl removeFromSuperview];
self.refreshControl = nil;
Sviatoslav Yakymiv
  • 7,887
  • 2
  • 23
  • 43
8

I had bad experience when set tableView.refreshConrol = nil , because when I set it back to old refreshControl, it started animation only in a second, so it looked not good, so when I need to disable refreshControl I use:

tableView.refreshControl?.endRefreshing()
tableView.refreshControl?.alpha = 0

when I need it back I use:

tableView.refreshControl?.alpha = 1
// and if I need to show refreshing indicator immediately I write:
tableView.refreshControl?.beginRefreshing()

P.S. Setting isHidden, isEnabled, isUserInteractionEnabled didn't help

Paul T.
  • 4,938
  • 7
  • 45
  • 93
5

There’s a very simple solution you can try: [self.refreshControl removeFromSuperview];

fish2000
  • 4,289
  • 2
  • 37
  • 76
jenish
  • 268
  • 3
  • 11
2

An old question, but I was looking for an answer and nothing worked exactly like I wanted.

This is what worked for me:

Swift 4

func createRefreshControl() {
    refreshControl = UIRefreshControl()
    refreshControl?.addTarget(self, action: #selector(self.myTableRefreshFunction), for: UIControlEvents.valueChanged)
    refreshControl?.tintColor = UIColor.white
    refreshControl?.endRefreshing()
}

func removeRefreshControl() {
    refreshControl?.removeTarget(self, action: #selector(self.myTableRefreshFunction), for: UIControlEvents.valueChanged)
    refreshControl = nil
}

I call createRefreshControl() when I want the control created, and removeRefreshControl when I want it removed.

I had to remove the same target I initially added to the refresh control, otherwise it would refresh one time before it was actually removed.

iOS_Mouse
  • 754
  • 7
  • 13
1

You can not remove the UIRefreshControl using setEnabled:NO,so to this you have to remove it from it's superview.I have tried a sample using Reachability class provided by Apple.

To add UIRefreshControl you can use this:

UIRefreshControl *refContr=[[UIRefreshControl alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
        [refContr setTintColor:[UIColor blueColor]];
        [refContr setBackgroundColor:[UIColor greenColor]];

    [self.view addSubview:refContr];
    [refContr setAutoresizingMask:(UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleLeftMargin)];        
    [refContr addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];

Then Implemented reachability class notification :

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];

You can do it by using bool flag to check the connectivity ,Here i'm providing this example using reachability class by apple to check my connectivity.

switch (netStatus)
{
    case NotReachable:        {

         for (UIRefreshControl *subView in [myView subviews]) {
             if ([subview isKindOfClass:[UIRefreshControl class]]) {
                 [subView removeFromSuperview];
             }
         }
          //or you could use [UIRefreshControl setHidden:YES];

          connectionRequired = YES;
          break;
    }

    case ReachableViaWiFi:        {
         for (UIRefreshControl *subView in [myView subviews]) {
             if ([subview isKindOfClass:[UIRefreshControl class]]) {
                 [subview removeFromSuperview];
             }else{
               [self.view addSubview:refContr];
         }
         //or you could use [UIRefreshControl setHidden:NO];
        break;
    }
} 

Hope this will work for you.

Arun_
  • 1,806
  • 2
  • 20
  • 40
  • in case ReachableViaWiFi you'll be added with n = myView.subviews.count of refreshControls, think its not desired behavior – slxl Dec 14 '15 at 13:08
1

To hide refresh control and avoid warning Just use

Objective C

[self.refreshControl removeFromSuperview];

Swift

self.refreshControl.removeFromSuperview()
priwiljay
  • 2,603
  • 1
  • 18
  • 21
1

I solved this problem by calling "yourRefreshControl".endEditing() inside the refresh function.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
0
[refreshControl setTintColor:[UIColor clearColor]];

also you can do something like this:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView.contentOffset.y < 0)
        scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, 0);
}
ChikabuZ
  • 10,031
  • 5
  • 63
  • 86
0

I solved it this way:

-(void)updateUIWithAuthState:(BOOL)isAuthenticated {
    self.loginButton.enabled = !isAuthenticated;
    self.loginButton.tintColor = isAuthenticated ? [UIColor clearColor] : nil;

    self.logoutButton.enabled = isAuthenticated;
    self.logoutButton.tintColor = isAuthenticated ? nil : [UIColor clearColor];

    self.tableView.userInteractionEnabled = isAuthenticated;
    self.data = nil;
    [self.tableView reloadData];
}
0

The best to implement UIRefreshControl is below.

 -(void)addRefreshControll{
    self.refreshControl=[[UIRefreshControl alloc] init];
    self.refreshControl.tintColor=[UIColor colorWithRed:0 green:183.0/255.0 blue:213/255.0 alpha:1.0];
    self.refreshControl.attributedTitle = [[NSAttributedString alloc]initWithString:@"Loading history..."];
    [self.refreshControl addTarget:self action:@selector(loadMoreChatFromCoreData) forControlEvents:UIControlEventValueChanged];
    self.tableView.refreshControl = self.refreshControl;
}

When there is no more record to load then remove refreshControl by below line

self.tableView.refreshControl = nil;

I have implemented same working fine.

Pandey_Laxman
  • 3,889
  • 2
  • 21
  • 39
0

Tried using this solution from iOS_Mouse on Xcode 12. IOS 14.4.1 but it didn't work properly. Managed to tweak it to be this. (note that this is for my use case with UISegmentedControl. I have a different tables, of which only 1 I wanted to be able to use Pull To Refresh feature.

  var refreshControl = UIRefreshControl()

      @IBAction func vcLibFilterPressed(_ sender: UISegmentedControl) {
        removeRefreshControl()
        switch vcLibSegmentedControl.selectedSegmentIndex {
        
        case 0:        // Plan
          enableDisableSegmentedControl()
          vcLibTableView.reloadData()
          
          
        case 1:        // Tag
          vcLibTableView.reloadData()
          createRefreshControl()
    
        default:
          break
        }
      }


  func createRefreshControl() {
    refreshControl.addTarget(self, action: #selector(syncDropbox), for: .valueChanged)
    refreshControl.tintColor = UIColor.red
    refreshControl.attributedTitle = NSAttributedString(string: " ↓ Refresh ↓ ")
    tableView.refreshControl = refreshControl
  }

  func removeRefreshControl() {
    refreshControl.removeTarget(self, action: #selector(syncDropbox), for: .valueChanged)
    tableView.refreshControl = nil
  }
  
  
  @objc func syncDropbox(refreshControl: UIRefreshControl) {
    if vcLibSegmentedControl.selectedSegmentIndex == 1 {
      // DO Internet Stuffs

      // somewhere in your code you might need to call:
      refreshControl.endRefreshing()
    }
  }
app4g
  • 670
  • 4
  • 24