要素がアクティブ化されたときにindexpath.rowを取得するにはどうすればよいですか?


104

ボタン付きのテーブルビューがあり、そのうちの1つがタップされたときにindexpath.rowを使用したいと思います。これは私が現在持っているものですが、常に0です。

var point = Int()
func buttonPressed(sender: AnyObject) {
    let pointInTable: CGPoint =         sender.convertPoint(sender.bounds.origin, toView: self.tableView)
    let cellIndexPath = self.tableView.indexPathForRowAtPoint(pointInTable)
    println(cellIndexPath)
    point = cellIndexPath!.row
    println(point)
}

ポイント変数の代わりにIndexPathForSelectedRow()を使用する必要がありますか?またはどこで使用する必要がありますか?
Vincent

回答:


164

giorashcは彼の答えでそれをほとんど持っていました、しかし彼は細胞が余分なcontentView層を持っているという事実を見落としました。したがって、1層深くする必要があります。

guard let cell = sender.superview?.superview as? YourCellClassHere else {
    return // or fatalError() or whatever
}

let indexPath = itemTable.indexPath(for: cell)

これは、ビュー階層内でtableViewにサブビューとしてセルがあり、その後に独自の「コンテンツビュー」があるため、セル自体を取得するには、このコンテンツビューのスーパービューを取得する必要があるためです。この結果、ボタンがセルのコンテンツビューに直接ではなくサブビューに含まれている場合、ボタンにアクセスするためにいくつでも深いレイヤーに移動する必要があります。

上記はそのようなアプローチの1つですが、必ずしも最良のアプローチではありません。機能している間は、UITableViewCellビュー階層など、Appleが必ずしも文書化したことがないものています。これは将来変更される可能性があり、上記のコードは結果として予期しない動作をする可能性があります。

上記の結果として、寿命と信頼性の理由から、別のアプローチを採用することをお勧めします。このスレッドには多くの代替案が記載されています。ぜひお読みになることをお勧めしますが、私の個人的なお気に入りは次のとおりです。

セルクラスのクロージャーのプロパティを保持し、ボタンのアクションメソッドでこれを呼び出すようにします。

class MyCell: UITableViewCell {
    var button: UIButton!

    var buttonAction: ((Any) -> Void)?

    @objc func buttonPressed(sender: Any) {
        self.buttonAction?(sender)
    }
}

次に、でセルを作成するときcellForRowAtIndexPathに、クロージャーに値を割り当てることができます。

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! MyCell
    cell.buttonAction = { sender in
        // Do whatever you want from your button here.
    }
    // OR
    cell.buttonAction = buttonPressed(closure: buttonAction, indexPath: indexPath) // <- Method on the view controller to handle button presses.
}

ハンドラーコードをここに移動することで、既存のindexPath引数を利用できます。文書化されていない特性に依存しないため、これは上記の方法よりもはるかに安全な方法です。


2
よく見つかりました。私は有能な開発者です、私は約束します;)-私の答えを修正しました。
ジェイコブキング

12
これは、ボタンからセルを取得する適切な方法ではありません。セルのレイアウトは何年にもわたって変更されており、このようなコードはそれが発生すると機能しなくなります。このアプローチは使用しないでください。
rmaddy

11
これは悪い解決策です。Appleが必ずしも同意したことがないUITableViewCellsに関する詳細を前提としています。UITableViewCellsには​​contentViewプロパティがありますが、contentViewのスーパービューが常にCellである保証はありません。
bpapa 2017

1
@PintuRajputあなたのビュー階層を私に説明してくれませんか?ボタンはセルのコンテンツビューの直接のサブビューではないため、これが表示される可能性があります。
Jacob King

2
@ymutlu私は完全に同意します、私は答えでこれを述べました。また、私ははるかに堅牢なソリューションを提案しました。オリジナルをそのまま残した理由は、他の開発者にアプローチで問題を完全にかわすよりも見せた方がいいと感じたからです。:)
ジェイコブキング

61

