UITableViewControllerなしのUIRefreshControl


308

すぐには実現できないようですがUIRefreshControlUITableViewControllerサブクラスを使用せずに新しいiOS 6 クラスを活用するこっけいな方法はありますか?

私は頻繁に使用するUIViewControllerUITableViewサブビューとに準拠UITableViewDataSourceしてUITableViewDelegate使用するのではなくUITableViewControllerあからさま。


6
@DaveDeLong:いいえ、デイブ、彼は何をすべきですか?このページの後半で、彼のソリューションはサポートされていないとのことですが、どのソリューションが適切ですか?
マット

3
@matt彼はを使用し、直接でUITableViewControllerを使用するようにAPIに要求するバグを報告する必要があります。UIRefreshControlUITableView
Dave DeLong

31
UITableViewControllerには、あまりにも多くのあいまいでニッチなバグと、重要なビュー階層に関する問題がありました(TableViewサブビューで標準のVCを使用するように切り替えると、すべて魔法のように消えます)。「UITVCを使用する」というのは、IMHOのあらゆるソリューションの出発点としては不十分です。
アダム

@Adamこれらのバグは、 'UITableViewController'を子ビューコントローラーとして使用するときに表面化しますか?この方法で使用したときに問題が発生したことはありません。
memmons

回答:


388

直感的に、そしてDrummerBのインスピレーションに基づいて、UIRefreshControlインスタンスをサブビューとしてに追加してみましたUITableView。そしてそれは魔法のようにうまくいきます!

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.myTableView addSubview:refreshControl];

これにより、UIRefreshControlテーブルビューの上に追加され、UITableViewController:) を使用しなくても期待どおりに機能します。


編集:これはまだ機能しますが、いくつか指摘したように、この方法でUIRefreshControlを追加すると、わずかな「途切れ」が生じます。その解決策は、UITableViewControllerをインスタンス化し、UIRefreshControlとUITableViewをそれに設定することです。

UITableViewController *tableViewController = [[UITableViewController alloc] init];
tableViewController.tableView = self.myTableView;

self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;

48
参考までに、これはサポートされていない動作であり、将来変更される可能性があります。Appleが行う唯一の保証は、提供されたapi(この場合-[UITableViewController setRefreshControl:])に従ってそれを使用すると機能し続けることです。
Dave DeLong

18
この実装では、トリガーする直前handleRefresh:に、スクロールビューの値が一瞬だけ予期しない変化をするようですcontentInsets。他の誰かがこれを経験したり修正したりしていますか?(そうです、そもそもこれはサポートされていません!)
Tim

6
このためには、本当にコンテナービューを使用する必要があります。ビューコントローラのクラスをのカスタムサブクラスに設定するだけでUITableViewController、「正しく実行」されます。
ユージーン

3
iOS7(iOS6では不明)では、アプリを閉じて再度開いたときを除いて、編集されたバージョンは正常に機能します。アニメーションは、2回更新するまで再生されません。アプリを再度開いた後の初回の更新では、プルダウンした距離に基づいて増分する円ではなく、完全な円になります。修正はありますか?
david2391 2013年

30
UITableViewControllerをバイパスしている間に上記の「スラッタ」を回避するには、次のようにテーブルビューの下に更新コントロールを追加します[_tableView insertSubview:_refreshControl atIndex:0];。iOS 7と8の両方でテスト済み;)
ptitvinou 2014年

95

受け入れ答えによって引き起こされるスタッターを排除するために、あなたはあなたを割り当てることができますUITableViewUITableViewController

_tableViewController = [[UITableViewController alloc]initWithStyle:UITableViewStylePlain];
[self addChildViewController:_tableViewController];

_tableViewController.refreshControl = [UIRefreshControl new];
[_tableViewController.refreshControl addTarget:self action:@selector(loadStream) forControlEvents:UIControlEventValueChanged];

_theTableView = _tableViewController.tableView;

編集:

テーブルビューでデータを更新した後、スタッターUIRefreshControlなしUITableViewControllerでノーを追加し、素晴らしいアニメーションを保持する方法。

UIRefreshControl *refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.theTableView addSubview:refreshControl];
[self.theTableView sendSubviewToBack:refreshControl];

後で更新されたデータを処理するとき...

- (void)handleRefresh:(UIRefreshControl *)refreshControl {
    [self.theTableView reloadData];
    [self.theTableView layoutIfNeeded];
    [refreshControl endRefreshing];
}

7
新しいUITableViewを作成する必要さえありません。initWithStyle:existingTableView.styleと言うだけです-その後、newTableViewController.tableView = existingTableViewを実行して、refreshControlを割り当てます。これを3行に要約します。
Trenskow 2013

[_tablewViewController didMoveToParentViewController:self];サブビューを追加した後に呼び出すことを忘れないでください。
qix 2013

4
self.tableView = _tableViewController.tableView;する必要があります_tableViewController.tableView = self.tableView
アリ

