Wie erhalte ich die indexpath.row, wenn ein Element aktiviert ist?


104

Ich habe eine Tabellenansicht mit Schaltflächen und möchte die indexpath.row verwenden, wenn eine davon angetippt wird. Dies ist, was ich derzeit habe, aber es ist immer 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)
}

sollte ich IndexPathForSelectedRow () anstelle der Punktvariablen verwenden? oder wo soll ich es verwenden?
Vincent

Antworten:


164

Giorashc hatte es fast mit seiner Antwort, aber er übersah die Tatsache, dass Zellen eine zusätzliche contentViewSchicht haben. Wir müssen also eine Schicht tiefer gehen:

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

let indexPath = itemTable.indexPath(for: cell)

Dies liegt daran, dass innerhalb der Ansichtshierarchie eine Tabellenansicht Zellen als Unteransichten hat, die anschließend ihre eigenen "Inhaltsansichten" haben. Aus diesem Grund müssen Sie die Übersicht dieser Inhaltsansicht abrufen, um die Zelle selbst abzurufen. Wenn Ihre Schaltfläche in einer Unteransicht und nicht direkt in der Inhaltsansicht der Zelle enthalten ist, müssen Sie daher viele Ebenen tiefer gehen, um darauf zuzugreifen.

Das Obige ist ein solcher Ansatz, aber nicht unbedingt der beste. Es ist zwar funktionsfähig, setzt jedoch Details zu einem voraus UITableViewCell, die Apple nie unbedingt dokumentiert hat, z. B. die Ansichtshierarchie. Dies könnte in Zukunft geändert werden, und der obige Code kann sich daher möglicherweise unvorhersehbar verhalten.

Aus Gründen der Langlebigkeit und Zuverlässigkeit empfehle ich daher einen anderen Ansatz. In diesem Thread sind viele Alternativen aufgeführt, und ich empfehle Ihnen, diese zu lesen, aber mein persönlicher Favorit lautet wie folgt:

Halten Sie eine Eigenschaft eines Abschlusses für Ihre Zellenklasse gedrückt, und lassen Sie die Aktionsmethode der Schaltfläche dies aufrufen.

class MyCell: UITableViewCell {
    var button: UIButton!

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

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

Wenn Sie dann Ihre Zelle in erstellen cellForRowAtIndexPath, können Sie Ihrem Abschluss einen Wert zuweisen.

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

Wenn Sie Ihren Handlercode hierher verschieben, können Sie das bereits vorhandene indexPathArgument nutzen. Dies ist ein viel sichererer Ansatz als der oben aufgeführte, da er nicht auf undokumentierten Merkmalen beruht.


2
Gut erkannt. Ich bin ein kompetenter Entwickler, das verspreche ich;) - habe meine Antwort geändert.
Jacob King

12
Dies ist nicht der richtige Weg, um die Zelle über eine Schaltfläche abzurufen. Das Layout einer Zelle hat sich im Laufe der Jahre geändert, und Code wie dieser funktioniert in diesem Fall nicht. Verwenden Sie diesen Ansatz nicht.
rmaddy

11
Dies ist eine schlechte Lösung. Es werden Details zu UITableViewCells vorausgesetzt, denen Apple nie unbedingt zugestimmt hat. Während UITableViewCells über eine contentView-Eigenschaft verfügen, kann nicht garantiert werden, dass die Übersicht von contentView immer die Zelle ist.
Papa

1
@PintuRajput Können Sie mir bitte Ihre Ansichtshierarchie beschreiben? Sie sehen dies wahrscheinlich, weil Ihre Schaltfläche keine direkte Unteransicht der Inhaltsansicht der Zelle ist.
Jacob King

2
@ymutlu Ich stimme vollkommen zu, ich habe dies in der Antwort angegeben. Ich habe auch eine viel robustere Lösung vorgeschlagen. Der Grund, warum ich das Original beibehalten habe, ist, dass ich der Meinung bin, dass es besser ist, anderen Entwicklern die Probleme mit einem Ansatz zu zeigen, als ihm insgesamt auszuweichen. Dies lehrt sie nichts, was Sie sehen. :)
Jacob King

61

