web-dev-qa-db-de.com

uibutton im TableView gedrückt: Swift Best Practices

Ich habe eine Tabellenansicht mit einer variablen Anzahl von Zellen, die Schüler repräsentieren, die ihrem jeweiligen Lehrer entsprechen. Es handelt sich dabei um benutzerdefinierte Zellen mit einer Schaltfläche, die einen Wechsel zu einem neuen virtuellen Computer auslöst und detaillierte Informationen über den Schüler anzeigt, dessen Zelle es war. Meine Frage ist:

Was ist die beste Methode in Swift, um festzustellen, welche Taste gedrückt wurde?

Sobald ich den Indexpfad kenne, kann ich feststellen, welche Informationen des Schülers an den nächsten VC weitergeleitet werden müssen. Im folgenden Beitrag gibt es eine gute Antwort für Objective C, aber ich bin nicht sicher, wie ich es in Swift übersetzen sollte. Jede Hilfe wäre sehr dankbar.

Ermitteln, welcher UIButton in einer UITableView gedrückt wurde

34
jamike

Wenn Ihr Code dies zulässt, würde ich empfehlen, dass Sie das Tag UIButton gleich dem Code indexPath.row setzen. Wenn also die Aktion ausgelöst wird, können Sie das Tag ziehen und somit während der ausgelösten Methode aus den Schaltflächendaten rudern. In cellForRowAtIndexPath können Sie beispielsweise das Tag setzen:

button.tag = indexPath.row
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)

dann können Sie in buttonClicked: das Tag und damit die Zeile abrufen:

func buttonClicked(sender:UIButton) {

    let buttonRow = sender.tag
}

Andernfalls, wenn dies aus irgendeinem Grund nicht förderlich für Ihren Code ist, können Sie die schnelle Übersetzung von dieser Objective-C-Antwort, auf die Sie verweisen, mit :

- (void)checkButtonTapped:(id)sender
{
    CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    if (indexPath != nil)
    {
     ...
    }
}

ist:

func checkButtonTapped(sender:AnyObject) {
      let buttonPosition = sender.convert(CGPoint.zero, to: self.tableView)
    let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
    if indexPath != nil {
        ...
    }
}
78
Lyndsey Scott

Swift 3.0-Lösung

cell.btnRequest.tag = indexPath.row

    cell.btnRequest.addTarget(self,action:#selector(buttonClicked(sender:)), for: .touchUpInside)

    func buttonClicked(sender:UIButton) {

            let buttonRow = sender.tag
        }
14
Sourabh Sharma

Aktualisiert für Swift 3

Wenn Sie nur einen Vorgang bei einer Berührung auslösen möchten, ist es gegen die beste Vorgehensweise, dies über ein UIButton-Gerät zu tun. Sie können einfach den integrierten UIKit-Handler verwenden, um eine Zelle auszuwählen, d. H. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath). Sie könnten es folgendermaßen implementieren:

Erstellen Sie eine benutzerdefinierte UITableViewCell

class StudentCell: UITableViewCell {
    // Declare properties you need for a student in a custom cell.
    var student: SuperSpecialStudentObject!

    // Other code here...
}

Wenn Sie Ihr UITableView laden, übergeben Sie die Daten aus Ihrem Datenmodell in die Zelle:

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
}

Verwenden Sie dann didSelectRow atIndexPath, um zu ermitteln, wann eine Zelle ausgewählt wurde, auf die Zelle und ihre Daten zuzugreifen und den Wert als Parameter an performSegue zu übergeben.

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)
    }
}

Und schließlich in 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
        }
    }
}

Wenn Sie möchten, dass sie nur einen Teil der Zelle auswählen und nicht, wenn sie sich irgendwo in der Zelle befinden, können Sie Ihrer Zelle einen Zubehörartikel hinzufügen, z it) und verwenden stattdessen override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath).

6
darksinge

Swift 3

@ cellForRowAt indexPath

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

Dann

func BtnAction(_ sender: Any)
    {

        let btn = sender as? UIButton

    }
2
Shibin k kurian

Eine andere mögliche Lösung wäre dispatch_block_t. Wenn Sie dies mit Storyboard tun, müssen Sie zuerst eine Membervariable in Ihrer benutzerdefinierten UITableViewCell-Klasse erstellen.

var tapBlock: dispatch_block_t?

Dann müssen Sie eine IBAction anlegen und die tapBlock aufrufen.

@IBAction func didTouchButton(sender: AnyObject) {
    if let tapBlock = self.tapBlock {
        tapBlock()
    }
}

In Ihrem View Controller mit der UITableView können Sie einfach auf die Schaltflächenereignisse wie folgt reagieren

