iOS 7 UIRefreshControl tintColor not working for beginRefreshing

IphoneObjective CXcodeUser InterfaceIos7

Iphone Problem Overview


I'm trying to set a tintColor on my UIRefreshControl (building on iOS 7). I enabled refreshing for the tableViewController in storyboard, then in my ViewController viewDidLoad method i did the following:

[self.refreshControl setTintColor:[UIColor redColor]];

So now, when I pull to refresh, the color of the refresh control is red indeed:

redSpiny

I want my view to update automatically when it appears, so I did:

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

It didn't show the spinning wheel, according to https://stackoverflow.com/a/16250679/1809736, I added

[self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:NO];

to force show it. It shows it, but now it is back to default color:

enter image description here

If I try manually to pull to refresh afterwards, it is red.

I tried building it on iOS6 and it works as it should, so is that an iOS7 bug?

P.S.: it is not a problem with the simulator, I tried building it on device, same bug.

P.P.S: I built an example project, can you tell me if you have the same bug or if there is a problem in my code? Here is the link: http://d.pr/f/pGrV

Thanks a lot !

Iphone Solutions


Solution 1 - Iphone

Hey just stumbled into this exact issue.

Interestingly I fixed my code by setting the contentOffset first then calling beginRefreshing

if(self.tableView.contentOffset.y == 0){
    self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
    [self.refreshControl beginRefreshing];
}

You may want to animate this process:

[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
    self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
} completion:^(BOOL finished) {
    [self.refreshControl beginRefreshing];
}];

Hope this helps you.

W

Solution 2 - Iphone

SWIFT SOLUTION ! Insert the following code in the viewDidLoad:

self.refreshControl.tintColor = UIColor.orangeColor()
self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height)
self.refreshControl.beginRefreshing()

Swift 3.1

self.refreshControl.tintColor = UIColor.orange
self.tableView.contentOffset = CGPoint(x:0, y:-self.refreshControl.frame.size.height)
self.refreshControl.beginRefreshing()

Solution 3 - Iphone

@william-george's answer set me in the right direction, but was giving me weird autolayout animation issues.

So here's the version that worked for me:

- (void)programaticallyRefresh {
    // Hack necessary to keep UIRefreshControl's tintColor
    [self.scrollView setContentOffset:CGPointMake(0, -1.0f) animated:NO];
    [self.scrollView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:YES];
    [self.refreshControl beginRefreshing];
    [self refresh];
}

-refresh is the method tied to the UIRefreshControl.

Solution 4 - Iphone

Add an extension for UIResfreshControl.

extension UIRefreshControl {
    func beginRefreshingManually() {
        self.tintColor = UIColor.white
        if let scrollView = superview as? UIScrollView {
            scrollView.setContentOffset(CGPoint(x: 0, y:scrollView.contentOffset.y - frame.height), animated: false)
        }
        beginRefreshing()
    }
}

Solution 5 - Iphone

None of these answers are working for me correctly on iOS8, with the closest being @jpsim's answer but that still left an unsightly black refresh control during its fade-in animation (it would cross-fade between black and while over the course of the animation).

The solution that worked for me was to put this immediately after creating the refresh control in my viewDidLoad:

self.refreshControl = [[UIRefreshControl alloc] init];
self.refreshControl.tintColor = [UIColor whiteColor];
...
self.refreshControlHeight = self.refreshControl.frame.size.height;
[self.tableView setContentOffset:CGPointMake(0, -1) animated:NO];
[self.tableView setContentOffset:CGPointMake(0, 0) animated:NO];

Then to show the UIRefreshControl programmatically:

[self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControlHeight) animated:YES];
[self.refreshControl beginRefreshing];

I had to store the height of the refresh control, as while it was set for the first invocation, subsequent calls would have a 0 height.

Solution 6 - Iphone

Solution for the tintColor issue: add this in viewDidLoad

[self.refreshControl setTintColor:[UIColor whiteColor]];
[self.refreshControl tintColorDidChange];

Now you have a white indicator when you call beginRefresh manually.

Solution 7 - Iphone

SWIFT:

I am using Swift and > iOS8. Most of the described workarounds didn't work for me. That's how I got it working:

In viewDidLoad:

customRefreshControl.tintColor = UIColor.clearColor()

The following doesn't have to be inside viewDidLoad. I put it in an extra function which get's called every time I update the tableView:

