UITableViewで押されたUIButtonを検出する


212

私はUITableView5 を持っていUITableViewCellsます。各セルには、UIButton次のように設定されたが含まれています。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
     NSString *identifier = @"identifier";
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
         [cell autorelelase];

         UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
         [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
         [button setTag:1];
         [cell.contentView addSubview:button];

         [button release];
     }

     UIButton *button = (UIButton *)[cell viewWithTag:1];
     [button setTitle:@"Edit" forState:UIControlStateNormal];

     return cell;
}

私の質問はこれです:buttonPressedAction:メソッドで、どのボタンが押されたかを知るにはどうすればよいですか。タグの使用を検討しましたが、これが最適なルートかどうかはわかりません。何とかindexPathしてをコントロールにタグ付けできるようにしたいのですが。

- (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;
    // how do I know which button sent this message?
    // processing button press for this row requires an indexPath. 
}

これを行う標準的な方法は何ですか?

編集:

私は次のようにして少し解決しました。これが標準的な方法であるのか、それとももっと良い方法があるのか​​、私はまだ意見を知りたいのですが。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
     NSString *identifier = @"identifier";
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
         [cell autorelelase];

         UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
         [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
         [cell.contentView addSubview:button];

         [button release];
     }

     UIButton *button = (UIButton *)[cell.contentView.subviews objectAtIndex:0];
     [button setTag:indexPath.row];
     [button setTitle:@"Edit" forState:UIControlStateNormal];

     return cell;
}

- (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;
    int row = button.tag;
}

セルがデキューされる可能性があるため、セルの作成時にタグを設定できないことに注意してください。とても汚れた感じです。もっと良い方法があるはずです。


タグソリューションの使用に問題はありません。セルは再利用されるため、ここで行っている方法でタグを行インデックスに設定することは理にかなっています。これは、以下に示すように、タッチ位置を行インデックスに変換するよりもはるかにエレガントな解決策です。
Erik van der Neut

回答:


400

Appleのアクセサリサンプルでは、​​次の方法が使用されます。

[button addTarget:self action:@selector(checkButtonTapped:) forControlEvents:UIControlEventTouchUpInside];

次に、タッチハンドラーでタッチ座標が取得され、その座標からインデックスパスが計算されます。

- (void)checkButtonTapped:(id)sender
{
    CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    if (indexPath != nil)
    {
     ...
    }
}

ええ、これは私が解決したものです(私の編集を参照してください)。私はそれが最適ではないことに同意します。
手綱

2
ただし、UIButtonを自分でUITableViewCellに追加するので、セルの作成時に行うことと一貫している必要があります。このアプローチは実際にはエレガントに見えませんが、認めざるを得ません
ウラジミール

1
最初のソリューションでは、最初のスーパービューの呼び出しでcontentViewが得られ、最後に2番目の呼び出しでUITableViewCellが得られるため、[[button superview] superview]を取得する必要があります。2番目のソリューションは、セルを追加または削除する場合、行インデックスを無効にするため、うまく機能しません。したがって、私は概説されている最初の解決策を採用しましたが、それは完璧に機能しました。
raidfive 2010年

3
これにより、ボタンを所有するセルが確実に選択されます。UIView * view = button; while(![view isKindOfClass:[UITableViewCell class]]){view = [view superview]}
Jacob Lyles

1
使用時にトラップがあります:[button addTarget:self action:@selector(checkButtonTapped :) forControlEvents:UIControlEventTouchUpInside]; addTarget:action:forControlEvents:は、テーブルをスクロールすると複数の複製されたターゲットとアクションを追加するため、以前のターゲットとアクションは削除されないため、ボタンをクリックすると、メソッドcheckButtonTapped:が何度も呼び出されます。ターゲットとアクションを追加する前に削除することをお
勧め

48

スーパービューのスーパービューを使用してセルのindexPathへの参照を取得する方法が完全に機能することを発見しました。ヒントリンクテキストを提供してくれたiphonedevbook.com(macnsmith)に感謝

