UITableViewCellからUITableViewを取得する方法?


100

私が持っているUITableViewCellオブジェクトにリンクされていると私は、セルが表示されている場合伝える必要があります。私が行った調査から、これは何とかUITableViewそれを含むにアクセスする必要があることを意味します(そこから、それが表示されているかどうかを確認する方法がいくつかあります)。だからUITableViewCell、へのポインターがあるUITableViewか、またはセルからポインターを取得する他の方法があったかどうか疑問に思っていますか?


2
これの目的は何ですか?
max_ 2013年

[cell superView]多分?
Chris Loonam 2013年

6
なぜこれが必要だと思うのかを説明する価値があります。これは、セルが画面上にあるかどうかを知るための正当な理由があまり考えられないので、これが悪いデザインの兆候である可能性があるためです。
Paul.s 2013年

@ Paul.sセル内の画像にジェスチャー認識機能があり、セルがタッチされると、適切に表示するために必要な数のセルをオーバーレイする別のオーバーレイビューが開きます。これが機能するためには、TableViewまたはそれに表示される他のビューが表示される必要があります。ソリューションに本当に満足しているわけではありませんが、UITableViewCellのUITableViewを取得することは、私たちが思いついた最高の方法です。
chadbag 2015年

1
@chadbagは心配しないで、うまくいけば私は同じ問題を持つ誰かにアイデアを与えました。
PJ_Finnegan 2017

回答:


153

iOSバージョンのチェックを回避するには、UITableViewが見つかるまで、セルのビューからスーパービューを繰り返し調べます。

id view = [tableViewCellInstance superview];

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

UITableView *tableView = (UITableView *)view;

1
ありがとう。これはiOS 8でもう一度変更されたようで、すべてのバージョンを適切に処理します。
djskinner 14年

2
今後の更新での互換性の問題を回避するために、テーブルビューへの弱い参照をセルに追加することをお勧めします。
Cenny

1
むしろ弱い方法。ビューの階層が将来変更された場合は機能しません。
RomanN

2
テーブルビューへのポインタを実際に保持する弱いプロパティを単に作成する方が良いでしょう。サブクラス@property (weak, nonatomic) UITableView *tableView;tableView:cellForRowAtIndexPath:ただのセットでcell.tableView = tableView;
AlejandroIván

現在の経験では、セルをデキューした後、テーブルビューがセルのスーパービューとしてすぐに表示されません-セルをビューの外にスクロールして再び戻すと、テーブルビューがスーパービューとして見つかります(再帰検索で説明されているように)他の答え)。Autolayoutおよびおそらく他の要因が理由である可能性があります。
ジョニー

48

iOS7では、ベータ5はのUITableViewWrapperViewスーパービューですUITableViewCellUITableView監督でもありUITableViewWrapperViewます。

したがって、iOS 7のソリューションは

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

したがって、iOS 6までのiOSのソリューションは

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


5
ああ、これは残忍なAPIの変更です。6と7の両方をサポートしている場合の分岐のアップルのベストプラクティスは何ですか?
Ryan Romanchuk 2013

@RyanRomanchukここに1つの良い提案があります:devforums.apple.com/message/865550#865550-セルの作成時に、関連するtableViewへの弱いポインターを作成します。または、iOSのバージョンをチェックして適切なsuperViewを返すUITableViewCell新しいメソッドでカテゴリを作成しますrelatedTableView
memmons 2013

3
このために、UIViewで再帰的なカテゴリを記述します。バージョンを確認する必要はありません。テーブルビューセルまたはビュースタックの最上部が見つかるまで、superviewを呼び出すだけです。この私がiOSの6で使用したものであり、それはiOSの7に変更することなく働いていたとiOS 8にsitllを動作するはずです
スティーブン・フィッシャー

許しを請う; テーブルビューが見つかるまでです。:)
スティーブンフィッシャー

39

Swift 5拡張

再帰的に

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

ループの使用

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

経験則ではアンラップ力を使用しないことです
ノアティボー

1
@thibautnoah開封view != nil前にチェックがあります。ただし、よりクリーンなコードに更新
Orkhan Alikhanov

25

iOS7以前は、セルのスーパービューはそれUITableViewを含むものでした。iOS7 GMの時点では(おそらく一般リリースにも含まれると思われます)、セルのスーパービューはでUITableViewWrapperViewあり、スーパービューはUITableViewです。この問題には2つの解決策があります。

解決策1:UITableViewCellカテゴリを作成する

@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

これは、を使用cell.superviewする場合の優れた置き換えであり[cell relatedTable]、既存のコードを簡単にリファクタリングできます。検索してで置き換え、アサートをスローするだけで、ビュー階層が変更された場合や将来復帰した場合にすぐに表示されます。あなたのテストで。

解決策#2:に弱UITableView参照を追加するUITableViewCell

@interface SOUITableViewCell

   @property (weak, nonatomic) UITableView *tableView;

@end

これははるかに優れた設計ですが、既存のプロジェクトで使用するにはもう少しコードのリファクタリングが必要になります。あなたにはtableView:cellForRowAtIndexPathあなたのセルクラスとして使用SOUITableViewCellかを確認してくださいカスタムセルクラスからサブクラス化されSOUITableViewCell、セルののtableViewプロパティにのtableViewを割り当てます。セル内では、を使用して、含まれているテーブルビューを参照できますself.tableView