この種の問題への私のアプローチは、セルとテーブルビューの間でデリゲートプロトコルを使用することです。これにより、ボタンハンドラーをセルサブクラスに保持できるため、ビューコントローラーのボタンハンドラーロジックを維持しながら、タッチビルダーアクションハンドラーをInterface Builderのプロトタイプセルに割り当てることができます。

またtag、セルのインデックスが変更されたときに(挿入、削除、または並べ替えの結果として)問題となる、ビュー階層またはプロパティの使用をナビゲートする潜在的に脆弱なアプローチを回避します。

CellSubclass.swift

protocol CellSubclassDelegate: class {
    func buttonTapped(cell: CellSubclass)
}

class CellSubclass: UITableViewCell {

@IBOutlet var someButton: UIButton!

weak var delegate: CellSubclassDelegate?

override func prepareForReuse() {
    super.prepareForReuse()
    self.delegate = nil
}

@IBAction func someButtonTapped(sender: UIButton) {
    self.delegate?.buttonTapped(self)
}

ViewController.swift

class MyViewController: UIViewController, CellSubclassDelegate {

    @IBOutlet var tableview: UITableView!

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellSubclass

        cell.delegate = self

        // Other cell setup

    } 

    //  MARK: CellSubclassDelegate

    func buttonTapped(cell: CellSubclass) {
        guard let indexPath = self.tableView.indexPathForCell(cell) else {
            // Note, this shouldn't happen - how did the user tap on a button that wasn't on screen?
            return
        }

        //  Do whatever you need to do with the indexPath

        print("Button tapped on row \(indexPath.row)")
    }
} 

buttonTappedデリゲート関数であり、View Controllerにあります。私の例では、someButtonTapped細胞内のアクションメソッドである
Paulw11

@ paulw11セルにメンバーボタンがありません。このメソッドでタップされました@IBAction func someButtonTapped(sender: UIButton) { self.delegate?.buttonTapped(self) }
EIキャプテンv2.0

1
これはかなり良い解決策ですが(スーパービューを見ているタグを使用して、現在投票数が多い2つほど悪くはありません)、追加するコードが多すぎるように感じます。
bpapa 2017

2
これは正しい解決策であり、受け入れられる答えになるはずです。これはタグのプロパティを悪用せず、セルの構築(Appleによって簡単に変更できる)を想定しておらず、セルが移動されたり既存のセルの間に新しいセルが追加されたりしても(追加のコーディングなしで)機能します。
Robotic Cat

1
@ Paulw11最初はこれはたくさんのコードだと思っていましたが、以前使用していたものよりはるかに弾力性があることが証明されました。この堅牢なソリューションを投稿していただきありがとうございます。
エイドリアン

53

UPDATE:ボタン(セクションと行の両方)を含むセルのindexPathを取得します。

ボタンの位置を使用する

buttonTappedメソッド内で、ボタンの位置を取得し、それをtableViewの座標に変換して、その座標で行のindexPathを取得できます。

func buttonTapped(_ sender:AnyObject) {
    let buttonPosition:CGPoint = sender.convert(CGPoint.zero, to:self.tableView)
    let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
}

:tableViewセルがそこにあるにもかかわらず、関数を使用すると、ある時点で行view.convert(CGPointZero, to:self.tableView)が検索さnilれる場合に、エッジケースに遭遇することがあります。これを修正するには、次のように、原点からわずかにオフセットされた実際の座標を渡してみてください。

let buttonPosition:CGPoint = sender.convert(CGPoint.init(x: 5.0, y: 5.0), to:self.tableView)

以前の回答:タグプロパティの使用(行のみを返す)

スーパービューツリーに登ってUIButtonを保持するセルへのポインターを取得するのではなく、この Antonioによって言及され、この回答で説明されている、以下に示すbutton.tagプロパティを利用した、より安全で再現性の高い手法があります。

ではcellForRowAtIndexPath:、あなたのタグプロパティを設定します。

button.tag = indexPath.row
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)

次に、buttonClicked:関数でそのタグを参照して、ボタンが配置されているindexPathの行を取得します。