let cell = tableView.dequeueReusableCellWithIdentifier("YourCellIdentifier", forIndexPath: indexPath) as! YourCustomTableViewCell

cell.tapBlock = {
   println("Button tapped")
}

Sie müssen jedoch beim Zugriff auf self im Block darauf achten, dass kein Haltezyklus erstellt wird. Stellen Sie sicher, dass Sie als [weak self] darauf zugreifen.

2
gpichler

Es ist nie eine gute Idee, Tags zu verwenden, um Zellen und Indexpfade zu identifizieren. Am Ende erhalten Sie einen falschen Indexpfad und folglich die falschen Zellen und Informationen. 

Ich schlage vor, Sie versuchen den Code unten (Arbeiten mit UICollectionView, haben es nicht mit TableView getestet, aber es wird wahrscheinlich gut funktionieren):

Swift 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

        }
    }
}
1

Als Folgemaßnahme zu den Kommentaren von @Lyndsey und @ longbow fiel mir auf, dass, als ich den Wechsel des Storyboards vom Button zum Ziel-VC hatte, das prepareForSegue aufgerufen wurde, bevor die Funktion buttonClicked die URLPath-Variable aktualisieren konnte. Um das Problem zu beheben, habe ich die Übergabe direkt von der ersten VC auf die Ziel-VC festgelegt und die Übergabe programmgesteuert ausführen lassen, nachdem der Code in buttonClicked ausgeführt wurde. Vielleicht nicht ideal, aber es scheint zu funktionieren.

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
    }
0
jamike

Ich wollte den indexPath-Ansatz verwenden, bis ich begriff, dass er in manchen Situationen unzuverlässig/falsch ist (z. B. gelöschte oder verschobene Zelle).

Was ich getan habe, ist einfacher. Zum Beispiel zeige ich eine Reihe von Farben und ihre RGB-Werte an - eine pro Tabellenzelle. Jede Farbe ist in einer Reihe von Farbstrukturen definiert. Zur Verdeutlichung sind dies:

struct ColorStruct {
    var colorname:String    = ""
    var red:   Int = 0
    var green: Int = 0
    var blue:  Int = 0
}

var colors:[ColorStruct] = []       // The color array

Meine Prototypzelle hat eine Variable, die den aktuellen Index/Schlüssel in meinem Array enthält:

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)")
    }
}

Diese Lösung umfasst drei Codezeilen, eine in der Prototypzellendefinition , die zweite in der Logik, die eine neue Zelle auffüllt, und die dritte in der IBAction-Funktion , die aufgerufen wird, wenn die Schaltfläche einer Zelle ist gedrückt. Da ich den "Schlüssel" (Index) für die Daten in jeder Zelle effektiv ausgeblendet habe, während ich die neue Zelle fülle, ist keine Berechnung erforderlich.

0
Vic W.

Ich mache es über 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
    }

und auf dem nächsten Controller lade ich einfach die vollständigen Elementeigenschaften neu, indem ich deren ID kenne und sie auf das var thisItem in DetailVC setze

0
longbow

Erkennen der Section und row for UiTableView indexPath beim Klicken der Schaltfläche 

//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")
        }

    }

Hier ist die UITableView-Zellenmethode, um das Targate hinzuzufügen 

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
    }
0
pansora abhay

Swift 5. In cellForRowAtIndexPath setzen Sie das Tag:

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

Dann holen Sie in shareBtnPressed das Tag

  @IBAction func shareBtnPressed(_ sender: UIButton) {

    let buttonRow = sender.tag

    print("Video Shared in row \(buttonRow)")
}
0
Team chang

Ich fand eine sehr einfache und sichere Methode, um jede Zelle in tableView und collectionView mithilfe einer Model-Klasse zu verwalten, und dies funktioniert genauso gut.

Es gibt in der Tat einen viel besseren Weg, jetzt damit umzugehen. Dies funktioniert für die Verwaltung von Zellen und Werten

hier ist meine Ausgabe (Screenshot) also siehe

hier ist mein Code

  1. Es ist sehr einfach, model clas zu erstellen. Befolgen Sie die nachstehenden Anweisungen. _. Erstellen Sie eine Swift-Klasse mit dem Namen "RNCheckedModel", und geben Sie den Code wie folgt ein.

klasse RNCheckedModel: NSObject {

var is_check = false
var user_name = ""

}
  1. erstellen Sie Ihre Zellklasse

klasse InviteCell: UITableViewCell {

@IBOutlet var imgProfileImage: UIImageView!
@IBOutlet var btnCheck: UIButton!
@IBOutlet var lblName: UILabel!
@IBOutlet var lblEmail: UILabel!
}
  1. und schließlich die Modellklasse in Ihrem UIViewController verwenden, wenn Sie Ihr UITableView verwenden.

klasse 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()

}

 }
0