Подтвердить что ты не робот

Обнаружение uibutton, нажатого в виде таблицы: Swift Best Practices

У меня есть табличное представление с переменным числом ячеек, представляющих студентов, которые соответствуют их конкретному преподавателю. Они являются пользовательскими ячейками с кнопкой, которая запускает segue в новый VC, в результате чего детальная информация о студенте, чья ячейка была. Мой вопрос:

Какова наилучшая практика быстрого определения того, какая кнопка была нажата?

Как только я узнаю путь к индексу, я могу определить, какая информация о студенте должна быть передана следующему VC. На объекте C в сообщении ниже есть отличный ответ, но я не уверен, как перевести на Swift. Любая помощь будет высоко оценена.

Обнаружение, которое UIButton было нажато в UITableView

4b9b3361

Ответ 1

Если ваш код позволяет, я бы рекомендовал вам установить тег UIButton равным indexPath.row, поэтому, когда его действие будет запущено, вы можете вытащить тег и, таким образом, выровнять данные кнопки во время запуска метода, Например, в cellForRowAtIndexPath вы можете установить тег:

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

то в buttonClicked: вы можете получить тег и, следовательно, строку:

func buttonClicked(sender:UIButton) {

    let buttonRow = sender.tag
}

В противном случае, если это почему-то не способствует вашему коду, перевод Swift этого Objective-C ответит вам на:

- (void)checkButtonTapped:(id)sender
{
    CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    if (indexPath != nil)
    {
     ...
    }
}

является:

func checkButtonTapped(sender:AnyObject) {
      let buttonPosition = sender.convert(CGPoint.zero, to: self.tableView)
    let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
    if indexPath != nil {
        ...
    }
}

Ответ 2

Решение Swift 3.0

cell.btnRequest.tag = indexPath.row

    cell.btnRequest.addTarget(self,action:#selector(buttonClicked(sender:)), for: .touchUpInside)

    func buttonClicked(sender:UIButton) {

            let buttonRow = sender.tag
        }

Ответ 3

Обновлено для Swift 3

Если единственное, что вы хотите сделать, это вызвать пошаговое нажатие, было бы против лучшей практики делать это через UIButton. Вы можете просто использовать встроенный обработчик UIKit для выбора ячейки, т.е. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath). Вы можете реализовать его, выполнив следующее:

Создайте пользовательский UITableViewCell

class StudentCell: UITableViewCell {
    // Declare properties you need for a student in a custom cell.
    var student: SuperSpecialStudentObject!

    // Other code here...
}

Когда вы загружаете свой UITableView, передайте данные в ячейку из вашей модели данных:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "StudentCell", for: indexPath) as! StudentCell
    cell.student = superSpecialDataSource[indexPath.row]
    return cell
}

Затем используйте didSelectRow atIndexPath, чтобы определить, когда ячейка выбрана, получить доступ к ячейке и ее данным и передать значение в качестве параметра performSegue.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath) as! StudentCell

    if let dataToSend = cell.student {
        performSegue(withIdentifier: "DestinationView", sender: dataToSend)
    }
}

И наконец, в prepareForSegue:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "DestinationView" {
        let destination = segue.destination as! DestinationViewController
        if let dataToSend = sender as? SuperSpecialStudentObject {
            destination.student = dataToSend
        }
    }
}