-(void)buttonPressed:(id)sender {
 UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
 NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
...

}

Cocoanut、あなたのコードの断片は私にこの問題の私自身のバリエーションの正しい方向を示しました。ありがとう!他の誰かがそれを必要とする場合、私の特別なケースは、ボタンがフッターの一部として表示されていたカスタムセルにあったことです。以下にコードを追加します
ソフトウェアは

あなた(Stackoverflowリーダー)がこれを試してもうまくいかない場合は、実装でUIButtonが実際にUITableViewCellの孫であるかどうかを確認してください。私の実装では、UIButtonはUITableViewCellの直接の子だったので、Cocoanutのコードの「スーパービュー」の1つを取り出す必要があり、それが機能しました。
Jon Schneider 2013年

29
これは非常に間違っており、新しいバージョンのOSでは壊れています。所有していないスーパービューツリーを歩いてはいけません。
ケンリク2013年

2
これはiOS 6では私には機能しましたが、iOS 7では機能しません。@ KenrikMarchには有効なポイントがあるようです。
Jon Schneider

3
iOS 7では、スーパービューがさらに1つ上になりました。例:[[[sender superview] superview] superView];
CW0007007 14年

43

これが私のやり方です。シンプルで簡潔:

- (IBAction)buttonTappedAction:(id)sender
{
    CGPoint buttonPosition = [sender convertPoint:CGPointZero
                                           toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    ...
}

2
さらにシンプル:;-)のCGPointZero代わりに使用CGPointMake(0, 0)
Jakob W

使いやすい。さらに、Swift 3に簡単に翻訳できます。あなたは最高です:)
Francisco Romero

これを下のスウィフトに翻訳しました。私が見つけた最も簡単な解決策。クリス、ありがとう!
Rutger Huijsmans

6

他の場所でこの問題の素晴らしい解決策が見つかりました。ボタンのタグをいじる必要はありません。

- (void)buttonPressedAction:(id)sender {

    NSSet *touches = [event allTouches];
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];

    // do stuff with the indexPath...
}

5
この例では、「イベント」オブジェクトをどこから取得するのかは明確ではありません。
Nick Ludlam、2010年

これは私が行ったソリューションです。インデックスが変更されるため、行の追加/削除時にタグを使用することは予測できません。また、
raidfive

@NickLudlam:おそらく、メソッド名ではありませんbuttonPressedAction:けどbuttonPressedAction:forEvent:
KPM、2012年

5

ランタイムインジェクションを使用する場合のようNSIndexPathに情報を送信するのはどうですかUIButton

1)インポート時にランタイムが必要です

2)静的定数を追加する

3)以下をNSIndexPath使用してランタイム時にボタンに追加します:

(void)setMetaData:(id)target withObject:(id)newObj

4)ボタンを押したときにメタデータを取得:

(id)metaData:(id)target

楽しい

    #import <objc/runtime.h>
    static char const * const kMetaDic = "kMetaDic";


    #pragma mark - Getters / Setters

- (id)metaData:(id)target {
    return objc_getAssociatedObject(target, kMetaDic);
}

- (void)setMetaData:(id)target withObject:(id)newObj {
    objc_setAssociatedObject(target, kMetaDic, newObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}



    #On the cell constructor
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    ....
    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    ....
    [btnSocial addTarget:self
                                   action:@selector(openComments:)
                         forControlEvents:UIControlEventTouchUpInside];

    #add the indexpath here or another object
    [self setMetaData:btnSocial withObject:indexPath];

    ....
    }



    #The action after button been press:

    - (IBAction)openComments:(UIButton*)sender{

        NSIndexPath *indexPath = [self metaData:sender];
        NSLog(@"indexPath: %d", indexPath.row);

        //Reuse your indexpath Now
    }

1
テーブルが再配置されるか、行が削除された場合、これは機能しません。
Neil、

5

(@Vladimir)の答えはSwiftです。

var buttonPosition = sender.convertPoint(CGPointZero, toView: self.tableView)
var indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)!

