LyricalPanda
LyricalPanda

Reputation: 1414

UIRefreshControl needs to be pulled down too far

I'm trying to implement a UIRefreshControl in my project on a UICollectionView. It works as intended, but I need to pull the the UICollectionView about 3x as much as the height of the refreshControl when it's spinning (once the user has let go). I've seen other apps where you only need to pull down to the height of the refreshControl when it's spinning. How do I change the amount I need to pull down?

Code:

(void)viewDidLoad {
  [super viewDidLoad];

  [self.collectionView setBackgroundColor:[UIColor clearColor]];
  self.refreshControl = [UIRefreshControl new];
  [self.refreshControl addTarget:self action:@selector(refreshFavorites) forControlEvents:UIControlEventValueChanged];
  [self.collectionView addSubview:self.refreshControl];
  self.collectionView.alwaysBounceVertical = YES;
} 

Attached are pictures to help visualize what I mean. My collectionView has one row (solid red). The space between the red and the refreshControl is simply the space I'm pulling down to try to activate the refreshControl.

In this image I've almost activated the control and I've already pulled down a considerable amount on the screen.

enter image description here

In this image I've activated the control and it's starting to load (after it jumps ~20-30 pix, but it still highlights how significant this is)

enter image description here

This image shows the control in its "resting" state after it's been activated and is doing the animation.

enter image description here

I've tried setting the frame of the UIRefreshControl to try to control the height, but this doesn't work. I've also changed around the size in collectionView:layout:sizeForItemAtIndexPath which hasn't helped.

Upvotes: 24

Views: 5083

Answers (6)

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 120052

Swift

Based on @BB9z Objective-C answer:

refreshControl.setValue(100, forKey: "_snappingHeight")

Upvotes: 1

ios developer
ios developer

Reputation: 51

In my case, I found the issue with UIviewcontroller's simulated size that was free from.

I just make changes in the simulated size of UIViewcontroller that is fixed.

This one change solved my issue.

Upvotes: 5

Alexandre Nussbaumer
Alexandre Nussbaumer

Reputation: 331

Non of the solutions listed here worked for me. The solution I found is using refresh.didMoveToSuperview() in viewDidAppear.

Complete solution:

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        refresh.didMoveToSuperview()
    }

Upvotes: 3

amirkrd
amirkrd

Reputation: 203

For people still looking for an answer to this. I have a definite and best possible answer so far...

SWIFT 3:

Inside viewDidLoad() :

self.refresher = UIRefreshControl()
self.refresher.tintColor = UIColor.red
self.scrollView.addSubview(self.refresher)

Next If your View is a ViewController - implement the ScrollViewDelegate like so:

class YourViewController: UIViewController, UIScrollViewDelegate

Once you have your scrollView @IBOulet linked... Go ahead and set the scrollView delegate to self inside viewDidLoad() like so:

self.scrollView.delegate = self

Next, since you have the delegates set up, set up the scrollViewDidScroll Function and implement it, to check if the person pulled down passed -90 like so :

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView == self.scrollView
    {
        if scrollView.contentOffset.y < -90  && !self.refresher.isRefreshing{
            self.refresher.beginRefreshing()
            self.Refresh()
            print("zz refreshing")
        }
    }
}

Now, notice we do not have the

self.refresher.addTarget(self, action: #selector(self.Refresh()), for: UIControlEvents.valueChanged)

and nor do we need it at all. Instead we are calling beginRefresh() followed by the actual function... Just make sure to call endRefresh() somewhere you want it to end.

Upvotes: 5

darkbreed
darkbreed

Reputation: 151

Had an issue with this in iOS 10.2 So for anyone still suffering with this, I found a way to do it without the need to touch the private API methods (although this did fix the issue as well).

I found that I was having an issue when the device was rotating on iPad. UIRefreshControl was behaving ok in portrait but would start to go wrong when the device was rotated. After that it didn't matter what orientation it was in, it would always need to be dragged way too far than needed to be activated.

My solution was based on this answer: Offsetting UIRefreshControl

I tried setting the frame of the UIRefreshControl whenever the device rotated and it worked. Its not ideal as it doesn't use AutoLayout but its a bit cleaner than playing with private APIs.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];

-(void)didRotate:(NSNotification *)notification {

    [self.refreshControl setFrame:CGRectMake(0, 0, 20, 20)];
    [self.refreshControl setNeedsLayout];

}

Upvotes: 0

BB9z
BB9z

Reputation: 2720

If a UIRefreshControl needs to be pulled down too far to trigger, it might be a bug of iOS SDK.

I recommend solve this situation with a view controller’s help:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    // Force update the snapping height of refresh control.
    [self.refreshControl didMoveToSuperview];
}

Also if you don’t mind use private API and needs more accurate control, you can write like this:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    @try {
        [self.refreshControl setValue:@(60) forKey:@"_snappingHeight"];
    }
    @catch (NSException *exception) {
    }
}

This works on iOS 7-10. No App Store review issue.


Look inside

UIRefreshControl has a property called snappingHeight. This property controls the distance user needs pull down. But unfortunately, this value not set properly always, especially you have a custom view hierarchy.

UIRefreshControl have a private method calls _updateSnappingHeight, this method also called in didMoveToSuperview. So an manual didMoveToSuperview call can update the wrong snapping value.

Upvotes: 26

Related Questions