Mein Ansatz für diese Art von Problem besteht darin, ein Delegatenprotokoll zwischen der Zelle und der Tabellenansicht zu verwenden. Auf diese Weise können Sie den Schaltflächenhandler in der Unterklasse der Zelle belassen, wodurch Sie den Handler für Ausbesserungsaktionen der Prototypzelle in Interface Builder zuweisen können, während die Logik der Schaltflächenhandler im Ansichtscontroller erhalten bleibt.

Außerdem wird der potenziell fragile Ansatz des Navigierens in der Ansichtshierarchie oder der Verwendung der tagEigenschaft vermieden , der Probleme beim Ändern von Zellindizes (aufgrund des Einfügens, Löschens oder Neuordnens) aufweist.

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

buttonTappedist die Delegatenfunktion und befindet sich im View Controller. In meinem Beispiel someButtonTappedist die Aktionsmethode in der Zelle
Paulw11

@ paulw11 Ich habe Zelle hat kein Mitglied buttonTapped in dieser Methode@IBAction func someButtonTapped(sender: UIButton) { self.delegate?.buttonTapped(self) }
EI Captain v2.0

1
Dies ist eine ziemlich gute Lösung (bei weitem nicht so schlecht wie die beiden, die derzeit mehr Stimmen haben und das Tag verwenden, das die Übersicht betrachtet), aber es scheint zu viel zusätzlicher Code zu sein, um hinzugefügt zu werden.
Papa

2
Dies ist die richtige Lösung und sollte die akzeptierte Antwort sein. Es missbraucht die Tag-Eigenschaft nicht, geht nicht von der Konstruktion von Zellen aus (die von Apple leicht geändert werden können) und funktioniert weiterhin (ohne zusätzliche Codierung), wenn Zellen verschoben oder neue Zellen zwischen vorhandenen Zellen hinzugefügt werden.
Robotic Cat

1
@ Paulw11 Ich dachte anfangs, dies sei viel Code, aber es hat sich als viel widerstandsfähiger erwiesen als das, was ich zuvor verwendet habe. Vielen Dank, dass Sie diese robuste Lösung veröffentlicht haben.
Adrian

53

AKTUALISIEREN : Abrufen des Indexpfads der Zelle, die die Schaltfläche enthält (sowohl Abschnitt als auch Zeile):

Verwenden der Tastenposition

Innerhalb Ihrer buttonTappedMethode können Sie die Position der Schaltfläche erfassen, sie in eine Koordinate in der Tabellenansicht konvertieren und dann den Indexpfad der Zeile an dieser Koordinate abrufen.

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

ANMERKUNG : Manchmal kann es vorkommen, dass Sie bei Verwendung der Funktion auf einen Randfall stoßen view.convert(CGPointZero, to:self.tableView), nilum an einem Punkt nach einer Zeile zu suchen, obwohl dort eine tableView-Zelle vorhanden ist. Um dies zu beheben, versuchen Sie, eine echte Koordinate zu übergeben, die leicht vom Ursprung versetzt ist, z.

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

Vorherige Antwort: Verwenden der Tag-Eigenschaft (gibt nur die Zeile zurück)

Anstatt in die Überwachungsbäume zu klettern, um einen Zeiger auf die Zelle zu erhalten, in der sich der UIButton befindet, gibt es eine sicherere, wiederholbarere Technik, bei der die von Antonio oben erwähnte Eigenschaft button.tag verwendet wird, die in beschrieben ist dieser Antwort gezeigte :

In cellForRowAtIndexPath:legen Sie die Tag-Eigenschaft fest:

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

Anschließend buttonClicked:verweisen Sie in der Funktion auf dieses Tag, um die Zeile des Indexpfads abzurufen, in der sich die Schaltfläche befindet:

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

Ich bevorzuge diese Methode, da ich festgestellt habe, dass das Schwingen in den Übersichtsbäumen eine riskante Möglichkeit sein kann, eine App zu entwerfen. Auch für Ziel-C habe ich diese Technik in der Vergangenheit verwendet und war mit dem Ergebnis zufrieden.


5
Dies ist eine gute Möglichkeit, und ich werde sie verbessern, um Ihre Wiederholung ein wenig in Gang zu bringen. Der einzige Fehler ist jedoch, dass sie keinen Zugriff auf den Abschnitt indexPath.section gewährt, wenn dies ebenfalls benötigt wird. Tolle Antwort!
Jacob King

