web-dev-qa-db-ja.com

UITableviewの行番号を知る方法

各セルのアクセサリビューとしてUITableViewCellUISwitchを使用しています。セルのスイッチの値を変更すると、スイッチがどの行にあるかをどのようにして知ることができますか?スイッチ値変更イベントの行番号が必要です。

38
Jean Paul Scott

タグ、サブクラス、またはビュー階層ナビゲーションはあまりに多くの作業!です。アクションメソッドでこれを行います。

CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView]; 
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];

送信者のOriginがセルのフレーム内にある限り(robに感謝!)、通常はそうである限り、あらゆる種類のビュー、マルチセクションテーブル、それに投げることができるもので動作します。

そして、これはUITableView Swift extension:

extension UITableView {
    func indexPath(for view: UIView) -> IndexPath? {
        let location = view.convert(CGPoint.zero, to: self)
        return self.indexPathForRow(at: location)
    }
}
158
jrturton

tagプロパティを行番号に設定すると(他の回答で提案されているように)、tableView:cellForRowAtIndexPath:で毎回更​​新する必要があります(セルを別の行に再利用できるため)。

代わりに、行番号が必要な場合は、superview(またはその他のビュー)からUISwitchまでUITableViewCellチェーンをたどって、次にUITableViewとし、テーブルビューにセルのインデックスパスを要求します。

static NSIndexPath *indexPathForView(UIView *view) {
    while (view && ![view isKindOfClass:[UITableViewCell class]])
        view = view.superview;
    if (!view)
        return nil;
    UITableViewCell *cell = (UITableViewCell *)view;
    while (view && ![view isKindOfClass:[UITableView class]])
        view = view.superview;
    if (!view)
        return nil;
    UITableView *tableView = (UITableView *)view;
    return [tableView indexPathForCell:cell];
}

tableView:cellForRowAtIndexPath:には何も必要ありません。

5
rob mayoff

cellForRowAtIndexPath:で、コントロールのtagプロパティをindexPath.rowに設定します

3
wattson12

受け入れられた解決策は巧妙なハックです。

ただし、IViewですでに利用可能なtagプロパティを利用できるのに、なぜヒットポイントを使用する必要があるのですか? タグは行またはセクションのいずれかのみを保存できますなので、単一のIntであると言えます。

ええと...ルーツを忘れないでください(CS101)。 1つのIntは、2倍小さいサイズの整数を2つ格納できます。そして、これはこれの拡張です:

_extension Int {

    public init(indexPath: IndexPath) {
        var marshalledInt: UInt32 = 0xffffffff

        let rowPiece = UInt16(indexPath.row)
        let sectionPiece = UInt16(indexPath.section)
        marshalledInt = marshalledInt & (UInt32(rowPiece) << 16)
        marshalledInt = marshalledInt + UInt32(sectionPiece)

        self.init(bitPattern: UInt(marshalledInt))
    }

    var indexPathRepresentation: IndexPath {
        let section = self & 0x0000ffff

        let pattern: UInt32 = 0xffff0000
        let row = (UInt32(self) & pattern) >> 16
        return IndexPath(row: Int(row), section: Int(section))
    }
}
_

tableView(_:, cellForRowAt:)では、次のことができます。

_cell.yourSwitch.tag = Int(indexPath: indexPath)
_

そして、アクションハンドラで次のことができます。

_func didToogle(sender: UISwitch){
    print(sender.tag.indexPathRepresentation)
}
_

ただし、その制限に注意してください:行とセクションは65535より大きくする必要はありません。(UInt16.max)

TableViewのインデックスがそれほど高くなることはないと思いますが、そうなった場合は、自分自身に挑戦して、より効率的なパッキングスキームを実装してください。たとえば、セクションが非常に小さい場合、セクションを表すために16ビットすべてを必要とするわけではありません。次のようなintレイアウトを使用できます。

_{section area length}{all remaining}[4 BITS: section area length - 1]
_

つまり、4 LSBsは、セクションに少なくとも1ビットを割り当てるとすると、セクション領域の長さ-1を示します。したがって、セクションが0の場合、行は最大27ビット([1] [27] [4])を占めることができ、これで十分です。

3
ambientlight

私はサブビューを使用することを好みますが、レイアウトがわかっている場合は、それが一般的に非常にシンプルで1行短いものです...

    NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)[[sender superview] superview]];

それ以上、ネストされている場合は、スーパービューを追加します。

もう少し情報:

あなたがしているのは、セルである親ビューとその親ビューを要求することだけです。次に、取得したセルのインデックスパスをテーブルビューに要求します。

2
Andres Canella

これを行う一般的な方法の1つは、コントロール(この場合はスイッチ)のtagを、行または表現されたオブジェクトを識別するために使用できるものに設定することです。

たとえば、tableView:cellForRowAtIndexPath:でスイッチのtagプロパティをindexPath.rowに設定し、アクションメソッドで送信者からタグを取得できます。

個人的には、私はこのアプローチが好きではなく、UITableViewCellをサブクラス化することを好みます。また、他のビューのタグとの競合を防ぐために、タグに「オフセット」を追加することをお勧めします。

1
Daniel Rinser

この投稿で受け入れられた答えは完全に問題ありません。この投稿の@robmayoffから派生した次のものも完全に問題ないことを読者に提案したいと思います。

- (NSIndexPath *)indexPathForView:(UIView *)view inTableView:(UITableView *)tableView {
    while (view && ![view isKindOfClass:[UITableViewCell class]])
        view = view.superview;
    UITableViewCell *cell = (UITableViewCell *)view;
    return [tableView indexPathForCell:cell];
}

一部の人は、whileループのために、このアプローチに含まれる計算作業が多すぎると主張しています。別の方法として、ビューの原点をテーブルビューの座標空間に変換してindexPathForRowAtPoint:を呼び出すと、さらに多くの作業が非表示になります。

このアプローチはSDKの変更の可能性に比べて安全ではないと主張する人もいます。実際、Appleは、テーブルビューのセル階層をすでに1回変更しており、contentViewをセルに追加しています。このアプローチは、そのような変更の前後に機能します。ビューの祖先がchainのスーパービュー(UIKitの基本となるもの)で見つけることができます。これは優れたコードです。

1
danh

同僚が次のことを提案し、それをUITableViewカテゴリに追加しました。

+(UITableViewCell*)findParentCellForSubview:(UIView*)view
{
    while (([view isKindOfClass:[UITableViewCell class]] == NO) && ([view superview] != nil))
        view = [view superview];

    if ([view superview] != nil)
        return (UITableViewCell*)view;

    return nil;
}

まだひどいですが、動作します。

0
jimkberry

SuperViewを使用するもう1つの方法。 UIViewのカテゴリのように機能します。

- (UITableViewCell *)superCell
{
    if (!self.superview) {
        return nil;
    }

    if ([self.superview isKindOfClass:[UITableViewCell class]]) {
        return (UITableViewCell *)self.superview;
    }

    return [self.superview superCell];
}
0
serj

私は複数のセクションについては知りませんが、1つのセクションのためにあなたを与えることができます...

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger index=indexPath.row;
NSString *string=[[NSString alloc]initWithFormat:@"%ld",(long)index];
}

これから行番号を取得し、文字列に保存できます。

0
Smit