Bagaimana cara mendapatkan UITableView dari UITableViewCell?


100

Saya memiliki UITableViewCellyang terkait dengan suatu objek dan saya perlu mengetahui apakah sel tersebut terlihat. Dari penelitian yang telah saya lakukan, ini berarti saya perlu mengakses UITableViewyang berisi itu (dari sana, ada beberapa cara untuk memeriksa apakah itu terlihat). Jadi saya bertanya-tanya apakah UITableViewCellmemiliki pointer ke UITableView, atau apakah ada cara lain untuk mendapatkan pointer dari sel?


2
Apa tujuannya ini?
maks_

[cell superView]mungkin?
Chris Loonam

6
Perlu dijelaskan mengapa Anda merasa membutuhkan ini - karena ini mungkin pertanda desain yang buruk karena saya tidak dapat benar-benar memikirkan banyak alasan yang sah bagi sebuah sel untuk mengetahui apakah itu ada di layar atau tidak.
Paul.

@ Paul.s Kami memiliki pengenal isyarat pada gambar dalam sel dan ketika sel disentuh, itu membuka tampilan overlay lain, pikirkan gaya popover, yang harus overlay sebanyak sel yang diperlukan untuk ditampilkan dengan benar. Agar ini bekerja, perlu TableView atau tampilan lain yang diberikan untuk ditampilkan. Tidak terlalu senang dengan solusi tetapi untuk mendapatkan efek yang diinginkan, mendapatkan UITableView dari UITableViewCell adalah yang terbaik yang kami hasilkan.
chadbag

1
@chadbag jangan khawatir, semoga saya memberikan ide kepada orang lain dengan masalah yang sama.
PJ_Finnegan

Jawaban:


153

Untuk menghindari memeriksa versi iOS, jalankan superview secara berulang dari tampilan sel sampai UITableView ditemukan:

id view = [tableViewCellInstance superview];

while (view && [view isKindOfClass:[UITableView class]] == NO) {
    view = [view superview]; 
}

UITableView *tableView = (UITableView *)view;

1
Terima kasih. Tampaknya ini telah berubah sekali lagi di iOS 8 dan ini menangani semua versi dengan baik.
djskinner

2
Saya akan merekomendasikan menambahkan referensi lemah ke tampilan tabel ke sel untuk menghindari masalah kompatibilitas di pembaruan mendatang.
Cenny

1
Cara yang agak lemah. Ini tidak akan berfungsi jika hierarki tampilan berubah di masa mendatang.
RomanN

2
Akan lebih baik untuk membuat properti lemah yang benar-benar memegang pointer ke tableview. Di subclass @property (weak, nonatomic) UITableView *tableView;dan di tableView:cellForRowAtIndexPath:set saja cell.tableView = tableView;.
Alejandro Iván

Pengalaman saat ini adalah bahwa setelah dequeue sel, tableview tidak langsung muncul sebagai superview sel - jika saya menggulir sel keluar dari tampilan dan kembali lagi saya menemukan tampilan tabel sebagai superview (pencarian rekursif seperti yang dijelaskan di jawaban lain). Autolayout dan kemungkinan faktor lainnya adalah kemungkinan penyebabnya.
Jonny

48

Di iOS7 beta 5 UITableViewWrapperViewadalah tampilan super dari a UITableViewCell. Juga UITableViewsuperview dari a UITableViewWrapperView.

Jadi untuk iOS 7 solusinya adalah

UITableView *tableView = (UITableView *)cell.superview.superview;

Jadi untuk iOS hingga iOS 6 solusinya adalah

UITableView *tableView = (UITableView *)cell.superview;


5
Ya ampun, ini adalah perubahan API yang brutal. Apa praktik terbaik apel untuk percabangan jika Anda mendukung 6 dan 7?
Ryan Romanchuk

@RyanRomanchuk Berikut adalah satu saran bagus: devforums.apple.com/message/865550#865550 - buat penunjuk lemah ke tableView terkait Anda saat sel dibuat. Bergantian, buat UITableViewCellkategori dengan metode baru, relatedTableViewyang memeriksa versi iOS dan mengembalikan superView yang sesuai.
memmons

3
Tulis kategori rekursif di UIView untuk ini. Anda tidak perlu memeriksa versinya; cukup panggil superview sampai Anda menemukan sel tampilan tabel atau bagian atas tumpukan tampilan. Ini adalah apa yang saya gunakan di iOS 6, dan bekerja tanpa modifikasi di iOS 7. Dan seharusnya berfungsi pada sitll di iOS 8.
Steven Fisher

Mohon maaf; Maksud saya sampai Anda menemukan tampilan tabel. :)
Steven Fisher

