【问题标题】:Detecting which UIButton was pressed in a UITableView检测在 UITableView 中按下了哪个 UIButton
【发布时间】:2010-12-20 15:16:54
【问题描述】:

我有一个UITableView 和 5 个UITableViewCells。每个单元格包含一个UIButton,其设置如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
     NSString *identifier = @"identifier";
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
         [cell autorelelase];

         UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
         [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
         [button setTag:1];
         [cell.contentView addSubview:button];

         [button release];
     }

     UIButton *button = (UIButton *)[cell viewWithTag:1];
     [button setTitle:@"Edit" forState:UIControlStateNormal];

     return cell;
}

我的问题是:在buttonPressedAction: 方法中,我如何知道按下了哪个按钮。我考虑过使用标签,但我不确定这是最好的路线。我希望能够以某种方式将indexPath 标记到控件上。

- (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;
    // how do I know which button sent this message?
    // processing button press for this row requires an indexPath. 
}

这样做的标准方法是什么?

编辑:

我已经通过执行以下操作解决了这个问题。我仍然想知道这是标准的做法还是有更好的方法?

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
     NSString *identifier = @"identifier";
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
         [cell autorelelase];

         UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
         [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
         [cell.contentView addSubview:button];

         [button release];
     }

     UIButton *button = (UIButton *)[cell.contentView.subviews objectAtIndex:0];
     [button setTag:indexPath.row];
     [button setTitle:@"Edit" forState:UIControlStateNormal];

     return cell;
}

- (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;
    int row = button.tag;
}

需要注意的重要一点是,我无法在创建单元格时设置标记,因为单元格可能会被出列。感觉很脏。一定有更好的办法。

【问题讨论】:

  • 我认为使用您的标签解决方案没有任何问题。单元格被重复使用,因此按照您在此处执行的方式将标记设置为行索引是有意义的。我发现这是一个比将触摸位置转换为行索引更优雅的解决方案,如下所示。

标签: ios iphone uitableview uibutton


【解决方案1】:
func buttonAction(sender:UIButton!)
    {
        var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tablevw)
        let indexPath = self.tablevw.indexPathForRowAtPoint(position)
        let cell: TableViewCell = tablevw.cellForRowAtIndexPath(indexPath!) as TableViewCell
        println(indexPath?.row)
        println("Button tapped")
    }

