簡體   English   中英

檢測在 tableview 中按下的 uibutton:Swift 最佳實踐

[英]detecting uibutton pressed in tableview: Swift Best Practices

我有一個表格視圖,其中包含可變數量的單元格,代表與其特定教師相對應的學生。 它們是自定義單元格,帶有一個按鈕,可觸發新 VC 的轉場,顯示該單元格所在學生的詳細信息。 我的問題是:

快速識別按下哪個按鈕的最佳實踐是什么?

一旦我知道索引路徑,我就可以確定哪些學生的信息需要傳遞給下一個 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 更新

如果您只想在觸摸時觸發轉場,那么通過 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 中的任何單元格,這是一個完美的工作。

現在確實有更好的方法來處理這個問題。 這將適用於管理單元格和值

這是我的輸出(截圖)所以看看這個

這是我的代碼

  1. 創建模型類非常簡單,請按照以下步驟操作。 創建名為“RNCheckedModel”的swift類,編寫如下代碼。

類 RNCheckedModel: NSObject {

var is_check = false
var user_name = ""

}
  1. 創建你的細胞類

類邀請單元: UITableViewCell {

@IBOutlet var imgProfileImage: UIImageView!
@IBOutlet var btnCheck: UIButton!
@IBOutlet var lblName: UILabel!
@IBOutlet var lblEmail: UILabel!
}
  1. 最后在使用UITableView時在UIViewController 中使用模型類。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM