IOS7 UIRefreshControl tintColor 不能用于开始刷新

我试图在我的 UIRefreshControl (基于 iOS7)上设置一个 tintColor。 我在情节串连图板中为 tableViewController 启用了刷新功能,然后在 ViewController viewDidLoad方法中执行了以下操作:

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

所以现在,当我拉到刷新时,刷新控件的颜色实际上是红色的:

redSpiny

我希望我的视图在出现时自动更新,所以我这样做了:

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

它没有显示纺车,根据 https://stackoverflow.com/a/16250679/1809736,我补充说

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

强迫自己表现出来。 它显示了它,但现在它又回到了默认颜色:

enter image description here

如果我尝试手动拉刷新后,它是红色的。

我试过在 iOS6上构建它,它能正常工作,那么这是 iOS7的 bug 吗?

附注: 这不是模拟器的问题,我试过在设备上构建它,同样的错误。

附注: 我建立了一个示例项目,你能告诉我,如果你有相同的错误或如果有一个问题在我的代码?这里是链接: http://d.pr/f/pGrV

非常感谢!

20192 次浏览

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];
}];

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

Try setting the tintColor of your UIRefreshControl in viewWillAppear.

@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 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.

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.

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];

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 :)

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()

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()


}

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.

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
})
}

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
}
}
}

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()
}
}

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.

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). 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()
}
}

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:

How do I "hide" a UIRefreshControl?

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()
}
}

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 without adjusting content inset.

So you're going to hide original activity indicator replacing it with yours activity indicator (or custom view like LottieAnimationView).


import UIKit


final class CustomRefreshControl: UIRefreshControl {


private let loaderView = UIActivityIndicatorView(frame: .zero)


override init() {


super.init(frame: .zero)
setup()
}


required init?(coder aDecoder: NSCoder) {


super.init(coder: aDecoder)
self.setup()
}


override func beginRefreshing() {


super.beginRefreshing()
self.loaderView.startAnimating()
}


override func endRefreshing() {


super.endRefreshing()
self.loaderView.stopAnimating()
}


override func didAddSubview(_ subview: UIView) {


super.didAddSubview(subview)
subview.alpha = subview === self.loaderView ? 1 : 0
}
}


// MARK: - Private


private extension CustomRefreshControl {


func setup() {


self.tintColor = .clear // hides default indicator view only when user pulls
self.addSubview(self.loaderView)
self.loaderView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.loaderView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
self.loaderView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
self.loaderView.heightAnchor.constraint(equalToConstant: 32),
self.loaderView.widthAnchor.constraint(equalToConstant: 32)
])


self.addTarget(self, action: #selector(self.beginRefreshing), for: .valueChanged)
}
}