Can I use UIRefreshControl in a UIScrollView?

I already have about 5 UIScrollView already in my application that load several .xib files. Now we want to use UIRefreshControl . They are built for use with UITableViewControllers (following the link of the UIRefreshControl class). I do not want to repeat how all 5 UIScrollView . I already tried using UIRefreshControl in my UIScrollView , and it works as expected, with the exception of a few things.

  • Immediately after the image is updated in the bootloader, the UIScrollView reset by about 10 pixels, which does not happen when I very carefully drag the UIScrollView down very slowly.

  • When I scroll down and start rebooting, release UIScrollView , UIScrollView , where I let it go. After the reboot is complete, UIScrollView jumps up without animation.

Here is my code:

 -(void)viewDidLoad { UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init]; [refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged]; [myScrollView addSubview:refreshControl]; } -(void)handleRefresh:(UIRefreshControl *)refresh { // Reload my data [refresh endRefreshing]; } 

Is it possible to save a lot of time and use UIRefreshControl in a UIScrollView ?

Thank!

+70
ios iphone uiscrollview uirefreshcontrol
Feb 16 '13 at 0:04
source share
8 answers

I got a UIRefreshControl to work with a UIScrollView :

 - (void)viewDidLoad { UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; scrollView.userInteractionEnabled = TRUE; scrollView.scrollEnabled = TRUE; scrollView.backgroundColor = [UIColor whiteColor]; scrollView.contentSize = CGSizeMake(500, 1000); UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init]; [refreshControl addTarget:self action:@selector(testRefresh:) forControlEvents:UIControlEventValueChanged]; [scrollView addSubview:refreshControl]; [self.view addSubview:scrollView]; } - (void)testRefresh:(UIRefreshControl *)refreshControl { refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:@"Refreshing data..."]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [NSThread sleepForTimeInterval:3];//for 3 seconds, prevent scrollview from bouncing back down (which would cover up the refresh view immediately and stop the user from even seeing the refresh text / animation) dispatch_async(dispatch_get_main_queue(), ^{ NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"MMM d, h:mm a"]; NSString *lastUpdate = [NSString stringWithFormat:@"Last updated on %@", [formatter stringFromDate:[NSDate date]]]; refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:lastUpdate]; [refreshControl endRefreshing]; NSLog(@"refresh end"); }); }); } 

You must update the data in a separate stream or block the main stream (which the user interface uses to update the user interface). Therefore, when the main thread is busy updating data, the user interface is also blocked or frozen, and you will never see a smooth animation or counter.

EDIT: ok, I am doing the same thing as OP, and now I have added some text to it (ie "Pull to Refresh"), and it needs to go back to the main stream to update this text.

Updated answer.

+93
Feb 21 '13 at 17:08
source share

Adding to the answers above, in some situations you cannot set contentSize (perhaps using an automatic layout?) Or the height of the contentSize is less than or equal to the height of the UIScrollView. In these cases, the UIRefreshControl will not work because the UIScrollView will not bounce.

To fix this set, the alwaysBounceVertical property is TRUE .

+40
Dec 04 '13 at 15:55
source share

If you are fortunate enough to support iOS 10+, you can simply set refreshControl for UIScrollView . This works just like the preexisting refreshControl on a UITableView .

+14
Jul 28 '16 at 1:05
source share

This is how you do it in C # / Monotouch. I cannot find any samples for C # anywhere, so here it is .. Thanks Log139!

 public override void ViewDidLoad () { //Create a scrollview object UIScrollView MainScrollView = new UIScrollView(new RectangleF (0, 0, 500, 600)); //set the content size bigger so that it will bounce MainScrollView.ContentSize = new SizeF(500,650); // initialise and set the refresh class variable refresh = new UIRefreshControl(); refresh.AddTarget(RefreshEventHandler,UIControlEvent.ValueChanged); MainScrollView.AddSubview (refresh); } private void RefreshEventHandler (object obj, EventArgs args) { System.Threading.ThreadPool.QueueUserWorkItem ((callback) => { InvokeOnMainThread (delegate() { System.Threading.Thread.Sleep (3000); refresh.EndRefreshing (); }); }); } 
+10
Mar 22 '13 at 2:52
source share

For the question about the jump, Tim Norman answers , solves it.

Here is the quick version if you are using swift2:

 import UIKit class NoJumpRefreshScrollView: UIScrollView { /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. override func drawRect(rect: CGRect) { // Drawing code } */ override var contentInset:UIEdgeInsets { willSet { if self.tracking { let diff = newValue.top - self.contentInset.top; var translation = self.panGestureRecognizer.translationInView(self) translation.y -= diff * 3.0 / 2.0 self.panGestureRecognizer.setTranslation(translation, inView: self) } } } } 
+2
Jan 19 '16 at 4:05
source share

How to do it in Swift 3:

 override func viewDidLoad() { super.viewDidLoad() let scroll = UIScrollView() scroll.isScrollEnabled = true view.addSubview(scroll) let refreshControl = UIRefreshControl() refreshControl.addTarget(self, action: #selector(pullToRefresh(_:)), for: .valueChanged) scroll.addSubview(refreshControl) } func pullToRefresh(_ refreshControl: UIRefreshControl) { // Update your conntent here refreshControl.endRefreshing() } 
+2
Aug 31 '17 at 21:01
source share

I made UIRefreshControl work correctly inside a UIScrollView . I inherited a UIScrollView , blocked changing the contents of the Inset, and overriding the contentOffset setter:

 class ScrollViewForRefreshControl : UIScrollView { override var contentOffset : CGPoint { get {return super.contentOffset } set { if newValue.y < -_contentInset.top || _contentInset.top == 0 { super.contentOffset = newValue } } } private var _contentInset = UIEdgeInsetsZero override var contentInset : UIEdgeInsets { get { return _contentInset} set { _contentInset = newValue if newValue.top == 0 && contentOffset.y < 0 { self.setContentOffset(CGPointZero, animated: true) } } } } 
+1
Dec 05 '14 at 16:06
source share

You can simply instantiate the update control and add it at the top of the scroll. then in delegate methods you customize your behavior to your requirements.

-3
Feb 20 '13 at
source share



All Articles