UITableViewCell 내부에서 버튼 클릭 가져 오기


140

테이블 뷰가있는 뷰 컨트롤러와 테이블 셀 템플릿에 대한 별도의 펜촉이 있습니다. 셀 템플릿에는 몇 가지 버튼이 있습니다. 테이블보기를 정의 한보 기 컨트롤러 내부에서 클릭 한 셀의 색인과 함께 버튼 클릭에 액세스하고 싶습니다.

그래서 내가 ViewController.h있고 ViewController.m어디에 UITableView있고 TableTemplate.h, TableTemplate.m그리고 TableTemplate.xib펜촉이 정의되어 있습니다. 에 셀 인덱스가있는 버튼 클릭 이벤트를 원합니다 ViewController.m.

내가 어떻게 할 수 있습니까?

답변:


258

1) cellForRowAtIndexPath:방법에서 버튼 태그를 색인으로 지정 하십시오 .

cell.yourbutton.tag = indexPath.row;

2) 다음과 같이 버튼의 대상과 동작을 추가하십시오.

[cell.yourbutton addTarget:self action:@selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];

3) 다음과 같이 색인을 기반으로 한 코드 작업 ViewControler:

-(void)yourButtonClicked:(UIButton*)sender
{
     if (sender.tag == 0) 
     {
         // Your code here
     }
}

여러 섹션에 대한 업데이트 :

이 링크 를 확인하여 여러 행 및 섹션에 대한 테이블보기에서 단추 클릭을 감지 할 수 있습니다 .


1
이 작업은 2 단계에서 IB (Interface Builder)를 통해 수행 할 수도 있습니다. 버튼 태그가 설정되어 있는지 확인하십시오. 당신은 정말로 당신의 행동 부름을 섞고 싶지 않습니다. IB를 통해 수행하거나 코드에서 명시 적으로 수행하십시오.
Sententia

@Mani MVC를 깨뜨리지 않습니다. 액션은 셀이 아닌 TableView에 있습니다.
davecom

@davecom 버튼 대상을 셀 (IB를 통해)로 설정하면 tableView에서 어떻게 트리거됩니까? 또는 버튼 대상을 셀의 xib에있는 tableview에 연결하는 방법이 있습니까?
마니

24
이 솔루션은 행 삽입 및 삭제를 시작할 때 문제가 발생합니다. 행이 이동 될 때 태그가 업데이트되지 않습니다. 행에 대한 참조를 유지하는 대신 고유 한 객체 ID에 대한 참조를 유지하는 것이 좋습니다.
Vincent Cheong

1
뷰의 태그 속성에 값을 할당 할 때마다 나중에 냄새를 맡을 수있는 매우 나쁜 코드 냄새가납니다. 처음으로 찾은 SO 게시물이 아니라 목표를 달성하는 더 좋은 방법을 찾으십시오.
TigerCoding

148

대표자들이 갈 길입니다.

다른 답변에서 볼 수 있듯이 뷰를 사용하면 구식이 될 수 있습니다. 내일 또 다른 포장지가있을 수 있으며이를 사용해야 할 수도있는 사람 cell superview]superview]superview]superview]. 태그를 사용하면 셀을 식별하기위한 n 개의 조건이 생길 수 있습니다. 그 모든 설정을 피하기 위해. (이렇게하면 재사용 가능한 셀 클래스가 작성됩니다. 기본 클래스와 동일한 셀 클래스를 사용할 수 있으며 위임 메소드를 구현하기 만하면됩니다.)

먼저 우리는 인터페이스 (프로토콜)가 필요합니다.이 프로토콜은 셀에서 버튼 클릭을 전달 (대리)하는 데 사용됩니다. ( 프로토콜에 대해 별도의 .h 파일을 생성하고 테이블 뷰 컨트롤러와 사용자 정의 셀 클래스 모두에 포함하거나 테이블 뷰 컨트롤러에 포함될 사용자 정의 셀 클래스에 추가 할 수 있습니다 )

@protocol CellDelegate <NSObject>
- (void)didClickOnCellAtIndex:(NSInteger)cellIndex withData:(id)data;
@end

이 프로토콜을 사용자 정의 셀 및 테이블보기 컨트롤러에 포함하십시오. 그리고 테이블 뷰 컨트롤러가이 프로토콜을 확인하는지 확인하십시오.

사용자 정의 셀에서 두 가지 특성을 작성하십시오.

@property (weak, nonatomic) id<CellDelegate>delegate;
@property (assign, nonatomic) NSInteger cellIndex;

