web-dev-qa-db-ja.com

UIRefreshControl-UITableViewControllerがUINavigationController内にある場合、beginRefreshingが機能しない

UITableViewController(UINavigationController内)にUIRefreshControlをセットアップしましたが、期待どおりに動作します(つまり、プルダウンが正しいイベントを発生させます)。ただし、次のようにリフレッシュコントロールでbeginRefreshingインスタンスメソッドをプログラムで呼び出した場合:

[self.refreshControl beginRefreshing];

何も起こりません。アニメートされ、スピナーが表示されます。更新後にendRefreshingメソッドを呼び出すと、正しく機能します。

この動作を使用して基本的なプロトタイププロジェクトを作成しました。UITableViewControllerをアプリケーションデリゲートのルートビューコントローラーに直接追加すると、正しく動作します。

self.viewController = tableViewController;
self.window.rootViewController = self.viewController;

しかし、最初にtableViewControllerをUINavigationControllerに追加し、次にrootViewControllerとしてNavigation Controllerを追加すると、beginRefreshingメソッドは機能しなくなります。例えば。

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tableViewController];
self.viewController = navController;
self.window.rootViewController = self.viewController;

これは、Navigation Controller内のネストされたビュー階層がRefresherコントロールでNiceを再生していないことに関係があると思います-提案はありますか?

ありがとう

112
wows

プログラムで更新を開始する場合、たとえばcontentoffsetを変更して、テーブルビューを自分でスクロールする必要があるようです。

[self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:YES];

この理由は、ユーザーがテーブルビューの中央/下にいるときに更新コントロールにスクロールすることが望ましくない可能性があるためだと思いますか?

@muhasturkによるSwift 2.2バージョン

self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true)

一言で言えば、このポータブルを維持するには、この拡張機能を追加します

IRefreshControl + ProgramaticallyBeginRefresh.Swift

extension UIRefreshControl {
    func programaticallyBeginRefreshing(in tableView: UITableView) {
        beginRefreshing()
        let offsetPoint = CGPoint.init(x: 0, y: -frame.size.height)
        tableView.setContentOffset(offsetPoint, animated: true)        
    }
}
179

UITableViewControllerには、iOS 7以降ではautomaticallyAdjustsScrollViewInsetsプロパティがあります。TableViewには、すでにcontentOffsetが含まれている場合があり、通常は(0、-64)です。

したがって、プログラミングでリフレッシュを開始した後にrefreshControlを表示する正しい方法は、refreshControlの高さを既存のcontentOffsetに追加することです。

 [self.refreshControl beginRefreshing];
 [self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];
76
Igotit

上記の戦略を使用したSwift拡張です。

extension UIRefreshControl {
    func beginRefreshingManually() {
        if let scrollView = superview as? UIScrollView {
            scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - frame.height), animated: true)
        }
        beginRefreshing()
    }
}
31
Chris Wagner

他の答えはどれも私にとってはうまくいきませんでした。それらはスピナーを表示および回転させますが、更新アクション自体は発生しません。これは動作します:

id target = self;
SEL selector = @selector(example);
// Assuming at some point prior to triggering the refresh, you call the following line:
[self.refreshControl addTarget:target action:selector forControlEvents:UIControlEventValueChanged];

// This line makes the spinner start spinning
[self.refreshControl beginRefreshing];
// This line makes the spinner visible by pushing the table view/collection view down
[self.tableView setContentOffset:CGPointMake(0, -1.0f * self.refreshControl.frame.size.height) animated:YES];
// This line is what actually triggers the refresh action/selector
[self.refreshControl sendActionsForControlEvents:UIControlEventValueChanged];

この例ではテーブルビューを使用していますが、コレクションビューでも同じように使用できます。

13
Kyle Robson

すでに述べたアプローチ:

[self.refreshControl beginRefreshing];
 [self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];

スピナーが見えるようになります。しかし、それは動きません。変更した1つのことは、これら2つのメソッドの順序であり、すべてが機能していました。

[self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];
[self.refreshControl beginRefreshing];
12
ancajic