func buttonClicked(sender:UIButton) {
    let buttonRow = sender.tag
}

私はこの方法を好みます。なぜなら、スーパービューツリーでスイングすることは、アプリを設計するための危険な方法になる可能性があることを発見したからです。また、objective-Cについては、過去にこの手法を使用したことがあり、結果に満足しています。


5
これは良い方法です。担当者を少し始めるために賛成しますが、唯一の欠点は、必要な場合にindexPath.sectionにアクセスできないことです。素晴らしい答えです!
ジェイコブキング

ジェイコブ、ありがとう!担当カルマに感謝します。あなたが取得したい場合indexPath.sectionに加えて、indexPath.row(などのタグプロパティをリセットせずindexPath.section)、にcellForRowAtIndexPath:あなただけに、タグを変更することができbutton.tag = indexPath、その後にbuttonClicked:機能あなたが使用して、両方にアクセスすることができましたsender.tag.rowし、sender.tag.section
アイアンジョンボニー

1
これは新機能ですか、Swift 2.3で変更されていない限り、AnyObject型ではなくInt型のタグプロパティを覚えていると思います。
ジェイコブキング

@JacobKingありがとうございます!私の悪い点は、そのコメントを書くときに完全にスペースを空けていて、タグがAnyObject型であると思っていました。Derp-気にしないで。indexPathをタグとして渡すことができれば便利です...
Iron John Bonney

3
本当に良いアプローチでもありません。1つには、セクションが1つしかないテーブルビューでのみ機能します。
bpapa 2017

16

UITableViewの拡張機能を使用して、任意のビューのセルをフェッチします。


メッセージをテーブルビューに送信するデリゲートプロパティを使用してカスタムセルタイプを設定するという@ Paulw11の回答は良い方法ですが、設定するにはある程度の作業が必要です。

テーブルビューセルのビュー階層を歩いてセルを探すのは悪い考えだと思います。それは壊れやすい-後でレイアウトのためにビューでボタンを囲むと、そのコードは壊れる可能性があります。

ビュータグの使用も脆弱です。セルを作成するときにタグを設定することを忘れないでください。別の目的でビュータグを使用するビューコントローラーでそのアプローチを使用すると、重複したタグ番号が含まれる可能性があり、コードが期待どおりに機能しない可能性があります。

テーブルビューのセルに含まれている任意のビューのindexPathを取得できるUITableViewの拡張機能を作成しました。Optional渡されたビューが実際にテーブルビューセル内にない場合は、nilになるを返します。以下は全体の拡張ソースファイルです。このファイルをプロジェクトに配置し、included indexPathForView(_:)メソッドを使用して、ビューを含むindexPathを見つけるだけです。

//
//  UITableView+indexPathForView.swift
//  TableViewExtension
//
//  Created by Duncan Champney on 12/23/16.
//  Copyright © 2016-2017 Duncan Champney.
//  May be used freely in for any purpose as long as this 
//  copyright notice is included.

import UIKit

public extension UITableView {
  
  /**
  This method returns the indexPath of the cell that contains the specified view
   
   - Parameter view: The view to find.
   
   - Returns: The indexPath of the cell containing the view, or nil if it can't be found
   
  */
  
    func indexPathForView(_ view: UIView) -> IndexPath? {
        let center = view.center
        let viewCenter = self.convert(center, from: view.superview)
        let indexPath = self.indexPathForRow(at: viewCenter)
        return indexPath
    }
}

これを使用するには、セルに含まれているボタンのIBActionでメソッドを呼び出すだけです。

func buttonTapped(_ button: UIButton) {
  if let indexPath = self.tableView.indexPathForView(button) {
    print("Button tapped at indexPath \(indexPath)")
  }
  else {
    print("Button indexPath not found")
  }
}

(このindexPathForView(_:)関数は、渡されたビューオブジェクトが現在画面上にあるセルに含まれている場合にのみ機能することに注意してください。画面上にないビューは実際には特定のindexPathに属していないため、これは合理的です。含まれているセルがリサイクルされるときに、別のindexPathに割り当てられます。)