В качестве альтернативы, если вы хотите, чтобы они только выбирали часть ячейки, а не когда они касались где-либо внутри ячейки, вы могли бы добавить в свою ячейку дополнительный элемент, такой как деталь дополнительного аксессуара (выглядит как круг с "i" внутри него" и вместо этого используйте override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath).

Ответ 4

Другим возможным решением будет использование dispatch_block_t. Если вы делаете это с помощью Storyboard, вам сначала нужно создать переменную-член в своем пользовательском классе UITableViewCell.

var tapBlock: dispatch_block_t?

Затем вам нужно создать IBAction и вызвать tapBlock.

@IBAction func didTouchButton(sender: AnyObject) {
    if let tapBlock = self.tapBlock {
        tapBlock()
    }
}

В вашем представлении контроллер с UITableView вы можете просто реагировать на события кнопки, подобные этой

let cell = tableView.dequeueReusableCellWithIdentifier("YourCellIdentifier", forIndexPath: indexPath) as! YourCustomTableViewCell

cell.tapBlock = {
   println("Button tapped")
}

Однако вы должны знать при доступе к self внутри блока, чтобы не создавать цикл сохранения. Обязательно получите доступ к нему как [weak self].

Ответ 5

Swift 3

@cellForRowAt indexPath

cell.Btn.addTarget(self, action: #selector(self.BtnAction(_:)), for: .touchUpInside)

Тогда

func BtnAction(_ sender: Any)
    {

        let btn = sender as? UIButton

    }

Ответ 6

В качестве комментария к комментариям @Lyndsey и @longbow, я заметил, что когда у меня был segue в раскадровке, идущей от кнопки до места назначения VC, вызов prepareForSegue вызывался до того, как функция buttonClicked могла обновить переменную urlPath. Чтобы разрешить это, я установил segue непосредственно из первого VC в destinationVC и выполнил segue программно после того, как был выполнен код в buttonClicked. Может быть, не идеальный, но, похоже, работает.

func buttonClicked(sender:UIButton) {
    let studentDic = tableData[sender.tag] as NSDictionary
    let studentIDforTherapyInt = studentDic["ID"] as Int
    studentIDforTherapy = String(studentIDforTherapyInt)
    urlPath = "BaseURL..."+studentIDforTherapy
    self.performSegueWithIdentifier("selectTherapySegue", sender: sender)
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    if (segue.identifier == "selectTherapySegue") {
        let svc = segue.destinationViewController as SelectTherapyViewController;
        svc.urlPath = urlPath
    }

Ответ 7

Простой и простой способ обнаружения события кнопки и выполнения некоторого действия

class youCell: UITableViewCell
{
    var yourobj : (() -> Void)? = nil
     override func awakeFromNib()
        {
        super.awakeFromNib()
        }

 @IBAction func btnAction(sender: UIButton)
    {
        if let btnAction = self.yourobj
        {
            btnAction()
        }
    }
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell = youtableview.dequeueReusableCellWithIdentifier(identifier) as? youCell
        cell?.selectionStyle = UITableViewCellSelectionStyle.None

cell!. yourobj =
            {
                //Do whatever you want to do when the button is tapped here
                self.view.addSubview(self.someotherView)
        }

          return cell

}

Ответ 8

Никогда не стоит использовать теги для идентификации ячеек и indexPaths, в итоге вы получите неправильный indexPath и, следовательно, неправильную ячейку и информацию.

Я предлагаю вам попробовать приведенный ниже код (работая с UICollectionView, не тестировал его с TableView, но, вероятно, он будет работать просто отлично):

SWIFT 4

@objc func buttonClicked(_ sender: UIButton) {
    if let tableView = tableViewNameObj {
        let point = tableView.convert(sender.center, from: sender.superview!)

        if let wantedIndexPath = tableView.indexPathForItem(at: point) {
            let cell = tableView.cellForItem(at: wantedIndexPath) as! SpecificTableViewCell

        }
    }
}

Ответ 9

Определение раздела и строки для индекса UiTableView при нажатии кнопки.

//MARK:- Buttom Action Method
    @objc func checkUncheckList(_sender:UIButton)
    {
        if self.arrayRequestList != nil
        {

          let strSection = sender.title(for: .disabled)

          let dict = self.arrayRequestList![Int(strSection!)!]["record"][sender.tag]

          print("dict:\(dict)")

          self.requestAcceptORReject(dict: dict, strAcceptorReject: "1")
        }

    }

Вот метод ячейки UITableView для добавления targate

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "OtherPropertySelectiingCell", for: indexPath as IndexPath) as! OtherPropertySelectiingCell
        cell.btnAccept.tag = indexPath.row
        cell.btnAccept.setTitle("\(indexPath.section)", for: .disabled)
        cell.btnAccept.addTarget(self, action: #selector(checkUncheckList(_sender:)), for: .touchUpInside)
        return cell
    }

Ответ 10

Я делаю это через prepareforSegue

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

            let indexPath = self.tableView.indexPathForSelectedRow()
            let item = tableViewCollection[indexPath!.row].id
            let controller = segue.destinationViewController as? DetailVC

            controller?.thisItem = item
    }

а на следующем контроллере я просто перезагружу полные свойства элемента, зная его идентификатор и установив его в var thisItem в DetailVC

Ответ 11

Я собирался использовать подход indexPath, пока не понял, что в некоторых ситуациях он будет ненадежным/неправильным (например, удаленная или перемещенная ячейка).

То, что я сделал, проще. Например, я показываю серию цветов и их значения RGB - по одному на ячейку табличного представления. Каждый цвет определяется в массиве цветовых структур. Для ясности это:

struct ColorStruct {
    var colorname:String    = ""
    var red:   Int = 0
    var green: Int = 0
    var blue:  Int = 0
}

var colors:[ColorStruct] = []       // The color array

В моей ячейке-прототипе есть переменная для хранения фактического индекса/ключа в моем массиве:

class allListsCell: UITableViewCell {
    @IBOutlet var cellColorView: UIView!
    @IBOutlet var cellColorname: UILabel!
    var colorIndex = Int()  // ---> points directly back to colors[]

    @IBAction func colorEditButton(_ sender: UIButton, forEvent event: UIEvent) {
        print("colorEditButton: colors[] index:\(self.colorIndex), \(colors[self.colorIndex].colorname)")
    }
}

В этом решении используются три строки кода: одна в определении ячейки прототипа, вторая в логике, заполняющей новую ячейку, и третья в функции IBAction, которая вызывается при нажатии любой кнопки ячейки. Поскольку я эффективно скрыл "ключ" (индекс) для данных в каждой ячейке, КАК я заполняю эту новую ячейку, вычисление не требуется - и если вы перемещаете ячейки, нет необходимости что-либо обновлять.

Ответ 12

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

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

вот мой вывод (скриншот), так что смотрите это

вот мой код

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

класс 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()

}

 }

Ответ 13

Swift 5. В cellForRowAtIndexPath вы устанавливаете тег:

cell.shareButton.tag = indexPath.row
cell.shareButton.addTarget(self, action: #selector(shareBtnPressed(_:)), for: .touchUpInside)

Затем в shareBtnPressed вы получаете тег

  @IBAction func shareBtnPressed(_ sender: UIButton) {

    let buttonRow = sender.tag

    print("Video Shared in row \(buttonRow)")
}