Wie bekomme ich UITableView von UITableViewCell?

100

Ich habe eine, UITableViewCelldie mit einem Objekt verknüpft ist, und ich muss feststellen, ob die Zelle sichtbar ist. Nach meinen Recherchen bedeutet dies, dass ich irgendwie auf das zugreifen muss, das UITableViewes enthält (von dort aus gibt es verschiedene Möglichkeiten, um zu überprüfen, ob es sichtbar ist). Ich frage mich also, ob UITableViewCelles einen Zeiger auf das UITableViewgibt oder ob es eine andere Möglichkeit gibt, einen Zeiger aus der Zelle zu erhalten.

sinθ
quelle
2
Was ist der Zweck davon?
max_
[cell superView]vielleicht?
Chris Loonam
6
Es lohnt sich zu erklären, warum Sie denken, dass Sie dies brauchen - da dies ein Zeichen für schlechtes Design sein kann, da ich mir nicht wirklich viele legitime Gründe vorstellen kann, warum eine Zelle weiß, ob sie auf dem Bildschirm angezeigt wird oder nicht.
Pauls
@ Paul.s Wir haben eine Gestenerkennung für ein Bild in einer Zelle. Wenn die Zelle berührt wird, wird eine weitere Überlagerungsansicht geöffnet, denken Sie an einen Popover-Stil, die so viele Zellen überlagern sollte, wie für eine ordnungsgemäße Anzeige erforderlich sind. Damit dies funktioniert, muss die TableView oder eine andere Ansicht angezeigt werden. Nicht wirklich zufrieden mit den Lösungen, aber um den gewünschten Effekt zu erzielen, ist es das Beste, was wir uns ausgedacht haben, die UITableView der UITableViewCell zu erhalten.
Chadbag
1
@chadbag keine Sorge, hoffentlich habe ich jemand anderem mit dem gleichen Problem eine Idee gegeben.
PJ_Finnegan

Antworten:

153

Um zu vermeiden, dass die iOS-Version überprüft wird, gehen Sie iterativ die Übersichtsansichten aus der Zellenansicht nach oben, bis eine UITableView gefunden wird:

id view = [tableViewCellInstance superview];

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

UITableView *tableView = (UITableView *)view;
Muhammad Idris
quelle
1
Vielen Dank. Es scheint, dass sich dies in iOS 8 erneut geändert hat und alle Versionen gut gepflegt werden.
Djskinner
2
Ich würde empfehlen, der Zelle einen schwachen Verweis auf die Tabellenansicht hinzuzufügen, um Kompatibilitätsprobleme bei zukünftigen Updates zu vermeiden.
Cenny
1
Eher ein schwacher Weg. Es wird nicht funktionieren, wenn sich die Hierarchie der Ansicht in Zukunft ändert.
RomanN
2
Es ist besser, einfach eine schwache Eigenschaft zu erstellen, die tatsächlich einen Zeiger auf die Tabellenansicht enthält. In der Unterklasse @property (weak, nonatomic) UITableView *tableView;und in tableView:cellForRowAtIndexPath:gerade gesetzt cell.tableView = tableView;.
Alejandro Iván
Derzeit wird nach dem Deaktivieren einer Zelle eine Tabellenansicht nicht sofort als Übersicht der Zelle angezeigt. Wenn ich die Zelle aus der Ansicht heraus und wieder hinein scrolle, finde ich eine Tabellenansicht als Übersicht (rekursive Suche wie in beschrieben) Andere Antwort). Autolayout und möglicherweise andere Faktoren sind wahrscheinliche Gründe.
Jonny
48

In iOS7 Beta 5 UITableViewWrapperViewist die Übersicht von a UITableViewCell. Auch UITableViewist Übersicht von a UITableViewWrapperView.

Für iOS 7 ist die Lösung also

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

Für iOSes bis iOS 6 ist die Lösung also

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

KGS-12
quelle
5
Oh man, das ist eine brutale API-Änderung. Was ist die beste Vorgehensweise von Apple für die Verzweigung, wenn Sie sowohl 6 als auch 7 unterstützen?
Ryan Romanchuk
@RyanRomanchuk Hier ist ein guter Vorschlag: devforums.apple.com/message/865550#865550 - Erstellen Sie beim Erstellen der Zelle einen schwachen Zeiger auf Ihre zugehörige Tabellenansicht. Alternativ können Sie eine UITableViewCellKategorie mit einer neuen Methode erstellen , relatedTableViewdie eine iOS-Version überprüft und die entsprechende SuperView zurückgibt.
Memmons
3
Schreiben Sie dazu eine rekursive Kategorie in UIView. Sie müssen die Version nicht überprüfen. Rufen Sie einfach die Übersicht auf, bis Sie eine Tabellenansichtszelle oder den oberen Rand des Ansichtsstapels finden. Dies ist, was ich in iOS 6 verwendet habe, und es funktionierte ohne Änderung in iOS 7. Und sollte in iOS 8 funktionieren.
Steven Fisher
Um Entschuldigung bitten; Ich meinte, bis Sie die Tabellenansicht finden. :)
Steven Fisher
39

