Czy Mogę używać UIRefreshControl w UIScrollView?

Mam około 5 UIScrollView ' s już w mojej aplikacji, które wszystkie ładują Wiele .xib plików. Teraz chcemy użyć UIRefreshControl. Są one zbudowane do użycia z UITableViewControllers (zgodnie z odniesieniem do klasy UIRefreshControl). Nie chcę ponownie robić tego, jak działa wszystkie 5 UIScrollView. Próbowałem już używać UIRefreshControl w moich UIScrollView i działa zgodnie z oczekiwaniami, z wyjątkiem kilku rzeczy.

  1. Zaraz po tym, jak odświeżany obraz zamienia się w loader, UIScrollView skacze w dół o około 10 pikseli, co tylko nie zdarza się, gdy jestem bardzo ostrożny, aby przeciągnąć UIScrollview w dół bardzo powoli.

  2. Kiedy przewijam w dół i uruchamiam przeładowanie, a następnie puszczam UIScrollView, UIScrollView zostaje tam, gdzie go puszczam. Po zakończeniu przeładowania, UIScrollView wskakuje na górę bez animacji.

Oto Mój kod:

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

Czy Jest jakiś sposób na zaoszczędzenie czasu i użycie UIRefreshControl w UIScrollView?

Dziękuję!!!

Author: Meet Doshi, 2013-02-16

8 answers

Mam UIRefreshControl do pracy z 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");
        });
    });
}

Należy wykonać aktualizację danych w oddzielnym wątku lub zablokuje główny wątek(którego UI używa do aktualizacji interfejsu). Podczas gdy główny wątek jest zajęty aktualizowaniem danych, interfejs użytkownika jest również zablokowany lub zamrożony i nigdy nie widzisz płynnych animacji ani spinnera.

EDIT: ok, robię to samo co OP i dodałem do niego trochę tekstu (np. "Pull to Refresh") i musi wrócić do głównego wątku, aby to zaktualizować tekst.

Zaktualizowana odpowiedź.

 87
Author: Padin215,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-03 22:48:20

Dodając do powyższych odpowiedzi, w niektórych sytuacjach nie można ustawić contentSize (może używając auto layout?) lub wysokość contentSize jest mniejsza lub równa wysokości UIScrollView. W takich przypadkach UIRefreshControl nie będzie działać, ponieważ UIScrollView nie odbije się.

Aby naprawić ten zestaw Właściwość alwaysBounceVertical do TRUE .

 37
Author: Diego Frata,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-04 16:00:58

Jeśli i kiedy masz szczęście wspierać iOS 10+, możesz teraz po prostu ustawić refreshControl z UIScrollView. Działa to w ten sam sposób co poprzednio istniejące refreshControl na UITableView.

 12
Author: Ben Packard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 01:05:18

Oto Jak to zrobić w C # / Monotouch. Nie mogę znaleźć żadnych próbek do C# gdziekolwiek, więc tutaj jest.. Dzięki 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
Author: John Gavilan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-18 11:01:09

W kwestii skoków, odpowiedź Tima Normana rozwiązuje to.

Oto wersja swift, jeśli używasz 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
Author: Surely,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:39

Jak to zrobić w 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
Author: Camilo Ortegón,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-31 21:01:18

Sprawiłem, że UIRefreshControl działa prawidłowo wewnątrz UIScrollView. Odziedziczyłem UIScrollView, zablokowałem zmianę contentInset i nadpisałem 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
Author: aedificator,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-18 05:23:35

Możesz po prostu utworzyć instancję kontrolki Odśwież i dodać ją u góry widoku przewijania. następnie w metodzie delegate dostosowujesz jego zachowanie do swoich wymagań.

 -2
Author: Gianluca Tranchedone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-05 18:59:46