@Linusなぜそれが必要なのですか?カスタムviewControllerのviewDidLoadメソッドのsubViewとして_tableViewController.viewを追加しましたが、これでうまくいくようです。_tableViewController.tableViewを設定する必要すらありませんでした
taber

UITableViewControllerを使用できず、内部でテーブルビューのあるUIViewControllerを使用している理由。stackoverflow.com/questions/18900428/...
lostintranslation

21

あなたがしようとしているのは、あなたが使っているViewController内のコンテナビューを使うことです。専用のテーブルビューでクリーンなUITableViewControllerサブクラスを定義し、それをViewControllerに配置できます。


2
正確に言うと、最もクリーンな方法は、UITableViewControllerタイプの子ビューコントローラーを追加することです。
Zdenek 2013年

次に、を使用してUITableViewControllerを取得できますself.childViewControllers.firstObject
cbh2000 2015年

^を使用prepareForSegueして、コンテナビューでUITableViewControllerオブジェクトへの参照を取得することを検討することもできます。これは、ストーリーボードからのインスタンス化時にセグエイベントとしてすぐにトリガーされるためです。
crarho 2017年

18

UIRefreshControlはUIViewサブクラスなので、独自に使用できます。それがどのようにそれ自体をレンダリングするかはわかりませんが。レンダリングは単にフレームに依存する可能性がありますが、UIScrollViewまたはUITableViewControllerに依存することもできます。

いずれにせよ、それはエレガントなソリューションというよりはハックのようなものになるでしょう。利用可能なサードパーティのクローンの1つを調べるか、独自のクローンを作成することをお勧めします。

ODRefreshControl

ここに画像の説明を入力してください

SlimeRefresh

ここに画像の説明を入力してください


1
お返事ありがとうございますが、私が探していたものとは異なります。しかし、あなたの投稿の最初の文は、結局のところ何が解決策になるかを試すように促しました!
ケラー

7
ODRefreshControlは素晴らしいです-ヒントをありがとう!非常に単純で、仕事をします。そしてもちろん、Appleよりもはるかに柔軟です。API全体で最悪のクラスであるIMOがUITableViewControllerを使用する理由を理解できませんでした。それは(次の)何もしませんが、あなたの見方に柔軟性を与えません。
n13 2013

実際には、プロジェクトで使用するものに依存します。私の場合、uitableviewを使用すると、プロジェクトの複雑さが増し、コードを2つのセグメントに分割したuitableviewcontrollerに切り替えました。 1つの巨大なファイルの代わりにデバッグに使用される..
kush

@ n13こんにちは、UITableViewControllerを使用する十分な理由は、静的セルを使用して設計できることです。残念ながら、UIViewControllerにあるtableViewでは使用できません。
Jean Le Moignan

@JeanLeMoignaniOSツールとライブラリは急速に変化しているため、3年前の私のコメントは無効になっています。SnapKitを使用してすべてのユーザーインターフェイスをコードで作成するように切り替えました。正直なところ、インターフェイスビルダーで1万回クリックするよりもはるかに効率的です。また、UITableViewControllerをUIViewControllerに変更するのは簡単で、わずか1秒しかかかりませんが、IBではそれが非常に面倒です。
n13

7

-endRefreshNSObject -performSelector:withObject:afterDelay:またはGCDのいずれかを使用して、tableViewがコンテンツを再ロードした後、refreshControl メソッドの呼び出しを1秒未満の遅延にしてみてくださいdispatch_after

このために、UIRefreshControlにカテゴリを作成しました。

@implementation UIRefreshControl (Delay)

- (void)endRefreshingAfterDelay:(NSTimeInterval)delay {
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self endRefreshing];
    });
}

@end

私はそれをテストし、これはコレクションビューでも動作します。0.01秒ほどの遅延で十分であることに気づきました。

// My data refresh process here while the refresh control 'isRefreshing'
[self.tableView reloadData];
[self.refreshControl endRefreshingAfterDelay:.01];

4
遅延と待機は本当に悪いスタイルです。
orkoden 2014年

2
@orkoden同意する。これは、すでにサポートされていないの使用に対する回避策ですUIRefreshControl
ボリーバ2014年

非常に簡単に、遅延のあるメソッドを呼び出すことができます。[self performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.01]
orkoden 2014年

2
最終的な効果はまったく同じであり、「ハック」のエレガントさを向上させません。-performSelector:withObject:afterDelay:これを使用するかGCD を使用するかは、個人の好みの問題です。
ボリーバ2014年

7

iOS 10 Swift 3.0

それは簡単です

import UIKit

class ViewControllerA: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var myTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        myTableView.delegate = self
        myTableView.dataSource = self

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

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

    // MARK: - Table view data source

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 12
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)

        cell.textLabel?.text = "Cell \(String(indexPath.row))"
        return cell
    }

}

ここに画像の説明を入力してください

iOS 10 UIRefreshControlについて学びたい場合は、こちらをお読みください


3

サブビューとして更新コントロールを追加すると、セクションヘッダーの上に空のスペースが作成されます。

代わりに、UITableViewControllerを自分のUIViewControllerに埋め込んでから、 tableViewプロパティを指すようにプロパティを。最小限のコード変更。:-)