UIButtonIBAction를 위임 클릭 ( 같은 뷰 컨트롤러에 다시 위임 할 필요가 사용자 정의 셀 클래스의 모든 행동을 수행 할 수 있습니다 )

- (IBAction)buttonClicked:(UIButton *)sender {
    if (self.delegate && [self.delegate respondsToSelector:@selector(didClickOnCellAtIndex:withData:)]) {
        [self.delegate didClickOnCellAtIndex:_cellIndex withData:@"any other cell data/property"];
    }
}

cellForRowAtIndexPath셀을 큐 해제 한 후 테이블보기 컨트롤러 에서 위의 특성을 설정하십시오.

cell.delegate = self;
cell.cellIndex = indexPath.row; // Set indexpath if its a grouped table.

그리고 테이블 뷰 컨트롤러에서 델리게이트를 구현하십시오.

- (void)didClickOnCellAtIndex:(NSInteger)cellIndex withData:(id)data
{
    // Do additional actions as required.
    NSLog(@"Cell at Index: %d clicked.\n Data received : %@", cellIndex, data);
}

이것은 테이블 뷰 컨트롤러에서 사용자 정의 셀 버튼 동작을 얻는 이상적인 방법입니다.


2
왜 델리게이트를 셀의 강력한 속성으로 만들었습니까? 컨트롤러가 셀을 약하게 보유하고 있다는 것을 알지 못하면 유지주기를 제공합니다.
JulianSymes

셀이 삭제 된 후 업데이트 된 _cellIndex beign은 어떻습니까?
skornos

2
내 친구 중 한 사람이 각 셀에서 대리자를 사용하면 메모리 소비가 발생한다고 말하면서 태그를 사용한다고 들었습니다. 이것이 사실입니까?
비스타


@the_UB 태그 설정과 단일 참조 저장 사이에는 그리 많지 않습니다. 태그가 더 많은 메모리를 차지할 수 있습니다.
이안 워버튼

66

태그를 사용하는 대신 다른 접근 방식을 취했습니다. UITableViewCell (OptionButtonsCell)의 하위 클래스에 대리자를 만들고 indexPath var를 추가했습니다. 스토리 보드의 버튼에서 @IBAction을 OptionButtonsCell에 연결하고 올바른 indexPath가있는 대리자 메서드를 관심있는 모든 사람에게 보냅니다. 인덱스 경로의 셀에서 현재 indexPath를 설정하고 작동합니다. :)

코드 자체를 말하도록하십시오 :

스위프트 3 Xcode 8

OptionButtonsTableViewCell.swift

import UIKit
protocol OptionButtonsDelegate{
    func closeFriendsTapped(at index:IndexPath)
}
class OptionButtonsTableViewCell: UITableViewCell {
    var delegate:OptionButtonsDelegate!
    @IBOutlet weak var closeFriendsBtn: UIButton!
    var indexPath:IndexPath!
    @IBAction func closeFriendsAction(_ sender: UIButton) {
        self.delegate?.closeFriendsTapped(at: indexPath)
    }
}

MyTableViewController.swift

class MyTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, OptionButtonsDelegate {...

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "optionCell") as! OptionButtonsTableViewCell
    cell.delegate = self
    cell.indexPath = indexPath
    return cell   
}

func closeFriendsTapped(at index: IndexPath) {
     print("button tapped at index:\(index)")
}

이 줄에서 오류가 발생합니다. class MyTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, OptionButtonsDelegate // error : 프로토콜 'UITableViewDataSource'에 대한 'MyTableViewController'의 중복 준수
Ulug'bek Ro'zimboyev

UITableViewDataSource를 여러 번 준수하려고하는 것처럼 보입니다. 아마도 데이터 소스를 이미 준수하고있는 확장 기능이 있습니까? 코드 없이는 더 이상 도움을 줄 수 없습니다.
Maciej Chrzastek

1
segue를 수행하고 다른 뷰 컨트롤러로 이동하기 위해 데이터를 전달하는 방법은 무엇입니까?
Milad Faridnia

2
가장 깨끗한 솔루션!
appsunited

31

이것은 도움이 될 것입니다 :-

UITableViewCell* cell = (UITableViewCell*)[sender superview];
NSIndexPath* indexPath = [myTableView indexPathForCell:cell];

여기서 sender 는 이벤트를 보내는 UIButton 인스턴스입니다. myTableView 는 처리중인 UITableView 인스턴스입니다.