チェックするとindexPath != nil指が出てきますが...「NSIndexPathはNSStringのサブタイプではありません」


5

Swift 4.2およびiOS 12では、問題を解決するために、5つの完全な例のいずれかを選択できます。


#1。使用するUIViewconvert(_:to:)UITableViewindexPathForRow(at:)

import UIKit

private class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}
import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        cell.button.addTarget(self, action: #selector(customCellButtonTapped), for: .touchUpInside)
        return cell
    }

    @objc func customCellButtonTapped(_ sender: UIButton) {
        let point = sender.convert(CGPoint.zero, to: tableView)
        guard let indexPath = tableView.indexPathForRow(at: point) else { return }
        print(indexPath)
    }

}

#2。使用するUIViewconvert(_:to:)UITableViewindexPathForRow(at:)(代替)

これは、niltargetパラメーターに渡す前の例の代替addTarget(_:action:for:)です。このように、最初のレスポンダがアクションを実装しない場合、適切な実装が見つかるまで、レスポンダチェーン内の次のレスポンダに送信されます。

import UIKit

private class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        button.addTarget(nil, action: #selector(TableViewController.customCellButtonTapped), for: .touchUpInside)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}
import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        return cell
    }

    @objc func customCellButtonTapped(_ sender: UIButton) {
        let point = sender.convert(CGPoint.zero, to: tableView)
        guard let indexPath = tableView.indexPathForRow(at: point) else { return }
        print(indexPath)
    }

}

#3。とデリゲートパターンUITableViewの使用indexPath(for:)

この例では、ビューコントローラーをセルのデリゲートとして設定します。セルのボタンがタップされると、デリゲートの適切なメソッドの呼び出しがトリガーされます。

import UIKit

protocol CustomCellDelegate: AnyObject {
    func customCellButtonTapped(_ customCell: CustomCell)
}

class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)
    weak var delegate: CustomCellDelegate?

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @objc func buttonTapped(sender: UIButton) {
        delegate?.customCellButtonTapped(self)
    }

}
import UIKit

class TableViewController: UITableViewController, CustomCellDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        cell.delegate = self
        return cell
    }

    // MARK: - CustomCellDelegate

    func customCellButtonTapped(_ customCell: CustomCell) {
        guard let indexPath = tableView.indexPath(for: customCell) else { return }
        print(indexPath)
    }

}

#4。委任のためUITableViewindexPath(for:)とクロージャーの使用

これは、プロトコルデリゲート宣言の代わりにクロージャーを使用してボタンタップを処理する、前の例の代替です。

import UIKit

class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)
    var buttontappedClosure: ((CustomCell) -> Void)?

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @objc func buttonTapped(sender: UIButton) {
        buttontappedClosure?(self)
    }

}
import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        cell.buttontappedClosure = { [weak tableView] cell in
            guard let indexPath = tableView?.indexPath(for: cell) else { return }
            print(indexPath)
        }
        return cell
    }

}

#5。使用するUITableViewCellaccessoryTypeUITableViewDelegatetableView(_:accessoryButtonTappedForRowWith:)

あなたのボタンがある場合UITableViewCellの標準付属のコントロールは、その上の任意のタップへの呼び出しをトリガーするUITableViewDelegates 'をtableView(_:accessoryButtonTappedForRowWith:)使用すると、関連するインデックスのパスを取得することができ、。

import UIKit

private class CustomCell: UITableViewCell {

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        accessoryType = .detailButton
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}
import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        return cell
    }

    override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
        print(indexPath)
    }

}

5
func buttonAction(sender:UIButton!)
    {
        var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tablevw)
        let indexPath = self.tablevw.indexPathForRowAtPoint(position)
        let cell: TableViewCell = tablevw.cellForRowAtIndexPath(indexPath!) as TableViewCell
        println(indexPath?.row)
        println("Button tapped")
    }

3

私はあなたが言ったようにタグプロパティを使用し、タグを次のように設定します:

[button setTag:indexPath.row];