Danke Jacob! Ich schätze das Wiederholungskarma. Wenn Sie das indexPath.sectionzusätzlich zu indexPath.row(ohne Zurücksetzen der Tag-Eigenschaft als indexPath.section) erhalten möchten , können cellForRowAtIndexPath:Sie in einfach das Tag in ändern button.tag = indexPathund dann in der buttonClicked:Funktion mit sender.tag.rowund auf beide zugreifen sender.tag.section.
Eisen John Bonney

1
Ist dies eine neue Funktion, da ich mich sicher erinnere, dass die Tag-Eigenschaft vom Typ Int und nicht vom Typ AnyObject ist, es sei denn, dies hat sich in Swift 2.3 geändert?
Jacob King

@ JacobKing du hast recht! Mein schlechtes Ich war beim Schreiben dieses Kommentars total distanziert und dachte, dass das Tag vom Typ AnyObject ist. Derp - stört mich nicht. Es wäre nützlich, wenn Sie den indexPath als Tag übergeben könnten ...
Iron John Bonney

3
Auch kein wirklich guter Ansatz. Zum einen würde es nur in Tabellenansichten funktionieren, in denen es einen einzelnen Abschnitt gibt.
Papa

16

Verwenden Sie eine Erweiterung von UITableView, um die Zelle für eine beliebige Ansicht abzurufen:


Die Antwort von @ Paulw11, einen benutzerdefinierten Zelltyp mit einer Delegate-Eigenschaft einzurichten, die Nachrichten an die Tabellenansicht sendet, ist ein guter Weg, erfordert jedoch einen gewissen Arbeitsaufwand für die Einrichtung.

Ich halte es für eine schlechte Idee, in der Ansichtshierarchie der Tabellenansichtszelle nach der Zelle zu suchen. Es ist zerbrechlich - wenn Sie Ihre Schaltfläche später zu Layoutzwecken in eine Ansicht einfügen, wird dieser Code wahrscheinlich beschädigt.

Die Verwendung von Ansichts-Tags ist ebenfalls fragil. Sie müssen daran denken, die Tags beim Erstellen der Zelle einzurichten. Wenn Sie diesen Ansatz in einem Ansichts-Controller verwenden, der Ansichts-Tags für einen anderen Zweck verwendet, können doppelte Tag-Nummern vorhanden sein und Ihr Code funktioniert möglicherweise nicht wie erwartet.

Ich habe eine Erweiterung für UITableView erstellt, mit der Sie den Indexpfad für jede Ansicht abrufen können, die in einer Tabellenansichtszelle enthalten ist. Es Optionalwird ein Wert zurückgegeben, der gleich Null ist, wenn die übergebene Ansicht tatsächlich nicht in eine Tabellenansichtszelle fällt. Unten finden Sie die gesamte Erweiterungsquelldatei. Sie können diese Datei einfach in Ihr Projekt indexPathForView(_:)einfügen und dann mit der enthaltenen Methode den Indexpfad finden, der eine beliebige Ansicht enthält.

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

Um es zu verwenden, können Sie einfach die Methode in der IBAction für eine Schaltfläche aufrufen, die in einer Zelle enthalten ist:

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

(Notiere dass der indexPathForView(_:) Funktion nur funktioniert, wenn das übergebene Ansichtsobjekt in einer Zelle enthalten ist, die derzeit auf dem Bildschirm angezeigt wird. Dies ist sinnvoll, da eine Ansicht, die nicht auf dem Bildschirm angezeigt wird, nicht zu einem bestimmten Indexpfad gehört einem anderen Indexpfad zugewiesen werden, wenn die enthaltene Zelle recycelt wird.)

BEARBEITEN:

Sie können ein funktionierendes Demo-Projekt, das die obige Erweiterung verwendet, von Github herunterladen : TableViewExtension.git


Danke, ich habe die Erweiterung verwendet, um den Indexpfad einer Textansicht in einer Zelle zu erhalten - hat perfekt funktioniert.
Jeremy Andrews

9

Zum Swift2.1

Ich habe einen Weg gefunden, es zu tun, hoffentlich hilft es.

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

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

