146

I'm trying to add a UIRefreshControl to a UICollectionView, but the problem is that the refresh control does not appear unless the collection view fills up the height of its parent container. In other words, unless the collection view is long enough to require scrolling, it cannot be pulled down to reveal the refresh control view. As soon as the collection exceeds the height of its parent container, it is pulled down and reveals the refresh view.

I have set up a quick iOS project with just a UICollectionView inside the main view, with an outlet to the collection view so that I can add the UIRefreshControlto it in viewDidLoad. There is also a prototype cell with the reuse identifier cCell

This is all the code in the controller, and it demonstrates the issue pretty well. In this code I set the height of the cell to 100, which isn't enough to fill the display, and therefore the view cannot be pulled and the refresh control won't show. Set it to something higher to fill the display, then it works. Any ideas?

@interface ViewController () <UICollectionViewDelegateFlowLayout, UICollectionViewDataSource>
@property (strong, nonatomic) IBOutlet UICollectionView *collectionView;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
    [self.collectionView addSubview:refreshControl];
}

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 1;
}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    return [collectionView dequeueReusableCellWithReuseIdentifier:@"cCell" forIndexPath:indexPath];
}

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return CGSizeMake(self.view.frame.size.width, 100);
}
Merott
  • 6,527
  • 6
  • 32
  • 50

7 Answers7

402

Try this:

self.collectionView.alwaysBounceVertical = YES;

Complete code for a UIRefreshControl

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
refreshControl.tintColor = [UIColor grayColor];
[refreshControl addTarget:self action:@selector(refershControlAction) forControlEvents:UIControlEventValueChanged];
[self.collectionView addSubview:refreshControl];
self.collectionView.alwaysBounceVertical = YES;
Merott
  • 6,527
  • 6
  • 32
  • 50
Larry
  • 4,036
  • 1
  • 11
  • 2
  • 1
    Awesome, that line is the exact fix, the rest of the code doesn't matter. Not a bad start for you on StackOverflow! Cheers! – Merott Feb 04 '13 at 19:11
  • 7
    Yup no problem. It's that obvious that i am a newbie huh. Anyway turns out that i was stuck in the same situation when i saw your post. When i found the solution i though i should definitely share it. Cheers – Larry Feb 04 '13 at 21:01
  • Can i ask why both options weren't provided? Setting that variable via the Interface editor and via code? – Dean Thomas May 19 '13 at 11:20
  • 1
    How do you control when the spinner stops? I'm using this with a Parse app and the reloadData method inside the refershControlAction but the wheel just keeps spinning for ever. Any idea of what could be happening there? – Juan González Jul 07 '13 at 16:15
  • 6
    Well, Juan, you probably have already found the answer to your question. But, in order to return the refresh control to its normal state following a refresh, you must call `[refreshControl endRefreshing]`. – Merott Jul 29 '13 at 23:40
  • 2
    This is unfortunately no longer supported. UIRefreshControl can only be used with UITableViewController and now it is strictly enforced. – Luke Van In May 08 '14 at 21:27
  • @lukevanin is there any workaround that ? or any other way to implement a pull-to-refresh animation in a collection view ? – Boaz Saragossi Jun 02 '14 at 14:20
  • @LukeVanIn, where are you getting this info? As of iOS 7.1 I can use the refresh control with any UIScrollView, including UICollectionView. – phatmann Aug 26 '14 at 23:31
  • 1
    @phatmann I came to this conclusion by trying to use the control in a UICollectionView, which produced a runtime error - I can't recall the exact message. So I went looking for a solution, came across this post, and noted my experience. Also, the documentation states "Note: Because the refresh control is specifically designed for use in a table view that's managed by a table view controller, using it in a different context can result in undefined behavior." https://developer.apple.com/library/ios/documentation/uikit/reference/UIRefreshControl_class/Reference/Reference.html. – Luke Van In Aug 27 '14 at 07:29
  • the refresh control on UICollectionView in ios8 seems to be working well. – tmr Feb 07 '15 at 07:43
  • 3
    In iOS 10, `UIScrollView` (a superview of `UICollectionView`) has a `refreshControl` property now https://developer.apple.com/videos/play/wwdc2016/219/?time=2033 – Streeter Nov 30 '16 at 04:04
  • Perfect answer save my time. – Digvijay Gida Aug 07 '19 at 12:30
  • @Streeter The presenter is just *so excited*, it made my day – Matt Mc Jan 21 '20 at 06:05
  • I was seriously looking for this only, just one line and it works like a champ !! – Sagar Daundkar Jun 01 '20 at 05:59
29

Attributes/Scroll View/Bounce Vertically in Storyboard/Xib

enter image description here

Mick MacCallum
  • 124,539
  • 40
  • 277
  • 276
avdyushin
  • 1,782
  • 15
  • 19
22

Larry's answer in swift:

    let refreshControl = UIRefreshControl()
    refreshControl.tintColor = UIColor.blueColor()
    refreshControl.addTarget(self, action: "refresh", forControlEvents: .ValueChanged)
    collectionView.addSubview(refreshControl)
    collectionView.alwaysBounceVertical = true

Swift 3:

    let refreshControl = UIRefreshControl()
    refreshControl.tintColor = .blue
    refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
    collectionView.addSubview(refreshControl)
    collectionView.alwaysBounceVertical = true
Brian
  • 26,298
  • 13
  • 78
  • 84
Andrew Schreiber
  • 12,208
  • 6
  • 40
  • 52
  • Const gives an error of undefined variable. if someone encounters the same situation just replace it with `UIColor.whiteColor()` or whatever color you like. – Faisal May 13 '16 at 05:04
  • 1
    Also for Swift 2.2 use `action: #selector(self.refresh)` – Faisal May 13 '16 at 05:05
  • Should now be `collectionView.refreshControl = refreshControl` instead of `collectionView.addSubview(refreshControl)` – Des Jan 10 '21 at 20:12
2

If your collectionview has a content size big enough to scroll vertically, it's OK, but in your case it's not.

You must enable the property AlwaysBounceVertical, so you could set self.collectionView.alwaysBounceVertical = YES;

Nicholas
  • 10,490
  • 19
  • 67
  • 85
Nghia Luong
  • 794
  • 1
  • 5
  • 11
0

I too was facing the same issue, I was not able to use the UIRefreshControl until the UICollectionView's content size was large enough to scroll vertically,

Setting the bounces property of UICollectionView solved this

[self.collectionView setBounces:YES];
[self.collectionView setAlwaysBounceVertical:YES];
Saif
  • 1,778
  • 1
  • 14
  • 32
0

I'm calling beginRefreshing() right after viewDidLoad(), but on some screens it doesn't work. And only collectionView.layoutIfNeeded() in viewDidLoad() helped me

0

You must check in api call if collection view is in refreshing state then end refreshing to dismiss refreshing control.

private let refreshControl = UIRefreshControl()
 refreshControl.tintColor = .white
 refreshControl.addTarget(self, action: #selector(refreshData), for: .valueChanged)
 collectionView.addSubview(refreshControl)
 @objc func refreshData() {
    // API Call
 }
// Disable refresh control if already refreshing 
if refreshControl.isRefreshing {
    refreshControl.endRefreshing()
}
Asad Jamil
  • 160
  • 8