39

Ekstensi Swift 5

Secara rekursif

extension UIView {
    func parentView<T: UIView>(of type: T.Type) -> T? {
        guard let view = superview else {
            return nil
        } 
        return (view as? T) ?? view.parentView(of: T.self)
    }
}

extension UITableViewCell {
    var tableView: UITableView? {
        return parentView(of: UITableView.self)
    }
}

Menggunakan loop

extension UITableViewCell {
    var tableView: UITableView? {
        var view = superview
        while let v = view, v.isKind(of: UITableView.self) == false {
            view = v.superview
        }
        return view as? UITableView
    }
}

Aturan praktisnya adalah tidak menggunakan paksa membuka bungkus
thibaut noah

1
@thibautnoah Ada view != nilpemeriksaan sebelum membuka bungkusnya . Diperbarui ke kode pembersih
Orkhan Alikhanov

25

Sebelum iOS7, tampilan super sel adalah UITableViewyang berisi itu. Pada GM iOS7 (jadi mungkin akan dirilis ke publik juga) tampilan super sel adalah UITableViewWrapperViewdengan tampilan supernya menjadi UITableView. Ada dua solusi untuk masalah ini.

Solusi # 1: Buat UITableViewCellkategori

@implementation UITableViewCell (RelatedTable)

- (UITableView *)relatedTable
{
    if ([self.superview isKindOfClass:[UITableView class]])
        return (UITableView *)self.superview;
    else if ([self.superview.superview isKindOfClass:[UITableView class]])
        return (UITableView *)self.superview.superview;
    else
    {
        NSAssert(NO, @"UITableView shall always be found.");
        return nil;
    }

}
@end

Ini adalah pengganti drop-in yang baik untuk digunakan cell.superview, membuatnya mudah untuk memfaktorkan ulang kode yang ada - cukup telusuri dan ganti dengan [cell relatedTable], dan berikan pernyataan untuk memastikan bahwa jika hierarki tampilan berubah atau kembali di masa mendatang, maka akan segera muncul dalam ujianmu.

Solusi # 2: Tambahkan UITableViewreferensi Lemah keUITableViewCell

@interface SOUITableViewCell

   @property (weak, nonatomic) UITableView *tableView;

@end

Ini adalah desain yang jauh lebih baik, meskipun akan membutuhkan lebih banyak pemfaktoran ulang kode untuk digunakan dalam proyek yang sudah ada. Dalam tableView:cellForRowAtIndexPathpenggunaan SOUITableViewCell sebagai kelas sel Anda atau pastikan kelas sel khusus Anda disubkelas dari SOUITableViewCelldan tetapkan tableView ke properti tableView sel. Di dalam sel, Anda kemudian dapat merujuk ke tampilan tabel yang berisi menggunakan self.tableView.


Saya tidak setuju bahwa solusi 2 adalah desain yang lebih baik. Ini memiliki lebih banyak poin kegagalan dan membutuhkan intervensi manual yang disiplin. Solusi pertama sebenarnya cukup andal, meskipun saya akan menerapkannya seperti yang disarankan @idris dalam jawabannya untuk sedikit lebih futureproofing.
devios1

1
Tes [self.superview.superview isKindOfClass:[UITableView class]]harus menjadi yang pertama, karena iOS 7 semakin banyak.
CopperCash

7

Jika terlihat maka itu memiliki superview. Dan ... kejutan ... superview adalah objek UITableView.

Namun, memiliki tampilan super bukanlah jaminan untuk tampil di layar. Tetapi UITableView menyediakan metode untuk menentukan sel mana yang terlihat.

Dan tidak, tidak ada referensi khusus dari sel ke tabel. Tetapi ketika Anda membuat subkelas UITableViewCell, Anda dapat memperkenalkan dan menyetelnya saat pembuatan. (Saya sering melakukannya sendiri sebelum memikirkan hierarki subview.)

Pembaruan untuk iOS7: Apple telah mengubah hierarki subview di sini. Seperti biasa ketika mengerjakan hal-hal yang tidak terdokumentasi dengan rinci, selalu ada risiko hal-hal tersebut berubah. Jauh lebih hemat untuk "merayapi" hierarki tampilan hingga objek UITableView akhirnya ditemukan.


14
Ini tidak lagi benar di iOS7, Di iOS7 beta5 UITableViewWrapperView adalah superview dari UITableViewCell ... menyebabkan masalah saya sekarang
Gabe

Terima kasih atas komentarnya. Kalau begitu, saya harus memeriksa beberapa kode saya.
Hermann Klecker

7

Solusi cepat 2.2.

