web-dev-qa-db-ja.com

テーブルビューで押されたUibuttonの検出:Swift Best Practices

特定のインストラクタに対応する学生を表す可変数のセルを持つテーブルビューがあります。これらは、新しいVCへのセグエをトリガーするボタンを備えたカスタムセルであり、セルがあった学生に関する詳細情報を表示します。私の質問は:

Swiftどのボタンが押されたかを識別するためのベストプラクティスは何ですか?

インデックスパスがわかれば、次のVCに渡す必要がある学生の情報を特定できます。下記の投稿でObjective Cに対する素晴らしい答えがありますが、Swiftに翻訳する方法がわかりません。どんな助けでも大歓迎です。

ITableViewで押されたUIButtonの検出

39
jamike

コードで許可されている場合は、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
}

それ以外の場合、何らかの理由でコードに役立たない場合は、Swift このObjective-Cのリンク先)の翻訳

- (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 {
        ...
    }
}
81
Lyndsey Scott

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
        }
15
Sourabh Sharma

Swiftに更新

タッチでセグエをトリガーすることだけが必要な場合、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
        }
    }
}
_

または、セル内の任意の場所に触れるのではなく、セルの一部のみを選択する場合は、詳細アクセサリアイテムなどのアクセサリアイテムをセルに追加できます。 it)そして、代わりにoverride func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath)を使用します。

6
darksinge

スイフト3

@ cellForRowAt indexPath

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

それから

func BtnAction(_ sender: Any)
    {

        let btn = sender as? UIButton

    }
2
Shibin k kurian

別の可能な解決策は、dispatch_block_tを使用することです。 Storyboardでこれを行う場合、最初にカスタムUITableViewCellクラスにメンバー変数を作成する必要があります。

var tapBlock: dispatch_block_t?

次に、IBActionを作成し、tapBlockを呼び出す必要があります。

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

UITableViewを持つView Controllerで、このようなボタンイベントに単純に反応できます

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

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

ただし、ブロック内のselfにアクセスするときは、保持サイクルを作成しないように注意する必要があります。必ず[weak self]としてアクセスしてください。

2
gpichler

タグを使用してセルとindexPathを識別することは決して良い考えではありません。最終的に、誤ったindexPathになり、その結果、間違ったセルと情報になります。

以下のコードを試してみることをお勧めします(UICollectionViewでの作業、TableViewでのテストは行っていませんが、おそらく問題なく動作します)。

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

        }
    }
}
2

@Lyndseyと@longbowのコメントをフォローアップすると、ストーリーボードでセグエをボタンからdestinationVCに移動すると、buttonClicked関数がurlPath変数を更新する前にprepareForSegueが呼び出されていることに気付きました。これを解決するために、セグエを最初のVCからdestinationVCに直接設定し、buttonClickedのコードが実行された後にプログラムでセグエを実行しました。理想的ではないかもしれませんが、動作しているようです。

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

Sectionおよびrowの検出iTableViewクリック時の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メソッドがあります

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

状況によっては(たとえば、セルの削除や移動など)信頼性が低い/間違っていることを理解するまで、indexPathアプローチを使用していました。

私がしたことはもっと簡単です。例として、一連の色とそのRGB値(Tableviewセルごとに1つ)を表示しています。各色は、色構造の配列で定義されます。明確にするために、これらは次のとおりです。

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

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

プロトタイプセルには、実際のインデックス/キーを配列に保持する変数があります。

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

このソリューションは、3行のコードを取ります。1行はプロトタイプセル定義、2行目は新しいセルに入力するロジック、3行目はIBAction関数セルのボタンが押されたときに呼び出されます。新しいセルにデータを入力している各セルのデータの「キー」(インデックス)を事実上非表示にしているため、計算は不要です。また、セルを移動する場合、何も更新する必要はありません。

0
Vic W.

私は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に設定することにより、アイテムのプロパティ全体をリロードします。

0
longbow

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)")
}
0
Team chang

Modelクラスを使用してtableViewとcollectionViewのセルを管理するための非常に簡単で十分な方法を見つけました。これは完璧に機能します。

実際にこれを処理するより良い方法があります。これは、セルと値を管理するために機能します

ここに私の出力(スクリーンショット)があるのでこれを参照してください

こちらが私のコードです

  1. モデルclasを作成するのは非常に簡単です。以下の手順に従ってください。 Swift "RNCheckedModel"という名前のクラスを作成し、以下のようにコードを記述します。

クラスRNCheckedModel:NSObject {

var is_check = false
var user_name = ""

}
  1. セルクラスを作成する

クラスInviteCell:UITableViewCell {

@IBOutlet var imgProfileImage: UIImageView!
@IBOutlet var btnCheck: UIButton!
@IBOutlet var lblName: UILabel!
@IBOutlet var lblEmail: UILabel!
}
  1. 最後に、UIViewControllerUITableViewを使用するときにモデルクラスを使用します。

クラス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