次に、buttonPressedAction内に次のようにタグを取得します。

((UIButton *)sender).tag

または

UIButton *button = (UIButton *)sender; 
button.tag;

5
このアプローチは、セクションのあるテーブルでは完全に壊れています。
ohhorob

いいえ、単純な関数を使用してセクションをタグに配置することもできます。
ACBurk、

2
tag整数です。インデックスパスをビュータグにエンコード/デコードするのは少し扱いに​​くいようです。
ohhorob

それは正しいですが、セクションがある場合に使用するものではありませんが、それは解決策です。私が言いたかったのは、それがこの方法を使用して実行できること、壊れていなかったことだけです。より良い、より複雑なバージョンは、UITableView内のボタンの位置からインデックスパスを決定します。ただし、reinはセルが5つ(セクションなし)しかないと述べているので、その方法はおそらく複雑で、最初のコメントとこのコメント全体のスレッドは無意味です。
ACBurk

3

私はタグの方法が好きですが...何らかの理由でタグを使用したくない場合NSArrayは、既製のボタンのメンバーを作成できます。

NSArray* buttons ;

次に、tableViewをレンダリングする前にこれらのボタンを作成し、それらを配列にプッシュします。

次に、tableView:cellForRowAtIndexPath:あなたができる関数の中で:

UIButton* button = [buttons objectAtIndex:[indexPath row] ] ;
[cell.contentView addSubview:button];

次に、buttonPressedAction:関数で行うことができます

- (void)buttonPressedAction:(id)sender {
   UIButton* button = (UIButton*)sender ;
   int row = [buttons indexOfObject:button] ;
   // Do magic
}

2

セクションを処理するには-カスタムUITableViewCellにNSIndexPathを保存しました

IN CLKIndexPricesHEADERTableViewCell.xib

IN IB XButtonにUIButtonを追加-アクションを追加しないでください!

アウトレット@propertyを追加します(保持、非アトミック)IBOutlet UIButton * buttonIndexSectionClose;

CTRL +ドラッグしてIBのアクションを実行しないでください(以下のコードで行います)

@interface CLKIndexPricesHEADERTableViewCell : UITableViewCell
...
@property (retain, nonatomic) IBOutlet UIButton *buttonIndexSectionClose;
@property (nonatomic, retain) NSIndexPath * indexPathForCell;
@end

viewForHeaderInSection内(テーブルにセクションが1つしかない場合は、cellForRow ....などでも機能するはずです)

- viewForHeaderInSection is called for each section 1...2...3
- get the cell CLKIndexPricesHEADERTableViewCell 
- getTableRowHEADER just does the normal dequeueReusableCellWithIdentifier
- STORE the indexPath IN the UITableView cell
- indexPath.section = (NSInteger)section
- indexPath.row = 0 always (we are only interested in sections)

- (UIView *) tableView:(UITableView *)tableView1 viewForHeaderInSection:(NSInteger)section {


    //Standard method for getting a UITableViewCell
    CLKIndexPricesHEADERTableViewCell * cellHEADER = [self getTableRowHEADER];

...セクションを使用してセルのデータを取得する

...記入してください

   indexName        = ffaIndex.routeCode;
   indexPrice       = ffaIndex.indexValue;

   //

   [cellHEADER.buttonIndexSectionClose addTarget:self
                                          action:@selector(buttonDELETEINDEXPressedAction:forEvent:)
                                forControlEvents:UIControlEventTouchUpInside];


   cellHEADER.indexPathForCell = [NSIndexPath indexPathForRow:0 inSection:section];


    return cellHEADER;
}

ユーザーがセクションヘッダーの削除ボタンを押すと、

