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 62

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

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

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

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

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

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

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

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

CellSubclass.Свифт

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.Свифт

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.ГИТ

на 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 будет равен нулю.

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

функция для получения объекта ячейки с помощью 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
}

теперь мы можем получить indexPath на кнопке нажмите, как показано ниже

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

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