ソリューション2の方が優れた設計であることに同意しません。障害点が多く、手動での介入が必要です。最初の解決策は実際には非常に信頼できますが、@ idrisが彼の回答で提案したように、もう少し将来を保証するために実装します。
devios1 14

1
[self.superview.superview isKindOfClass:[UITableView class]]iOS 7がどんどん増えているので、テストが最初であるべきです。
CopperCash 2014年

7

表示されている場合は、スーパービューがあります。そして...驚き...スーパービューはUITableViewオブジェクトです。

ただし、スーパービューがあることは画面上にあることを保証するものではありません。ただし、UITableViewは、どのセルが表示されるかを決定するメソッドを提供します。

そして、いいえ、セルからテーブルへの専用の参照はありません。しかし、UITableViewCellをサブクラス化する場合、UITableViewCellを導入して作成時に設定できます。(私は、サブビューの階層を考える前に、自分でたくさんやった。)

iOS7のアップデート: Appleはサブビュー階層をここで変更しました。詳細が文書化されていないものを扱う場合はいつものように、物事が変化するリスクが常にあります。UITableViewオブジェクトが最終的に見つかるまで、ビュー階層を「クロール」する方がはるかに節約できます。


14
これは、iOS7ではもはや当てはまりません。iOS7のベータ5では、UITableViewWrapperViewはUITableViewCellのスーパービューです...今問題を引き起こしています
Gabe

コメントをありがとう。次に、コードの一部を確認する必要があります。
ヘルマンクレッカー2013

7

Swift 2.2ソリューション。

特定のタイプのビューを再帰的に検索するUIViewの拡張。

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

以上コンパクト(kabilroberaiに感謝):

import UIKit

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

あなたの細胞ではあなたはそれを呼び出すだけです:

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

UITableViewCellは、cellForRowAtIndexPathの実行後にのみUITableViewに追加されることに注意してください。


あなたは全体のコンパクト化することができますlookForSuperviewOfType::それも迅速-EY作り、1つのラインにメソッド本体をreturn superview as? T ?? superview?.superviewOfType(type)
kabiroberai

@kabiroberai、ありがとうございます。答えにあなたのアドバイスを加えました。
Mehdzor

再帰呼び出しで使用される名前は一致する必要があります。したがって、これを読む必要があります:... ?? superview?.lookForSuperviewOfType(type)
robert

7

スーパービューを呼び出したり、レスポンダーチェーンを経由したりして、最終的に何をやっても壊れやすくなります。これを行う最良の方法は、セルが何かを知りたい場合、セルが尋ねたい質問に答える何らかのメソッドに応答するオブジェクトをセルに渡し、コントローラーが答えるべきものを決定するロジックを実装することです(あなたの質問から、何かが目に見えるかどうかを細胞が知りたいと思います)。

セルにデリゲートプロトコルを作成し、セルのデリゲートをtableViewControllerに設定し、tableViewCotrollerのすべてのUI「制御」ロジックを移動します。

テーブルビューのセルは、情報のみを表示するダムビューである必要があります。


たぶん、デリゲートを通してもっと良いです。親から問題があったので、渡されたテーブルへの参照を一時的に使用しています。
Cristi Băluță 2013年

1
私が説明したのは、基本的にデリゲートパターンを使用しています:)
Javier Soto

これは受け入れられる答えであるはずです。人々はこの質問を見て、150以上の賛成投票の答えを見て、セルからtableViewを取得することは問題ないと考えています。
Alex Terente

6

親のtableViewを取得するために、UITableViewCellにカテゴリを作成しました。

@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

ベスト、


3

上記の回答に基づくSwiftのバージョンは次のとおりです。ExtendedCell後で使用するために一般化しました。

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

この助けを願っています:)


2

このソリューションUITableViewWrapperViewは、UITableViewCellオブジェクトがiOS7 beta5 のオブジェクトのスーパービューであるというGabeの提案に基づいています 。

サブクラス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

私は上記の回答から少しだけ借りて修正し、次のスニペットを思いつきました。

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

クラスを渡すと、UITableView以外のビューをトラバースおよび取得するための柔軟性が提供されます。


2

この問題に対する私の解決策は他の解決策と多少似ていますが、エレガントなforループを使用しており、短いものです。また、将来性を備えている必要があります。

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

呼び出されたときにセルがまだテーブルビューにない場合、これは永久にループします。修正するには、ゼロのチェックを追加します for (view = self.superview; view && ![view isKindOfClass:UITableView.class]; view = view.superview);
アントニオ・ヌネス

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

1

スーパービューの代わりに、["UItableViewvariable" visibleCells]を使用してみてください。

私はそれをforeachループで使用して、アプリが見たセルをループし、機能しました。

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

魅力のように機能します。


1

最小限のテストが行​​われましたが、この非汎用のSwift 3の例は機能するようです:

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

このコード`UITableView *tblView=[cell superview];は、タブビューセルを含むUItableviewのインスタンスを提供します


UITableViewCellの直接のスーパービューはUITableViewではないため、これは機能しません。iOS 7以降、UITableViewCellスーパービューはUITableViewWrapperViewです。脆弱性が低く、信頼性の高いアプローチについては、ここにある他の回答を参照してください。
JaredH 2014年

0

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

そうしないと、Appleがビュー階層を再び変更すると、コードが壊れます。

階層も横断する別の答えは再帰的です。


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 ありがとう



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の回答から私はSwiftでUITableViewCellの拡張を書きました

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
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.