检测在表格视图中按下的 uibutton:Swift 最佳实践



我有一个表格视图,其中包含可变数量的单元格,代表与其特定教师相对应的学生。 它们是带有一个按钮的自定义单元格,该按钮可以触发新 VC 的 segue,显示有关其单元格所在的学生的详细信息。 我的问题是:

swift 中识别按下了哪个按钮的最佳实践是什么?

一旦我知道索引路径,我就可以确定哪个学生的信息需要传递给下一个VC。在下面的帖子中,目标 C 有一个很好的答案,但我不确定如何翻译成 Swift。 任何帮助将不胜感激。

检测在 UITableView 中按下了哪个 UIButton

如果你的代码允许,我建议你将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
}

否则,如果由于某种原因这不利于您的代码,那么您链接到的这个 Objective-C 的 Swift 翻译答案:

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

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
        }

针对 Swift 3 进行了更新

如果您唯一想做的是在触摸时触发 segue,那么通过 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)

另一种可能的解决方案是使用 dispatch_block_t .如果使用情节提要执行此操作,则首先必须在自定义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] .

Swift 3

@ cellForRowAt indexPath

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

然后

func BtnAction(_ sender: Any)
    {
        let btn = sender as? UIButton
    }

使用标签来标识单元格和索引路径从来都不是一个好主意,最终你会得到错误的 indexPath,从而导致错误的单元格和信息。

我建议你试试下面的代码(使用UICollectionView,没有用TableView测试它,但它可能会正常工作):

斯威夫特 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
        }
    }
}

检测 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单元格方法

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
    }

Swift 5。在cellForRowAtIndexPath中,您可以设置标记:

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

然后在共享BtnPress中获取标签

  @IBAction func shareBtnPressed(_ sender: UIButton) {
    let buttonRow = sender.tag
    print("Video Shared in row (buttonRow)")
}

作为@Lyndsey和@longbow评论的后续,我注意到当我在情节提要中将 segue 从按钮转到目的地VC时,在buttonClicked函数可以更新urlPath变量之前,正在调用prepareForSegue。 为了解决这个问题,我直接从第一个 VC 设置了 segue 到目标 VC,并在执行 buttonClicked 中的代码后以编程方式执行 segue。 也许不理想,但似乎正在工作。

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
    }

针对 Swift 5 进行了更新:

将以下代码放在 ViewController 类中

@IBAction func buttonClicked(_ sender: UIButton) {
    if let tableView = tableView {
        let point = tableView.convert(sender.center, from: sender.superview!)
//can call wantedIndexPath.row here
        }
    }
}

我正在通过 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
    }

在下一个控制器上,我将重新加载完整的项目属性,方法是知道它的 id 并将其设置为 DetailVC 中的 var thisItem

我将使用 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
我的

原型单元格有一个 var 来将实际的索引/键保存到我的数组中:

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 函数中,当按下任何单元的按钮时调用该函数。因为我在填充新单元格时有效地隐藏了每个单元格中数据的"键"(索引),所以不需要计算 - 而且 - 如果您移动单元格,则无需更新任何内容。

最新更新