web-dev-qa-db-fra.com

Puis-je utiliser un UIRefreshControl dans un UIScrollView?

J'ai déjà environ 5 UIScrollView dans mon application qui chargent tous plusieurs .xib des dossiers. Nous voulons maintenant utiliser un UIRefreshControl. Ils sont conçus pour être utilisés avec UITableViewControllers (par référence de classe UIRefreshControl). Je ne veux pas refaire le fonctionnement des 5 UIScrollView. J'ai déjà essayé d'utiliser le UIRefreshControl dans mes UIScrollView, et cela fonctionne comme prévu à l'exception de quelques éléments.

  1. Juste après que l'image d'actualisation se soit transformée en chargeur, le UIScrollView saute d'environ 10 pixels, ce qui ne se produit que lorsque je fais très attention de faire glisser le UIScrollview très lentement.

  2. Lorsque je fais défiler vers le bas et lance le rechargement, puis lâchez le UIScrollView, le UIScrollView reste où je l'ai laissé aller. Une fois le rechargement terminé, le UIScrollView monte en haut sans animation.

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

Existe-t-il un moyen de gagner du temps et d'utiliser un UIRefreshControl dans un UIScrollView?

Je vous remercie!!!

69
SirRupertIII

J'ai un UIRefreshControl pour travailler avec un 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");
        });
    });
}

Vous devez faire la mise à jour des données sur un thread séparé ou cela verrouillera le thread principal (que l'interface utilisateur utilise pour mettre à jour l'interface utilisateur). Ainsi, alors que le thread principal est occupé à mettre à jour les données, l'interface utilisateur est également verrouillée ou gelée et vous ne voyez jamais les animations fluides ou le spinner.

EDIT: ok, je fais la même chose que OP et j'ai maintenant ajouté du texte (c'est-à-dire "Pull to Refresh") et il doit revenir sur le fil principal pour mettre à jour ce texte.

Réponse mise à jour.

93
Padin215

En ajoutant aux réponses ci-dessus, dans certaines situations, vous ne pouvez pas définir le contentSize (en utilisant la disposition automatique peut-être?) Ou la hauteur du contentSize est inférieure ou égale à la hauteur de l'UIScrollView. Dans ces cas, UIRefreshControl ne fonctionnera pas car UIScrollView ne rebondira pas.

Pour résoudre ce problème, définissez la propriété alwaysBounceVertical sur TRUE .

40
Diego Frata

Si et quand vous avez la chance de prendre en charge iOS 10+, vous pouvez maintenant simplement définir le refreshControl du UIScrollView. Cela fonctionne de la même manière que le refreshControl existant sur UITableView.

14
Ben Packard

Voici comment procéder en C #/Monotouch. Je ne peux pas trouver d'échantillons pour C # n'importe où, alors le voici .. Merci 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
John Gavilan

Pour le problème de saut, la réponse de Tim Norman le résout.

Voici la version Swift si vous utilisez 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
Surely

Comment le faire en 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
Camilo Ortegón

J'ai fait fonctionner UIRefreshControl correctement dans UIScrollView. J'ai hérité de UIScrollView, bloqué la modification de contentInset et remplacé le 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
aedificator

Depuis iOS 10 UIScrollView déjà a une propriété refreshControl . Ce refreshControl apparaîtra lorsque vous créez un UIRefereshControl et l'affectez à cette propriété.

Il n'y a plus besoin d'ajouter UIRefereshControl comme sous-vue.

func configureRefreshControl () {
   // Add the refresh control to your UIScrollView object.
   myScrollingView.refreshControl = UIRefreshControl()
   myScrollingView.refreshControl?.addTarget(self, action:
                                      #selector(handleRefreshControl),
                                      for: .valueChanged)
}

@objc func handleRefreshControl() {
   // Update your content…

   // Dismiss the refresh control.
   DispatchQueue.main.async {
      self.myScrollingView.refreshControl?.endRefreshing()
   }
}

Un objet UIRefreshControl est un contrôle standard que vous attachez à n'importe quel objet UIScrollView

Code et citation de https://developer.Apple.com/documentation/uikit/uirefreshcontrol

0
MacMark

Pour le problème de saut, remplacer contentInset le résout uniquement avant iOS 9. J'ai juste essayé un moyen d'éviter le problème de saut:

let scrollView = UIScrollView()
let refresh = UIRefreshControl()
//        scrollView.refreshControl = UIRefreshControl() this will cause the jump issue
refresh.addTarget(self, action: #selector(handleRefreshControl), for: .valueChanged)
scrollView.alwaysBounceVertical = true
//just add refreshControl and send it to back will avoid jump issue
scrollView.addSubview(refresh)
scrollView.sendSubviewToBack(refresh)

fonctionne sur iOS 9 10 11 et ainsi de suite, et j'espère qu'ils (Apple) vont juste résoudre le problème.

0
Dante