編集:

上記の拡張機能を使用する実用的なデモプロジェクトをGithubからダウンロードできます: TableViewExtension.git


おかげで、セル内のテキストビューのindexPathを取得するために拡張機能を使用しました。
ジェレミーアンドリュース

9

ために Swift2.1

私はそれを行う方法を見つけました、うまくいけば、それが役立つでしょう。

let point = tableView.convertPoint(CGPoint.zero, fromView: sender)

    guard let indexPath = tableView.indexPathForRowAtPoint(point) else {
        fatalError("can't find point in tableView")
    }

エラーが発生した場合、どういう意味ですか?tableViewでポイントを見つけられない理由は何ですか?
OOProg 2016

これ(または、UIView Convertingメソッドを使用した同様の方法)は、受け入れられる答えです。それが現在#4である理由がわからない、それはテーブルビューのプライベート階層についての仮定を行わないため、タグプロパティを使用せず(ほとんどの場合悪い考え)、追加のコードの多くを必要としません。
bpapa 2017

9

Swift 4ソリューション:

セルにボタン(myButton)またはその他のビューがあります。このようにcellForRowAtにタグを割り当てます

cell.myButton.tag = indexPath.row

今、あなたはtapFunctionまたは他のものをタップします。このようにして取り出し、ローカル変数に保存します。

currentCellNumber = (sender.view?.tag)!

この後、このcurrentCellNumberのどこでも使用して、選択したボタンのindexPath.rowを取得できます。

楽しい!


そのアプローチは機能しますが、私の回答で述べたように、ビュータグは壊れやすいです。たとえば、単純な整数タグは、セクション化されたテーブルビューでは機能しません。(IndexPathその2つの整数。)私のアプローチは常に動作します、そしてボタンにタグをインストールする必要はありません(または他のタップ可能なビューが。)
ダンカンC

6

Swift 4では、これを使用してください:

func buttonTapped(_ sender: UIButton) {
        let buttonPostion = sender.convert(sender.bounds.origin, to: tableView)

        if let indexPath = tableView.indexPathForRow(at: buttonPostion) {
            let rowIndex =  indexPath.row
        }
}

最もきれいな答えが選択されるべきです。注意すべき唯一のことtableViewは、この回答が機能する前に参照する必要があるアウトレット変数であることです。
10000RubyPools 2018

魅力的な作品!!
Parthpatel1105

4

非常に簡単なインデックスパスの迅速な取得4、5

 let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! Cell
  cell.btn.tag = indexPath.row


  cell.btn.addTarget(self, action: "buttonTapped:", forControlEvents: 
UIControlEvents.TouchUpInside)

Btn Click内でIndexPathを取得する方法:

    func buttonTapped(_ sender: UIButton) {`
          print(sender.tag) .  


}

3

イベントハンドラーの送信者はボタン自体なので、ボタンのtagプロパティを使用して、で初期化されたインデックスを格納しますcellForRowAtIndexPath

しかし、もう少し作業を行うことで、私は完全に異なる方法で行うでしょう。カスタムセルを使用している場合、これが私が問題に取り組む方法です。

  • カスタムテーブルセルに 'indexPath`プロパティを追加する
  • 初期化する cellForRowAtIndexPath
  • タップハンドラーをビューコントローラーからセル実装に移動する
  • 委任パターンを使用して、タップイベントについてビューコントローラーに通知し、インデックスパスを渡します

アントニオ、私にはカスタムセルがあり、これをあなたのやり方でやりたいです。ただし、機能していません。「スワイプして表示する削除ボタン」コードを実行したいのですが、これはtableView commitEditingStyleメソッドです。そのコードをmainVCクラスから削除してcustomCellクラスに配置しましたが、コードは機能しなくなりました。何が欠けていますか?
Dave G

これはxセクションを持つセルのindexPathを取得する最良の方法だと思いますが、MVCアプローチで箇条書きの3と4の必要性はわかりません
Edward

2

デリゲートコールバックを使用するというPaulw11の提案を見た後、私はそれについて少し詳しく説明し、別の同様の提案を転送したいと思いました。デリゲートパターンを使用したくない場合は、次のように迅速にクロージャーを利用できます。

あなたの細胞クラス:

class Cell: UITableViewCell {
    @IBOutlet var button: UIButton!

    var buttonAction: ((sender: AnyObject) -> Void)?

    @IBAction func buttonPressed(sender: AnyObject) {
        self.buttonAction?(sender)
    }
}

あなたのcellForRowAtIndexPath方法:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! Cell
    cell.buttonAction = { (sender) in
        // Do whatever you want from your button here.
    }
    // OR
    cell.buttonAction = buttonPressed // <- Method on the view controller to handle button presses.
}

