[英]detecting uibutton pressed in tableview: Swift Best Practices
我有一个表格视图,其中包含可变数量的单元格,代表与其特定教师相对应的学生。 它们是自定义单元格,带有一个按钮,可触发新 VC 的转场,显示该单元格所在学生的详细信息。 我的问题是:
快速识别按下哪个按钮的最佳实践是什么?
一旦我知道索引路径,我就可以确定哪些学生的信息需要传递给下一个 VC。 在下面的帖子中对目标 C 有一个很好的答案,但我不确定如何转换为 Swift。 任何帮助将非常感激。
如果您的代码允许,我建议您将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 更新
如果您只想在触摸时触发转场,那么通过 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”的圆圈it) 并使用override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath)
代替。
另一种可能的解决方案是使用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]
身份访问它。
斯威夫特 3
@cellForRowAt indexPath
cell.Btn.addTarget(self, action: #selector(self.BtnAction(_:)), for: .touchUpInside)
然后
func BtnAction(_ sender: Any)
{
let btn = sender as? UIButton
}
使用标签来标识单元格和 indexPaths 从来都不是一个好主意,最终你会得到一个错误的 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 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")
}
}
这里是 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
}
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)")
}
作为@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
}
为 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 值——每个 tableview 单元格一个。 每种颜色都在颜色结构数组中定义。 为清楚起见,这些是:
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 函数中,当任何单元格的按钮被按下时调用。 因为我在填充新单元格时有效地隐藏了每个单元格中数据的“键”(索引),所以不需要计算 - 而且 - 如果您移动单元格,则无需更新任何内容。
我找到了一种非常简单和 saficiat 的方法来使用 Model 类来管理 tableView 和 collectionView 中的任何单元格,这是一个完美的工作。
现在确实有更好的方法来处理这个问题。 这将适用于管理单元格和值
这是我的代码
类 RNCheckedModel: NSObject {
var is_check = false
var user_name = ""
}
类邀请单元: UITableViewCell {
@IBOutlet var imgProfileImage: UIImageView!
@IBOutlet var btnCheck: UIButton!
@IBOutlet var lblName: UILabel!
@IBOutlet var lblEmail: UILabel!
}
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()
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.