UITableViewCell'den UITableView nasıl edinilir?


100

Bir var UITableViewCellbir nesne bağlantısı verilen ve hücre görülebilir olmadığını söylemek gerekir. Yaptığım araştırmaya göre, bu onu içerenlere bir şekilde erişmem gerektiği anlamına geliyor UITableView(buradan, görünür olup olmadığını kontrol etmenin birkaç yolu var). Merak ediyorum UITableViewCell, bir işaretçi var mı UITableView, yoksa hücreden bir işaretçi almanın başka bir yolu var mı?


2
Bunun amacı nedir?
max_

[cell superView]olabilir?
Chris Loonam

6
Neden buna ihtiyacın olduğunu düşündüğünü açıklamaya değer - çünkü bu kötü bir tasarımın işareti olabilir çünkü bir hücrenin ekranda olup olmadığını bilmek için pek çok meşru neden düşünemiyorum.
Paul.

@ Paul.s Hücredeki bir görüntü üzerinde bir hareket tanıyıcımız var ve hücreye dokunulduğunda, düzgün bir şekilde görüntülenmesi için gerektiği kadar çok hücreyi kaplaması gereken başka bir bindirme görünümü açar, açılır pencere stilini düşünün. Bunun işe yaraması için TableView veya başka bir görünüme ihtiyacı var. Çözümlerden pek memnun değil ama istenen efekti elde etmek için UITableViewCell'in UITableView'unu elde etmek bulduğumuz en iyisidir.
chadbag

1
@chadbag endişelenme, umarım aynı sorunu olan başka birine fikir vermişimdir.
PJ_Finnegan

Yanıtlar:


153

İOS sürümünü kontrol etmekten kaçınmak için, bir UITableView bulunana kadar hücre görünümünden denetimleri yinelemeli olarak yukarı doğru yürüyün:

id view = [tableViewCellInstance superview];

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

UITableView *tableView = (UITableView *)view;

1
Teşekkürler. Görünüşe göre bu iOS 8'de bir kez daha değişti ve bu tüm sürümleri güzelce hallediyor.
djskinner

2
Gelecekteki güncellemelerde uyumluluk sorunlarını önlemek için hücreye tablo görünümüne zayıf bir referans eklemenizi tavsiye ederim.
Cenny

1
Daha ziyade zayıf bir yol. Gelecekte görünümün hiyerarşisi değişirse işe yaramaz.
ROMANN

2
Aslında tablo görünümüne bir işaretçi tutan zayıf bir özellik oluşturmak daha iyi olacaktır. Alt sınıfta @property (weak, nonatomic) UITableView *tableView;ve tableView:cellForRowAtIndexPath:sadece sette cell.tableView = tableView;.
Alejandro Iván

Mevcut deneyim, bir hücrenin sırasını çözdükten sonra, bir tablo görünümünün hemen hücrenin süpervizyonu olarak görünmemesidir - hücreyi görünümden çıkarırsam ve tekrar içeri alırsam, bir tablo görünümünü bir süpervizyon olarak bulurum (özyinelemeli arama) diğer cevaplar). Otomatik düzen ve muhtemelen diğer faktörler olası nedenlerdir.
Jonny

48

İOS7 beta 5 UITableViewWrapperView, bir UITableViewCell. Ayrıca UITableView, bir UITableViewWrapperView.

Yani iOS 7 için çözüm şudur:

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

Dolayısıyla, iOS 6'ya kadar olan iOS'ler için çözüm

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


5
Oh adamım, bu acımasız bir API değişikliği. Hem 6'yı hem de 7'yi destekliyorsanız, Apple'ın dallanma için en iyi uygulaması nedir?
Ryan Romanchuk

@RyanRomanchuk İşte iyi bir öneri: devforums.apple.com/message/865550#865550 - hücre oluşturulduğunda ilgili tableView için zayıf bir işaretçi oluşturun. Alternatif olarak, bir iOS sürümünü kontrol eden ve uygun superView'i döndüren UITableViewCellyeni bir yöntemle bir kategori oluşturun relatedTableView.
memmons

3
Bunun için UIView üzerine yinelemeli bir kategori yazın. Sürümü kontrol etmenize gerek yoktur; Bir tablo görünümü hücresi veya görünüm yığınının üstünü bulana kadar süpervizasyonu çağırmanız yeterlidir. Bu iOS 6'da kullandığım şeydi ve iOS 7'de değişiklik yapmadan çalıştı. Ve iOS 8'de sitll çalışması gerekir.
Steven Fisher