- (void)buttonDELETEINDEXPressedAction:(id)sender forEvent:(UIEvent *)event
{
    NSLog(@"%s", __PRETTY_FUNCTION__);


    UIView *  parent1 = [sender superview];   // UiTableViewCellContentView
    //UIView *myContentView = (UIView *)parent1;

    UIView *  parent2 = [parent1 superview];  // custom cell containing the content view
    //UIView *  parent3 = [parent2 superview];  // UITableView containing the cell
    //UIView *  parent4 = [parent3 superview];  // UIView containing the table


    if([parent2 isMemberOfClass:[CLKIndexPricesHEADERTableViewCell class]]){
        CLKIndexPricesHEADERTableViewCell *myTableCell = (CLKIndexPricesHEADERTableViewCell *)parent2;

        //UITableView *myTable = (UITableView *)parent3;
        //UIView *mainView = (UIView *)parent4;

        NSLog(@"%s indexPath.section,row[%d,%d]", __PRETTY_FUNCTION__, myTableCell.indexPathForCell.section,myTableCell.indexPathForCell.row);

        NSString *key = [self.sortedKeysArray objectAtIndex:myTableCell.indexPathForCell.section];
        if(key){
            NSLog(@"%s DELETE object at key:%@", __PRETTY_FUNCTION__,key);
            self.keyForSectionIndexToDelete = key;
            self.sectionIndexToDelete = myTableCell.indexPathForCell.section;

            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Remove Index"
                                                                message:@"Are you sure"
                                                               delegate:self
                                                      cancelButtonTitle:@"No"
                                                      otherButtonTitles:@"Yes", nil];
            alertView.tag = kALERTVIEW_REMOVE_ONE_INDEX;
            [alertView show];
            [alertView release];
            //------
        }else{
            NSLog(@"ERROR: [%s] key is nil for section:%d", __PRETTY_FUNCTION__,myTableCell.indexPathForCell.section);
        }

    }else{
        NSLog(@"ERROR: [%s] CLKIndexPricesHEADERTableViewCell not found", __PRETTY_FUNCTION__);
    }
}

この例では、削除ボタンを追加したので、確認のためにUIAlertViewを表示する必要があります

セクションとキーを、VCのivarにセクションに関する情報を格納する辞書に格納します

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
   if(alertView.tag == kALERTVIEW_REMOVE_ONE_INDEX){
        if(buttonIndex==0){
            //NO
            NSLog(@"[%s] BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
            //do nothing
        }
        else if(buttonIndex==1){
            //YES
            NSLog(@"[%s] BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
            if(self.keyForSectionIndexToDelete != nil){

                //Remove the section by key
                [self.indexPricesDictionary removeObjectForKey:self.keyForSectionIndexToDelete];

                //sort the keys so sections appear alphabetically/numbericsearch (minus the one we just removed)
                [self updateTheSortedKeysArray];                

                //Delete the section from the table using animation
                [self.tableView beginUpdates];

                [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:self.sectionIndexToDelete]
                              withRowAnimation:UITableViewRowAnimationAutomatic];
                [self.tableView endUpdates];

                //required to trigger refresh of myTableCell.indexPathForCell else old values in UITableViewCells
                [self.tableView reloadData];
            }else{
                NSLog(@"ERROR: [%s] OBJECT is nil", __PRETTY_FUNCTION__);
            }
        }
        else {
            NSLog(@"ERROR: [%s] UNHANDLED BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
        }
    }else {
        NSLog(@"ERROR: [%s] unhandled ALERTVIEW TAG:%d", __PRETTY_FUNCTION__,alertView.tag);
    }
}

2
A better way would be to subclass your button and add a indexPath property to it.

//Implement a subclass for UIButton.

@interface NewButton:UIButton
@property(nonatomic, strong) NSIndexPath *indexPath;


Make your button of type NewButton in the XIB or in the code whereever you are initializing them.

Then in the cellForRowAtIndexPath put the following line of code.

button.indexPath = indexPath;

return cell; //As usual



Now in your IBAction

-(IBAction)buttonClicked:(id)sender{
   NewButton *button = (NewButton *)sender;

//Now access the indexPath by buttons property..

   NSIndexPath *indexPath = button.indexPath; //:)
}

deleteRowsAtIndexPathsを呼び出すと、セルのindexPathが変更される可能性があるため、これは少しバグがあります。
ジョン・ギブ

deleteRowsAtIndexPathsにより、cellForRowAtIndexPathが再度呼び出されます。次に、ボタンには新しい正しいindexPathが含まれます。
mmmanishs 2014年

1

それも私にとってはうまくいきます、ありがとう@Cocoanut

スーパービューのスーパービューを使用してセルのindexPathへの参照を取得する方法が完全に機能することを発見しました。ヒントリンクテキストを提供してくれたiphonedevbook.com(macnsmith)に感謝

-(void)buttonPressed:(id)sender {
 UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
 NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
...

}

0

タグパターンを使用できます。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
     NSString *identifier = @"identifier";
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
         [cell autorelelase];

         UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
         [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
         [button setTag:[indexPath row]]; //use the row as the current tag
         [cell.contentView addSubview:button];

         [button release];
     }

     UIButton *button = (UIButton *)[cell viewWithTag:[indexPath row]]; //use [indexPath row]
     [button setTitle:@"Edit" forState:UIControlStateNormal];

     return cell;
}

