Comment obtenir le indexpath.row lorsqu'un élément est activé?


104

J'ai une vue de table avec des boutons et je veux utiliser l'indexpath.row lorsque l'un d'eux est touché. C'est ce que j'ai actuellement, mais c'est toujours 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)
}

dois-je utiliser IndexPathForSelectedRow () au lieu de la variable point? ou où dois-je l'utiliser?
Vincent

Réponses:


166

giorashc l'a presque eu avec sa réponse, mais il a oublié le fait que les cellules ont une contentViewcouche supplémentaire . Ainsi, nous devons aller plus loin:

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

let indexPath = itemTable.indexPath(for: cell)

En effet, dans la hiérarchie des vues, une tableView a des cellules en tant que sous-vues qui ont ensuite leurs propres `` vues de contenu '', c'est pourquoi vous devez obtenir la vue de supervision de cette vue de contenu pour obtenir la cellule elle-même. En conséquence, si votre bouton est contenu dans une sous-vue plutôt que directement dans la vue de contenu de la cellule, vous devrez aller autant de couches plus profondes pour y accéder.

Ce qui précède est une de ces approches, mais pas nécessairement la meilleure approche. Bien qu'il soit fonctionnel, il suppose des détails sur un UITableViewCellqu'Apple n'a jamais nécessairement documenté, comme sa hiérarchie de vues. Cela pourrait être changé dans le futur, et le code ci-dessus pourrait bien se comporter de manière imprévisible en conséquence.

En raison de ce qui précède, pour des raisons de longévité et de fiabilité, je recommande d'adopter une autre approche. Il existe de nombreuses alternatives répertoriées dans ce fil, et je vous encourage à lire, mais mon préféré est le suivant:

Maintenez une propriété d'une fermeture sur votre classe de cellule, demandez à la méthode d'action du bouton de l'appeler.

class MyCell: UITableViewCell {
    var button: UIButton!

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

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

Ensuite, lorsque vous créez votre cellule dans cellForRowAtIndexPath, vous pouvez attribuer une valeur à votre fermeture.

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

En déplaçant votre code de gestionnaire ici, vous pouvez tirer parti de l' indexPathargument déjà présent . C'est une approche beaucoup plus sûre que celle répertoriée ci-dessus car elle ne repose pas sur des traits non documentés.


2
Bien repéré. Je suis un développeur compétent, je le promets;) - J'ai modifié ma réponse.
Jacob King

12
Ce n'est pas la bonne façon d'obtenir la cellule à partir d'un bouton. La disposition d'une cellule a changé au fil des ans et un code comme celui-ci ne fonctionnera pas lorsque cela se produira. N'utilisez pas cette approche.
rmaddy

11
C'est une mauvaise solution. Il suppose des détails sur UITableViewCells sur lesquels Apple n'a jamais nécessairement accepté. Bien que UITableViewCells ait une propriété contentView, il n'y a aucune garantie que la supervision de contentView sera toujours la Cell.
bpapa

1
@PintuRajput Pouvez-vous me décrire votre hiérarchie de vues s'il vous plaît? Vous voyez probablement cela parce que votre bouton n'est pas une sous-vue directe de la vue de contenu de la cellule.
Jacob King

2
@ymutlu Je suis tout à fait d'accord, je l'ai dit dans la réponse. J'ai également proposé une solution beaucoup plus robuste. La raison pour laquelle j'ai laissé l'original en place est que je pense qu'il est préférable de montrer aux autres développeurs les problèmes avec une approche plutôt que de l'esquiver complètement, cela ne leur apprend rien de ce que vous voyez. :)
Jacob King

61

Mon approche à ce genre de problème consiste à utiliser un protocole délégué entre la cellule et la tableview. Cela vous permet de conserver le gestionnaire de boutons dans la sous-classe de cellules, ce qui vous permet d'affecter le gestionnaire d'actions de retouche à la cellule prototype dans Interface Builder, tout en conservant la logique du gestionnaire de boutons dans le contrôleur de vue.

Cela évite également l'approche potentiellement fragile de la navigation dans la hiérarchie des vues ou l'utilisation de la tagpropriété, qui pose des problèmes lorsque les index de cellules changent (à la suite de l'insertion, de la suppression ou de la réorganisation)

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

buttonTappedest la fonction de délégué et se trouve dans le contrôleur de vue. Dans mon exemple, someButtonTappedest la méthode d'action dans la cellule
Paulw11

@ paulw11 I got cell has no member buttonTapped in this method@IBAction func someButtonTapped(sender: UIButton) { self.delegate?.buttonTapped(self) }
EI Captain v2.0

1
C'est une très bonne solution (loin d'être aussi mauvaise que les deux actuellement avec plus de votes, en utilisant la balise regardant le superview) mais cela donne trop de code supplémentaire à ajouter.
bpapa le