private func startRefreshControlAnimation() {
        
    self.tableView.setContentOffset(CGPointMake(0, -self.customRefreshControl.frame.size.height), animated: true)
        
    CATransaction.begin()
    self.customRefreshControl.beginRefreshing()
    CATransaction.commit()

}

Solution 8 - Iphone

I combined some of the previous answers. This works for me on iOS 9 and Swift 2:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    let contentOffset = self.tableView.contentOffset.y
    UIView.animateWithDuration(0, delay: 0, options: .BeginFromCurrentState, animations: {
        print(self.tableView.contentOffset.y)
            self.tableView.setContentOffset(CGPointMake(0, -self.refreshControl.frame.size.height), animated: false)
        }, completion: { finished in
            self.refreshControl.beginRefreshing()
            self.tableView.setContentOffset(CGPointMake(0, contentOffset/2-self.refreshControl.frame.size.height), animated: true)
            self.refresh() // Code that refresh table data
    })        
}

Solution 9 - Iphone

I develop for iOS using Xamarin (C#) and came across the same issue.

I fixed the coloring issue, by setting the AttributedTitle of the RefreshControl :

private CGPoint originalOffset;
...
public override void ViewDidLoad ()
{
     base.ViewDidLoad ();
     ...
     originalOffset = TableView.ContentOffset; // Store the original offset of the table view
     RefreshControl = new UIRefreshControl (){ TintColor = UIColor.Red };
     RefreshControl.ValueChanged += ((s,e) => { Update (this, EventArgs.Empty); });
     // Hack so the TintColor of the RefreshControl will be properly set
     RefreshControl.AttributedTitle = new NSAttributedString ("Fetching data");
}

My Update method looks like this :

private async void Update(object sender, EventArgs args)
{
     try {
          TableView.UserInteractionEnabled = false;
          // I find -100 to be a big enough offset
		  TableView.SetContentOffset (new CGPoint (0, -100), true);
		  RefreshControl.BeginRefreshing ();
          ... // Fetch data & update table source 
		  TableView.ReloadData ();
	  } catch(Exception) {
		  // Respond to exception
	  } finally {
          // Put the offset back to the original
		  TableView.SetContentOffset (originalOffset, true);
		  RefreshControl.EndRefreshing ();
		  TableView.UserInteractionEnabled = true;
	  }
}

Once the ViewDidAppear, I call Update programmatically. Before setting the attributed title, my spinner would've been black. Now it has the proper red color.

It's worth noticing, that this 'hack/fix' also comes with a second bug. The first time you refresh, you'll notice that the AttributedTitle is not displayed. Refreshing a second (,third,fourth,...) time will display the title properly. But if you don't want a title, you just initialize it with an empty string, and this is not a big issue to you.

I hope this can be of use to others.

Solution 10 - Iphone

this hack is very working

var refreshWasProgramBeginning: Bool = false

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    
    if !refreshWasProgramBeginning {
        UIView.animate(withDuration: 0.25, animations: {
            self.tableView.contentOffset = CGPoint.init(x: 0, y: -self.refreshControl.frame.height)
        }) { (_) in
            self.refreshControl.beginRefreshing()
            self.refreshWasProgramBeginning = true
        }
    }
}

Solution 11 - Iphone

I am working with Xamarin C# (iOS 10) and found that a combination of all of these answers is what fixed it for me.

In my ViewDidLoad I have the following:

    RefreshControl = new UIRefreshControl();
    RefreshControl.TintColor = UIColor.White;
    RefreshControl.ValueChanged += OnRefresh;
    RefreshControl.BackgroundColor = UIColor.Clear;

And later I programmatically call the refresh animation in my ViewDidAppear with the following:

    BeginInvokeOnMainThread(() =>
    {
        UIView.Animate(0, 0.2, UIViewAnimationOptions.BeginFromCurrentState, () =>
        {
            TableView.SetContentOffset(new CGPoint(0, TableView.ContentOffset.Y - RefreshControl.Frame.Size.Height), true);
            RefreshControl.AttributedTitle = new NSAttributedString("");
        },
        () =>
        {
            RefreshControl.BeginRefreshing();
        });
    });

Note the setting of the attributed title and animation block were the parts I was missing for my RefreshControl to take my white tint color.

Thanks to all that have contributed to this question.

Solution 12 - Iphone

