Можно ли использовать UIRefreshControl в UIScrollView?
У меня уже есть около 5 UIScrollView
в моем приложении, которые все загружают несколько .xib
файлов. Теперь мы хотим использовать UIRefreshControl
. Они построены для использования с UITableViewControllers (по ссылке на класс UIRefreshControl). Я не хочу переделывать, как работают все 5 UIScrollView
. Я уже пытался использовать UIRefreshControl
в моем UIScrollView
, и это работает, как и ожидалось, за исключением нескольких вещей.
-
Сразу после того, как обновленное изображение превращается в загрузчик,
UIScrollView
прыгает вниз примерно на 10 пикселей, чего только не делает случается, когда я очень осторожно тащуUIScrollview
вниз очень медленно. -
Когда я прокручиваю вниз и инициирую перезагрузку, затем отпускаю
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];
}
Есть ли способ сэкономить кучу времени и использовать UIRefreshControl
в UIScrollView
?
Спасибо!!!
8 ответов:
Я получил
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"); }); }); }
Необходимо выполнить обновление данных в отдельном потоке, иначе он заблокирует основной поток (который пользовательский интерфейс использует для обновления пользовательского интерфейса). Таким образом, пока основной поток занят обновлением данных, пользовательский интерфейс также заблокирован или заморожен, и вы никогда не увидите плавную анимацию или спиннер.
EDIT: ок, я делаю то же самое, что и OP, и теперь я добавил к нему текст (т. е." Pull To Refresh"), и ему нужно вернуться в основной поток, чтобы обновить его текст.
Обновленный ответ.
Добавляя к вышеприведенным ответам, в некоторых ситуациях вы не можете установить размер содержимого (возможно, с помощью автоматической компоновки?) или высота contentSize меньше или равна высоте UIScrollView. В этих случаях UIRefreshControl не будет работать, потому что UIScrollView не будет отскакивать.
Чтобы исправить это, установите свойство alwaysBounceVertical в TRUE.
Если и когда вам посчастливится поддерживать iOS 10+, Теперь вы можете просто установить
refreshControl
изUIScrollView
. Это работает так же, как и ранее существовавшиеrefreshControl
наUITableView
.
Вот как это делается в C# / Monotouch. Я не могу нигде найти образцы для C#, так что вот он.. Спасибо 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 (); }); }); }
Для прыжковой проблемы, Ответ Тима Нормана решает ее.
Вот версия swift, если вы используете 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) } } } }
Как это сделать в 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() }
Я заставил
UIRefreshControl
работать должным образом внутриUIScrollView
. Я унаследовалUIScrollView
, заблокировал изменение contentInset и переопределил 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) } } } }