UIRefreshControl tanpa UITableViewController

308

Hanya ingin tahu, karena sepertinya tidak segera, tetapi apakah ada cara licik untuk memanfaatkan kelas iOS 6 baru UIRefreshControltanpa menggunakan UITableViewControllersubkelas?

Saya sering menggunakan UIViewControllerdengan UITableViewsubview dan menyesuaikan diri UITableViewDataSourcedan UITableViewDelegatebukannya menggunakan UITableViewControlleroutright.

Keller
sumber
6
@ DaveDeLong: Tidak, Dave, apa yang harus dia lakukan? Kemudian pada halaman ini Anda mengatakan solusinya tidak didukung, jadi apa solusi yang tepat?
matt
3
@ Matt dia harus menggunakan UITableViewControllerdan mengajukan bug meminta API untuk menggunakan UIRefreshControldengan secara UITableViewlangsung.
Dave DeLong
31
UITableViewController telah (dan terus memiliki) terlalu banyak bug dan ceruk yang tidak jelas dan masalah dengan hierarki tampilan non-sepele ... yang secara ajaib hilang ketika Anda beralih menggunakan VC standar dengan subview TableView. "Gunakan UITVC" adalah awal yang buruk untuk solusi apa pun, IMHO.
Adam
@ Adam Apakah bug ini muncul saat menggunakan 'UITableViewController' sebagai pengontrol tampilan anak (sehingga memberikan akses untuk melihat penyesuaian tanpa mucking di hierarki tableViewControllers)? Saya tidak pernah mengalami masalah saat menggunakannya dengan cara ini.
memmons

Jawaban:

388

Pada firasat, dan berdasarkan inspirasi DrummerB, saya mencoba hanya menambahkan UIRefreshControlcontoh sebagai subview ke saya UITableView. Dan secara ajaib itu hanya bekerja!

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

Ini menambahkan tampilan UIRefreshControltabel di atas Anda dan berfungsi seperti yang diharapkan tanpa harus menggunakan UITableViewController:)


EDIT: Ini di atas masih berfungsi tetapi karena beberapa telah menunjukkan, ada sedikit "gagap" ketika menambahkan UIRefreshControl dengan cara ini. Solusi untuk itu adalah dengan instantiate UITableViewController, dan kemudian setting UIRefreshControl dan UITableView Anda untuk itu, yaitu:

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;
Keller
sumber
48
FYI, ini adalah perilaku yang tidak didukung dan dapat berubah di masa mendatang. Satu-satunya jaminan yang dibuat oleh Apple adalah bahwa menggunakannya sesuai dengan api yang disediakan (dalam hal ini -[UITableViewController setRefreshControl:]) akan terus berfungsi.
Dave DeLong
18
Dengan implementasi ini, tampaknya tepat sebelum memicu handleRefresh:ada perubahan tak terduga dalam nilai tampilan gulir contentInsetsselama sepersekian detik. Adakah orang lain yang mengalami atau memperbaiki ini? (yup, saya tahu ini tidak didukung sejak awal!)
Tim
6
Anda harus menggunakan Tampilan Wadah untuk ini. Cukup setel kelas pengontrol tampilan ke subkelas kustom UITableViewControllerAnda dan Anda akan "melakukannya dengan benar".
Eugene
3
Di iOS7 (tidak dikenal untuk iOS6) versi yang diedit berfungsi dengan baik kecuali ketika Anda menutup dan membuka kembali aplikasi. Animasi tidak akan diputar hingga Anda menyegarkan kembali. Pertama kali menyegarkan setelah membuka kembali aplikasi, itu hanya lingkaran penuh, bukan lingkaran yang bertambah berdasarkan seberapa jauh Anda menariknya ke bawah. Apakah ada perbaikan?
david2391
30
Untuk menghindari memiliki "slutter" seperti yang disebutkan di atas sementara masih melewati UITableViewController, hanya menambahkan kontrol penyegaran di bawah tampilan tabel seperti: [_tableView insertSubview:_refreshControl atIndex:0];. Diuji dengan iOS 7 dan 8;)
ptitvinou
95

Untuk menghilangkan gagap yang disebabkan oleh jawaban yang diterima, Anda dapat menugaskan Anda UITableViewuntuk a UITableViewController.

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

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

_theTableView = _tableViewController.tableView;

EDIT:

Cara untuk menambahkan UIRefreshControltanpa UITableViewControllertanpa gagap dan mempertahankan animasi yang bagus setelah menyegarkan data di tampilan tabel.

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

Kemudian ketika menangani data yang di-refresh ...