Swift 4/4.1の場合

既存の答えの組み合わせは私のために仕事をします:

refreshControl.beginRefreshing()
tableView.setContentOffset(CGPoint(x: 0, y: tableView.contentOffset.y - (refreshControl.frame.size.height)), animated: true)

お役に立てれば!

7
Isaac Bosca

この質問もご覧ください

beginRefreshingを呼び出してcontentOffsetが0の場合、UIRefreshControlにとげが表示されない

TableViewのcontentOffsetプロパティが0の場合にのみ発生するため、私にはバグのように見えます

次のコード(UITableViewControllerのメソッド)で修正しました:

- (void)beginRefreshingTableView {

    [self.refreshControl beginRefreshing];

    if (self.tableView.contentOffset.y == 0) {

        [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){

            self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);

        } completion:^(BOOL finished){

        }];

    }
}
7
Peter Lapisu

これは、スピナーを表示し、アニメーション化するSwift 3拡張です。

import UIKit
extension UIRefreshControl {

func beginRefreshingWithAnimation() {

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {

        if let scrollView = self.superview as? UIScrollView {
            scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - self.frame.height), animated: true)
          }
        self.beginRefreshing()
      }
   }
}
5
Ghulam Rasool

それは私にとって完璧な作品です:

スウィフト3:

self.tableView.setContentOffset(CGPoint(x: 0, y: -self.refreshControl!.frame.size.height - self.topLayoutGuide.length), animated: true)
4
Meilbn

@Dymitry Shevchenkoソリューションに加えて。

この問題に対する素敵な回避策を見つけました。メソッドを上書きするUIRefreshControlの拡張を作成できます:

// Adds code forgotten by Apple, that changes content offset of parent scroll view (table view).
- (void)beginRefreshing
{
    [super beginRefreshing];

    if ([self.superview isKindOfClass:[UIScrollView class]]) {
        UIScrollView *view = (UIScrollView *)self.superview;
        [view setContentOffset:CGPointMake(0, view.contentOffset.y - self.frame.size.height) animated:YES];
    }
}

Interface Builderのリフレッシュ制御にIdentity Inspectorでカスタムクラスを設定することにより、新しいクラスを使用できます。

3
lyzkov

フォートSwift 2.2+

    self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true)
3
muhasturk

Swift 3.1にRxswiftを使用する場合、以下を使用できます。

func manualRefresh() {
    if let refreshControl = self.tableView.refreshControl {
        self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.height), animated: true)
        self.tableView.refreshControl?.beginRefreshing()
        self.tableView.refreshControl?.sendActions(for: .valueChanged)
    }
}

これはSwift 3.1、iOS 10で動作します

3
jkyin

Swift 5の場合、欠けているのはrefreshControl.sendActions(.valueChanged)を呼び出すことだけです。よりクリーンにするために拡張を行いました。

extension UIRefreshControl {

    func beginRefreshingManually() {
        if let scrollView = superview as? UIScrollView {
            scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - frame.height), animated: false)
        }
        beginRefreshing()
        sendActions(for: .valueChanged)
    }

}
1
LukasHromadnik

ユーザーに「データが更新されている」視覚的なサインを表示するために、同じ手法を使用します。結果ユーザーがバックグラウンドからアプリを持ち込むと、ユーザーが自分自身を更新するためにテーブルをプルするようなUIでフィード/リストが更新されます。私のバージョンには3つのものが含まれています

1)「ウェイクアップ」を送信するユーザー

- (void)applicationDidBecomeActive:(UIApplication *)application {
    [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationHaveToResetAllPages object:nil];
}

2)UIViewControllerのオブザーバー

- (void)viewDidLoad {
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(forceUpdateData) name:kNotificationHaveToWakeUp:nil];
}

3)プロトコル

#pragma mark - ForcedDataUpdateProtocol

- (void)forceUpdateData {
    self.tableView.contentOffset = CGPointZero;

    if (self.refreshControl) {
        [self.refreshControl beginRefreshing];
        [self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:YES];
        [self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:1];
    }
}

result

0
WINSergey