Phát hiện UIButton nào đã được nhấn trong UITableView


212

Tôi có một UITableViewvới 5 UITableViewCells. Mỗi ô chứa một ô UIButtonđược thiết lập như sau:

- (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;
}

Câu hỏi của tôi là thế này: trong buttonPressedAction:phương thức, làm thế nào để tôi biết nút nào đã được nhấn. Tôi đã cân nhắc sử dụng thẻ nhưng tôi không chắc đây là tuyến tốt nhất. Tôi muốn có thể bằng cách nào đó gắn thẻ indexPathlên điều khiển.

- (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. 
}

Cách tiêu chuẩn để làm điều này là gì?

Biên tập:

Tôi đã giải quyết nó bằng cách làm như sau. Tôi vẫn muốn có một ý kiến ​​cho dù đây là cách làm tiêu chuẩn hay có cách nào tốt hơn?

- (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;
}

Điều quan trọng cần lưu ý là tôi không thể đặt thẻ trong quá trình tạo ô vì thay vào đó, ô có thể bị mất hiệu lực. Nó cảm thấy rất bẩn. Phải có cách tốt hơn.


Tôi không thấy bất kỳ vấn đề với việc sử dụng giải pháp thẻ của bạn. Các ô được sử dụng lại, vì vậy sẽ rất hợp lý khi đặt thẻ thành chỉ mục hàng theo cách bạn đang thực hiện ở đây. Tôi thấy đây là một giải pháp tao nhã hơn nhiều so với việc chuyển đổi vị trí cảm ứng thành chỉ số hàng, như được đề xuất dưới đây.
Erik van der Neut

Câu trả lời:


400

Trong mẫu Phụ kiện của Apple, phương pháp sau được sử dụng:

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

Sau đó, trong điều khiển cảm ứng lấy tọa độ cảm ứng và đường dẫn chỉ mục được tính từ tọa độ đó:

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

Vâng đây là những gì tôi giải quyết (xem chỉnh sửa của tôi). Tôi đồng ý với bạn rằng nó không tối ưu.
kiềm chế

2
Nhưng bạn tự thêm UIButton vào UITableViewCell để bạn phải nhất quán với những gì bạn làm khi tạo ô. Mặc dù cách tiếp cận này không thực sự có vẻ thanh lịch, tôi phải thừa nhận
Vladimir

1
Đối với giải pháp đầu tiên, bạn sẽ cần lấy [[nút giám sát] giám sát] vì cuộc gọi giám sát đầu tiên sẽ cung cấp cho bạn contentView, và cuối cùng lần thứ hai sẽ cung cấp cho bạn UITableViewCell. Giải pháp thứ hai không hoạt động tốt nếu bạn thêm / xóa các ô vì nó sẽ làm mất hiệu lực chỉ mục hàng. Do đó, tôi đã đi với giải pháp đầu tiên như đã vạch ra và nó hoạt động hoàn hảo.
raidfive

3
Điều này đáng tin cậy sẽ chọn ra ô sở hữu nút: UIView * view = button; while (! [view isKindOfClass: [UITableViewCell class]]) {view = [view giám sát]}
Jacob Lyles

1
Có một cái bẫy khi sử dụng: [nút addTarget: self action: @selector (checkButtonTapped :) forControlEvents: UIControlEventTouchUpInside]; bởi vì addTarget: action: forControlEvents: sẽ thêm nhiều mục tiêu và hành động trùng lặp khi bạn cuộn bảng, nó sẽ không xóa các mục tiêu và hành động trước đó, vì vậy phương thức checkButtonTapped: sẽ được gọi nhiều lần khi bạn nhấp vào nút. Bạn nên loại bỏ mục tiêu và hành động trước khi thêm chúng
bandw 30/11/14

48

Tôi đã tìm thấy phương pháp sử dụng giám sát của giám sát để có được một tham chiếu đến indexPath của ô hoạt động hoàn hảo. Cảm ơn iphencedevbook.com (macnsmith) cho văn bản liên kết mẹo

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

}

Cocoanut, đoạn mã của bạn đã chỉ cho tôi đi đúng hướng cho biến thể của riêng tôi về vấn đề này. Cảm ơn! Trong trường hợp bất cứ ai khác cần nó, trường hợp đặc biệt của tôi là nút nằm trong một ô tùy chỉnh đang được hiển thị như một phần của chân trang. Tôi sẽ thêm mã bên dưới
phần mềm được phát triển vào