Pardon; Masa görünümünü bulana kadar demek istedim. :)
Steven Fisher

39

Swift 5 uzantısı

Tekrarlı

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)
    }
}

Döngü kullanma

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
    }
}

Genel bir kural, güç
paketini açmamaktır

1
@thibautnoah Paket açılmadan view != nilönce bir kontrol var . Daha temiz koda güncellendi
Orkhan Alikhanov

25

İOS7'den önce, hücrenin süpervizyonu, UITableViewonu içeren şeydi . İOS7 GM itibariyle hücrenin Superview bir olduğunu (yani muhtemelen de kamu sürümde olacaktır) UITableViewWrapperViewonun Superview olmak UITableView. Sorunun iki çözümü var.

1.Çözüm: Bir UITableViewCellkategori oluşturun

@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

Bu, kullanmak için iyi bir alternatiftir cell.superview, mevcut kodunuzu yeniden düzenlemeyi kolaylaştırır - sadece arayın ve değiştirin[cell relatedTable] ve gelecekte görünüm hiyerarşisi değişirse veya geri dönerse hemen görünmesini sağlamak için bir iddiada bulunun testlerinde.

2.Çözüm: Zayıf bir UITableViewreferans ekleyinUITableViewCell

@interface SOUITableViewCell

   @property (weak, nonatomic) UITableView *tableView;

@end

Bu çok daha iyi bir tasarım, ancak mevcut projelerde kullanmak için biraz daha fazla kod yeniden düzenleme gerektirecek. Senin içinde tableView:cellForRowAtIndexPathcep sınıf olarak kullanılmak SOUITableViewCell veya emin özel hücre sınıfı gelen subclassed edilir SOUITableViewCellve hücrenin tableView özelliğine tableView atayın. Hücrenin içinde daha sonra kullanarak içeren tablo görünümüne başvurabilirsiniz self.tableView.


Çözüm 2'nin daha iyi bir tasarım olduğuna katılmıyorum. Daha fazla başarısızlık noktası vardır ve disiplinli manuel müdahale gerektirir. İlk çözüm aslında oldukça güvenilir, ancak @idris'in cevabında biraz daha geleceğe yönelik bir koruma için önerdiği şekilde uygulayacağım.
devios1

1
Test [self.superview.superview isKindOfClass:[UITableView class]]ilk olmalı, çünkü iOS 7 gittikçe daha fazla.
CopperCash

7

Görünüyorsa, bir süpervizyona sahiptir. Ve ... sürpriz ... süpervizör bir UITableView nesnesidir.

Ancak, gözetim altında olmak ekranda olmanın garantisi değildir. Ancak UITableView, hangi hücrelerin görünür olduğunu belirlemek için yöntemler sağlar.

Ve hayır, bir hücreden bir tabloya özel bir referans yoktur. Ancak, UITableViewCell alt sınıfını oluşturduğunuzda, birini tanıtabilir ve oluştururken ayarlayabilirsiniz. (Alt görünüm hiyerarşisini düşünmeden önce bunu kendim yaptım.)

İOS7 için güncelleme: Apple burada alt görünüm hiyerarşisini değiştirdi. Her zaman olduğu gibi, ayrıntılı olarak belgelenmemiş şeylerle çalışırken, işlerin değişme riski her zaman vardır. Sonunda bir UITableView nesnesi bulunana kadar görünüm hiyerarşisini "taramak" çok daha tasarrufludur.


14
Bu artık iOS7'de geçerli değil, iOS7 beta5'te UITableViewWrapperView, bir UITableViewCell'in denetimidir ... şu anda bana sorunlara neden oluyor
Gabe

Yorum için teşekkürler. O zaman kodumun bir kısmını kontrol etmem gerekecek.
Hermann Klecker

7

Swift 2.2 çözümü.

Belirli bir türe sahip bir görünümü yinelemeli olarak arayan UIView uzantısı.

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
    }
}

veya daha kompakt (kabiroberai sayesinde):

import UIKit

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

Hücrende ona sadece diyorsun:

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

UITableViewCell'in yalnızca cellForRowAtIndexPath yürütmesinden sonra UITableView'a eklendiğini unutmayın.


Tüm lookForSuperviewOfType:metot gövdesini tek bir hatta sıkıştırarak daha da hızlı hale getirebilirsiniz:return superview as? T ?? superview?.superviewOfType(type)
kabiroberai

