Чи можу я використовувати UIRefreshControl в UIScrollView?


77

У UIScrollViewмоїй програмі вже є близько 5, які завантажують кілька .xibфайлів. Тепер ми хочемо використовувати a UIRefreshControl. Вони створені для використання з UITableViewControllers (за посиланням на клас UIRefreshControl). Я не хочу повторювати, як працюють всі 5 UIScrollView. Я вже пробував використовувати UIRefreshControlin у моїх UIScrollView, і він працює як слід, за винятком кількох речей.

  1. Одразу після того, як оновлене зображення перетворюється на завантажувач, UIScrollViewстрибки зменшуються приблизно на 10 пікселів, чого не відбувається, коли я дуже обережно перетягую UIScrollviewвниз дуже повільно.

  2. Коли я прокручую вниз і ініціюю перезавантаження, то відпускаю UIScrollView, UIScrollViewзалишається там, де я його пустив. Після завершення перезавантаження UIScrollViewстрибає вгору без анімації.

Ось мій код:

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

Чи є спосіб заощадити купу часу та використовувати a UIRefreshControlу UIScrollView?

Дякую!!!


1
+1 за те, що ви навіть можете це зробити. Але у мене проблеми з відтворенням будь-якого із симптомів. Я помічаю зовсім іншу проблему, яка attributedTitleз’являється не в тому місці, коли я вперше натискаю кнопку для оновлення, якщо я явно не встановив початкову attributedTitle(наприклад, щось на зразок «Потягнути для оновлення»), коли я вперше створюю елемент керування оновленням. Отже, кілька запитань: 1. Чи ініціалізуєте ви attributedTitleщось під час цього початкового створення? Також, 2. Чи використовуєте ви автоматичну розкладку? 3. Ви робите щось ще з якимись UIScrollViewDelegateметодами?
Роб

Відповіді:


18

Оскільки iOS 10 UIScrollView вже має властивість refreshControl . Це оновленняControl з’явиться, коли ви створите UIRefereshControl і призначите його цій властивості.

Більше не потрібно додавати UIRefereshControl як підпрогляд.

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()
   }
}

Об'єкт UIRefreshControl - це стандартний елемент керування, який ви приєднуєте до будь-якого об'єкта UIScrollView

Код і цитата з https://developer.apple.com/documentation/uikit/uirefreshcontrol


94

Я мав UIRefreshControlпрацювати з 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");
        });
    });
}

Потрібно зробити оновлення даних в окремому потоці, інакше він заблокує основний потік (який користувальницький інтерфейс використовує для оновлення інтерфейсу). Отже, поки основний потік зайнятий оновленням даних, інтерфейс користувача також заблокований або заморожений, і ви ніколи не бачите плавних анімацій або обертання.

РЕДАГУВАТИ: добре, я роблю те саме, що і OP, і тепер я додав до нього деякий текст (тобто, "Потягнути, щоб оновити"), і йому потрібно повернутися до основної нитки, щоб оновити цей текст.

Оновлена ​​відповідь.


4
Напевно, вам захочеться розмістити свій endRefreshingблок у головній черзі, оскільки це метод інтерфейсу користувача.
Скотт Берревоєц

Я теж так думав, і моя перша публікація включала отримання основного потоку, але це не викликає помилок або збоїв.
Padin215

Це рішення працює для мене. Однак, на мій погляд, блешня uirefreshcontrol - це все понад усе. Чи є спосіб дістатися до, щоб показатись нижче мого погляду?
Salx

@Salx ... те саме: /
jsetting32

2
@ jsetting32 - Я виправив свою проблему з цим рядком коду: [refreshControl.superview sendSubviewToBack: refreshControl];
Salx

42

Додаючи до наведених вище відповідей, в деяких ситуаціях ви не можете встановити contentSize (можливо, використовуючи автоматичне розміщення?), Або висота contentSize менша або дорівнює висоті UIScrollView. У цих випадках UIRefreshControl не працюватиме, оскільки UIScrollView не відскакує.

Щоб виправити це значення, властивість alwaysBounceVertical має значення TRUE .


1
Я зіткнувся з тим самим питанням, слідуючи наведеній вище відповіді. Однак, коли я зберігав завждиBounceVertical до TRUE, він працював добре. Дякую, друже.
Noundla Sandeep

Дякую. Я думав, що я єдиний маю цю проблему.
MetaSnarf

Не забувайте scrollView.bounces = trueтакож!
SoftDesigner

Дякую, відскок над висотою вмісту менший, ніж UIScrollView
Рамі Сабрі

14

Якщо і коли вам пощастило достатньо , щоб бути підтримкою IOS 10+, тепер ви можете просто встановити refreshControlз UIScrollView. Це працює так само, як і раніше існували refreshControlна UITableView.


11

Ось як ви це робите в C # / Monotouch. Я не можу знайти будь-які зразки для C # в будь-якому місці, так що ось це ..

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

Дуже дякую. Чудово працює! Я використовував його на WebView таким чином:webView.ScrollView.AddSubview (refresh);
Jannie Theunissen

2

Щодо питання про стрибки, це вирішує відповідь Тіма Нормана .

Ось швидка версія, якщо ви використовуєте 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

Як це зробити в 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()
}

1

я зробив UIRefreshControl роботу належним чином всередині UIScrollView. Я успадкував UIScrollView, заблокував зміну contentInset та перевизначений setOffset 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
Спочатку це рішення здавалось багатообіцяючим, але воно мало (незрозумілий?) Побічний ефект, оскільки мій вид прокрутки не реагував на взаємодію користувача протягом декількох секунд після завантаження подання.
cthulhu

1

Що стосується проблеми зі стрибками, перевизначення contentInset вирішує це лише до iOS 9. Я просто спробував уникнути проблеми зі стрибком:

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)

працює на iOS 9 10 11 тощо, і я сподіваюся, що вони (Apple) просто вирішили проблему.


0

Починаючи з iOS 10, UIScrollView має властивість refreshControl, яку ви можете встановити як UIRefreshControl. Як завжди, вам не потрібно керувати рамкою управління. Просто налаштуйте елемент керування, встановіть ціль-дію для події valueChanged і призначте властивості.

Незалежно від того, чи використовуєте ви звичайний вид прокрутки, подання таблиці або подання колекції, кроки для створення елемента керування оновленням однакові.

if #available(iOS 10.0, *) {
  let refreshControl = UIRefreshControl()
  refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
  refreshControl.addTarget(self,
                           action: #selector(refreshOptions(sender:)),
                           for: .valueChanged)
  scrollView.refreshControl = refreshControl
}

@objc private func refreshOptions(sender: UIRefreshControl) {
  // Perform actions to refresh the content
  // ...
  // and then dismiss the control
  sender.endRefreshing()
}

-3

Ви можете просто створити екземпляр елемента керування оновленням і додати його у верхній частині перегляду прокрутки. тоді в методах делегатів ви пристосовуєте його поведінку до ваших вимог.


Якщо ви прочитали моє запитання, це те, що я зробив. Ви робите це по-іншому?
SirRupertIII
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.