This is a bug which occurs when calling beginRefreshing() on the refresh control right after setting its tintColor property (or calling it from viewDidLoad() (details [here][1]). There is an easy workaround however, by wrapping the beginRefreshing() call inside a defer statement (Swift 4):

override func viewDidLoad() {
    super.viewDidLoad()
    refreshControl.tintColor = .red
    defer {
        refreshControl.beginRefreshing()
    }
}

[1]: https://github.com/davbeck/RefreshTintFail "here"

Solution 13 - Iphone

Set manually content offset for your tableView/scrollView before begin spinning:

tableView.setContentOffset(CGPoint(x: 0, y: tableView.contentOffset.y - (refreshControl.frame.size.height)), animated: true)
refreshControl.beginRefreshing()
......

Solution 14 - Iphone

Try setting the tintColor of your UIRefreshControl in viewWillAppear.

Solution 15 - Iphone

i found some Work Around i hope it works for you

 [_TBL setContentOffset:CGPointMake(0,_TBL.contentOffset.y-_refreshControl.frame.size.height) animated:YES];
[_refreshControl performSelector:@selector(beginRefreshing) withObject:nil afterDelay:0.25];
[self getLatestUpdates];

Solution 16 - Iphone

I created a drop-in UIRefreshControl+beginRefreshing category that fixes this issue.

In brief, it fixes tintColor issue and manually tableView adjust contentOffset to make sure the refresh control is visible. Please try :)

Solution 17 - Iphone

When I set

tableView.refreshControl = refreshControl 

several times where refreshControl is a different instance each time, I had the issue when refresh control color was always black and setting tint color to a different value didn't help.

So that I set tableView.refreshControl = refreshControl only once and when I need to hide it I set alpha value, more details in this thread:

https://stackoverflow.com/questions/19480424/how-do-i-hide-a-uirefreshcontrol/54461766#54461766

Solution 18 - Iphone

Using UIView.animate didn't work for me on Swift 4.

Here's what I ended up using

extension UIRefreshControl {
    func beginRefreshingManually(with scrollView: UIScrollView, isFirstTime: Bool) {
        if self.isRefreshing { return }
        
        // Workaround: If we call setContentOffset on the first time that the screen loads
        // we get a black refreshControl with the wrong size.
        // We could just set the scrollView.contentOffset everytime, but it does not animate the scrolling.
        // So for every other time, we call the setContentOffset animated.
        if isFirstTime {
            scrollView.contentOffset = CGPoint(x: 0, y: -self.frame.size.height)
        } else {
            scrollView.setContentOffset(CGPoint(x: 0, y: -self.frame.size.height), animated: true)
        }
        self.beginRefreshing()
    }
}

Solution 19 - Iphone

Force the setTintColor to run in the main thread. (Main thread updates the ui).

[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
    [self.refreshControl setTintColor:[UIColor redColor]];
    [self.refreshControl beginRefreshing];
}];

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNoé MalzieuView Question on Stackoverflow
Solution 1 - IphoneWilliam GeorgeView Answer on Stackoverflow
Solution 2 - IphoneFox5150View Answer on Stackoverflow
Solution 3 - IphonejpsimView Answer on Stackoverflow
Solution 4 - IphoneSefa AyçiçekView Answer on Stackoverflow
Solution 5 - IphoneRichard SimView Answer on Stackoverflow
Solution 6 - IphoneUpCatView Answer on Stackoverflow
Solution 7 - IphoneflocbitView Answer on Stackoverflow
Solution 8 - IphoneWendy ChuView Answer on Stackoverflow
Solution 9 - IphoneThomas De WildeView Answer on Stackoverflow
Solution 10 - IphoneAndrey GerashchenkoView Answer on Stackoverflow
Solution 11 - IphoneMatthewView Answer on Stackoverflow
Solution 12 - IphonebolivaView Answer on Stackoverflow
Solution 13 - IphoneprotspaceView Answer on Stackoverflow
Solution 14 - IphoneMacMarkView Answer on Stackoverflow
Solution 15 - IphonekhaledView Answer on Stackoverflow
Solution 16 - IphoneHlungView Answer on Stackoverflow
Solution 17 - IphonePaul T.View Answer on Stackoverflow
Solution 18 - IphoneMarcio GranzottoView Answer on Stackoverflow
Solution 19 - IphoneTreeTreeView Answer on Stackoverflow