- (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;
    //button.tag has the row number (you can convert it to indexPath)
}

単一のセルに複数のコントロールがある場合、コントロールにタグを付けるにはどうすればよいですか?
手綱

私は必ずこれがうまくいくないよ-セルが行#1のために作成されたならば、それはタグ1を得るでしょう、それが行#3のためにデキュー取得した場合、それはまだ1のタグを持つことになり、ない3
手綱

2番目のコメントについては正しいと思います。私の悪い。あなたの最善の解決策は、UIButtonをサブクラス化し、別のプロパティを1つまたは2つ追加し、適切な場合にそれらを設定/取得することです(コードにあるタグ1を付けます)
Nir Levy

0

何か不足していますか?送信者を使用してボタンを識別することはできませんか?送信者は次のような情報を提供します。

<UIButton: 0x4b95c10; frame = (246 26; 30 30); opaque = NO; tag = 104; layer = <CALayer: 0x4b95be0>>

次に、ボタンのプロパティを変更する場合は、送信者に伝えた背景画像を言います。

[sender setBackgroundImage:[UIImage imageNamed:@"new-image.png"] forState:UIControlStateNormal];

タグが必要な場合は、ACBurkの方法で問題ありません。


1
彼らは、ボタンが関連する「オブジェクト」を探しています
ohhorob

0
// how do I know which button sent this message?
// processing button press for this row requires an indexPath.

実際にはかなり簡単です:

- (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;
    CGPoint rowButtonCenterInTableView = [[rowButton superview] convertPoint:rowButton.center toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:rowButtonCenterInTableView];
    MyTableViewItem *rowItem = [self.itemsArray objectAtIndex:indexPath.row];
    // Now you're good to go.. do what the intention of the button is, but with
    // the context of the "row item" that the button belongs to
    [self performFooWithItem:rowItem];
}

私のためにうまくいく:P

ターゲットアクションの設定を調整する場合は、メソッドにイベントパラメータを含め、そのイベントのタッチを使用してタッチの座標を解決できます。座標はまだタッチビュー境界で解決する必要がありますが、一部の人にとってはそれがより簡単に見えるかもしれません。


0

nsmutable配列を作成し、すべてのボタンをその配列に配置しますusint [array addObject:yourButton];

ボタン押し方式で