Nếu bạn (trình đọc Stackoverflow) thử điều này và nó không phù hợp với bạn, hãy kiểm tra xem trong triển khai của bạn, UIButton của bạn có thực sự là cháu của UITableViewCell của bạn không. Trong quá trình triển khai, UIButton của tôi là con trực tiếp của UITableViewCell của tôi, vì vậy tôi cần phải lấy ra một trong những "giám sát" trong mã của Cocoanut, và sau đó nó hoạt động.
Jon Schneider

29
Điều này là rất, rất sai và bị hỏng trong các phiên bản mới hơn của HĐH. Đừng đi bộ giám sát cây bạn không sở hữu.
Kenrik ngày

2
Điều này đã làm việc cho tôi dưới iOS 6, nhưng đã bị hỏng trong iOS 7. Có vẻ như @KenrikMarch có một điểm hợp lệ!
Jon Schneider

3
trong iOS 7, đó là thêm một bước tăng cường giám sát. ví dụ: [[[người gửi giám sát] giám sát] superView];
CW0007007

43

Đây là cách tôi làm điều đó. Đơn giản và súc tích:

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

2
Thậm chí đơn giản hơn: sử dụng CGPointZerothay vì CGPointMake(0, 0);-)
Jakob W

Dễ dàng để làm việc với nó. Hơn nữa, dễ dàng dịch nó sang Swift 3. Bạn là người giỏi nhất :)
Francisco Romero

Đã dịch cái này sang Swift xuống bên dưới. Giải pháp dễ nhất tôi có thể tìm thấy. Cảm ơn Chris!
Rutger Huijsmans

6

Tìm thấy một giải pháp tốt cho vấn đề này ở nơi khác, không gây rối với các thẻ trên nút:

- (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...
}

5
Không rõ ràng trong ví dụ này nơi bạn lấy đối tượng 'sự kiện' từ đó.
Nick Ludlam

Đây là giải pháp tôi đã đi với. Việc sử dụng thẻ là không thể đoán trước khi thêm / xóa hàng vì chỉ mục của chúng thay đổi. Ngoài ra,
raidfive

@NickLudlam: có lẽ là tên phương pháp không phải là buttonPressedAction:nhưng buttonPressedAction:forEvent:.
KPM

5

Làm thế nào về việc gửi thông tin như NSIndexPathtrong việc UIButtonsử dụng tiêm thời gian chạy.

1) Bạn cần thời gian chạy khi nhập

2) thêm hằng số tĩnh

3) thêm NSIndexPathvào nút của bạn trong thời gian chạy bằng cách sử dụng:

(void) setMetaData: (id) đích vớiObject: (id) newObj

4) nhấn nút lấy siêu dữ liệu bằng cách sử dụng:

(id) metaData: (id) mục tiêu

Thưởng thức

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

1
NẾU bảng được sắp xếp lại hoặc một hàng bị xóa thì điều này sẽ không hoạt động.
Neil

5

Câu trả lời của Do (@Vladimir) là Swift:

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

Mặc dù việc kiểm tra indexPath != nilmang lại cho tôi ngón tay ... "NSIndexPath không phải là một kiểu con của NSString"


5

Với Swift 4.2 và iOS 12, bạn có thể chọn một trong 5 ví dụ hoàn chỉnh sau để giải quyết vấn đề của mình.


# 1. Sử dụng UIView's convert(_:to:)UITableView' sindexPathForRow(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. Sử dụng UIView's convert(_:to:)UITableView' s indexPathForRow(at:)(thay thế)

Đây là một thay thế cho ví dụ trước nơi chúng ta chuyển nilđến targettham số trong addTarget(_:action:for:). Bằng cách này, nếu người phản hồi đầu tiên không thực hiện hành động, nó sẽ được gửi đến người phản hồi tiếp theo trong chuỗi phản hồi cho đến khi tìm thấy cách thực hiện đúng.

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. Sử dụng UITableView's indexPath(for:)và mô hình đại biểu

Trong ví dụ này, chúng tôi đặt bộ điều khiển khung nhìn làm đại biểu của ô. Khi nhấn nút của ô, nó sẽ kích hoạt một cuộc gọi đến phương thức thích hợp của đại biểu.

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. Sử dụng UITableView's indexPath(for:)và đóng cửa cho đoàn

Đây là một thay thế cho ví dụ trước, trong đó chúng tôi sử dụng một bao đóng thay vì khai báo ủy nhiệm giao thức để xử lý nhấn nút.

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. Sử dụng UITableViewCell's accessoryTypeUITableViewDelegate' stableView(_:accessoryButtonTappedForRowWith:)

