Как получить indexpath.row при активации элемента?


104

У меня есть табличное представление с кнопками, и я хочу использовать 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 () вместо точечной переменной? или где мне его использовать?
Винсент,

Ответы:


166

У giorashc почти все было в порядке со своим ответом, но он упустил из виду тот факт, что в ячейках есть дополнительный contentViewслой. Таким образом, мы должны пойти на один слой глубже:

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

let indexPath = itemTable.indexPath(for: cell)

Это связано с тем, что внутри иерархии представлений у tableView есть ячейки в качестве подпредставлений, которые впоследствии имеют свои собственные «представления содержимого», поэтому вы должны получить супервизор этого представления содержимого, чтобы получить саму ячейку. В результате, если ваша кнопка содержится в подпредставлении, а не непосредственно в представлении содержимого ячейки, вам придется пройти на несколько уровней глубже, чтобы получить к ней доступ.

Вышеупомянутый - один из таких подходов, но не обязательно лучший. Несмотря на то, что он функциональный, он предполагает детали о 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 03

11
Это плохое решение. Он предполагает подробности о UITableViewCells, с которыми Apple никогда не соглашалась. Хотя UITableViewCells имеет свойство contentView, нет гарантии, что супервизор contentView всегда будет Cell.
bpapa

1
@PintuRajput Не могли бы вы описать мне вашу иерархию представлений? Вы, вероятно, видите это, потому что ваша кнопка не является прямым подчиненным представлением представления содержимого ячейки.
Джейкоб Кинг

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является функцией делегата и находится в контроллере представления. В моем примере someButtonTappedэто метод действия в ячейке
Paulw11,

@ paulw11 У меня в ячейке нет кнопки участника, нажатой в этом методе@IBAction func someButtonTapped(sender: UIButton) { self.delegate?.buttonTapped(self) }
EI Captain v2.0

1
Это довольно хорошее решение (далеко не так плохо, как два, которые в настоящее время имеют больше голосов, используя тег, смотрящий на супервизор), но похоже, что для добавления слишком много дополнительного кода.
bpapa

2
Это правильное решение и должен быть принятым ответом. Он не злоупотребляет свойством тега, не предполагает построения ячеек (которые могут быть легко изменены Apple) и по-прежнему будет работать (без дополнительного кодирования), когда ячейки перемещаются или новые ячейки добавляются между существующими ячейками.
Robotic Cat

1
@ Paulw11 Сначала я думал, что это много кода, но оказалось, что он намного более устойчив, чем то, что я использовал раньше. Спасибо за размещение этого надежного решения.
Адриан

53

ОБНОВЛЕНИЕ : получение 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)
}

ПРИМЕЧАНИЕ . Иногда вы можете столкнуться с крайним случаем, когда использование функции view.convert(CGPointZero, to:self.tableView)приводит к поиску nilстроки в точке, даже если там есть ячейка tableView. Чтобы исправить это, попробуйте передать реальную координату, которая немного смещена от начала координат, например:

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

Предыдущий ответ: Использование свойства тега (возвращает только строку)

Вместо того, чтобы лазить по деревьям супервизора, чтобы захватить указатель на ячейку, которая содержит UIButton, существует более безопасный и повторяемый метод, использующий свойство 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
}

Я предпочитаю этот метод, так как обнаружил, что раскачивание в деревьях супервизора может быть рискованным способом разработки приложения. Кроме того, для объективного C я использовал эту технику в прошлом и был доволен результатом.


5
Это хороший способ сделать это, и я буду поддерживать его, чтобы немного поднять вашу репутацию, однако единственный недостаток заключается в том, что он не дает доступа к indexPath.section, если это также необходимо. Хотя отличный ответ!
Джейкоб Кинг

Спасибо, Джейкоб! Я ценю репутационную карму. Если вы хотите получить indexPath.sectionв дополнение к indexPath.row(без сброса свойства тега как indexPath.section), cellForRowAtIndexPath:вы можете просто изменить тег на button.tag = indexPath, а затем в buttonClicked:функции, к которой вы можете получить доступ, используя sender.tag.rowи sender.tag.section.
Айрон Джон Бонни

1
Это новая функция, потому что я уверен, что помню свойство тега типа Int, а не типа AnyObject, если это не изменилось в Swift 2.3?
Джейкоб Кинг,

@JacobKing, ты прав! Жалко, что при написании этого комментария я полностью выделил интервал и подумал, что это тег типа AnyObject. Дерп - не обращай на меня внимания. Было бы полезно, если бы вы могли передать indexPath как тег ...
Iron John Bonney,

3
Тоже не очень хороший подход. Во-первых, он будет работать только в представлениях таблиц, где есть один раздел.
bpapa

16

Используйте расширение для UITableView, чтобы получить ячейку для любого представления:


Ответ @ Paulw11 о настройке пользовательского типа ячейки со свойством делегата, который отправляет сообщения в табличное представление, - хороший способ, но для его настройки требуется определенный объем работы.