-

 (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;

for(int i=0;i<[yourArray count];i++){

if([buton isEqual:[yourArray objectAtIndex:i]]){

//here write wat u need to do

}
}

0

ボタンがテーブルのフッターにある場合の、Cocoanutsの回答のわずかなバリエーション(これは私がこれを解決するのに役立ちました)(「クリックされたセル」を見つけることができなくなります):

-(IBAction) buttonAction:(id)sender;
{
    id parent1 = [sender superview];   // UiTableViewCellContentView
    id parent2 = [parent1 superview];  // custom cell containing the content view
    id parent3 = [parent2 superview];  // UITableView containing the cell
    id parent4 = [parent3 superview];  // UIView containing the table

    UIView *myContentView = (UIView *)parent1;
    UITableViewCell *myTableCell = (UITableViewCell *)parent2;
    UITableView *myTable = (UITableView *)parent3;
    UIView *mainView = (UIView *)parent4;

    CGRect footerViewRect = myTableCell.frame;
    CGRect rect3 = [myTable convertRect:footerViewRect toView:mainView];    

    [cc doSomethingOnScreenAtY:rect3.origin.y];
}

0

いつもタグを使っています。

をサブクラス化し、UITableviewCellそこからボタンプレスを処理する必要があります。


よくわかりません。タグプロパティはセルの作成時に設定されます。このセルは、同じ識別子を持つ各行で再利用できます。このタグは、再利用可能な汎用セルのコントロールに固有です。このタグを使用して、一般的な方法で作成されたセルのボタンを区別するにはどうすればよいですか?コードを投稿していただけませんか?
手綱

0

それは簡単です; カスタムセルを作成し、ボタンのアウトレットを取ります

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
         NSString *identifier = @"identifier";
        customCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];

    cell.yourButton.tag = indexPath.Row;

- (void)buttonPressedAction:(id)sender

上記のメソッドのIDを (UIButton *)

sender.tagを実行すると、どのボタンがタップされているかという値を取得できます。


0

ボタンをサブクラス化して必要な値を保存します。プロトコル(ControlWithDataなど)を作成することもできます。ボタンをテーブルビューセルに追加するときに値を設定します。タッチアップイベントで、送信者がプロトコルに従っているかどうかを確認し、データを抽出します。通常、テーブルビューセルにレンダリングされる実際のオブジェクトへの参照を保存します。


0

SWIFT 2アップデート

ここでは、どのボタンがタップされたかを確認する方法と、そのボタンから別のViewControllerにデータを送信する方法を説明しますindexPath.row

@IBAction func yourButton(sender: AnyObject) {


     var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
        let indexPath = self.tableView.indexPathForRowAtPoint(position)
        let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as
        UITableViewCell
        print(indexPath?.row)
        print("Tap tap tap tap")

    }

ViewControllerクラスを使用していて、tableViewを追加している人のために、私はTableViewControllerの代わりにViewControllerを使用しているので、それにアクセスするために手動でtableViewを追加しました。

次に、そのボタンをタップしてセルのデータを渡すときに、データを別のVCに渡すコードを示します。 indexPath.row

@IBAction func moreInfo(sender: AnyObject) {

    let yourOtherVC = self.storyboard!.instantiateViewControllerWithIdentifier("yourOtherVC") as! YourOtherVCVIewController



    var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
    let indexPath = self.tableView.indexPathForRowAtPoint(position)
    let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as
    UITableViewCell
    print(indexPath?.row)
    print("Button tapped")


    yourOtherVC.yourVarName = [self.otherVCVariable[indexPath!.row]]

    self.presentViewController(yourNewVC, animated: true, completion: nil)

}

0

ここで、カスタムセルを使用していることに注意してください。このコードは完全に機能しています。

 @IBAction func call(sender: UIButton)
    {
        var contentView = sender.superview;
        var cell = contentView?.superview as EmployeeListCustomCell
        if (!(cell.isKindOfClass(EmployeeListCustomCell)))
        {
            cell = (contentView?.superview)?.superview as EmployeeListCustomCell
        }

        let phone = cell.lblDescriptionText.text!
        //let phone = detailObject!.mobile!
        let url:NSURL = NSURL(string:"tel://"+phone)!;
        UIApplication.sharedApplication().openURL(url);
    }

0

Chris Schwerdtの解決策がSwiftで私のために働いた:

@IBAction func rateButtonTapped(sender: UIButton) {
    let buttonPosition : CGPoint = sender.convertPoint(CGPointZero, toView: self.ratingTableView)
    let indexPath : NSIndexPath = self.ratingTableView.indexPathForRowAtPoint(buttonPosition)!

    print(sender.tag)
    print(indexPath.row)
}