手順:

  1. Storyboardで新しいUITableViewControllerを作成し、それを元のUIViewControllerに埋め込みます
  2. @IBOutlet weak var tableView: UITableView!以下に示すように、新しく埋め込まれたUITableViewControllerからのものと置き換えます

class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let tableViewController = self.childViewControllers.first as! UITableViewController
        tableView = tableViewController.tableView
        tableView.dataSource = self
        tableView.delegate = self

        // Now we can (properly) add the refresh control
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: "handleRefresh:", forControlEvents: .ValueChanged)
        tableViewController.refreshControl = refreshControl
    }

    ...
}

2

Swift 2.2の場合。

最初にUIRefreshControl()を作成します。

var refreshControl : UIRefreshControl!

viewDidLoad()メソッドに次を追加します。

refreshControl = UIRefreshControl()
    refreshControl.attributedTitle = NSAttributedString(string: "Refreshing..")
    refreshControl.addTarget(self, action: #selector(YourUIViewController.refresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
    self.tableView.addSubview(refreshControl)

そしてリフレッシュ機能を作る

func refresh(refreshControl: UIRefreshControl) {

    // do something ...

    // reload tableView
    self.tableView.reloadData()

    // End refreshing
    refreshControl.endRefreshing()
}

0

これは少し異なる別のソリューションです。

私はいくつかのビュー階層の問題のためにそれを使用する必要がありました:ビュー階層のさまざまな場所にビューを渡す必要があるいくつかの機能を作成していましたが、UITableViewControllerのテーブルビューを使用すると壊れてしまいました。 self.view)は、通常のビューだけでなく、一貫性のないコントローラー/ビュー階層を作成し、クラッシュを引き起こしました。

基本的に、UITableViewControllerの独自のサブクラスを作成し、loadViewをオーバーライドしてself.viewに別のビューを割り当て、tableViewプロパティをオーバーライドして別のテーブルビューを返します。

例えば:

@interface MyTableVC : UITableViewController
@end

@interface MyTableVC ()
@property (nonatomic, strong) UITableView *separateTableView;
@end

@implementation MyTableVC

- (void)loadView {
    self.view = [[UIView alloc] initWithFrame:CGRectZero];
}

- (UITableView *)tableView {
    return self.separateTableView;
}

- (void)setTableView:(UITableView *)tableView {
    self.separateTableView = tableView;
}

@end

ケラーのソリューションと組み合わせると、tableViewがVCのルートビューではなく通常のビューになり、ビュー階層の変更に対してより堅牢になるという意味で、これはより堅牢になります。このように使用する例:

MyTableVC *tableViewController = [[MyTableVC alloc] init];
tableViewController.tableView = self.myTableView;

self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;

これには別の使用方法があります:

この方法でサブクラス化すると、self.viewがself.tableViewから分離されるため、このUITableViewControllerを通常のコントローラーのように使用し、他のサブビューをself.viewに追加することができます。ビューコントローラーは、UITableViewControllerの子を持つ代わりに、UITableViewControllerのサブクラスを直接使用します。

注意すべきいくつかのこと:

superを呼び出さずにtableViewプロパティをオーバーライドしているため、注意が必要な場合があり、必要に応じて処理する必要があります。たとえば、上記の例でテーブルビューを設定しても、テーブルビューがself.viewに追加されず、必要なフレームが設定されません。また、この実装では、クラスがインスタンス化されるときにデフォルトのtableViewは提供されません。これは、追加することも検討できます。それはケースバイケースであり、このソリューションは実際にはケラーのソリューションとうまく適合しているため、ここには含めません。


1
質問には「UITableViewControllerサブクラスを使用せずに」と指定されており、この回答はUITableViewControllerサブクラスに対するものです。
ブライアン

0

これを試して、

上記のソリューションは問題ありませんが、tableView.refreshControlは、iOS 9.xまでUITableViewControllerでのみ使用可能で、iOS 10.x以降のUITableViewで提供されます。

Swift 3で書かれた-

let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(FeedViewController.loadNewData), for: UIControlEvents.valueChanged)
// Fix for the RefreshControl Not appearing in background
tableView?.addSubview(refreshControl)
tableView.sendSubview(toBack: refreshControl)

これまでのところ最良の解決策
Karthik KM

-1

ケラーの最初の提案は、iOS 7で奇妙なバグを引き起こし、ビューコントローラーが再表示された後にテーブルのインセットが増加します。uitableviewcontrollerを使用して2番目の答えに変更すると、問題が修正されました。


-6

UITableViewサブビューでUIViewControllerを使用し、UITableViewDataSourceおよびUITableViewDelegateに準拠している場合、以下を使用できることがわかります。

self.refreshControl = [[UIRefreshControl alloc]init];
[self.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];

9
同意しない。 self.refreshControlのプロパティでありUITableViewController、ではありませんUIViewController
Rickster 2013

機能していません。referhControlはuiviewcontrollerに対して定義されていません
OMGPOP
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.