Я думаю, что ходить по иерархии представлений ячейки таблицы в поисках ячейки - плохая идея. Он хрупкий - если позже вы поместите кнопку в представление для целей макета, этот код, скорее всего, сломается.

Использование тегов просмотра также ненадежно. Вы должны не забыть настроить теги при создании ячейки, и если вы используете этот подход в контроллере представления, который использует теги представления для другой цели, вы можете иметь повторяющиеся номера тегов, и ваш код может не работать должным образом.

Я создал расширение для UITableView, которое позволяет вам получить indexPath для любого представления, содержащегося в ячейке представления таблицы. Он возвращает значение Optionalnil, если переданное представление фактически не попадает в ячейку табличного представления. Ниже представлен полный исходный файл расширения. Вы можете просто поместить этот файл в свой проект, а затем использовать включенный 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

Этот (или аналогичный, с использованием методов преобразования UIView) должен быть принятым ответом. Не знаю, почему сейчас он №4, поскольку он не делает предположений о частной иерархии табличного представления, не использует свойство тега (почти всегда плохая идея) и не требует большого количества дополнительного кода.
bpapa

9

Решение Swift 4:

У вас есть кнопка (myButton) или любое другое представление в ячейке. Назначьте тег в cellForRowAt как это

cell.myButton.tag = indexPath.row

Теперь у вас tapFunction или любой другой. Извлеките его вот так и сохраните в локальной переменной.

currentCellNumber = (sender.view?.tag)!

После этого вы можете использовать в любом месте этот currentCellNumber, чтобы получить indexPath.row выбранной кнопки.

Наслаждайтесь!


Этот подход работает, но, как упоминалось в моем ответе, теги просмотра хрупкие. Например, простой целочисленный тег не будет работать для представления таблицы с разделами. (IndexPath, это 2 целых числа.) Мой подход всегда будет работать, и нет необходимости устанавливать тег в кнопку (или другое интерактивное представление).
Дункан К.

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

Работайте как шарм !!
Parthpatel1105

4

Очень просто получить Index Path Swift 4, 5

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

Как получить IndexPath внутри Btn Click:

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

3

Поскольку отправителем обработчика событий является сама кнопка, я бы использовал tagсвойство кнопки для хранения индекса, инициализированного в cellForRowAtIndexPath.

Но приложив немного больше усилий, я бы поступил совершенно по-другому. Если вы используете настраиваемую ячейку, я бы подошел к проблеме следующим образом:

  • добавить свойство indexPath в ячейку настраиваемой таблицы
  • инициализировать его в cellForRowAtIndexPath
  • переместите обработчик касания из контроллера представления в реализацию ячейки
  • использовать шаблон делегирования, чтобы уведомить контроллер представления о событии касания, передав путь индекса

Антонио, у меня есть индивидуальная ячейка, и я хотел бы сделать это по-вашему. Однако это не работает. Я хочу, чтобы мой код «проведите пальцем по экрану, чтобы открыть кнопку удаления» был запущен, а это метод tableView commitEditingStyle. Я удалил этот код из класса mainVC и поместил его в класс customCell, но теперь код больше не работает. Что мне не хватает?
Дэйв Джи

Я думаю, что это лучший способ получить indexPath ячейки с x секциями, однако я не вижу необходимости в маркерах 3 и 4 в подходе MVC
Эдвард

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

Я нашел очень простой способ управления любой ячейкой в ​​tableView и collectionView с помощью класса Model, и это отлично работает.

Сейчас действительно есть гораздо лучший способ справиться с этим. Это будет работать для управления ячейкой и значением.

Вот мой результат (снимок экрана), так что посмотрите это:

введите описание изображения здесь

  1. Создать класс модели очень просто , следуйте приведенной ниже процедуре. Создайте быстрый класс с именем RNCheckedModel, напишите код, как показано ниже.
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. и, наконец, используйте класс модели в своем UIViewController, когда вы используете свой 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

Я использовал метод 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 для вызова действия IB. В cellforrowatindexpath

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

Таким образом, вы можете получить доступ к indexpath внутри метода editButtonPressed

func editButtonPressed(_ sender: UIButton) {

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

}

Наиболее подходящий ответ
Амаленду Кар

Нет, теги будут отключены, когда пользователь добавляет или удаляет ячейки.
Коэн

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

Примечание: - если вы хотите получить UICollectionViewindexPath, вы можете использовать это 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 03

Это действительно работает. Единственное, чего вы должны быть осторожны, это то, что стек представлений у всех разный. Это может быть sender.superview, sender.superview.superview или sender.superview.superview.superview. Но это действительно хорошо работает.
Шон

0

Иногда кнопка может находиться внутри другого представления UITableViewCell. В этом случае superview.superview может не выдавать объект ячейки и, следовательно, indexPath будет равен нулю.

В этом случае мы должны продолжать поиск супервизора, пока не получим объект ячейки.

Функция для получения объекта ячейки с помощью супервизора

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

}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.