Swift 5 Erweiterung

Rekursiv

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

Schleife verwenden

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
quelle
Eine Faustregel ist, keine Gewalt beim Auspacken anzuwenden
Thibaut Noah
1
@thibautnoah view != nilVor dem Auspacken erfolgt eine Überprüfung. Aktualisiert auf saubereren Code
Orkhan Alikhanov
25

Vor iOS7 war die Übersicht der Zelle diejenige UITableView, die sie enthielt. Ab iOS7 GM (wird also vermutlich auch in der öffentlichen Version verfügbar sein) ist die Übersicht der Zelle eine, UITableViewWrapperViewwobei die Übersicht die ist UITableView. Es gibt zwei Lösungen für das Problem.

Lösung 1: eine erstellen UITableViewCellKategorie

@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

Dies ist ein guter Ersatz für die Verwendung cell.superview, macht es einfach, Ihren vorhandenen Code umzugestalten - suchen und ersetzen Sie ihn einfach [cell relatedTable]und geben Sie eine Bestätigung ein, um sicherzustellen, dass die Ansichtshierarchie, wenn sie sich in Zukunft ändert oder zurücksetzt, sofort angezeigt wird in Ihren Tests.

Lösung 2: Fügen Sie einen schwachen UITableViewVerweis auf hinzuUITableViewCell

@interface SOUITableViewCell

   @property (weak, nonatomic) UITableView *tableView;

@end

Dies ist ein viel besseres Design, obwohl für die Verwendung in vorhandenen Projekten etwas mehr Code-Refactoring erforderlich ist. tableView:cellForRowAtIndexPathVerwenden Sie in Ihrer Verwendung SOUITableViewCell als Ihre Zellenklasse oder stellen Sie sicher, dass Ihre benutzerdefinierte SOUITableViewCellZellenklasse von einer Unterklasse abweicht, und weisen Sie die tableView der tableView-Eigenschaft der Zelle zu. Innerhalb der Zelle können Sie dann mit auf die enthaltene Tabellenansicht verweisen self.tableView.

memmons
quelle
Ich bin nicht der Meinung, dass Lösung 2 ein besseres Design ist. Es hat mehr Fehlerquellen und erfordert disziplinierte manuelle Eingriffe. Die erste Lösung ist eigentlich ziemlich zuverlässig, obwohl ich sie implementieren würde, wie @idris in seiner Antwort für ein bisschen mehr Zukunftssicherheit vorgeschlagen hat.
Devios1
1
Test [self.superview.superview isKindOfClass:[UITableView class]]sollte der erste sein, denn das iOS 7 wird immer mehr.
CopperCash
7

Wenn es sichtbar ist, hat es eine Übersicht. Und ... Überraschung ... die Übersicht ist ein UITableView-Objekt.

Eine Übersicht ist jedoch keine Garantie dafür, dass Sie auf dem Bildschirm angezeigt werden. UITableView bietet jedoch Methoden, um zu bestimmen, welche Zellen sichtbar sind.

Und nein, es gibt keinen dedizierten Verweis von einer Zelle auf eine Tabelle. Wenn Sie jedoch UITableViewCell unterordnen, können Sie eine einführen und diese bei der Erstellung festlegen. (Ich habe das selbst oft gemacht, bevor ich an die Subview-Hierarchie dachte.)

Update für iOS7: Apple hat hier die Subview-Hierarchie geändert. Wie bei der Arbeit mit Dingen, die nicht detailliert dokumentiert sind, besteht wie immer das Risiko, dass sich Dinge ändern. Es ist weitaus sicherer, die Ansichtshierarchie zu "crawlen", bis schließlich ein UITableView-Objekt gefunden wird.

Hermann Klecker
quelle
14
Dies ist in iOS7 nicht mehr der Fall. In iOS7 Beta5 ist UITableViewWrapperView die Übersicht einer UITableViewCell ... was mir momentan Probleme bereitet
Gabe
Danke für den Kommentar. Ich muss dann einen Teil meines Codes überprüfen.
Hermann Klecker
7

Schnelle 2.2-Lösung.

Eine Erweiterung für UIView, die rekursiv nach einer Ansicht mit einem bestimmten Typ sucht.

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

oder kompakter (dank Kabiroberai):

import UIKit

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

In deiner Zelle nennst du es einfach:

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

Beachten Sie, dass UITableViewCell erst nach der Ausführung von cellForRowAtIndexPath zur UITableView hinzugefügt wird.

Mehdzor
quelle
Sie können den gesamten lookForSuperviewOfType:Methodenkörper in eine Zeile return superview as? T ?? superview?.superviewOfType(type)
komprimieren
@ Kabiroberai, danke. Ich habe Ihren Rat zur Antwort hinzugefügt.
Mehdzor
Der vom rekursiven Aufruf verwendete Name muss übereinstimmen. Das muss also lauten: ... ?? Superview? .lookForSuperviewOfType (Typ)
Robert
7