0

この問題には2つの部分があります。

1)UITableViewCellを含むインデックスパスの取得UIButton

次のようないくつかの提案があります。

  • 更新はUIButton「S tagcellForRowAtIndexPath:インデックスパスの使用方法row価値を。tag継続的に更新する必要があり、複数のセクションを持つテーブルビューでは機能しないため、これは良い解決策ではありません。

  • 追加NSIndexPathカスタムセルへのプロパティをし、代わりにそれを更新するUIButtontagcellForRowAtIndexPath:方法。これは複数セクションの問題を解決しますが、常に更新する必要があるため、まだ良くありません。

  • UITableViewカスタムセルを作成し、indexPathForCell:メソッドを使用してインデックスパスを取得する間、親への弱い参照を維持します。cellForRowAtIndexPath:メソッドで何かを更新する必要はありませんが、カスタムセルの作成時に弱い参照を設定する必要があります。

  • セルのsuperViewプロパティを使用して、parentへの参照を取得しますUITableView。カスタムセルにプロパティを追加する必要はなく、作成/後で設定/更新する必要もありません。しかし、セルsuperViewはiOSの実装の詳細に依存します。そのため、直接使用することはできません。

ただし、問題のセルがUITableViewにある必要があると確信しているため、これは単純なループを使用して実現できます。

UIView* view = self;
while (view && ![view isKindOfClass:UITableView.class])
    view = view.superview;
UITableView* parentTableView = (UITableView*)view;

したがって、これらの提案は、インデックスパスを取得するためのシンプルで安全なカスタムセルメソッドに組み合わせることができます。

- (NSIndexPath *)indexPath
{
    UIView* view = self;

    while (view && ![view isKindOfClass:UITableView.class])
        view = view.superview;

    return [(UITableView*)view indexPathForCell:self];
}

今後、この方法を使用して、どちらUIButtonが押されたかを検出できます。

2)ボタンプレスイベントについて他の当事者に通知する

UIButton正確なインデックスパスを持つどのカスタムセルでどのキーが押されたかを内部的に把握した後、この情報を他の関係者(おそらくはを処理するビューコントローラーUITableView)に送信する必要があります。したがって、このボタンクリックイベントはdidSelectRowAtIndexPath:、UITableViewデリゲートのメソッドと同様の抽象化およびロジックレベルで処理できます。

これには2つの方法を使用できます。

a)委任:カスタムセルはdelegateプロパティを持つことができ、プロトコルを定義できます。ボタンが押されると、そのdelegateプロパティでデリゲートメソッドを実行するだけです。ただし、このdelegateプロパティは、作成時に各カスタムセルに設定する必要があります。別の方法として、カスタムセルは、その親テーブルビューのデリゲートメソッドを実行することもできますdelegate

b)通知センター:カスタムセルは、カスタム通知名を定義し、この通知を、userInfoオブジェクトで提供されるインデックスパスと親テーブルビュー情報とともに投稿できます。各セルに何も設定する必要はなく、カスタムセルの通知にオブザーバーを追加するだけで十分です。


0

私はサブクラスのソリューションを使用しており、UIButtonここでそれを共有するだけでよいと思いました。Swiftのコードです。

class ButtonWithIndexPath : UIButton {
    var indexPath:IndexPath?
}

次に、indexPathを必ず更新してください。 cellForRow(at:)

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let returnCell = tableView.dequeueReusableCell(withIdentifier: "cellWithButton", for: indexPath) as! cellWithButton
    ...
    returnCell.button.indexPath = IndexPath
    returnCell.button.addTarget(self, action:#selector(cellButtonPressed(_:)), for: .touchUpInside)

    return returnCell
}

したがって、ボタンのイベントに応答するときは、次のように使用できます

func cellButtonPressed(_ sender:UIButton) {
    if sender is ButtonWithIndexPath {
        let button = sender as! ButtonWithIndexPath
        print(button.indexPath)
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.