셀 참조를 올바르게 얻으면 모든 작업이 완료됩니다.

셀의 contentView에서 버튼을 제거하고 하위 뷰이므로 UITableViewCell 인스턴스에 직접 추가해야 할 수도 있습니다.

또는

cell.contentView에서 다른 UIButton에 대한 태그 이름 지정 체계를 공식화 할 수 있습니다. 이 태그를 사용하면 나중에 필요에 따라 행 및 섹션 정보를 알 수 있습니다.


4
[[sender superview] superview] 여야합니다.
pierre23

2
이것은 매우 간단한 셀에 좋습니다. 그러나 셀에 깊은 시야가있는 경우 Mani의 대답이 가장 좋습니다.
Sententia

3
이제 iOS 7에서는 UITableViewCell * cell = (UITableViewCell *) [[[sender superview] superview] superview]; 감사합니다.
Rajan Maharjan


22

다음 코드가 도움이 될 수 있습니다.

내가 찍은 UITableView라는 이름의 사용자 정의 프로토 타입 셀 클래스와 UITableViewCell내부 UIViewController.

내가 그래서 ViewController.h, ViewController.m그리고 TableViewCell.h,TableViewCell.m

그 코드는 다음과 같습니다.

ViewController.h

@interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>

@property (strong, nonatomic) IBOutlet UITableView *tblView;

@end

ViewController.m

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return (YourNumberOfRows);
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *cellIdentifier = @"cell";

    __weak TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    if (indexPath.row==0) {
        [cell setDidTapButtonBlock:^(id sender)
         {
             // Your code here

         }];
    }    
    return cell;
}

커스텀 셀 클래스 :

TableViewCell.h

@interface TableViewCell : UITableViewCell

@property (copy, nonatomic) void (^didTapButtonBlock)(id sender);

@property (strong, nonatomic) IBOutlet UILabel *lblTitle;
@property (strong, nonatomic) IBOutlet UIButton *btnAction;

- (void)setDidTapButtonBlock:(void (^)(id sender))didTapButtonBlock;

@end

UITableViewCell.m

@implementation TableViewCell

- (void)awakeFromNib {
    // Initialization code
    [self.btnAction addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];

}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}
- (void)didTapButton:(id)sender {
    if (self.didTapButtonBlock)
    {
        self.didTapButtonBlock(sender);
    }
}

참고 : 여기에서는 UIControls스토리 보드를 모두 사용했습니다.

그게 당신을 도울 수 있기를 바랍니다 ... !!!


가장 좋은 방법
Daniel Raouf

15

아래 기술을 좋아하는 이유는 테이블 섹션을 식별하는 데 도움이되기 때문입니다.

셀 cellForRowAtIndexPath에 버튼 추가 :

 UIButton *selectTaskBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [selectTaskBtn setFrame:CGRectMake(15, 5, 30, 30.0)];
        [selectTaskBtn setTag:indexPath.section]; //Not required but may find useful if you need only section or row (indexpath.row) as suggested by MR.Tarun 
    [selectTaskBtn addTarget:self action:@selector(addTask:)   forControlEvents:UIControlEventTouchDown];
[cell addsubview: selectTaskBtn];

이벤트 추가 작업 :

-(void)addTask:(UIButton*)btn
{
    CGPoint buttonPosition = [btn convertPoint:CGPointZero toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    if (indexPath != nil)
    {
     int currentIndex = indexPath.row;
     int tableSection = indexPath.section;
    }
}

이 도움을 바랍니다.



12

스위프트 클로저 사용 :

class TheCell: UITableViewCell {

    var tapCallback: (() -> Void)?

    @IBAction func didTap(_ sender: Any) {
        tapCallback?()
    }
}

extension TheController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: TheCell.identifier, for: indexPath) as! TheCell {
            cell.tapCallback = {
                //do stuff
            }
            return cell
    }
}

7

UITableViewCell 구조가 변경되어 이제 "UITableViewCellScrollView"가 표시되므로 Tarun의 코드는 iOS7에서 작동하지 않습니다.

iOS 7에서 superview를 사용하여 UITableViewCell을 얻는 이 게시물 에는 향후 구조 변경에 관계없이 올바른 부모보기를 찾기위한 루프를 만드는 훌륭한 솔루션이 있습니다. 루프를 만드는 것으로 요약됩니다.

    UIView *superView = [sender superview];
    UIView *foundSuperView = nil;

    while (nil != superView && nil == foundSuperView) {
        if ([superView isKindOfClass:[UITableViewCell class]]) {
            foundSuperView = superView;
        } else {
            superView = superView.superview;
        }
    }