【讨论】:

    【解决方案2】:

    在别处找到了解决这个问题的好方法,不要乱用按钮上的标签:

    - (void)buttonPressedAction:(id)sender {
    
        NSSet *touches = [event allTouches];
        UITouch *touch = [touches anyObject];
        CGPoint currentTouchPosition = [touch locationInView:self.tableView];
        NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];
    
        // do stuff with the indexPath...
    }
    

    【讨论】:

    • 在此示例中不清楚您从何处获取“事件”对象。
    • 这是我采用的解决方案。在添加/删除行时使用标签是不可预测的,因为它们的索引会发生变化。还有,
    • @NickLudlam:可能方法名不是buttonPressedAction:,而是buttonPressedAction:forEvent:
    【解决方案3】:

    这就是我的做法。简洁明了:

    - (IBAction)buttonTappedAction:(id)sender
    {
        CGPoint buttonPosition = [sender convertPoint:CGPointZero
                                               toView:self.tableView];
        NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
        ...
    }
    

    【讨论】:

    • 更简单:使用CGPointZero 而不是CGPointMake(0, 0) ;-)
    • 易于使用。此外,很容易将其翻译成 Swift 3。你是最棒的 :)
    • 把它翻译成下面的 Swift。我能找到的最简单的解决方案。谢谢克里斯!
    【解决方案4】:

    使用 Swift 4.2 和 iOS 12,您可以选择 5 以下完整示例之一来解决您的问题。


    #1。使用UIViewconvert(_:to:)UITableViewindexPathForRow(at:)

    import UIKit
    
    private class CustomCell: UITableViewCell {
    
        let button = UIButton(type: .system)
    
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
    
            button.setTitle("Tap", for: .normal)
            contentView.addSubview(button)
    
            button.translatesAutoresizingMaskIntoConstraints = false
            button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
            button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
            button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
            button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
    }
    
    import UIKit
    
    class TableViewController: UITableViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
        }
    
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 3
        }
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
            cell.button.addTarget(self, action: #selector(customCellButtonTapped), for: .touchUpInside)
            return cell
        }
    
        @objc func customCellButtonTapped(_ sender: UIButton) {
            let point = sender.convert(CGPoint.zero, to: tableView)
            guard let indexPath = tableView.indexPathForRow(at: point) else { return }
            print(indexPath)
        }
    
    }
    

    #2。使用UIViewconvert(_:to:)UITableViewindexPathForRow(at:)(替代)

    这是上一个示例的替代方案,我们将nil 传递给addTarget(_:action:for:) 中的target 参数。这样,如果第一个响应者没有实现该动作,它将被发送到响应者链中的下一个响应者,直到找到正确的实现为止。

    import UIKit
    
    private class CustomCell: UITableViewCell {
    
        let button = UIButton(type: .system)
    
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
    
            button.setTitle("Tap", for: .normal)
            button.addTarget(nil, action: #selector(TableViewController.customCellButtonTapped), for: .touchUpInside)
            contentView.addSubview(button)
    
            button.translatesAutoresizingMaskIntoConstraints = false
            button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
            button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
            button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
            button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
    }
    
    import UIKit
    
    class TableViewController: UITableViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
        }
    
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 3
        }
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
            return cell
        }
    
        @objc func customCellButtonTapped(_ sender: UIButton) {
            let point = sender.convert(CGPoint.zero, to: tableView)
            guard let indexPath = tableView.indexPathForRow(at: point) else { return }
            print(indexPath)
        }
    
    }
    

    #3。使用UITableViewindexPath(for:) 和委托模式

    在本例中,我们将视图控制器设置为单元格的委托。当单元格的按钮被点击时,它会触发对代理的适当方法的调用。

    import UIKit
    
    protocol CustomCellDelegate: AnyObject {
        func customCellButtonTapped(_ customCell: CustomCell)
    }
    
    class CustomCell: UITableViewCell {
    
        let button = UIButton(type: .system)
        weak var delegate: CustomCellDelegate?
    
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
    
            button.setTitle("Tap", for: .normal)
            button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
            contentView.addSubview(button)
    
            button.translatesAutoresizingMaskIntoConstraints = false
            button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
            button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
            button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
            button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        @objc func buttonTapped(sender: UIButton) {
            delegate?.customCellButtonTapped(self)
        }
    
    }
    
    import UIKit
    
    class TableViewController: UITableViewController, CustomCellDelegate {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
        }
    
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 3
        }
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
            cell.delegate = self
            return cell
        }
    
        // MARK: - CustomCellDelegate
    
        func customCellButtonTapped(_ customCell: CustomCell) {
            guard let indexPath = tableView.indexPath(for: customCell) else { return }
            print(indexPath)
        }
    
    }
    

    #4。使用UITableViewindexPath(for:) 和委托闭包

    这是上一个示例的替代方案,我们使用闭包而不是协议委托声明来处理按钮点击。

    import UIKit
    
    class CustomCell: UITableViewCell {
    
        let button = UIButton(type: .system)
        var buttontappedClosure: ((CustomCell) -> Void)?
    
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
    
            button.setTitle("Tap", for: .normal)
            button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
            contentView.addSubview(button)
    
            button.translatesAutoresizingMaskIntoConstraints = false
            button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
            button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
            button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
            button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        @objc func buttonTapped(sender: UIButton) {
            buttontappedClosure?(self)
        }
    
    }
    
    import UIKit
    
    class TableViewController: UITableViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
        }
    
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 3
        }
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
            cell.buttontappedClosure = { [weak tableView] cell in
                guard let indexPath = tableView?.indexPath(for: cell) else { return }
                print(indexPath)
            }
            return cell
        }
    
    }
    

    #5。使用UITableViewCellaccessoryTypeUITableViewDelegatetableView(_:accessoryButtonTappedForRowWith:)

    如果您的按钮是UITableViewCell 的标准附件控件,任何点击它都会触发对UITableViewDelegatetableView(_:accessoryButtonTappedForRowWith:) 的调用,从而可以获取相关的索引路径。

    import UIKit
    
    private class CustomCell: UITableViewCell {
    
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            accessoryType = .detailButton
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
    }
    
    import UIKit
    
    class TableViewController: UITableViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
        }
    
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 3
        }
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
            return cell
        }
    
        override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
            print(indexPath)
        }
    
    }
    

    【讨论】:

      【解决方案5】:

      我使用子类UIButton 的解决方案,我想我应该在这里分享它,Swift 中的代码:

      class ButtonWithIndexPath : UIButton {
          var indexPath:IndexPath?
      }
      

      然后记得在cellForRow(at:)更新它的indexPath

      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      
          let returnCell = tableView.dequeueReusableCell(withIdentifier: "cellWithButton", for: indexPath) as! cellWithButton
          ...
          returnCell.button.indexPath = IndexPath
          returnCell.button.addTarget(self, action:#selector(cellButtonPressed(_:)), for: .touchUpInside)
      
          return returnCell
      }
      

      所以当响应按钮的事件时,你可以像这样使用它

      func cellButtonPressed(_ sender:UIButton) {
          if sender is ButtonWithIndexPath {
              let button = sender as! ButtonWithIndexPath
              print(button.indexPath)
          }
      }
      

      【讨论】:

        【解决方案6】:

        这个问题有两个部分:

        1)获取UITableViewCell的索引路径,其中包含pressed UIButton

        有一些建议如下:

        • 使用索引路径的row 值在cellForRowAtIndexPath: 方法中更新UIButtontag。这不是一个好的解决方案,因为它需要不断更新tag,并且它不适用于具有多个部分的表视图。

        • NSIndexPath 属性添加到自定义单元格并更新它,而不是cellForRowAtIndexPath: 方法中的UIButtontag。这解决了多个部分的问题,但仍然不好,因为它需要始终更新。

        • 在创建自定义单元格并使用indexPathForCell: 方法获取索引路径时,在自定义单元格中保持对父级UITableView 的弱引用。好像好一点,不需要在cellForRowAtIndexPath: 方法中更新任何东西,但仍然需要在创建自定义单元格时设置弱引用。

        • 使用单元格的superView 属性获取对父级UITableView 的引用。无需向自定义单元添加任何属性,也无需在创建/以后设置/更新任何内容。但是 cell 的 superView 取决于 iOS 的实现细节。所以不能直接使用。

        但这可以通过一个简单的循环来实现,因为我们确定有问题的单元格必须在 UITableView 中:

        UIView* view = self;
        while (view && ![view isKindOfClass:UITableView.class])
            view = view.superview;
        UITableView* parentTableView = (UITableView*)view;
        

        所以,这些建议可以组合成一个简单安全的自定义单元格方法来获取索引路径:

        - (NSIndexPath *)indexPath
        {
            UIView* view = self;
        
            while (view && ![view isKindOfClass:UITableView.class])
                view = view.superview;
        
            return [(UITableView*)view indexPathForCell:self];
        }
        

        从现在开始,这个方法可以用来检测哪个UIButton被按下了。

        2) 通知其他方按钮按下事件

        在内部知道哪个UIButton 被按到具有精确索引路径的自定义单元格中之后,需要将此信息发送给其他方(很可能是处理UITableView 的视图控制器)。所以,这个按钮点击事件可以在类似于 UITableView 委托的didSelectRowAtIndexPath: 方法的抽象和逻辑级别上处理。

        可以使用两种方法:

        a) 委托: 自定义单元格可以具有delegate 属性并且可以定义协议。当按下按钮时,它只是在delegate 属性上执行它的委托方法。但是这个delegate 属性需要在创建时为每个自定义单元格设置。作为替代方案,自定义单元格也可以选择在其父表视图的delegate 上执行其委托方法。

        b) 通知中心: 自定义单元格可以定义自定义通知名称,并使用userInfo 对象中提供的索引路径和父表视图信息发布此通知。无需为每个单元格设置任何内容,只需为自定义单元格的通知添加观察者即可。

        【讨论】:

          【解决方案7】:

          Chris Schwerdt 的解决方案,但后来在 Swift 中为我工作:

          @IBAction func rateButtonTapped(sender: UIButton) {
              let buttonPosition : CGPoint = sender.convertPoint(CGPointZero, toView: self.ratingTableView)
              let indexPath : NSIndexPath = self.ratingTableView.indexPathForRowAtPoint(buttonPosition)!
          
              print(sender.tag)
              print(indexPath.row)
          }
          

          【讨论】:

            【解决方案8】:

            To do (@Vladimir) 的回答是 Swift:

            var buttonPosition = sender.convertPoint(CGPointZero, toView: self.tableView)
            var indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)!
            

            虽然检查 indexPath != nil 给了我指点...“NSIndexPath 不是 NSString 的子类型”

            【讨论】:

              【解决方案9】:

              注意这里我使用的是自定义单元格,这段代码对我来说非常有效

               @IBAction func call(sender: UIButton)
                  {
                      var contentView = sender.superview;
                      var cell = contentView?.superview as EmployeeListCustomCell
                      if (!(cell.isKindOfClass(EmployeeListCustomCell)))
                      {
                          cell = (contentView?.superview)?.superview as EmployeeListCustomCell
                      }
              
                      let phone = cell.lblDescriptionText.text!
                      //let phone = detailObject!.mobile!
                      let url:NSURL = NSURL(string:"tel://"+phone)!;
                      UIApplication.sharedApplication().openURL(url);
                  }
              

              【讨论】:

                【解决方案10】:

                SWIFT 2 更新

                以下是如何找出点击了哪个按钮 + 从该按钮的 indexPath.row 向另一个 ViewController 发送数据,因为我认为这对大多数人来说都是重点!

                @IBAction func yourButton(sender: AnyObject) {
                
                
                     var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
                        let indexPath = self.tableView.indexPathForRowAtPoint(position)
                        let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as
                        UITableViewCell
                        print(indexPath?.row)
                        print("Tap tap tap tap")
                
                    }
                

                对于那些使用 ViewController 类并添加了 tableView 的人,我使用的是 ViewController 而不是 TableViewController,因此我手动添加了 tableView 以便访问它。

                这是在点击该按钮并传递 cell 的 indexPath.row 时将数据传递给另一个 VC 的代码

                @IBAction func moreInfo(sender: AnyObject) {
                
                    let yourOtherVC = self.storyboard!.instantiateViewControllerWithIdentifier("yourOtherVC") as! YourOtherVCVIewController
                
                
                
                    var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
                    let indexPath = self.tableView.indexPathForRowAtPoint(position)
                    let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as
                    UITableViewCell
                    print(indexPath?.row)
                    print("Button tapped")
                
                
                    yourOtherVC.yourVarName = [self.otherVCVariable[indexPath!.row]]
                
                    self.presentViewController(yourNewVC, animated: true, completion: nil)
                
                }
                

                【讨论】:

                  【解决方案11】:
                  A better way would be to subclass your button and add a indexPath property to it.
                  
                  //Implement a subclass for UIButton.
                  
                  @interface NewButton:UIButton
                  @property(nonatomic, strong) NSIndexPath *indexPath;
                  
                  
                  Make your button of type NewButton in the XIB or in the code whereever you are initializing them.
                  
                  Then in the cellForRowAtIndexPath put the following line of code.
                  
                  button.indexPath = indexPath;
                  
                  return cell; //As usual
                  
                  
                  
                  Now in your IBAction
                  
                  -(IBAction)buttonClicked:(id)sender{
                     NewButton *button = (NewButton *)sender;
                  
                  //Now access the indexPath by buttons property..
                  
                     NSIndexPath *indexPath = button.indexPath; //:)
                  }
                  

                  【讨论】:

                  • 这有点错误,因为如果调用 deleteRowsAtIndexPaths,单元格的 indexPath 可能会发生变化。
                  • deleteRowsAtIndexPaths 将导致 cellForRowAtIndexPath 再次被调用。然后按钮将具有新的正确 indexPaths。
                  【解决方案12】:

                  子类化按钮以存储所需的值,可能创建一个协议(ControlWithData 或其他东西)。在将按钮添加到表格视图单元格时设置值。在您的修饰事件中,查看发送者是否遵守协议并提取数据。我通常存储对在表格视图单元格上呈现的实际对象的引用。

                  【讨论】:

                    【解决方案13】:

                    这很简单;制作一个自定义单元格并取出按钮

                    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
                        {
                             NSString *identifier = @"identifier";
                            customCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
                    
                        cell.yourButton.tag = indexPath.Row;
                    
                    - (void)buttonPressedAction:(id)sender
                    

                    将上述方法中的id改为(UIButton *)

                    你可以通过 sender.tag 获得点击哪个按钮的值。

                    【讨论】:

                      【解决方案14】:

                      我总是使用标签。

                      您需要继承 UITableviewCell 并从那里处理按钮按下。

                      【讨论】:

                      • 我不太明白怎么做。 tag 属性是在单元格创建期间设置的 - 此单元格可重复用于具有相同标识符的每一行。此标记特定于通用可重用单元格中的控件。如何使用此标签来区分以通用方式创建的单元格中的按钮?你能发布一些代码吗?
                      【解决方案15】:

                      虽然我喜欢标签方式...如果您出于任何原因不想使用标签, 你可以为预制按钮创建一个成员NSArray

                      NSArray* buttons ;
                      

                      然后在渲染 tableView 之前创建这些按钮并将它们推送到数组中。

                      然后你可以在tableView:cellForRowAtIndexPath: 函数内部做:

                      UIButton* button = [buttons objectAtIndex:[indexPath row] ] ;
                      [cell.contentView addSubview:button];
                      

                      然后在buttonPressedAction:函数中,你可以这样做

                      - (void)buttonPressedAction:(id)sender {
                         UIButton* button = (UIButton*)sender ;
                         int row = [buttons indexOfObject:button] ;
                         // Do magic
                      }
                      

                      【讨论】:

                        【解决方案16】:

                        如何使用运行时注入在UIButton 中发送NSIndexPath 之类的信息。

                        1) 导入时需要运行时

                        2) 添加静态常量

                        3) 在运行时将NSIndexPath 添加到您的按钮:

                        (void)setMetaData:(id)target withObject:(id)newObj

                        4) 按下按钮获取元数据:

                        (id)metaData:(id)目标

                        享受

                            #import <objc/runtime.h>
                            static char const * const kMetaDic = "kMetaDic";
                        
                        
                            #pragma mark - Getters / Setters
                        
                        - (id)metaData:(id)target {
                            return objc_getAssociatedObject(target, kMetaDic);
                        }
                        
                        - (void)setMetaData:(id)target withObject:(id)newObj {
                            objc_setAssociatedObject(target, kMetaDic, newObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
                        }
                        
                        
                        
                            #On the cell constructor
                            - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
                            {
                            ....
                            cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
                            ....
                            [btnSocial addTarget:self
                                                           action:@selector(openComments:)
                                                 forControlEvents:UIControlEventTouchUpInside];
                        
                            #add the indexpath here or another object
                            [self setMetaData:btnSocial withObject:indexPath];
                        
                            ....
                            }
                        
                        
                        
                            #The action after button been press:
                        
                            - (IBAction)openComments:(UIButton*)sender{
                        
                                NSIndexPath *indexPath = [self metaData:sender];
                                NSLog(@"indexPath: %d", indexPath.row);
                        
                                //Reuse your indexpath Now
                            }
                        

                        【讨论】:

                        • 如果表格被重新排列或删除了一行,那么这将不起作用。
                        【解决方案17】:

                        在 Apple 的 Accessory 示例中,使用了以下方法:

                        [button addTarget:self action:@selector(checkButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
                        

                        然后在触摸处理程序中检索触摸坐标并根据该坐标计算索引路径:

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

                        【讨论】:

                        • 是的,这就是我决定的(见我的编辑)。我同意你的观点,这不是最优的。
                        • 但是您自己将 UIButton 添加到 UITableViewCell,因此您必须与创建单元格时的操作保持一致。虽然这种方法看起来并不优雅,但我不得不承认
                        • 对于第一个解决方案,您需要获取 [[button superview] superview],因为第一个 superview 调用将为您提供 contentView,最后第二个将为您提供 UITableViewCell。如果您要添加/删除单元格,则第二种解决方案效果不佳,因为它会使行索引无效。因此,我采用了概述的第一个解决方案,并且效果很好。
                        • 这将可靠地挑选出拥有按钮的单元格: UIView *view = button; while (![view isKindOfClass:[UITableViewCell class]]){ view = [view superview]}
                        • 使用时有一个陷阱:[button addTarget:self action:@selector(checkButtonTapped:) forControlEvents:UIControlEventTouchUpInside];因为 addTarget:action:forControlEvents: 会在你滚动表格时添加多个重复的目标和动作,它不会删除之前的目标和动作,所以方法 checkButtonTapped: 会在你点击按钮时被多次调用。在添加目标和操作之前,您最好删除它们
                        【解决方案18】:

                        处理部分 - 我将 NSIndexPath 存储在自定义 UITableViewCell 中

                        IN CLKIndexPricesHEADERTableViewCell.xib

                        在 IB 中将 UIButton 添加到 XIB - 不要添加操作!

                        添加出口@property (retain, nonatomic) IBOutlet UIButton *buttonIndexSectionClose;

                        不要在 IB 中按 CTRL+拖动操作(在下面的代码中完成)

                        @interface CLKIndexPricesHEADERTableViewCell : UITableViewCell
                        ...
                        @property (retain, nonatomic) IBOutlet UIButton *buttonIndexSectionClose;
                        @property (nonatomic, retain) NSIndexPath * indexPathForCell;
                        @end
                        

                        在 viewForHeaderInSection 中(如果您的表格只有 1 个部分,也应该适用于 cellForRow.... 等)

                        - viewForHeaderInSection is called for each section 1...2...3
                        - get the cell CLKIndexPricesHEADERTableViewCell 
                        - getTableRowHEADER just does the normal dequeueReusableCellWithIdentifier
                        - STORE the indexPath IN the UITableView cell
                        - indexPath.section = (NSInteger)section
                        - indexPath.row = 0 always (we are only interested in sections)
                        
                        - (UIView *) tableView:(UITableView *)tableView1 viewForHeaderInSection:(NSInteger)section {
                        
                        
                            //Standard method for getting a UITableViewCell
                            CLKIndexPricesHEADERTableViewCell * cellHEADER = [self getTableRowHEADER];
                        

                        ...使用该部分获取单元格的数据

                        ...填写

                           indexName        = ffaIndex.routeCode;
                           indexPrice       = ffaIndex.indexValue;
                        
                           //
                        
                           [cellHEADER.buttonIndexSectionClose addTarget:self
                                                                  action:@selector(buttonDELETEINDEXPressedAction:forEvent:)
                                                        forControlEvents:UIControlEventTouchUpInside];
                        
                        
                           cellHEADER.indexPathForCell = [NSIndexPath indexPathForRow:0 inSection:section];
                        
                        
                            return cellHEADER;
                        }
                        

                        USER 在 Section 标题上按下 DELETE 按钮,这会调用

                        - (void)buttonDELETEINDEXPressedAction:(id)sender forEvent:(UIEvent *)event
                        {
                            NSLog(@"%s", __PRETTY_FUNCTION__);
                        
                        
                            UIView *  parent1 = [sender superview];   // UiTableViewCellContentView
                            //UIView *myContentView = (UIView *)parent1;
                        
                            UIView *  parent2 = [parent1 superview];  // custom cell containing the content view
                            //UIView *  parent3 = [parent2 superview];  // UITableView containing the cell
                            //UIView *  parent4 = [parent3 superview];  // UIView containing the table
                        
                        
                            if([parent2 isMemberOfClass:[CLKIndexPricesHEADERTableViewCell class]]){
                                CLKIndexPricesHEADERTableViewCell *myTableCell = (CLKIndexPricesHEADERTableViewCell *)parent2;
                        
                                //UITableView *myTable = (UITableView *)parent3;
                                //UIView *mainView = (UIView *)parent4;
                        
                                NSLog(@"%s indexPath.section,row[%d,%d]", __PRETTY_FUNCTION__, myTableCell.indexPathForCell.section,myTableCell.indexPathForCell.row);
                        
                                NSString *key = [self.sortedKeysArray objectAtIndex:myTableCell.indexPathForCell.section];
                                if(key){
                                    NSLog(@"%s DELETE object at key:%@", __PRETTY_FUNCTION__,key);
                                    self.keyForSectionIndexToDelete = key;
                                    self.sectionIndexToDelete = myTableCell.indexPathForCell.section;
                        
                                    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Remove Index"
                                                                                        message:@"Are you sure"
                                                                                       delegate:self
                                                                              cancelButtonTitle:@"No"
                                                                              otherButtonTitles:@"Yes", nil];
                                    alertView.tag = kALERTVIEW_REMOVE_ONE_INDEX;
                                    [alertView show];
                                    [alertView release];
                                    //------
                                }else{
                                    NSLog(@"ERROR: [%s] key is nil for section:%d", __PRETTY_FUNCTION__,myTableCell.indexPathForCell.section);
                                }
                        
                            }else{
                                NSLog(@"ERROR: [%s] CLKIndexPricesHEADERTableViewCell not found", __PRETTY_FUNCTION__);
                            }
                        }
                        

                        在这个例子中,我添加了一个删除按钮,所以应该显示 UIAlertView 来确认它

                        我将节和密钥存储到字典中,该字典将有关节的信息存储在 VC 中的 ivar 中

                        - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
                           if(alertView.tag == kALERTVIEW_REMOVE_ONE_INDEX){
                                if(buttonIndex==0){
                                    //NO
                                    NSLog(@"[%s] BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
                                    //do nothing
                                }
                                else if(buttonIndex==1){
                                    //YES
                                    NSLog(@"[%s] BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
                                    if(self.keyForSectionIndexToDelete != nil){
                        
                                        //Remove the section by key
                                        [self.indexPricesDictionary removeObjectForKey:self.keyForSectionIndexToDelete];
                        
                                        //sort the keys so sections appear alphabetically/numbericsearch (minus the one we just removed)
                                        [self updateTheSortedKeysArray];                
                        
                                        //Delete the section from the table using animation
                                        [self.tableView beginUpdates];
                        
                                        [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:self.sectionIndexToDelete]
                                                      withRowAnimation:UITableViewRowAnimationAutomatic];
                                        [self.tableView endUpdates];
                        
                                        //required to trigger refresh of myTableCell.indexPathForCell else old values in UITableViewCells
                                        [self.tableView reloadData];
                                    }else{
                                        NSLog(@"ERROR: [%s] OBJECT is nil", __PRETTY_FUNCTION__);
                                    }
                                }
                                else {
                                    NSLog(@"ERROR: [%s] UNHANDLED BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
                                }
                            }else {
                                NSLog(@"ERROR: [%s] unhandled ALERTVIEW TAG:%d", __PRETTY_FUNCTION__,alertView.tag);
                            }
                        }
                        

                        【讨论】:

                          【解决方案19】:

                          当按钮位于表格的页脚时(这会阻止您找到“点击的单元格”),Cocoanuts 的答案(帮助我解决了这个问题)略有不同:

                          -(IBAction) buttonAction:(id)sender;
                          {
                              id parent1 = [sender superview];   // UiTableViewCellContentView
                              id parent2 = [parent1 superview];  // custom cell containing the content view
                              id parent3 = [parent2 superview];  // UITableView containing the cell
                              id parent4 = [parent3 superview];  // UIView containing the table
                          
                              UIView *myContentView = (UIView *)parent1;
                              UITableViewCell *myTableCell = (UITableViewCell *)parent2;
                              UITableView *myTable = (UITableView *)parent3;
                              UIView *mainView = (UIView *)parent4;
                          
                              CGRect footerViewRect = myTableCell.frame;
                              CGRect rect3 = [myTable convertRect:footerViewRect toView:mainView];    
                          
                              [cc doSomethingOnScreenAtY:rect3.origin.y];
                          }
                          

                          【讨论】:

                            【解决方案20】:

                            它也适用于我,谢谢@Cocoanut

                            我发现使用 superview 的 superview 获取对单元格 indexPath 的引用的方法效果很好。感谢 iphonedevbook.com (macnsmith) 提供的提示链接文本

                            -(void)buttonPressed:(id)sender {
                             UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
                             NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
                            ...
                            
                            }
                            

                            【讨论】:

                              【解决方案21】:

                              创建一个 nsmutable 数组并将所有按钮放入该数组中 usint[array addObject:yourButton];

                              在按钮按下方法中

                              -

                               (void)buttonPressedAction:(id)sender
                              {
                                  UIButton *button = (UIButton *)sender;
                              
                              for(int i=0;i<[yourArray count];i++){
                              
                              if([buton isEqual:[yourArray objectAtIndex:i]]){
                              
                              //here write wat u need to do
                              
                              }
                              }
                              

                              【讨论】:

                                【解决方案22】:
                                // how do I know which button sent this message?
                                // processing button press for this row requires an indexPath.
                                

                                其实很简单:

                                - (void)buttonPressedAction:(id)sender
                                {
                                    UIButton *button = (UIButton *)sender;
                                    CGPoint rowButtonCenterInTableView = [[rowButton superview] convertPoint:rowButton.center toView:self.tableView];
                                    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:rowButtonCenterInTableView];
                                    MyTableViewItem *rowItem = [self.itemsArray objectAtIndex:indexPath.row];
                                    // Now you're good to go.. do what the intention of the button is, but with
                                    // the context of the "row item" that the button belongs to
                                    [self performFooWithItem:rowItem];
                                }
                                

                                对我来说工作得很好:P

                                如果你想调整你的目标动作设置,你可以在方法中包含事件参数,然后使用该事件的触摸来解析触摸的坐标。坐标仍然需要在触摸视图边界中解析,但这对某些人来说似乎更容易。

                                【讨论】:

                                  【解决方案23】:

                                  我错过了什么吗?你不能只使用发件人来识别按钮。发件人会给你这样的信息:

                                  <UIButton: 0x4b95c10; frame = (246 26; 30 30); opaque = NO; tag = 104; layer = <CALayer: 0x4b95be0>>
                                  

                                  然后如果你想改变按钮的属性,说你刚刚告诉发件人的背景图片:

                                  [sender setBackgroundImage:[UIImage imageNamed:@"new-image.png"] forState:UIControlStateNormal];
                                  

                                  如果你需要标签,那么 ACBurk 的方法就可以了。

                                  【讨论】:

                                  • 他们正在寻找与按钮相关的“对象”
                                  【解决方案24】:

                                  我发现使用 superview 的 superview 获取对单元格 indexPath 的引用的方法效果很好。感谢 iphonedevbook.com (macnsmith) 的提示link text

                                  -(void)buttonPressed:(id)sender {
                                   UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
                                   NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
                                  ...
                                  
                                  }
                                  

                                  【讨论】:

                                  • Cocoanut,你的代码片段为我在这个问题上的变化指明了正确的方向。谢谢!如果其他人需要它,我的特殊情况是该按钮位于作为页脚的一部分显示的自定义单元格中。我将在下面添加代码
                                  • 如果您(Stackoverflow 阅读器)尝试此操作但对您不起作用,请检查在您的实现中您的 UIButton 是否实际上是您的 UITableViewCell 的孙子。在我的实现中,我的 UIButton 是我的 UITableViewCell 的直接子代,所以我需要在 Cocoanut 的代码中取出一个“superview”,然后它才能工作。
                                  • 这是非常非常错误的,并且在较新版本的操作系统中被破坏。不要走你不拥有的超级视图树。
                                  • 这在 iOS 6 下为我工作,但在 iOS 7 中被破坏。看来@KenrikMarch 有一个有效的观点!
                                  • 在 iOS 7 中,它是超级视图的 1 步。例如[[[sender superview] superview] superView];
                                  【解决方案25】:

                                  你可以使用标签模式:

                                  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
                                  {
                                       NSString *identifier = @"identifier";
                                       UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
                                       if (cell == nil) {
                                           cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
                                           [cell autorelelase];
                                  
                                           UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
                                           [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
                                           [button setTag:[indexPath row]]; //use the row as the current tag
                                           [cell.contentView addSubview:button];
                                  
                                           [button release];
                                       }
                                  
                                       UIButton *button = (UIButton *)[cell viewWithTag:[indexPath row]]; //use [indexPath row]
                                       [button setTitle:@"Edit" forState:UIControlStateNormal];
                                  
                                       return cell;
                                  }
                                  
                                  - (void)buttonPressedAction:(id)sender
                                  {
                                      UIButton *button = (UIButton *)sender;
                                      //button.tag has the row number (you can convert it to indexPath)
                                  }
                                  

                                  【讨论】:

                                  • 如果我在一个单元格上有多个控件,我将如何标记这些控件?
                                  • 我不确定这是否可行 - 如果为第 1 行创建单元格,那么它将获得标签 1。如果它为第 3 行出队,那么它仍然会有一个标签1,而不是 3。
                                  • 猜你对第二条评论是对的。我的错。我认为您最好的解决方案是继承 UIButton,添加另一个或两个您自己的属性,然后在适当的情况下设置/获取它们(坚持使用代码中的标签:1)
                                  【解决方案26】:

                                  我会像你说的那样使用标签属性,像这样设置标签:

                                  [button setTag:indexPath.row];
                                  

                                  然后像这样在 buttonPressedAction 中获取标签:

                                  ((UIButton *)sender).tag
                                  

                                  或者

                                  UIButton *button = (UIButton *)sender; 
                                  button.tag;
                                  

                                  【讨论】:

                                  • 这种方法对于有节的表来说完全不适用。
                                  • 不,您也可以使用一些简单的函数将部分放入标签中。
                                  • tag 是一个整数。将索引路径编码/解码为视图标签似乎有点笨拙。
                                  • 没错,但它是一种解决方案,但如果我有部分,我不会使用它。我想说的是,它可以使用这种方法来完成,它没有被破坏。更好、更复杂的版本将根据 UITableView 内按钮的位置确定索引路径。然而,由于 rein 说他只有五个单元格(没有部分),这可能会使该方法过于复杂,并且您的初始评论和整个评论线程毫无意义。
                                  猜你喜欢
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2016-01-31
                                  • 2020-12-14
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  相关资源
                                  最近更新 更多