@kabiroberai, teşekkür ederim. Cevaba tavsiyeni ekledim.
Mehdzor

Özyinelemeli çağrı tarafından kullanılan adın eşleşmesi gerekir. Yani bunun okunması gerekiyor: ... ?? denetleme? .lookForSuperviewOfType (type)
robert

7

Süper görünümü arayarak veya yanıtlayıcı zinciri aracılığıyla yapmayı başarabileceğiniz her şey çok kırılgan olacaktır. Bunu yapmanın en iyi yolu, hücreler bir şey bilmek istiyorsa, hücrenin sormak istediği soruyu yanıtlayan bir yönteme yanıt veren bir nesneyi hücreye iletmek ve denetleyicinin neyi yanıtlayacağını belirleme mantığını uygulamasını sağlamaktır. (Sorunuzdan, sanırım hücre bir şeyin görünür olup olmadığını bilmek istiyor).

Hücrede bir temsilci protokolü oluşturun, hücre temsilcisini tableViewController olarak ayarlayın ve tableViewCotroller'daki tüm kullanıcı arabirimi "kontrol" mantığını hareket ettirin.

Tablo görünümü hücreleri, yalnızca bilgileri görüntüleyecek olan dum görünümü olmalıdır.


Temsilciler aracılığıyla belki daha da iyi. Ebeveyn bana sorun çıkardığı için geçici olarak tabloya geçilmiş bir referans kullanıyorum.
Cristi Băluță

1
Açıkladığım şey temelde temsilci kalıbını kullanmak :)
Javier Soto

Bu kabul edilen cevap olmalı, insanlar bu soruyu görüyor, 150'den fazla ek oyu görüyorlar ve tableView'i hücreden almanın iyi olduğunu düşünüyorlar ve daha da fazlası maalesef buna güçlü bir referans veriyor.
Alex Terente

6

Ana tableView'i almak için UITableViewCell'de bir kategori oluşturdum:

@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

En iyi,


3

İşte yukarıdaki cevaplara dayanan Swift versiyonu. Daha ExtendedCellsonra kullanmak için genelleştirdim .

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
    }
}

Umarım bu yardımcı olur :)


2

Bu çözümü UITableViewWrapperViewGabe'nin, nesnenin gözetim altında olduğu yönündeki önerisine dayandırdım . UITableViewCell Gabe'nin nesnenin iOS7 beta5'teki nesnenin gözetimi olduğu önerisine dayandırdım.

Alt sınıf 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

Yukarıdaki cevaptan biraz ödünç aldım ve değiştirdim ve aşağıdaki pasajı buldum.

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

Sınıfta geçmek, diğer bazı durumlarda UITableView dışındaki görünümleri geçmek ve almak için esneklik sunar.


2

Bu soruna yönelik çözümüm diğer çözümlere biraz benziyor, ancak zarif bir döngü kullanıyor ve kısa. Ayrıca geleceğe dönük olmalıdır:

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

Bu, hücre çağrıldığında henüz tablo görünümünde değilse sonsuza kadar döngüye girer. Düzeltmek için sıfır için bir çek ekleyin: 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

Gözetim yerine, ["UItableViewvariable" visibleCells] kullanmayı deneyin.

Bunu foreach döngülerinde, uygulamanın gördüğü ve çalıştığı hücrelerde döngü yapmak için kullandım.

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 
    }
  }
}

Tıkır tıkır çalışıyor.


1

Minimal olarak test edildi, ancak genel olmayan bu Swift 3 örneği işe yarıyor gibi görünüyor:

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

bu kod `UITableView *tblView=[cell superview];size tabe görünüm hücresini içeren UItableview örneğini verecektir.


Bir UITableViewCell derhal Superview olduğu gibi bu işe yaramaz değil bir UITableView. İOS 7'den itibaren, UITableViewCell süpervizyonu bir UITableViewWrapperView'dir. Daha az kırılgan, daha güvenilir yaklaşımlar için buradaki diğer yanıtlara bakın.
JaredH

0

Üst UITableView'i bulmak için görünüm hiyerarşisini bu şekilde geçmenizi öneririm:

- (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 );
}

Aksi takdirde, Apple görünüm hiyerarşisini yeniden değiştirdiğinde kodunuz bozulur .

Hiyerarşiyi aşan bir başka cevap da özyinelemelidir.


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 Teşekkür Ederiz


0

Bir satır kodla alabilirsiniz.

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

@idris cevabından Swift'de UITableViewCell için bir genişletme yazdım

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
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.