2
C'est la bonne solution et devrait être la réponse acceptée. Il n'abuse pas de la propriété de balise, ne suppose pas la construction de cellules (qui peuvent facilement être modifiées par Apple) et fonctionnera toujours (sans codage supplémentaire) lorsque les cellules sont déplacées ou que de nouvelles cellules sont ajoutées entre les cellules existantes.
Robotic Cat

1
@ Paulw11 Au départ, je pensais que c'était beaucoup de code, mais il s'est avéré beaucoup plus résistant que ce que j'utilisais auparavant. Merci d'avoir publié cette solution robuste.
Adrian

53

UPDATE : Obtention de l'indexPath de la cellule contenant le bouton (à la fois section et ligne):

Utilisation de la position du bouton

À l'intérieur de votre buttonTappedméthode, vous pouvez saisir la position du bouton, la convertir en coordonnée dans la tableView, puis obtenir l'indexPath de la ligne à cette coordonnée.

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

REMARQUE : parfois, vous pouvez exécuter dans un cas de bord lorsque l'utilisation de la fonction view.convert(CGPointZero, to:self.tableView)entraîne la recherche nild'une ligne à un point, même s'il existe une cellule tableView. Pour résoudre ce problème, essayez de passer une coordonnée réelle légèrement décalée par rapport à l'origine, telle que:

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

Réponse précédente: Utilisation de la propriété de balise (ne renvoie que la ligne)

Plutôt que de grimper dans les arbres de supervision pour attraper un pointeur vers la cellule qui contient l'UIButton, il existe une technique plus sûre et plus reproductible utilisant la propriété button.tag mentionnée par Antonio ci-dessus, décrite dans cette réponse et illustrée ci-dessous:

Dans cellForRowAtIndexPath:vous définissez la propriété de balise:

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

Ensuite, dans la buttonClicked:fonction, vous référencez cette balise pour saisir la ligne de l'indexPath où se trouve le bouton:

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

Je préfère cette méthode car j'ai trouvé que se balancer dans les arbres de supervision peut être un moyen risqué de concevoir une application. Aussi, pour l'objectif-C, j'ai utilisé cette technique dans le passé et j'ai été satisfait du résultat.


5
C'est une bonne façon de le faire, et je vais voter pour que votre représentant démarre un peu, mais le seul défaut est qu'il ne donne pas accès à la section indexPath.section si cela est également nécessaire. Excellente réponse cependant!
Jacob King

Merci Jacob! J'apprécie le karma du représentant. Si vous vouliez obtenir le indexPath.sectionen plus de indexPath.row(sans réinitialiser la propriété de la balise en tant que indexPath.section), cellForRowAtIndexPath:vous pouvez simplement changer la balise en button.tag = indexPath, puis dans la buttonClicked:fonction, vous pouvez accéder à la fois en utilisant sender.tag.rowet sender.tag.section.
Iron John Bonney

1
Est-ce une nouvelle fonctionnalité, car je suis sûr que je me souviens que la propriété de balise est de type Int et non de type AnyObject, à moins que cela ne change dans swift 2.3?
Jacob King

@JacobKing vous avez raison! Mon mauvais j'ai totalement espacé lors de l'écriture de ce commentaire et je pensais que la balise était de type AnyObject. Derp - ne me dérange pas. Ce serait utile si vous pouviez passer l'indexPath en tant que balise ...
Iron John Bonney

3
Pas vraiment une bonne approche non plus. D'une part, cela ne fonctionnerait que dans les vues de table où il n'y a qu'une seule section.
bpapa

16

Utilisez une extension pour UITableView pour récupérer la cellule pour n'importe quelle vue:


La réponse de @ Paulw11 sur la configuration d'un type de cellule personnalisé avec une propriété de délégué qui envoie des messages à la vue tableau est une bonne solution, mais elle nécessite un certain travail de configuration.

Je pense que parcourir la hiérarchie des vues de la cellule de vue tableau à la recherche de la cellule est une mauvaise idée. Il est fragile - si vous enfermez ultérieurement votre bouton dans une vue à des fins de mise en page, ce code est susceptible de se casser.

L'utilisation des balises de vue est également fragile. Vous devez vous rappeler de configurer les balises lorsque vous créez la cellule, et si vous utilisez cette approche dans un contrôleur de vue qui utilise des balises de vue à une autre fin, vous pouvez avoir des numéros de balise en double et votre code peut ne pas fonctionner comme prévu.