2

Modelクラスを使用してtableViewとcollectionViewのセルを管理する非常に簡単な方法を見つけました。

確かに、これを処理するより良い方法があります。これは、セルと値の管理に役立ちます。

これが私の出力(スクリーンショット)ですので、これを見てください:

ここに画像の説明を入力してください

  1. モデルクラスの作成は非常に簡単です。以下の手順に従ってください。name RNCheckedModelでswiftクラスを作成し、以下のようにコードを記述します。
class RNCheckedModel: NSObject {

    var is_check = false
    var user_name = ""

    }
  1. セルクラスを作成する
class InviteCell: UITableViewCell {

    @IBOutlet var imgProfileImage: UIImageView!
    @IBOutlet var btnCheck: UIButton!
    @IBOutlet var lblName: UILabel!
    @IBOutlet var lblEmail: UILabel!
    }
  1. 最後に、UITableViewを使用する場合は、UIViewControllerでモデルクラスを使用します。
    class RNInviteVC: UIViewController, UITableViewDelegate, UITableViewDataSource {


    @IBOutlet var inviteTableView: UITableView!
    @IBOutlet var btnInvite: UIButton!

    var checkArray : NSMutableArray = NSMutableArray()
    var userName : NSMutableArray = NSMutableArray()

    override func viewDidLoad() {
        super.viewDidLoad()
        btnInvite.layer.borderWidth = 1.5
        btnInvite.layer.cornerRadius = btnInvite.frame.height / 2
        btnInvite.layer.borderColor =  hexColor(hex: "#512DA8").cgColor

        var userName1 =["Olivia","Amelia","Emily","Isla","Ava","Lily","Sophia","Ella","Jessica","Mia","Grace","Evie","Sophie","Poppy","Isabella","Charlotte","Freya","Ruby","Daisy","Alice"]


        self.userName.removeAllObjects()
        for items in userName1 {
           print(items)


            let model = RNCheckedModel()
            model.user_name = items
            model.is_check = false
            self.userName.add(model)
        }
      }
     @IBAction func btnInviteClick(_ sender: Any) {

    }
       func tableView(_ tableView: UITableView, numberOfRowsInSection 
       section: Int) -> Int {
        return userName.count
    }

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

        let image = UIImage(named: "ic_unchecked")
        cell.imgProfileImage.layer.borderWidth = 1.0
        cell.imgProfileImage.layer.masksToBounds = false
        cell.imgProfileImage.layer.borderColor = UIColor.white.cgColor
        cell.imgProfileImage.layer.cornerRadius =  cell.imgProfileImage.frame.size.width / 2
        cell.imgProfileImage.clipsToBounds = true

        let model = self.userName[indexPath.row] as! RNCheckedModel
        cell.lblName.text = model.user_name

        if (model.is_check) {
            cell.btnCheck.setImage(UIImage(named: "ic_checked"), for: UIControlState.normal)
        }
        else {
            cell.btnCheck.setImage(UIImage(named: "ic_unchecked"), for: UIControlState.normal)
        }

        cell.btnCheck.tag = indexPath.row
        cell.btnCheck.addTarget(self, action: #selector(self.btnCheck(_:)), for: .touchUpInside)

        cell.btnCheck.isUserInteractionEnabled = true

    return cell

    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80

    }

    @objc func btnCheck(_ sender: UIButton) {

        let tag = sender.tag
        let indexPath = IndexPath(row: tag, section: 0)
        let cell: InviteCell = inviteTableView.dequeueReusableCell(withIdentifier: "InviteCell", for: indexPath) as! InviteCell

        let model = self.userName[indexPath.row] as! RNCheckedModel

        if (model.is_check) {

            model.is_check = false
            cell.btnCheck.setImage(UIImage(named: "ic_unchecked"), for: UIControlState.normal)

            checkArray.remove(model.user_name)
            if checkArray.count > 0 {
                btnInvite.setTitle("Invite (\(checkArray.count))", for: .normal)
                print(checkArray.count)
                UIView.performWithoutAnimation {
                    self.view.layoutIfNeeded()
                }
            } else {
                btnInvite.setTitle("Invite", for: .normal)
                UIView.performWithoutAnimation {
                    self.view.layoutIfNeeded()
                }
            }

        }else {

            model.is_check = true
            cell.btnCheck.setImage(UIImage(named: "ic_checked"), for: UIControlState.normal)

            checkArray.add(model.user_name)
            if checkArray.count > 0 {
                btnInvite.setTitle("Invite (\(checkArray.count))", for: .normal)
                UIView.performWithoutAnimation {
                self.view.layoutIfNeeded()
                }
            } else {
                 btnInvite.setTitle("Invite", for: .normal)
            }
        }

        self.inviteTableView.reloadData()
    }

    func hexColor(hex:String) -> UIColor {
        var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()

        if (cString.hasPrefix("#")) {
            cString.remove(at: cString.startIndex)
        }

        if ((cString.count) != 6) {
            return UIColor.gray
        }

        var rgbValue:UInt32 = 0
        Scanner(string: cString).scanHexInt32(&rgbValue)

        return UIColor(
            red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
            green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
            blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
            alpha: CGFloat(1.0)
        )
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }

     }

