我可以在UIScrollView中使用UIRefreshControl吗?


77

UIScrollView我的应用程序中已经有大约5个可以加载多个.xib文件的文件。现在,我们要使用UIRefreshControl。它们被构建为与UITableViewControllers一起使用(每个UIRefreshControl类参考)。我不想重新做这5个UIScrollView工作。我已经尝试使用UIRefreshControlUIScrollView的,它如预期,除了几件事情的作品。

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

有什么办法可以节省大量时间并UIRefreshControl在中使用UIScrollView

谢谢!!!


1
+1表示发现您甚至可以执行此操作。但是,我无法重现任何一种症状。我注意到一个完全不同的问题,即当我第一次创建刷新控件时,attributedTitle除非我显式设置了初始名称attributedTitle(例如,设置为“ Pull to refresh”(类似“ Pull to refresh”之类的东西)),否则第一次出现该错误的位置就会出现在错误的位置。因此,有两个问题:1.您attributedTitle在此初始创建过程中是否将初始化为任何东西?另外,2.您是否正在使用自动布局?3.您在用任何UIScrollViewDelegate方法做其他事情吗?
罗布

Answers:


16

由于iOS 10 UIScrollView已经具有refreshControl属性。当您创建UIRefereshControl并将其分配给此属性时,将出现此refreshControl。

没有必要添加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");
        });
    });
}

需要在单独的线程上进行数据更新,否则它将锁定主线程(UI用于更新UI)。因此,当主线程忙于更新数据时,UI也被锁定或冻结,并且您永远看不到平滑的动画或微调器。

编辑:好的,我正在做与OP相同的操作,并且我现在已经向其中添加了一些文本(即“拉动刷新”),并且确实需要返回主线程来更新该文本。

更新的答案。


4
但是,您可能想要将其放入endRefreshing主队列块,因为它是一种UI方法。
Scott Berrevoets

我也这么认为,我的第一篇文章包括检索主线程,但这不会导致任何错误或崩溃。
Padin215

此解决方案为我工作。但是,在我看来,uirefreshcontrol微调器位于一切之上。有什么方法可以显示在我的视线下方吗?
Salx 2014年

@Salx ...同样的问题:/
jsetting32

2
@ jsetting32-我用以下代码行解决了我的问题:[refreshControl.superview sendSubviewToBack:refreshControl];
Salx 2014年

42

除了上述答案外,在某些情况下您无法设置contentSize(也许使用自动布局?),或者contentSize的高度小于或等于UIScrollView的高度。在这些情况下,由于UIScrollView不会反弹,所以UIRefreshControl将无法工作。

要解决此问题,请将属性alwaysBounceVertical设置为TRUE


1
按照上述答案,我也遇到过同样的问题。但是,当我将alwaysBounceVertical保持为TRUE时,它的效果很好。谢了哥们。
Noundla Sandeep'9

谢谢。我以为我是唯一有这个问题的人。
MetaSnarf '16

也不要忘记scrollView.bounces = true
SoftDesigner

谢谢,弹跳的内容高度小于UIScrollView
Ramy Sabry

14

如果当你有幸能够支持iOS的10+,你现在可以简单地设置refreshControlUIScrollView。这种工作方式相同,之前存在refreshControlUITableView


11

这是您在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 ();
        });
    });
}

非常感谢。很棒!我在这样的WebView上使用了它:webView.ScrollView.AddSubview (refresh);
Jannie Theunissen

2

对于Jumping问题,Tim Norman的答案解决了这一问题。

如果您使用的是swift2,则为swift版本:

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的更改并覆盖了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
最初,此解决方案似乎很有希望,但是它具有(无法解释的?)副作用,即滚动视图在加载视图后几秒钟没有响应用户交互。
cthulhu 2015年

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等平台上运行,我希望他们(苹果公司)可以解决此问题。


0

从iOS 10开始,UIScrollView具有refreshControl属性,您可以将其设置为UIRefreshControl。与往常一样,您不需要管理控件的框架。只需配置控件,设置valueChanged事件的target-action并分配给属性。

无论使用的是普通滚动视图,表视图还是集合视图,创建刷新控件的步骤都是相同的。

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.