- (void)handleRefresh:(UIRefreshControl *)refreshControl {
    [self.theTableView reloadData];
    [self.theTableView layoutIfNeeded];
    [refreshControl endRefreshing];
}
Piotr Tomasik
sumber
7
Anda bahkan tidak perlu membuat UITableView baru. Katakan saja initWithStyle: existingTableView.style - dan kemudian lakukan newTableViewController.tableView = existingTableView dan tetapkan refreshControl. Rebus ini menjadi tiga baris.
Trenskow
Jangan lupa menelepon [_tablewViewController didMoveToParentViewController:self];setelah menambahkan subview.
qix
4
self.tableView = _tableViewController.tableView;seharusnya_tableViewController.tableView = self.tableView
Ali
@Linus mengapa itu perlu? Saya menambahkan _tableViewController.view sebagai subView dalam metode viewDidLoad viewController kustom saya dan sepertinya melakukan triknya. Saya bahkan tidak perlu mengatur _tableViewController.tableView
taber
Alasan mengapa saya tidak dapat menggunakan UITableViewController dan saya menggunakan UIViewController dengan tampilan tabel di dalamnya. stackoverflow.com/questions/18900428/…
lostintranslation
21

Apa yang akan Anda coba adalah menggunakan tampilan wadah di dalam ViewController yang Anda gunakan. Anda dapat mendefinisikan subclass UITableViewController yang bersih dengan tampilan tabel khusus dan menempatkannya di ViewController.

Tomohisa Takaoka
sumber
2
Tepatnya, cara terbersih tampaknya menambahkan pengontrol tampilan anak tipe UITableViewController.
Zdenek
Anda kemudian dapat mengambil UITableViewController menggunakan self.childViewControllers.firstObject.
cbh2000
^ Anda mungkin juga ingin mempertimbangkan menggunakan prepareForSegueuntuk mendapatkan referensi ke objek UITableViewController dalam tampilan wadah karena ini segera dipicu sebagai peristiwa segue pada instantiasi dari storyboard.
crarho
18

Yah UIRefreshControl adalah subkelas UIView, jadi Anda bisa menggunakannya sendiri. Saya tidak yakin bagaimana cara menerjemahkannya sendiri. Render bisa bergantung pada frame, tetapi juga bisa bergantung pada UIScrollView atau UITableViewController.

Apa pun itu, ini akan lebih merupakan peretasan daripada solusi yang elegan. Saya sarankan Anda melihat ke salah satu klon pihak ke-3 yang tersedia atau menulis sendiri.

ODRefreshControl

masukkan deskripsi gambar di sini

SlimeRefresh

masukkan deskripsi gambar di sini

DrummerB
sumber
1
Terima kasih atas tanggapan Anda, tetapi tidak persis apa yang saya cari. Namun, kalimat pertama dari posting Anda menginspirasi saya untuk mencoba apa yang akhirnya menjadi solusinya!
Keller
7
ODRefreshControl luar biasa - terima kasih atas tipnya! Sangat sederhana, dan melakukan pekerjaan. Dan tentu saja jauh lebih fleksibel daripada Apple. Saya tidak pernah mengerti mengapa ada orang yang menggunakan UITableViewController, IMO kelas terburuk di seluruh API. Itu (tidak) apa-apa, tetapi membuat pandangan Anda tidak fleksibel.
n13
Itu sebenarnya tergantung pada proyek Anda apa yang Anda gunakan di sana .. menggunakan uitableview dalam kasus saya adalah meningkatkan kompleksitas proyek dan sekarang saya beralih ke uitableviewcontroller yang membagi kode saya menjadi dua segmen saya senang saya punya 2 file kecil yang bisa digunakan untuk debug bukannya satu file besar ..
kush
@ n13 Halo, alasan bagus untuk menggunakan UITableViewController adalah dapat mendesain dengan sel statis. Sayangnya tidak tersedia di tableView yang berada di UIViewController.
Jean Le Moignan
@JeanLeMoignanAlat dan pustaka iOS berubah dengan cepat sehingga komentar saya yang berusia 3 tahun tidak berlaku lagi. Saya telah beralih untuk membuat semua antarmuka pengguna dalam kode dengan SnapKit dan jujur ​​itu jauh lebih efisien daripada mengklik 10 ribu kali dalam pembangun antarmuka. Juga mengubah UITableViewController menjadi UIViewController mudah dan hanya membutuhkan waktu satu detik, sedangkan di IB itu sangat menyebalkan.
n13
7

Coba tunda panggilan ke -endRefreshmetode refreshControl dengan sepersekian detik setelah tableView memuat kembali isinya, baik dengan menggunakan NSObject -performSelector:withObject:afterDelay:atau GCD dispatch_after.

Saya membuat kategori di UIRefreshControl untuk ini:

@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

Saya mengujinya dan ini juga berfungsi pada Tampilan Koleksi. Saya perhatikan bahwa penundaan sekecil 0,01 detik sudah cukup:

// My data refresh process here while the refresh control 'isRefreshing'
[self.tableView reloadData];
[self.refreshControl endRefreshingAfterDelay:.01];
boliva
sumber
4
Penundaan dan menunggu adalah gaya yang benar-benar buruk.
orkoden
2
@orkoden saya setuju. Ini adalah solusi untuk penggunaan yang sudah tidak didukung UIRefreshControl.
boliva
Anda dapat memanggil metode dengan penundaan jauh lebih mudah. [self performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.01]
orkoden
2
Efek akhirnya persis sama, dan tidak membuat 'hack' lebih / kurang elegan. Apakah akan menggunakan -performSelector:withObject:afterDelay:atau GCD untuk ini adalah masalah selera pribadi.
boliva
7