Was auch immer Sie am Ende durch Aufrufen von Super View oder über die Responderkette erreichen können, wird sehr fragil sein. Der beste Weg, dies zu tun, wenn die Zellen etwas wissen wollen, besteht darin, ein Objekt an die Zelle zu übergeben, die auf eine Methode reagiert, die die Frage beantwortet, die die Zelle stellen möchte, und den Controller die Logik implementieren zu lassen, zu bestimmen, was zu beantworten ist (Aufgrund Ihrer Frage möchte die Zelle wissen, ob etwas sichtbar ist oder nicht).

Erstellen Sie ein Delegatenprotokoll in der Zelle, legen Sie den Delegaten der Zelle auf tableViewController fest und verschieben Sie die gesamte UI-Steuerlogik in tableViewCotroller.

Die Zellen der Tabellenansicht sollten eine Dum-Ansicht sein, in der nur Informationen angezeigt werden.

Javier Soto
quelle
Vielleicht sogar noch besser durch Delegierte. Ich verwende vorübergehend einen übergebenen Verweis auf die Tabelle, da der Elternteil mir Probleme gegeben hat.
Cristi Băluță
1
Was ich beschrieben habe, ist im Grunde das Delegatenmuster :)
Javier Soto
Dies sollte die akzeptierte Antwort sein, die Leute sehen diese Frage, sehen die 150+ Upvote-Antwort und sie denken, dass es in Ordnung ist, die tableView aus der Zelle zu holen und noch mehr, leider einen starken Verweis darauf zu behalten.
Alex Terente
6

Ich habe eine Kategorie in UITableViewCell erstellt, um die übergeordnete tableView abzurufen:

@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

Beste,

Boliva
quelle
3

Hier ist die Swift- Version basierend auf den obigen Antworten. Ich habe in ExtendedCellfür die spätere Verwendung verallgemeinert .

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

Ich hoffe das hilft :)

hqt
quelle
2

Ich habe diese Lösung auf Gabes Vorschlag gestützt, dass UITableViewWrapperViewObjekt die Übersicht über das UITableViewCellObjekt in iOS7 Beta5 ist.

Unterklasse 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
quelle
2

Ich habe mir ein wenig von der obigen Antwort geliehen und geändert und mir das folgende Snippet ausgedacht.

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

Das Bestehen im Unterricht bietet die Flexibilität, in anderen Fällen andere Ansichten als UITableView zu durchlaufen und abzurufen.

Azu
quelle
2

Meine Lösung für dieses Problem ähnelt etwas anderen Lösungen, verwendet jedoch eine elegante for-Schleife und ist kurz. Es sollte auch zukunftssicher sein:

- (UITableView *)tableView
{
    UIView *view;
    for (view = self.superview; ![view isKindOfClass:UITableView.class]; view = view.superview);
    return (UITableView *)view;
}
Bensge
quelle
Dies wird für immer wiederholt, wenn sich die Zelle beim Aufruf noch nicht in der Tabellenansicht befindet. Um dies zu beheben, fügen Sie einen Scheck für Null hinzu: 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
quelle
1

Verwenden Sie anstelle der Übersicht ["UItableViewvariable" visibleCells].

Ich habe das in einer foreach-Schleife verwendet, um die Zellen zu durchlaufen, die die App gesehen hat, und es hat funktioniert.

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

Klappt wunderbar.

user2497493
quelle
1

Minimal getestet, aber dieses nicht generische Swift 3-Beispiel scheint zu funktionieren:

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
quelle
0

Dieser Code `UITableView *tblView=[cell superview];gibt Ihnen eine Instanz der UItableview, die die Tabelle-Ansichtszelle enthält

Singh
quelle
Dies funktioniert nicht, da die unmittelbare Übersicht einer UITableViewCell keine UITableView ist. Ab iOS 7 ist die UITableViewCell-Übersicht eine UITableViewWrapperView. In den anderen Antworten finden Sie weniger fragile und zuverlässigere Ansätze.
JaredH
0

Ich schlage vor, dass Sie die Ansichtshierarchie auf diese Weise durchlaufen, um die übergeordnete UITableView zu finden:

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

Andernfalls wird Ihr Code beschädigt, wenn Apple die Ansichtshierarchie erneut ändert .

Eine andere Antwort , die auch die Hierarchie durchquert, ist rekursiv.

Bob Wakefield
quelle
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 Vielen Dank

jbchitaliya
quelle
0

Sie können es mit einer Codezeile erhalten.

UITableView *tableView = (UITableView *)[[cell superview] superview];
Karthik Damodara
quelle
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
quelle
0

von @idris Antwort Ich habe eine Erweiterung für UITableViewCell in Swift geschrieben

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
quelle