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?

sinθ
sumber
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;
Muhammad Idris
sumber
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;

KGS-12
sumber
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
    }
}
Orkhan Alikhanov
sumber
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.

memmons
sumber
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.

Hermann Klecker
sumber
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.

Mehdzor
sumber
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.

Javier Soto
sumber
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,

boliva
sumber
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 :)

hqt
sumber
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];
}
Carina
sumber
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.

Azu
sumber
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;
}
Bensge
sumber
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;
Gank
sumber
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.

pengguna2497493
sumber
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
    }
}
Murray Sagal
sumber
0

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

Singh
sumber
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.

Bob Wakefield
sumber
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

jbchitaliya.dll
sumber
0

Anda bisa mendapatkannya dengan satu baris kode.

UITableView *tableView = (UITableView *)[[cell superview] superview];
Karthik damodara
sumber
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
    }
}
neoneye
sumber
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
}
Kiattisak Anoochitarom
sumber