Nếu nút của bạn là một UITableViewCell's kiểm soát phụ kiện tiêu chuẩn, bất cứ tap vào nó sẽ kích hoạt một cuộc gọi đến UITableViewDelegate' s tableView(_:accessoryButtonTappedForRowWith:), cho phép bạn để có được những con đường chỉ số có liên quan.

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

3

Tôi sẽ sử dụng thuộc tính thẻ như bạn đã nói, thiết lập thẻ như vậy:

[button setTag:indexPath.row];

sau đó nhận thẻ bên trong buttonPressionAction như vậy:

((UIButton *)sender).tag

Hoặc là

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

5
Cách tiếp cận này hoàn toàn bị phá vỡ cho các bảng với các phần.
ohhorob

không, bạn chỉ có thể sử dụng một số chức năng đơn giản để đặt phần trong thẻ.
ACBurk

2
taglà một số nguyên. có vẻ hơi vụng về khi mã hóa / giải mã đường dẫn chỉ mục vào thẻ xem.
ohhorob

Điều đó đúng, nhưng đó là một giải pháp, mặc dù không phải là giải pháp mà tôi sẽ sử dụng nếu tôi có các phần. Tất cả những gì tôi đã cố gắng nói là nó có thể được thực hiện bằng phương pháp này, rằng nó không bị hỏng. Một phiên bản tốt hơn, phức tạp hơn sẽ xác định đường dẫn chỉ mục từ vị trí của nút bên trong UITableView. Tuy nhiên, vì kiềm chế đã nói rằng anh ta chỉ có năm ô (không có phần), nên có thể làm cho phương thức đó trở nên phức tạp và nhận xét ban đầu của bạn và toàn bộ chuỗi nhận xét này trở nên vô nghĩa.
ACBurk

3

Mặc dù tôi thích cách gắn thẻ ... nếu bạn không muốn sử dụng thẻ vì bất kỳ lý do gì, bạn có thể tạo thành viên NSArraycủa các nút có sẵn:

NSArray* buttons ;

sau đó tạo các nút đó trước khi hiển thị bảngView và đẩy chúng vào mảng.

Sau đó, bên trong tableView:cellForRowAtIndexPath:chức năng bạn có thể làm:

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

Sau đó, trong buttonPressedAction:chức năng, bạn có thể làm

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

2

ĐỂ XỬ LÝ CÁC PHẦN - Tôi đã lưu trữ NSIndexPath trong UITableViewCell tùy chỉnh

IN CLKIndexPricesHEADERTableViewCell.xib

IN IB Thêm UIButton vào XIB - KHÔNG thêm hành động!

Thêm ổ cắm @property (giữ lại, không biến đổi) IBOutlet UIButton * buttonIndexSectionC Đóng;

KHÔNG CTRL + DRAG một hành động trong IB (được thực hiện theo mã bên dưới)

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

Trong viewForHeaderInSection (cũng nên hoạt động cho cellForRow .... vv nếu bảng của bạn chỉ có 1 phần)

- 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];

... sử dụng phần để lấy dữ liệu cho tế bào của bạn

...điền nó vào

   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 nhấn nút XÓA trên tiêu đề Mục và cuộc gọi này

- (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__);
    }
}

Trong ví dụ này tôi đã thêm một nút Xóa vì vậy sẽ hiển thị UIAlertView để xác nhận nó

Tôi lưu trữ phần và khóa vào từ điển lưu trữ thông tin về phần trong một chiếc ngà trong VC

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

2
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; //:)
}

Điều này hơi có lỗi vì indexPath của một ô có thể thay đổi, nếu bạn gọi xóaRowsAtIndexPaths.
John Gibb

xóaRowsAtIndexPaths sẽ khiến cellForRowAtIndexPath được gọi lại. Sau đó, các nút sẽ có indexPath chính xác mới.
mmm biến mất

1

Nó cũng hoạt động với tôi, Cảm ơn @Cocoanut

Tôi đã tìm thấy phương pháp sử dụng giám sát của giám sát để có được một tham chiếu đến indexPath của ô hoạt động hoàn hảo. Cảm ơn iphencedevbook.com (macnsmith) cho văn bản liên kết mẹo

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

}

0

bạn có thể sử dụng mẫu thẻ:

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

Làm cách nào để gắn thẻ các điều khiển nếu tôi có nhiều điều khiển trên một ô?
kiềm chế

