我有一个表格视图,其中包含可变数量的单元格,代表与其特定教师相对应的学生。 它们是带有一个按钮的自定义单元格,该按钮可以触发新 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 函数中,当按下任何单元的按钮时调用该函数。因为我在填充新单元格时有效地隐藏了每个单元格中数据的"键"(索引),所以不需要计算 - 而且 - 如果您移动单元格,则无需更新任何内容。