링크에는 더 재사용 가능한 솔루션에 대한 코드가 있지만 작동해야합니다.


6

스위프트 2.2

해당 버튼에 대상을 추가해야합니다.

myButton.addTarget(self, action: #selector(ClassName.FunctionName(_:), forControlEvents: .TouchUpInside)

FunctionName : connected // 예를 들어

물론 버튼을 사용하고 있기 때문에 해당 버튼의 태그를 설정해야합니다.

myButton.tag = indexPath.row

UITableViewCell을 서브 클래 싱하여이를 달성 할 수 있습니다. 인터페이스 빌더에서 사용하고 해당 셀에 버튼을 놓은 다음 콘센트를 통해 연결하십시오.

연결된 함수에서 태그를 가져 오려면 :

func connected(sender: UIButton) {
    let buttonTag = sender.tag
    // Do any additional setup
}

6

클로저가있는 스위프트 3

좋은 해결책은 사용자 정의 UITableViewCell에서 클로저를 사용하여 조치를 위해 viewController를 콜백하는 것입니다.

셀에서 :

final class YourCustomCell: UITableViewCell {

    var callbackClosure: (() -> Void)?

    // Configure the cell here
    func configure(object: Object, callbackClosure: (() -> Void)?) {
       self.callbackClosure = callbackClosure
    }


// MARK: - IBAction
extension YourCustomCell {
    @IBAction fileprivate func actionPressed(_ sender: Any) {
        guard let closure = callbackClosure else { return }
        closure()
    }
}

View Controller에서 : 테이블 뷰 위임

extension YourViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        guard let cell: YourCustomCell = cell as? YourCustomCell else { return }
        cell.configure(object: object, callbackClosure: { [weak self] in
            self?.buttonAction()
        })
     }
 }

fileprivate extension YourViewController {

    func buttonAction() {
        // do your actions here 
    }
}

5

셀 내부의 버튼을 하위 클래스로 만드는 것이 가장 간단하다는 것을 알았습니다 (Swift 3).

class MyCellInfoButton: UIButton {
    var indexPath: IndexPath?
}

셀 클래스에서 :

class MyCell: UICollectionViewCell {
    @IBOutlet weak var infoButton: MyCellInfoButton!
   ...
}

셀을 대기열에서 제외시킬 때 테이블보기 또는 콜렉션보기의 데이터 소스에서 단추에 색인 경로를 제공하십시오.

cell.infoButton.indexPath = indexPath

따라서 다음 코드를 테이블 뷰 컨트롤러에 넣을 수 있습니다.

@IBAction func handleTapOnCellInfoButton(_ sender: MyCellInfoButton) {
        print(sender.indexPath!) // Do whatever you want with the index path!
}

그리고 인터페이스 빌더에서 버튼의 클래스를 설정하고 handleTapOnCellInfoButton함수에 연결하는 것을 잊지 마십시오 !


편집 :

의존성 주입 사용. 클로저 호출을 설정하려면

class MyCell: UICollectionViewCell {
    var someFunction: (() -> Void)?
    ...
    @IBAction func didTapInfoButton() {
        someFunction?()
    }
}

컬렉션 뷰 대리자의 willDisplay 메서드에 클로저를 삽입하십시오.

 func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    (cell as? MyCell)?.someFunction = {
        print(indexPath) // Do something with the indexPath.
    }
}

클로저 접근 방식은 내가 본 가장 스위프 티 방식입니다. 잘 하셨어요!
Clifton Labrum

5

그것은 나를 위해 일했습니다.

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     UIButton *Btn_Play = (UIButton *)[cell viewWithTag:101];
     [Btn_Play addTarget:self action:@selector(ButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)ButtonClicked:(UIButton*)sender {
     CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.Tbl_Name];
     NSIndexPath *indexPath = [self.Tbl_Name indexPathForRowAtPoint:buttonPosition];
}

1
// Add action in cell for row at index path -tableView

