ios 检测在 tableview 中按下的 uibutton:Swift 最佳实践
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27429652/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
detecting uibutton pressed in tableview: Swift Best Practices
提问by jamike
I have a tableview with a variable number of cells representing students that correspond to their particular instructor. They are custom cells with a button that triggers a segue to a new VC, bringing up detailed information on the student whose cell it was. My question is:
我有一个表格视图,其中包含可变数量的单元格,代表与其特定教师相对应的学生。它们是自定义单元格,带有一个按钮,可触发新 VC 的转场,显示该单元格所在学生的详细信息。我的问题是:
What is the best practice in swift for identifying which button was pressed?
快速识别按下哪个按钮的最佳实践是什么?
Once i know the index path, I can identify which student's information needs to be passed to the next VC. There is a great answer for objective C in the post below, but I'm not sure how to translate to Swift. Any help would be much appreciated.
一旦我知道索引路径,我就可以确定哪些学生的信息需要传递给下一个 VC。在下面的帖子中对目标 C 有一个很好的答案,但我不确定如何转换为 Swift。任何帮助将非常感激。
回答by Lyndsey Scott
If your code allows, I'd recommend you set the UIButton
tag equal to the indexPath.row
, so when its action is triggered, you can pull the tag and thus row out of the button data during the triggered method. For example, in cellForRowAtIndexPath
you can set the tag:
如果您的代码允许,我建议您将UIButton
标签设置为等于indexPath.row
,这样当它的动作被触发时,您可以拉出标签,从而在触发方法期间从按钮数据中取出。例如,cellForRowAtIndexPath
您可以设置标签:
button.tag = indexPath.row
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
then in buttonClicked:
, you can fetch the tag and thus the row:
然后在buttonClicked:
,您可以获取标签,从而获取行:
func buttonClicked(sender:UIButton) {
let buttonRow = sender.tag
}
Otherwise, if that isn't conducive to your code for some reason, the Swift translation of this Objective-C answer you linked to:
否则,如果由于某种原因这不利于您的代码,那么您链接到的这个 Objective-C 答案的 Swift 翻译:
- (void)checkButtonTapped:(id)sender
{
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
if (indexPath != nil)
{
...
}
}
is:
是:
func checkButtonTapped(sender:AnyObject) {
let buttonPosition = sender.convert(CGPoint.zero, to: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
if indexPath != nil {
...
}
}
回答by Sourabh Sharma
Swift 3.0 Solution
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
}
回答by darksinge
Updated for Swift 3
为 Swift 3 更新
If the only thing you want to do is trigger a segue on a touch, it would be against best practice to do so via a UIButton. You can simply use UIKit's built in handler for selecting a cell, i.e. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
. You could implement it doing something like the following:
如果您只想在触摸时触发转场,那么通过 UIButton 执行此操作将违反最佳实践。您可以简单地使用 UIKit 的内置处理程序来选择一个单元格,即func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
. 您可以执行以下操作来实现它:
Create a custom UITableViewCell
创建自定义 UITableViewCell
class StudentCell: UITableViewCell {
// Declare properties you need for a student in a custom cell.
var student: SuperSpecialStudentObject!
// Other code here...
}
When you load your UITableView, pass the data into the cell from you data model:
加载 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
}
Then use didSelectRow atIndexPath
to detect when a cell has been selected, access the cell and it's data, and pass the value in as a parameter to performSegue
.
然后用于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)
}
}
And finally in prepareForSegue
:
最后在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
}
}
}
Alternatively if you want them to only select a part of the cell instead of when they touch anywhere inside the cell, you could add an accessory item onto your cell such as the detail accessory item (looks like the circle with an "i" inside of it) and use override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath)
instead.
或者,如果您希望他们只选择单元格的一部分,而不是当他们接触单元格内的任何地方时,您可以在单元格中添加一个附件项目,例如细节附件项目(看起来像里面带有“i”的圆圈它)并override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath)
改为使用。
回答by gpichler
Another possible solution would be using dispatch_block_t
. If you do it with Storyboard you first have to create a member variable in your custom UITableViewCell
class.
另一种可能的解决方案是使用dispatch_block_t
. 如果您使用 Storyboard,您首先必须在您的自定义UITableViewCell
类中创建一个成员变量。
var tapBlock: dispatch_block_t?
Then you have to create an IBAction
and call the tapBlock
.
然后你必须创建一个IBAction
并调用tapBlock
.
@IBAction func didTouchButton(sender: AnyObject) {
if let tapBlock = self.tapBlock {
tapBlock()
}
}
In your view controller with the UITableView
you can simply react to the button events like this
在您的视图控制器中,UITableView
您可以简单地对这样的按钮事件做出反应
let cell = tableView.dequeueReusableCellWithIdentifier("YourCellIdentifier", forIndexPath: indexPath) as! YourCustomTableViewCell
cell.tapBlock = {
println("Button tapped")
}
However you have to be aware when accessing self
inside the block, to not create a retain cycle. Be sure to access it as [weak self]
.
但是,self
在块内部访问时必须注意,不要创建保留循环。请务必以[weak self]
.
回答by Shibin k kurian
Swift 3
斯威夫特 3
@ cellForRowAt indexPath
@cellForRowAt indexPath
cell.Btn.addTarget(self, action: #selector(self.BtnAction(_:)), for: .touchUpInside)
Then
然后
func BtnAction(_ sender: Any)
{
let btn = sender as? UIButton
}
回答by Ricardo Brancaglion
It's never a good idea to use tags to identify cells and indexPaths, eventually you'll end up with a wrong indexPath and consequently the wrong cell and information.
使用标签来标识单元格和 indexPaths 从来都不是一个好主意,最终你会得到一个错误的 indexPath 并因此得到错误的单元格和信息。
I suggest you try the code bellow (Working with UICollectionView, didn't tested it with a TableView, but it probably will work just fine):
我建议你试试下面的代码(使用 UICollectionView,没有用 TableView 测试它,但它可能会工作得很好):
SWIFT 4
快速 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
}
}
}
回答by pansora abhay
Detecting the Sectionand rowfor UiTableViewindexPath on click Button click
检测科和行为的UITableView上点击按钮点击indexPath
//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")
}
}
Here is UITableView Cell Method to add the targate
这里是 UITableView Cell 方法来添加 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
}
回答by Team chang
Swift 5. In cellForRowAtIndexPath you set the tag:
Swift 5. 在 cellForRowAtIndexPath 中设置标签:
cell.shareButton.tag = indexPath.row
cell.shareButton.addTarget(self, action: #selector(shareBtnPressed(_:)), for: .touchUpInside)
Then in shareBtnPressed you fetch the tag
然后在 shareBtnPressed 中获取标签
@IBAction func shareBtnPressed(_ sender: UIButton) {
let buttonRow = sender.tag
print("Video Shared in row \(buttonRow)")
}
回答by jamike
As a follow up to @Lyndsey and @longbow's comments, I noticed that when I had the segue in storyboard going from the button to the destinationVC, the prepareForSegue was being called before the buttonClicked function could update the urlPath variable. To resolve this, I set the segue directly from the first VC to the destinationVC, and had the segue performed programmatically after the code in buttonClicked was executed. Maybe not ideal, but seems to be working.
作为@Lyndsey 和@longbow 评论的后续,我注意到当故事板中的segue 从按钮到destinationVC 时,在buttonClicked 函数可以更新urlPath 变量之前调用prepareForSegue。为了解决这个问题,我将 segue 直接从第一个 VC 设置到 destinationVC,并在执行 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
}
回答by longbow
I am doing it via prepareforSegue
我是通过 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
}
and on the next controller i will just reload the full item properties, by knowing its id and setting it to the var thisItem in the DetailVC
在下一个控制器上,我将通过知道它的 id 并将其设置为 DetailVC 中的 var thisItem 来重新加载完整的项目属性