1

convertPointメソッドを使用してtableviewからポイントを取得し、このポイントをindexPathForRowAtPointメソッドに渡してindexPathを取得しました

 @IBAction func newsButtonAction(sender: UIButton) {
        let buttonPosition = sender.convertPoint(CGPointZero, toView: self.newsTableView)
        let indexPath = self.newsTableView.indexPathForRowAtPoint(buttonPosition)
        if indexPath != nil {
            if indexPath?.row == 1{
                self.performSegueWithIdentifier("alertViewController", sender: self);
            }   
        }
    }

1

#selectorを使用してIBactionを呼び出してみてください。cellforrowatindexpath

            cell.editButton.tag = indexPath.row
        cell.editButton.addTarget(self, action: #selector(editButtonPressed), for: .touchUpInside)

このようにして、editButtonPressedメソッド内のindexpathにアクセスできます。

func editButtonPressed(_ sender: UIButton) {

print(sender.tag)//this value will be same as indexpath.row

}

最も適切な回答
Amalendu Kar

いいえ、ユーザーがセルを追加または削除すると、タグはオフになります。
公園

1

私の場合、複数のセクションがあり、セクションと行のインデックスの両方が重要であるため、そのような場合は、UIButtonにプロパティを作成して、セルのindexPathを次のように設定しました。

fileprivate struct AssociatedKeys {
    static var index = 0
}

extension UIButton {

    var indexPath: IndexPath? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.index) as? IndexPath
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.index, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

次に、次のようにcellForRowAtのプロパティを設定します。

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! Cell
    cell.button.indexPath = indexPath
}

次に、handleTapActionで次のようにindexPathを取得できます。

@objc func handleTapAction(_ sender: UIButton) {
    self.selectedIndex = sender.indexPath

}

1

Swift 4および5

プロトコルデリゲートを使用する方法1

たとえば、UITableViewCell名前がMyCell

class MyCell: UITableViewCell {
    
    var delegate:MyCellDelegate!
    
    @IBAction private func myAction(_ sender: UIButton){
        delegate.didPressButton(cell: self)
    }
}

今作成します protocol