IOS 10 Swift 3.0

itu mudah

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
    }

}

masukkan deskripsi gambar di sini

Jika Anda ingin belajar tentang iOS 10 UIRefreshControl baca di sini .

Ashok R
sumber
3

Menambahkan kontrol penyegaran sebagai subview menciptakan ruang kosong di atas header bagian.

Sebagai gantinya, saya menyematkan UITableViewController ke dalam UIViewController saya, lalu mengubah tableViewproperti saya untuk mengarah ke yang tertanam, dan biola! Perubahan kode minimal. :-)

Langkah:

  1. Buat UITableViewController baru di Storyboard dan embed ke dalam UIViewController asli Anda
  2. Ganti @IBOutlet weak var tableView: UITableView!dengan yang dari UITableViewController yang baru tertanam, seperti yang ditunjukkan di bawah ini

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
    }

    ...
}
cbh2000
sumber
2

Untuk Swift 2.2.

Pertama buat UIRefreshControl ().

var refreshControl : UIRefreshControl!

Dalam metode viewDidLoad () Anda tambahkan:

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

Dan buat fungsi refresh

func refresh(refreshControl: UIRefreshControl) {

    // do something ...

    // reload tableView
    self.tableView.reloadData()

    // End refreshing
    refreshControl.endRefreshing()
}
stakahop
sumber
0

Inilah solusi lain yang sedikit berbeda.

Saya harus menggunakannya karena beberapa masalah hierarki tampilan yang saya miliki: Saya sedang membuat beberapa fungsi yang mengharuskan melewati tampilan di sekitar ke berbagai tempat dalam hierarki tampilan, yang rusak ketika menggunakan tampilan tabel UITableViewController b / c tableView adalah tampilan root UITableViewController's ( self.view) dan bukan hanya tampilan biasa, itu menciptakan hierarki controller / view yang tidak konsisten dan menyebabkan crash.

Pada dasarnya buatlah subkelas Anda sendiri dari UITableViewController dan atur loadView untuk menetapkan sendiri. Lihat tampilan yang berbeda, dan ganti properti tableView untuk mengembalikan tampilan tabel yang terpisah.

sebagai contoh:

@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

Ketika dikombinasikan dengan solusi Keller, ini akan lebih kuat dalam arti bahwa tableView sekarang menjadi tampilan biasa, bukan tampilan root VC, dan lebih kuat terhadap perubahan hierarki tampilan. Contoh menggunakannya dengan cara ini:

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;

Ada kemungkinan penggunaan lain untuk ini:

Karena subklas dengan cara ini memisahkan self.view dari self.tableView, sekarang mungkin untuk menggunakan UITableViewController ini sebagai pengontrol biasa, dan menambahkan subview lain ke self.view tanpa keanehan menambahkan subview ke UITableView, jadi orang mungkin mempertimbangkan untuk membuat lihat pengontrol secara langsung subkelas UITableViewController alih-alih memiliki anak-anak UITableViewController.

Beberapa hal yang harus diperhatikan:

Karena kita mengganti properti tableView tanpa memanggil super, mungkin ada beberapa hal yang harus diperhatikan dan harus ditangani jika perlu. Sebagai contoh, mengatur tampilan tabel dalam contoh saya di atas tidak akan menambahkan tampilan tabel ke self.view dan tidak mengatur bingkai yang mungkin ingin Anda lakukan. Juga, dalam implementasi ini tidak ada tableView default yang diberikan kepada Anda ketika kelas instantiated, yang juga merupakan sesuatu yang dapat Anda pertimbangkan untuk ditambahkan. Saya tidak memasukkannya di sini karena itu adalah kasus per kasus, dan solusi ini sebenarnya cocok dengan solusi Keller.

psilencer
sumber
1
Pertanyaan yang ditentukan "tanpa menggunakan subkelas UITableViewController", dan jawaban ini untuk subkelas UITableViewController.
Brian
0

Coba ini,

Solusi di atas baik-baik saja tetapi tableView.refreshControl hanya tersedia untuk UITableViewController, hingga iOS 9.x dan masuk dalam UITableView dari iOS 10.x dan seterusnya.

Ditulis dalam 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)
Ankit Kumar Gupta
sumber
Solusi terbaik sejauh ini
Karthik KM
-1

Saran pertama Keller menyebabkan bug aneh di iOS 7 di mana inset tabel meningkat setelah pengontrol tampilan muncul kembali. Mengubah ke jawaban kedua, menggunakan uitableviewcontroller, memperbaiki beberapa hal untuk saya.

Mark Bridges
sumber
-6

Ternyata Anda dapat menggunakan yang berikut saat Anda menggunakan UIViewController dengan subview UITableView dan menyesuaikan dengan UITableViewDataSource dan UITableViewDelegate:

self.refreshControl = [[UIRefreshControl alloc]init];
[self.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
Clint Pick
sumber
9
Tidak setuju. self.refreshControladalah properti untuk UITableViewController, bukan a UIViewController.
Rickster
TIDAK KERJA, refershControl tidak didefinisikan untuk UIViewController
OMGPOP