web-dev-qa-db-ja.com

UIScrollViewでUIRefreshControlを使用できますか?

アプリにはすでに5つのUIScrollViewがあり、すべてが複数の.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];
}

時間を節約し、UIRefreshControlUIScrollViewで使用する方法はありますか?

ありがとうございました!!!

69
SirRupertIII

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もロックアップまたはフリーズされ、スムーズなアニメーションやスピナーは表示されません。

編集:OK、私はOPと同じことをやっていて、それにいくつかのテキストを追加しました(つまり、「Pull to Refresh」)、そのテキストを更新するにはメインスレッドに戻る必要があります。

更新された回答。

93
Padin215

上記の答えに加えて、状況によってはcontentSizeを設定できない場合(おそらく自動レイアウトを使用しますか?)またはcontentSizeの高さがUIScrollViewの高さ以下である場合があります。これらの場合、UIScrollViewがバウンスしないため、UIRefreshControlは機能しません。

これを修正するには、プロパティalwaysBounceVertical[〜#〜] true [〜#〜]に設定します。

40
Diego Frata

IOS 10以降をサポートできるほど幸運な場合は、refreshControlUIScrollViewを簡単に設定できます。これは、以前に存在したrefreshControl on UITableViewと同じように機能します。

14
Ben Packard

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

ジャンプの問題については、 ティムノーマンの答え で解決します。

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
Surely

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

UIRefreshControl内でUIScrollViewが適切に動作するようにしました。 UIScrollViewを継承し、contentInsetの変更をブロックし、contentOffsetセッターをオーバーライドしました。

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

iOS 1 UIScrollViewはすでにrefreshControlプロパティを持っていますなので。このrefreshControlは、UIRefereshControlを作成し、このプロパティに割り当てたときに表示されます。

UIRefereshControlをサブビューとして追加するno needがあります。

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 のコードと引用

0
MacMark

ジャンプの問題の場合、オーバーライド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(Apple)が問題を解決することを願っています。

0
Dante