Ekstensi untuk UIView yang secara rekursif mencari tampilan dengan tipe tertentu.

import UIKit

extension UIView {
    func lookForSuperviewOfType<T: UIView>(type: T.Type) -> T? {
        guard let view = self.superview as? T else {
            return self.superview?.lookForSuperviewOfType(type)
        }
        return view
    }
}

atau lebih ringkas (berkat kabiroberai):

import UIKit

extension UIView {
    func lookForSuperviewOfType<T: UIView>(type: T.Type) -> T? {
        return superview as? T ?? superview?.superviewOfType(type)
    }
}

Di sel Anda, Anda hanya menyebutnya:

let tableView = self.lookForSuperviewOfType(UITableView)
// Here we go

Perhatikan bahwa UITableViewCell ditambahkan di UITableView hanya setelah eksekusi cellForRowAtIndexPath.


Anda dapat memadatkan seluruh lookForSuperviewOfType:metode bodi menjadi satu baris, membuatnya lebih cepat:return superview as? T ?? superview?.superviewOfType(type)
kabiroberai

@kabiroberai, terima kasih. Saya menambahkan saran Anda untuk jawabannya.
Mehdzor

Nama yang digunakan oleh panggilan rekursif harus cocok. Jadi ini perlu dibaca: ... ?? superview? .lookForSuperviewOfType (tipe)
robert

7

Apa pun yang akhirnya Anda lakukan dengan memanggil tampilan super atau melalui rantai penjawab akan menjadi sangat rapuh. Cara terbaik untuk melakukan ini, jika sel ingin mengetahui sesuatu, adalah dengan mengirimkan objek ke sel yang merespons beberapa metode yang menjawab pertanyaan yang ingin ditanyakan sel, dan meminta pengontrol menerapkan logika untuk menentukan apa yang harus dijawab. (dari pertanyaan Anda, saya kira sel ingin tahu apakah ada sesuatu yang terlihat atau tidak).

Buat protokol delegasi dalam sel, setel delegasi sel tableViewController dan pindahkan semua logika "pengontrol" ui di tableViewCotroller.

Sel tampilan tabel harus berupa tampilan dum yang hanya akan menampilkan informasi.


Mungkin lebih baik melalui delegasi. Saya sementara menggunakan referensi lulus ke tabel karena orang tua memberi saya masalah.
Cristi Băluță

1
Apa yang saya jelaskan pada dasarnya menggunakan pola delegasi :)
Javier Soto

Ini harus menjadi jawaban yang diterima, orang-orang melihat pertanyaan ini, melihat 150+ jawaban positif dan mereka berpikir tidak masalah untuk mendapatkan tableView dari sel dan bahkan lebih mungkin menyimpan referensi yang kuat sayangnya.
Alex Terente

6

Saya membuat kategori di UITableViewCell untuk mendapatkan tableView induknya:

@implementation UITableViewCell (ParentTableView)


- (UITableView *)parentTableView {
    UITableView *tableView = nil;
    UIView *view = self;
    while(view != nil) {
        if([view isKindOfClass:[UITableView class]]) {
            tableView = (UITableView *)view;
            break;
        }
        view = [view superview];
    }
    return tableView;
}


@end

Terbaik,


3

Berikut adalah versi Swift berdasarkan jawaban di atas. Saya telah menggeneralisasi ExtendedCelluntuk penggunaan nanti.

import Foundation
import UIKit

class ExtendedCell: UITableViewCell {

    weak var _tableView: UITableView!

    func rowIndex() -> Int {
        if _tableView == nil {
            _tableView = tableView()
        }

        return _tableView.indexPathForSelectedRow!.row
    }

    func tableView() -> UITableView! {
        if _tableView != nil {
            return _tableView
        }

        var view = self.superview
        while view != nil && !(view?.isKindOfClass(UITableView))! {
            view = view?.superview
        }

        self._tableView = view as! UITableView
        return _tableView
    }
}

Semoga bantuan ini :)


2

Saya mendasarkan solusi ini pada saran Gabe bahwa UITableViewWrapperViewobjek adalah tampilan super UITableViewCellobjek di iOS7 beta5.

Subkelas UITableviewCell:

- (UITableView *)superTableView
{
    return (UITableView *)[self findTableView:self];
}

- (UIView *)findTableView:(UIView *)view
{
    if (view.superview && [view.superview isKindOfClass:[UITableView class]]) {
        return view.superview;
    }
    return [self findTableView:view.superview];
}

2

Saya meminjam dan memodifikasi sedikit dari jawaban di atas dan menghasilkan cuplikan berikut.