J'ai créé une extension à UITableView qui vous permet d'obtenir l'indexPath pour toute vue contenue dans une cellule de vue de table. Il renvoie un Optionalqui sera nul si la vue transmise ne fait pas partie d'une cellule de vue de tableau. Vous trouverez ci-dessous le fichier source de l'extension dans son intégralité. Vous pouvez simplement placer ce fichier dans votre projet, puis utiliser la indexPathForView(_:)méthode incluse pour rechercher l'indexPath qui contient n'importe quelle vue.

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

Pour l'utiliser, vous pouvez simplement appeler la méthode dans IBAction pour un bouton contenu dans une cellule:

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

(Notez que la indexPathForView(_:)fonction ne fonctionnera que si l'objet de vue qui lui est transmis est contenu dans une cellule actuellement à l'écran. C'est raisonnable, car une vue qui n'est pas à l'écran n'appartient pas réellement à un indexPath spécifique; il est probable que être affecté à un indexPath différent lorsqu'il contient une cellule recyclée.)

ÉDITER:

Vous pouvez télécharger un projet de démonstration fonctionnel qui utilise l'extension ci-dessus à partir de Github: TableViewExtension.git


Merci, j'ai utilisé l'extension pour obtenir l'indexPath d'une vue de texte dans une cellule - a parfaitement fonctionné.
Jeremy Andrews

9

Pour Swift2.1

J'ai trouvé un moyen de le faire, j'espère que cela aidera.

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

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

Qu'est-ce que cela signifie si l'erreur s'exécute? Quelles sont les raisons pour lesquelles il ne trouve pas le point dans la tableView?
OOProg

Ceci (ou similaire, en utilisant les méthodes de conversion UIView) devrait être la réponse acceptée. Je ne sais pas pourquoi il s'agit actuellement du n ° 4, car il ne fait pas d'hypothèses sur l'héritage privé d'une vue table, il n'utilise pas la propriété tag (presque toujours une mauvaise idée) et n'implique pas beaucoup de code supplémentaire.
bpapa

9

Solution Swift 4:

Vous avez un bouton (myButton) ou toute autre vue dans la cellule. Attribuez une balise dans cellForRowAt comme ceci

cell.myButton.tag = indexPath.row

Maintenant, dans vous appuyez sur Fonction ou tout autre. Récupérez-le comme ceci et enregistrez-le dans une variable locale.

currentCellNumber = (sender.view?.tag)!

Après cela, vous pouvez utiliser n'importe où ce currentCellNumber pour obtenir le indexPath.row du bouton sélectionné.

Prendre plaisir!


Cette approche fonctionne, mais les balises de vue sont fragiles, comme mentionné dans ma réponse. Une simple balise entière ne fonctionnera pas pour une vue de table en coupe, par exemple. (un IndexPath it 2 entiers.) Mon approche fonctionnera toujours, et il n'est pas nécessaire d'installer une balise dans le bouton (ou dans une autre vue tappable.)
Duncan C

6

Dans Swift 4, utilisez simplement ceci:

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

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

La réponse la plus claire devrait être la réponse sélectionnée. La seule chose à noter est qu'il tableViews'agit d'une variable de sortie qui doit être référencée avant que cette réponse ne fonctionne.
10000RubyPools

Travaillez comme du charme !!
Parthpatel1105

4

Très simple obtention du chemin d'indexation rapide 4, 5

let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! Cell
cell.btn.tag = indexPath.row
cell.btn.addTarget(self, action: "buttonTapped:", forControlEvents: 
UIControlEvents.TouchUpInside)

Comment obtenir IndexPath Inside Btn Click:

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

3

Étant donné que l'expéditeur du gestionnaire d'événements est le bouton lui-même, j'utiliserais la tagpropriété du bouton pour stocker l'index, initialisé dans cellForRowAtIndexPath.

Mais avec un peu plus de travail, je ferais d'une manière complètement différente. Si vous utilisez une cellule personnalisée, voici comment j'aborderais le problème:

  • ajouter une propriété 'indexPath` à la cellule de table personnalisée
  • initialisez-le dans cellForRowAtIndexPath
  • déplacer le gestionnaire de tap du contrôleur de vue vers l'implémentation de cellule
  • utiliser le modèle de délégation pour informer le contrôleur de vue de l'événement tap, en passant le chemin d'index

Antonio, j'ai une cellule personnalisée et je serais ravi de le faire à votre façon. Cependant, cela ne fonctionne pas. Je veux que mon code «glisser pour révéler le bouton de suppression» s'exécute, qui est la méthode tableView commitEditingStyle. J'ai supprimé ce code de la classe mainVC et l'ai mis dans la classe customCell, mais maintenant le code ne fonctionne plus. Qu'est-ce que je rate?
Dave G

Je pense que c'est le meilleur moyen d'obtenir l'indexPath d'une cellule avec x sections, mais je ne vois pas la nécessité des puces 3 et 4 dans une approche MVC
Edward

2

Après avoir vu la suggestion de Paulw11 d'utiliser un rappel de délégué, je voulais développer légèrement dessus / proposer une autre suggestion similaire. Si vous ne souhaitez pas utiliser le modèle de délégué, vous pouvez utiliser les fermetures dans Swift comme suit:

Votre classe de cellule:

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

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

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

Votre cellForRowAtIndexPathméthode:

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

J'ai trouvé un moyen très simple d'utiliser pour gérer n'importe quelle cellule dans tableView et collectionView en utilisant une classe Model et cela fonctionne aussi parfaitement.

Il existe en effet une bien meilleure façon de gérer cela maintenant. Cela fonctionnera pour gérer la cellule et la valeur.

Voici ma sortie (capture d'écran) alors voyez ceci:

entrez la description de l'image ici

  1. Il est très simple de créer une classe de modèle , veuillez suivre la procédure ci-dessous. Créez une classe rapide avec un nom RNCheckedModel, écrivez le code comme ci-dessous.
class RNCheckedModel: NSObject {

    var is_check = false
    var user_name = ""

    }
  1. créez votre classe de cellule
class InviteCell: UITableViewCell {

    @IBOutlet var imgProfileImage: UIImageView!
    @IBOutlet var btnCheck: UIButton!
    @IBOutlet var lblName: UILabel!
    @IBOutlet var lblEmail: UILabel!
    }
  1. et enfin utilisez la classe de modèle dans votre UIViewController lorsque vous utilisez votre UITableView .
    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

J'ai utilisé la méthode convertPoint pour obtenir le point de tableview et passer ce point à la méthode indexPathForRowAtPoint pour obtenir 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

Essayez d'utiliser #selector pour appeler l'IBaction. Dans le cellforrowatindexpath

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

De cette façon, vous pouvez accéder au chemin d'index dans la méthode editButtonPressed

func editButtonPressed(_ sender: UIButton) {

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

}

Réponse la plus appropriée
Amalendu Kar

Non, les balises seront désactivées lorsque l'utilisateur ajoute ou supprime des cellules.
koen

1

Dans mon cas, j'ai plusieurs sections et l'index de la section et de la ligne est vital, donc dans un tel cas, je viens de créer une propriété sur UIButton dont j'ai défini la cellule indexPath comme suit:

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

Ensuite, définissez la propriété dans cellForRowAt comme ceci:

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

Ensuite, dans handleTapAction, vous pouvez obtenir l'indexPath comme ceci:

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

}

1

Swift 4 et 5

Méthode 1 utilisant le délégué de protocole

Par exemple, vous avez un UITableViewCellavec nomMyCell

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

Créez maintenant un protocol

protocol MyCellDelegate {
    func didPressButton(cell: UITableViewCell)
}

Étape suivante, créez une extension de UITableView

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

Dans votre UIViewControllerimplémentation le protocoleMyCellDelegate

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

Méthode 2 utilisant des fermetures

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

Remarque: - si vous souhaitez obtenir UICollectionViewindexPath, vous pouvez l'utiliser UICollectionView extensionet répéter les étapes ci-dessus

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

0

Dans Swift 3. Également utilisé des déclarations de garde, évitant une longue chaîne d'accolades.

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

    print(indexPath)
}

Cela ne fonctionnera pas. La supervision du bouton ne sera pas la cellule.
rmaddy

Cela fonctionne. La seule chose à laquelle vous devez faire attention est que la pile de vues de chacun est différente. Il peut s'agir de sender.superview, sender.superview.superview ou sender.superview.superview.superview. Mais ça marche vraiment bien.
Sean

0

Parfois, le bouton peut être à l'intérieur d'une autre vue de UITableViewCell. Dans ce cas, superview.superview peut ne pas donner l'objet de cellule et donc indexPath sera nul.

Dans ce cas, nous devons continuer à trouver la vue supervisée jusqu'à ce que nous obtenions l'objet cellule.

Fonction pour obtenir un objet de cellule par superview

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
}

Maintenant, nous pouvons obtenir indexPath en appuyant sur le bouton comme ci-dessous

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

}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.