Tôi không chắc điều này sẽ hoạt động - nếu ô được tạo cho hàng số 1 thì nó sẽ nhận được thẻ 1. Nếu nó bị mất cho hàng số 3 thì nó vẫn sẽ có thẻ 1, không phải 3.
kiềm chế

đoán bạn đúng về nhận xét thứ hai. lỗi của tôi. Tôi nghĩ giải pháp tốt nhất của bạn là phân lớp UIButton, thêm một hoặc hai thuộc tính của riêng bạn và sau đó đặt / lấy chúng trong các trường hợp thích hợp (gắn với thẻ: 1 bạn có trong mã của mình)
Nir Levy

0

Tui bỏ lỡ điều gì vậy? Bạn không thể chỉ sử dụng người gửi để xác định nút. Người gửi sẽ cung cấp cho bạn thông tin như thế này:

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

Sau đó, nếu bạn muốn thay đổi các thuộc tính của nút, hãy nói hình ảnh nền mà bạn vừa nói với người gửi:

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

Nếu bạn cần thẻ thì phương pháp của ACBurk vẫn ổn.


1
Họ đang tìm kiếm "đối tượng" mà nút liên quan đến
ohhorob

0
// how do I know which button sent this message?
// processing button press for this row requires an indexPath.

Khá đơn giản thực sự:

- (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];
}

Làm việc tốt cho tôi: P

nếu bạn muốn điều chỉnh thiết lập hành động mục tiêu của mình, bạn có thể đưa tham số sự kiện vào phương thức và sau đó sử dụng các lần chạm của sự kiện đó để giải quyết tọa độ của cảm ứng. Các tọa độ vẫn cần được giải quyết trong giới hạn chế độ xem cảm ứng, nhưng điều đó có vẻ dễ dàng hơn đối với một số người.


0

tạo một mảng nsmutable và đặt tất cả nút trong mảng đó usint [mảng addObject: yourButton];

trong phương pháp nhấn nút

-

 (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

}
}

0

Một biến thể nhỏ trong câu trả lời của Cocoanuts (đã giúp tôi giải quyết vấn đề này) khi nút nằm ở chân trang của bảng (điều này ngăn bạn tìm thấy 'ô được nhấp':

-(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];
}

0

Tôi luôn luôn sử dụng thẻ.

Bạn cần phải phân lớp UITableviewCellvà xử lý nhấn nút từ đó.


Tôi không hiểu lắm. Thuộc tính thẻ được thiết lập trong quá trình tạo ô - ô này có thể sử dụng lại cho mỗi hàng có cùng mã định danh. Thẻ này dành riêng cho điều khiển trong một ô có thể tái sử dụng chung. Làm cách nào tôi có thể sử dụng thẻ này để phân biệt các nút trong các ô được tạo theo cách chung? Bạn có thể gửi một số mã?
kiềm chế

0

Thật đơn giản; tạo một ô tùy chỉnh và lấy ra một nút

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
         NSString *identifier = @"identifier";
        customCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];

    cell.yourButton.tag = indexPath.Row;

- (void)buttonPressedAction:(id)sender

thay đổi id trong phương thức trên thành (UIButton *)

Bạn có thể nhận được giá trị mà nút nào đang được khai thác bằng cách thực hiện sender.tag.


0

Phân lớp nút để lưu trữ giá trị cần thiết, có thể tạo giao thức (ControlWithData hoặc một cái gì đó). Đặt giá trị khi bạn thêm nút vào ô xem bảng. Trong sự kiện liên lạc của bạn, hãy xem người gửi có tuân theo giao thức và trích xuất dữ liệu hay không. Tôi thường lưu trữ một tham chiếu đến đối tượng thực tế được hiển thị trên ô xem bảng.


0

CẬP NHẬT 2

Đây là cách tìm ra nút nào đã được gõ + gửi dữ liệu đến một ViewContoder khác từ nút đó indexPath.rowvì tôi cho rằng đó là điểm quan trọng nhất!

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

    }

Đối với những người đang sử dụng lớp ViewContoder và đã thêm bảngView, tôi đang sử dụng ViewContoder thay vì TableViewContaptor vì vậy tôi đã thêm thủ công bảngView để truy cập vào nó.

Đây là mã để truyền dữ liệu đến một VC khác khi nhấn vào nút đó và truyền vào ô indexPath.row

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

}

0

Lưu ý ở đây tôi đang sử dụng ô tùy chỉnh, mã này hoạt động hoàn hảo đối với tôi

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

0