protocol MyCellDelegate {
    func didPressButton(cell: UITableViewCell)
}

次のステップでは、拡張を作成します UITableView

extension UITableView {
    func returnIndexPath(cell: UITableViewCell) -> IndexPath?{
        guard let indexPath = self.indexPath(for: cell) else {
            return nil
        }
        return indexPath
    }
}

あなたUIViewControllerのプロトコルでMyCellDelegate

class ViewController: UIViewController, MyCellDelegate {
     
    func didPressButton(cell: UITableViewCell) {
        if let indexpath = self.myTableView.returnIndexPath(cell: cell) {
              print(indexpath)
        }
    }
}

クロージャーを使用する方法2

UIViewController

override func viewDidLoad() {
        super.viewDidLoad()
       //using the same `UITableView extension` get the IndexPath here
        didPressButton = { cell in
            if let indexpath = self.myTableView.returnIndexPath(cell: cell) {
                  print(indexpath)
            }
        }
    }
 var didPressButton: ((UITableViewCell) -> Void)

class MyCell: UITableViewCell {

    @IBAction private func myAction(_ sender: UIButton){
        didPressButton(self)
    }
}

注:UICollectionView-indexPath を取得する場合は、これUICollectionView extensionを使用して上記の手順を繰り返すことができます

extension UICollectionView {
    func returnIndexPath(cell: UICollectionViewCell) -> IndexPath?{
        guard let indexPath = self.indexPath(for: cell) else {
            return nil
        }
        return indexPath
    }
}

0

Swift 3でも使用されました。ガードステートメントを使用し、中括弧の長いチェーンを回避しました。

func buttonTapped(sender: UIButton) {
    guard let cellInAction = sender.superview as? UITableViewCell else { return }
    guard let indexPath = tableView?.indexPath(for: cellInAction) else { return }

    print(indexPath)
}

これは機能しません。ボタンのスーパービューはセルにはなりません。
rmaddy 2017

これは機能します。注意が必要なのは、全員のビュースタックが異なるということだけです。それは、sender.superview、sender.superview.superview、sender.superview.superview.superviewのいずれかです。しかし、それは本当にうまくいきます。
ショーン

0

ボタンがUITableViewCellの別のビューの中にある場合があります。その場合、superview.superviewはセルオブジェクトを提供しない可能性があるため、indexPathはnilになります。

その場合、セルオブジェクトを取得するまでスーパービューを見つけ続ける必要があります。

スーパービューでセルオブジェクトを取得する関数

func getCellForView(view:UIView) -> UITableViewCell?
{
    var superView = view.superview

    while superView != nil
    {
        if superView is UITableViewCell
        {
            return superView as? UITableViewCell
        }
        else
        {
            superView = superView?.superview
        }
    }

    return nil
}

これで、次のようにボタンタップでindexPathを取得できます

@IBAction func tapButton(_ sender: UIButton)
{
    let cell = getCellForView(view: sender)
    let indexPath = myTabelView.indexPath(for: cell)
}

0
// CustomCell.swift

protocol CustomCellDelegate {
    func tapDeleteButton(at cell: CustomCell)
}

class CustomCell: UICollectionViewCell {
    
    var delegate: CustomCellDelegate?
    
    fileprivate let deleteButton: UIButton = {
        let button = UIButton(frame: .zero)
        button.setImage(UIImage(named: "delete"), for: .normal)
        button.addTarget(self, action: #selector(deleteButtonTapped(_:)), for: .touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    @objc fileprivate func deleteButtonTapped(_sender: UIButton) {
        delegate?.tapDeleteButton(at: self)
    }
    
}

//  ViewController.swift

extension ViewController: UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier, for: indexPath) as? CustomCell else {
            fatalError("Unexpected cell instead of CustomCell")
        }
        cell.delegate = self
        return cell
    }

}

extension ViewController: CustomCellDelegate {

    func tapDeleteButton(at cell: CustomCell) {
        // Here we get the indexPath of the cell what we tapped on.
        let indexPath = collectionView.indexPath(for: cell)
    }

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