cell.buttonName.addTarget(self, action: #selector(ViewController.btnAction(_:)), for: .touchUpInside)

// Button Action

  @objc func btnAction(_ sender: AnyObject) {



        var position: CGPoint = sender.convert(.zero, to: self.tableView)


        let indexPath = self.tableView.indexPathForRow(at: position)
        let cell: UITableViewCell = tableView.cellForRow(at: indexPath!)! as
        UITableViewCell




}

1

신속한 4 :

inside the cellForItemAt ,
   
cell.chekbx.addTarget(self, action: #selector(methodname), for: .touchUpInside)

then outside of cellForItemAt
@objc func methodname()
{
//your function code
}


1

클로저를 사용하여 셀에서 UIViewController로 매개 변수 값을 전달하려면

//Your Cell Class
class TheCell: UITableViewCell {

    var callBackBlockWithParam: ((String) -> ()) = {_ in }

//Your Action on button
    @IBAction func didTap(_ sender: Any) {
        callBackBlockWithParam("Your Required Parameter like you can send button as sender or anything just change parameter type. Here I am passing string")
    }
}

//Your Controller
extension TheController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: TheCell.identifier, for: indexPath) as! TheCell {
            cell.callBackBlockWithParam = { (passedParamter) in 

             //you will get string value from cell class
                print(passedParamter)     
      }
            return cell
    }
}

0

@Mani 답변은 좋지만 셀의 contentView 내부의 뷰 태그는 종종 다른 목적으로 사용됩니다. 대신 셀의 태그 (또는 셀의 contentView 태그)를 사용할 수 있습니다.

1) cellForRowAtIndexPath:방법에서 셀의 태그를 색인으로 지정 하십시오 .

cell.tag = indexPath.row; // or cell.contentView.tag...

2) 다음과 같이 버튼의 대상과 동작을 추가하십시오.

[cell.yourbutton addTarget:self action:@selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];

3) 보낸 사람의 행을 반환하는 메소드를 작성하십시오 (@Stenio Ferreira 덕분에).

- (NSInteger)rowOfSender:(id)sender
{
    UIView *superView = sender.superview;
    while (superView) {
        if ([superView isKindOfClass:[UITableViewCell class]])
            break;
        else
            superView = superView.superview;
    }

    return superView.tag;
}

4) 색인에 따른 코드 조치 :

-(void)yourButtonClicked:(UIButton*)sender
{
     NSInteger index = [self rowOfSender:sender];
     // Your code here
}

0

CustomTableCell.h는 UITableViewCell입니다.

@property (weak, nonatomic) IBOutlet UIButton *action1Button;
@property (weak, nonatomic) IBOutlet UIButton *action2Button;

가져온 후 MyVC.m :

@interface MYTapGestureRecognizer : UITapGestureRecognizer
@property (nonatomic) NSInteger dataint;
@end

MyVC.m의 "cellForRowAtIndexPath"내부 :

//CustomTableCell 
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

//Set title buttons
[cell.action1Button setTitle:[NSString stringWithString:NSLocalizedString(@"action1", nil)] forState:UIControlStateNormal];
[cell.action2Button setTitle:[NSString stringWithString:NSLocalizedString(@"action2", nil)] forState:UIControlStateNormal];

//Set visibility buttons
[cell.action1Button setHidden:FALSE];
[cell.action2Button setHidden:FALSE];

//Do 1 action
[cell.action1Button addTarget:self action:@selector(do1Action :) forControlEvents:UIControlEventTouchUpInside];

//Do 2 action
MYTapGestureRecognizer *action2Tap = [[MYTapGestureRecognizer alloc] initWithTarget:self action:@selector(do2Action :)];
cancelTap.numberOfTapsRequired = 1;
cancelTap.dataint = indexPath.row;
[cell.action2Button setUserInteractionEnabled:YES];
[cell.action2Button addGestureRecognizer:action2Tap];

MyVC.m :

-(void)do1Action :(id)sender{
//do some action that is not necessary fr data
}

-(void)do2Action :(UITapGestureRecognizer *)tapRecognizer{
MYTapGestureRecognizer *tap = (MYTapGestureRecognizer *)tapRecognizer;
numberTag = tap.dataint;
FriendRequest *fr = [_list objectAtIndex:numberTag];

//connect with a WS o do some action with fr data

//actualize list in tableView
 [self.myTableView reloadData];
}

-1
cell.show.tag=indexPath.row;
     [cell.show addTarget:self action:@selector(showdata:) forControlEvents:UIControlEventTouchUpInside];

-(IBAction)showdata:(id)sender
{
    UIButton *button = (UIButton *)sender;

    UIStoryboard *storyBoard;
    storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    SecondViewController *detailView = [storyBoard instantiateViewControllerWithIdentifier:@"SecondViewController"];

    detailView.string=[NSString stringWithFormat:@"%@",[_array objectAtIndex:button.tag]];

    [self presentViewController:detailView animated:YES completion:nil];

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.