Giải pháp của Chris Schwerdt nhưng sau đó trong Swift đã làm việc cho tôi:

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

0

Vấn đề này có hai phần:

1) Lấy đường dẫn chỉ mục UITableViewCellchứaUIButton

Có một số gợi ý như:

  • Cập nhật UIButtontagtrong cellForRowAtIndexPath:phương pháp sử dụng chỉ số đường của rowgiá trị. Đây không phải là một giải pháp tốt vì nó yêu cầu cập nhật tagliên tục và nó không hoạt động với các chế độ xem bảng với nhiều hơn một phần.

  • Thêm một NSIndexPathtài sản để tế bào tùy chỉnh và cập nhật nó thay vì UIButton's tagtrong cellForRowAtIndexPath:phương pháp. Điều này giải quyết vấn đề nhiều phần nhưng vẫn không tốt vì nó yêu cầu cập nhật luôn.

  • Giữ một sự giới thiệu yếu cho cha mẹ UITableViewtrong ô tùy chỉnh trong khi tạo nó và sử dụng indexPathForCell:phương thức để có được đường dẫn chỉ mục. Có vẻ tốt hơn một chút, không cần cập nhật bất cứ điều gì trong cellForRowAtIndexPath:phương thức, nhưng vẫn yêu cầu thiết lập tham chiếu yếu khi ô tùy chỉnh được tạo.

  • Sử dụng superViewtài sản của tế bào để có được một tham chiếu đến cha mẹ UITableView. Không cần thêm bất kỳ thuộc tính nào vào ô tùy chỉnh và không cần thiết lập / cập nhật bất cứ điều gì khi tạo / sau này. Nhưng di động superViewphụ thuộc vào chi tiết triển khai iOS. Vì vậy, nó không thể được sử dụng trực tiếp.

Nhưng điều này có thể đạt được bằng cách sử dụng một vòng lặp đơn giản, vì chúng tôi chắc chắn rằng ô đang được đề cập phải nằm trong UITableView:

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

Vì vậy, những đề xuất này có thể được kết hợp thành một phương thức ô tùy chỉnh đơn giản và an toàn để có được đường dẫn chỉ mục:

- (NSIndexPath *)indexPath
{
    UIView* view = self;

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

    return [(UITableView*)view indexPathForCell:self];
}

Từ bây giờ, phương pháp này có thể được sử dụng để phát hiện UIButton được nhấn.

2) Thông báo cho các bên khác về sự kiện nhấn nút

Sau khi biết nội bộ UIButtonđược nhấn trong ô tùy chỉnh nào có đường dẫn chỉ mục chính xác, thông tin này cần được gửi cho các bên khác (rất có thể là bộ điều khiển xem xử lý UITableView). Vì vậy, sự kiện nhấn nút này có thể được xử lý ở mức độ trừu tượng và logic tương tự như didSelectRowAtIndexPath:phương thức của đại biểu UITableView.

Hai cách tiếp cận có thể được sử dụng cho việc này:

a) Phân quyền: ô tùy chỉnh có thể có thuộc delegatetính và có thể xác định giao thức. Khi nhấn nút, nó chỉ thực hiện các phương thức ủy nhiệm trên thuộc delegatetính của nó . Nhưng thuộc delegatetính này cần được đặt cho mỗi ô tùy chỉnh khi chúng được tạo. Thay vào đó, ô tùy chỉnh có thể chọn thực hiện các phương thức ủy nhiệm của nó trên chế độ xem bảng cha của delegatenó.

b) Trung tâm thông báo: các ô tùy chỉnh có thể xác định tên thông báo tùy chỉnh và đăng thông báo này với đường dẫn chỉ mục và thông tin xem bảng cha được cung cấp trong userInfođối tượng. Không cần thiết lập bất cứ điều gì cho mỗi ô, chỉ cần thêm một người quan sát cho thông báo của ô tùy chỉnh là đủ.


0

Tôi sử dụng một giải pháp mà lớp con UIButtonvà tôi nghĩ tôi chỉ nên chia sẻ nó ở đây, mã trong Swift:

class ButtonWithIndexPath : UIButton {
    var indexPath:IndexPath?
}

Sau đó nhớ cập nhật indexPath trong cellForRow(at:)

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
}

Vì vậy, khi trả lời sự kiện của nút, bạn có thể sử dụng nó như

func cellButtonPressed(_ sender:UIButton) {
    if sender is ButtonWithIndexPath {
        let button = sender as! ButtonWithIndexPath
        print(button.indexPath)
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.