- (id)recursivelyFindSuperViewWithClass:(Class)clazz fromView:(id)containedView {
    id containingView = [containedView superview];
    while (containingView && ![containingView isKindOfClass:[clazz class]]) {
        containingView = [containingView superview];
    }
    return containingView;
}

Meneruskan di kelas menawarkan fleksibilitas untuk melintasi dan mendapatkan tampilan selain UITableView di beberapa kesempatan lain.


2

Solusi saya untuk masalah ini agak mirip dengan solusi lain, tetapi menggunakan loop-for yang elegan dan pendek. Ini juga harus tahan masa depan:

- (UITableView *)tableView
{
    UIView *view;
    for (view = self.superview; ![view isKindOfClass:UITableView.class]; view = view.superview);
    return (UITableView *)view;
}

Ini akan mengulang selamanya jika sel belum ada dalam tampilan tabel saat dipanggil. Untuk memperbaikinya, tambahkan cek nihil: for (view = self.superview; view && ![view isKindOfClass:UITableView.class]; view = view.superview);
Antonio Nunes

2
UITableView *tv = (UITableView *) self.superview.superview;
BuyListController *vc = (BuyListController *) tv.dataSource;

1

Daripada superview, coba gunakan ["UItableViewvariable" visibleCells].

Saya menggunakannya di foreach loop untuk melakukan loop melalui sel yang dilihat aplikasi dan berhasil.

for (UITableView *v in [orderItemTableView visibleCells])//visibleCell is the fix.
{
  @try{
    [orderItemTableView reloadData];
    if ([v isKindOfClass:[UIView class]]) {
        ReviewOrderTableViewCell *cell = (ReviewOrderTableViewCell *)v;
        if (([[cell deleteRecord] intValue] == 1) || ([[[cell editQuantityText] text] intValue] == 0))
            //code here 
    }
  }
}

Bekerja seperti pesona.


1

Diuji minimal tetapi contoh Swift 3 non-umum ini tampaknya berfungsi:

extension UITableViewCell {
    func tableView() -> UITableView? {
        var currentView: UIView = self
        while let superView = currentView.superview {
            if superView is UITableView {
                return (superView as! UITableView)
            }
            currentView = superView
        }
        return nil
    }
}

0

kode ini `UITableView *tblView=[cell superview];akan memberi Anda contoh UItableview yang berisi sel tampilan tabe


Ini tidak akan berfungsi, karena superview langsung dari UITableViewCell bukanlah UITableView. Mulai iOS 7, superview UITableViewCell adalah UITableViewWrapperView. Lihat jawaban lain di sini untuk pendekatan yang tidak terlalu rapuh dan lebih dapat diandalkan.
JaredH

0

Saya sarankan Anda melintasi hierarki tampilan dengan cara ini untuk menemukan induk UITableView:

- (UITableView *) findParentTableView:(UITableViewCell *) cell
{
    UIView *view = cell;
    while ( view && ![view isKindOfClass:[UITableView class]] )
    {
#ifdef DEBUG
        NSLog( @"%@", [[view  class ] description] );
#endif
        view = [view superview];
    }

    return ( (UITableView *) view );
}

Jika tidak, kode Anda akan rusak ketika Apple mengubah hierarki tampilan lagi .

Jawaban lain yang juga melintasi hierarki adalah rekursif.


0
UITableViewCell Internal View Hierarchy Change in iOS 7

Using iOS 6.1 SDK

    <UITableViewCell>
       | <UITableViewCellContentView>
       |    | <UILabel>

Using iOS 7 SDK

    <UITableViewCell>
       | <UITableViewCellScrollView>
       |    | <UIButton>
       |    |    | <UIImageView>
       |    | <UITableViewCellContentView>
       |    |    | <UILabel>


The new private UITableViewCellScrollView class is a subclass of UIScrollView and is what allows this interaction:


![enter image description here][1]


  [1]: http://i.stack.imgur.com/C2uJa.gif

http://www.curiousfind.com/blog/646 Terima kasih


0

Anda bisa mendapatkannya dengan satu baris kode.

UITableView *tableView = (UITableView *)[[cell superview] superview];

0
extension UIView {
    func parentTableView() -> UITableView? {
        var viewOrNil: UIView? = self
        while let view = viewOrNil {
            if let tableView = view as? UITableView {
                return tableView
            }
            viewOrNil = view.superview
        }
        return nil
    }
}

0

dari jawaban @idris saya menulis perluasan untuk UITableViewCell di Swift

extension UITableViewCell {
func relatedTableView() -> UITableView? {
    var view = self.superview
    while view != nil && !(view is UITableView) {
        view = view?.superview
    }

    guard let tableView = view as? UITableView else { return nil }
    return tableView
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.