Was bedeutet es, wenn der Fehler ausgeführt wird? Was sind einige Gründe, warum der Punkt in der Tabellenansicht nicht gefunden werden kann?
OOProg

Dies (oder ähnliches unter Verwendung der UIView-Konvertierungsmethoden) sollte die akzeptierte Antwort sein. Ich bin mir nicht sicher, warum es derzeit die Nummer 4 ist, da es keine Annahmen über die private Hierarchie einer Tabellenansicht macht, die Tag-Eigenschaft nicht verwendet (fast immer eine schlechte Idee) und nicht viel zusätzlichen Code enthält.
Papa

9

Swift 4 Lösung:

Sie haben eine Schaltfläche (myButton) oder eine andere Ansicht in der Zelle. Weisen Sie das Tag in cellForRowAt wie folgt zu

cell.myButton.tag = indexPath.row

Jetzt in dir tapFunction oder eine andere. Holen Sie es so heraus und speichern Sie es in einer lokalen Variablen.

currentCellNumber = (sender.view?.tag)!

Danach können Sie diese currentCellNumber an einer beliebigen Stelle verwenden, um die indexPath.row der ausgewählten Schaltfläche abzurufen.

Genießen!


Dieser Ansatz funktioniert, aber Ansichts-Tags sind fragil, wie in meiner Antwort erwähnt. Ein einfaches Integer-Tag funktioniert beispielsweise nicht für eine Tabellenansicht mit Abschnitten. (ein IndexPath es 2 Ganzzahlen.) Mein Ansatz wird immer funktionieren, und es ist nicht erforderlich, ein Tag in die Schaltfläche (oder eine andere tippbare Ansicht) zu installieren
Duncan C

6

Verwenden Sie in Swift 4 einfach Folgendes:

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

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

Die sauberste Antwort sollte die ausgewählte sein. Es ist nur zu beachten, dass tableViewes sich um eine Ausgangsvariable handelt, auf die verwiesen werden muss, bevor diese Antwort funktioniert.
10000RubyPools

Arbeite wie Charme !!
Parthpatel1105

4

Sehr einfach, Indexpfad schnell zu bekommen 4, 5

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


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

So erhalten Sie IndexPath Inside Btn Klicken Sie auf:

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


}

3

Da der Absender des Ereignishandlers die Schaltfläche selbst ist, würde ich die tagEigenschaft der Schaltfläche verwenden , um den in initialisierten Index zu speichern cellForRowAtIndexPath.

Aber mit etwas mehr Arbeit würde ich das ganz anders machen. Wenn Sie eine benutzerdefinierte Zelle verwenden, würde ich das Problem folgendermaßen angehen:

  • Fügen Sie der benutzerdefinierten Tabellenzelle eine 'indexPath'-Eigenschaft hinzu
  • initialisiere es in cellForRowAtIndexPath
  • Verschieben Sie den Tap-Handler vom View-Controller zur Zellenimplementierung
  • Verwenden Sie das Delegierungsmuster, um den Ansichtscontroller über das Tap-Ereignis zu benachrichtigen und den Indexpfad zu übergeben

Antonio, ich habe eine eigene Zelle und würde dies gerne auf deine Weise tun. Es funktioniert jedoch nicht. Ich möchte, dass mein Code "Durch Streichen wird die Schaltfläche zum Löschen angezeigt" ausgeführt wird. Dies ist die tableView commitEditingStyle-Methode. Ich habe diesen Code aus der mainVC-Klasse entfernt und in die customCell-Klasse eingefügt, aber jetzt funktioniert der Code nicht mehr. Was vermisse ich?
Dave G

Ich denke, dies ist der beste Weg, um den Indexpfad einer Zelle mit x Abschnitten zu erhalten, aber ich sehe keine Notwendigkeit für die Aufzählungspunkte 3 und 4 in einem MVC-Ansatz
Edward

2

Nachdem ich Paulw11s Vorschlag gesehen hatte, einen Rückruf für Delegierte zu verwenden, wollte ich ihn etwas näher erläutern / einen anderen, ähnlichen Vorschlag vorbringen. Wenn Sie das Delegatenmuster nicht verwenden möchten, können Sie Verschlüsse schnell wie folgt verwenden:

Ihre Zellklasse:

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

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

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

