swift: как получить indexpath.строки при нажатии кнопки в ячейке?

У меня есть tableview с кнопками, и я хочу использовать indexpath.когда один из них постучал. Это то, что у меня есть в настоящее время, но это всегда 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)
}

13 ответов


giorashc почти сделал это с его ответом, но он упустил из виду тот факт, что у ячейки есть дополнительный


обновление: получение 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, есть более безопасный, более повторяемый метод с использованием кнопки.свойство tag отметил Антонио выше, описано в ответ, и показано ниже:

на cellForRowAtIndexPath: вы устанавливаете свойство тега:

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

затем в buttonClicked: функция, вы ссылаетесь на этот тег, чтобы захватить строку indexPath, где находится кнопка:

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

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


мой подход к этой проблеме заключается в использовании протокола делегата между ячейкой и tableview. Это позволяет сохранить обработчик кнопок в подклассе ячеек, что позволяет назначить обработчик действий touch up ячейке-прототипу в Interface Builder, сохраняя при этом логику обработчика кнопок в контроллере представления.

Это также позволяет избежать потенциально хрупкого подхода навигации по иерархии представлений или использования tag свойство, которое имеет проблемы при изменении индексов ячеек (в результате вставки, удаления или переупорядочения)

CellSubclass.Свифт!--9-->

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

class CellSubclass: UITableViewCell {

@IBOutlet var someButton: UIButton!

var delegate: CellSubclassDelegate?

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

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

ViewController.Свифт!--9-->

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

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


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

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

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

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

EDIT:

вы можете скачать рабочий демо-проект, который использует вышеуказанное расширение из Github:TableViewExtension.git


на Swift2.1

Я нашел способ сделать это, надеюсь, это поможет.

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

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

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

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

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

Swift 4 Решение:

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

cell.myButton.tag = indexPath.row

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

currentCellNumber = (sender.view?.tag)!

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

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


увидев предложение Paulw11 об использовании обратного вызова делегата, я хотел немного развить его/выдвинуть другое, подобное предложение. Если вы не хотите использовать шаблон делегата, вы можете использовать закрытие в swift следующим образом:

мобильный класс:

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

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

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


я использовал метод 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);
            }   
        }
    }

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

попробуйте использовать # selector для вызова IBaction.В 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

}

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