Ihre cellForRowAtIndexPathMethode:

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

Ich habe eine sehr einfache Möglichkeit gefunden, eine Zelle in tableView und collectionView mithilfe einer Model-Klasse zu verwalten, und dies funktioniert genauso perfekt.

Es gibt in der Tat einen viel besseren Weg, dies jetzt zu handhaben. Dies funktioniert für die Verwaltung von Zelle und Wert.

Hier ist meine Ausgabe (Screenshot).

Geben Sie hier die Bildbeschreibung ein

  1. Es ist sehr einfach, eine Modellklasse zu erstellen. Befolgen Sie dazu das folgende Verfahren. Erstellen Sie eine schnelle Klasse mit Namen RNCheckedModelund schreiben Sie den Code wie folgt.
class RNCheckedModel: NSObject {

    var is_check = false
    var user_name = ""

    }
  1. Erstellen Sie Ihre Zellklasse
class InviteCell: UITableViewCell {

    @IBOutlet var imgProfileImage: UIImageView!
    @IBOutlet var btnCheck: UIButton!
    @IBOutlet var lblName: UILabel!
    @IBOutlet var lblEmail: UILabel!
    }
  1. und schließlich verwenden Sie die Modellklasse in Ihrem UIViewController, wenn Sie Ihr UITableView verwenden .
    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

Ich habe die convertPoint-Methode verwendet, um einen Punkt aus der Tabellenansicht abzurufen und diesen Punkt an die indexPathForRowAtPoint-Methode zu übergeben, um indexPath abzurufen

 @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

Versuchen Sie, mit #selector die IBaction.In dem cellforrowatindexpath aufzurufen

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

Auf diese Weise können Sie auf den Indexpfad innerhalb der Methode editButtonPressed zugreifen

func editButtonPressed(_ sender: UIButton) {

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

}

Am besten geeignete Antwort
Amalendu Kar

Nein, Tags sind deaktiviert, wenn der Benutzer Zellen hinzufügt oder entfernt.
Koen

1

In meinem Fall habe ich mehrere Abschnitte und sowohl der Abschnitts- als auch der Zeilenindex sind von entscheidender Bedeutung. In einem solchen Fall habe ich gerade eine Eigenschaft auf UIButton erstellt, für die ich den Zellenindexpfad wie folgt festgelegt habe:

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

Setzen Sie dann die Eigenschaft in cellForRowAt wie folgt:

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

Dann können Sie in handleTapAction den indexPath wie folgt abrufen:

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

}

1

Swift 4 und 5

Methode 1 unter Verwendung des Protokolldelegaten

Zum Beispiel haben Sie einen UITableViewCellmit NamenMyCell

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

Erstellen Sie nun eine protocol

protocol MyCellDelegate {
    func didPressButton(cell: UITableViewCell)
}

Erstellen Sie im nächsten Schritt eine Erweiterung von UITableView

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

In Ihrem UIViewControllerimplementieren Sie das ProtokollMyCellDelegate

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

Methode 2 mit Verschlüssen

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

Hinweis: - Wenn Sie UICollectionViewindexPath erhalten möchten, können Sie dies verwenden UICollectionView extensionund die obigen Schritte wiederholen

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

0

In Swift 3. Auch Guard-Anweisungen verwendet, um eine lange Kette von Klammern zu vermeiden.

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

    print(indexPath)
}

Dies wird nicht funktionieren. Die Übersicht der Schaltfläche ist nicht die Zelle.
rmaddy

Das funktioniert. Das einzige, worauf Sie achten sollten, ist, dass jeder Ansichtsstapel anders ist. Dies kann sender.superview, sender.superview.superview oder sender.superview.superview.superview sein. Aber es funktioniert wirklich gut.
Sean

0

Manchmal befindet sich die Schaltfläche in einer anderen Ansicht von UITableViewCell. In diesem Fall gibt Aufsichtsübersicht möglicherweise nicht das Zellenobjekt an, und daher ist der Indexpfad Null.

In diesem Fall sollten wir die Übersicht so lange finden, bis wir das Zellenobjekt erhalten.

Funktion zum Abrufen des Zellenobjekts per Übersicht

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
}

Jetzt können wir indexPath beim Tippen auf die Schaltfläche wie unten erhalten

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

}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.