UITableView에서 어떤 UIButton을 눌렀는지 감지


212

나는이 UITableView5 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;
}

중요한 것은 셀이 대기열에서 제외 될 수 있으므로 셀을 만들 때 태그를 설정할 수 없다는 것입니다. 매우 더럽습니다. 더 좋은 방법이 있어야합니다.


태그 솔루션을 사용하는 데 아무런 문제가 없습니다. 셀은 재사용되므로 태그를 행 인덱스로 설정하는 것이 좋습니다. 아래에서 제안하는 것처럼 터치 위치를 행 인덱스로 변환하는 것보다 훨씬 더 우아한 솔루션입니다.
— Erik van der Neut

답변:


400

Apple의 액세서리 샘플에서는 다음 방법이 사용됩니다.

[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)
    {
     ...
    }
}

예, 이것이 내가 정한 것입니다 (내 편집 참조). 나는 그것이 최적이 아니라는 것에 동의합니다.
— 09:10에 rein

2
그러나 UIButtonViewCell에 UIButton을 추가하면 셀을 만들 때 수행하는 작업과 일관성이 있어야합니다. 이 접근법이 실제로 우아해 보이지는 않지만 인정해야합니다.
— Vladimir

1
첫 번째 솔루션의 경우 첫 번째 슈퍼 뷰 호출에서 contentView를 제공하고 두 번째 솔루션은 UITableViewCell을 제공하므로 [[button superview] superview]를 가져와야합니다. 두 번째 솔루션은 행 색인을 무효화하므로 셀을 추가 / 제거하는 경우 제대로 작동하지 않습니다. 따라서 개요대로 첫 번째 솔루션을 사용했으며 완벽하게 작동했습니다.
— raidfive

3
버튼을 소유 한 셀을 확실하게 선택합니다 : UIView * view = button; while (! [view isKindOfClass : [UITableViewCell class]]) {view = [view superview]}
— Jacob Lyles

1
다음을 사용할 때 트랩이 있습니다. [button addTarget : self action : @selector (checkButtonTapped :) forControlEvents : UIControlEventTouchUpInside]; addTarget : action : forControlEvents :는 테이블을 스크롤 할 때 중복 된 대상과 동작을 여러 개 추가하므로 이전 대상과 동작을 제거하지 않으므로 버튼을 클릭하면 checkButtonTapped : 메소드가 여러 번 호출됩니다. 당신은 더 나은에 추가하기 전에 목표와 행동을 제거 할 것
— 까지의

48

셀의 indexPath에 대한 참조를 얻기 위해 superview의 superview를 사용하는 방법이 완벽하게 작동한다는 것을 알았습니다. 팁 링크 텍스트를 위한 iphonedevbook.com (macnsmith)에게 감사 합니다

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

}

Cocoanut, 귀하의 코드 조각은이 문제에 대한 나의 변형을위한 올바른 방향을 제시했습니다. 감사! 다른 사람이 필요로하는 경우, 특별한 경우는 버튼이 바닥 글의 일부로 표시되는 사용자 정의 셀에 있다는 것입니다. 아래 코드를 추가하겠습니다
— 소프트웨어가

(Stackoverflow reader)이 시도하고 작동하지 않는 경우 구현에서 UIButton이 실제로 UITableViewCell의 손자인지 확인하십시오. 내 구현에서 UIButton은 UITableViewCell의 직접적인 자식이므로 Cocoanut 코드에서 "superview"중 하나를 가져와야 작동했습니다.
— 존 슈나이더

29
이것은 매우 잘못되어 새로운 버전의 OS에서 손상되었습니다. 소유하지 않은 슈퍼 뷰 나무를 걷지 마십시오.
— Kenrik 3

2
이것은 iOS 6에서 저에게 효과적 이었지만 iOS 7에서는 깨졌습니다. @KenrikMarch에 유효한 포인트가있는 것 같습니다!
— 존 슈나이더

3
iOS 7에서는 슈퍼 뷰를 한 단계 더 올립니다. 예를 들어 [[[sender superview] superview] superView];
— CW0007007 2012 년

43

내가하는 방법은 다음과 같습니다. 간단하고 간결합니다 :

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

2
더 간단한 : ;-) CGPointZero대신 사용CGPointMake(0, 0)
— Jakob W

사용하기 쉽습니다. 또한 Swift 3으로 쉽게 번역 할 수 있습니다. 최고입니다. :
— Francisco Romero

이것을 아래로 스위프트로 번역했습니다. 내가 찾을 수있는 가장 쉬운 해결책. 고마워 크리스!
— Rutger Huijsmans

6

다른 곳 에서이 문제에 대한 훌륭한 해결책을 찾았으며 버튼의 태그로 엉망이되지 않았습니다.

- (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
이 예제에서는 'event'객체를 얻는 곳이 명확하지 않습니다.
— Nick Ludlam

이것이 내가 함께했던 해결책입니다. 색인이 변경되어 행을 추가 / 제거 할 때 태그를 사용할 수 없습니다. 또한,
— raidfive

@NickLudlam : 아마도 메소드 이름은 buttonPressedAction:아니지만 buttonPressedAction:forEvent:입니다.
— KPM

5

어떻게 같은 정보 전송에 대한 NSIndexPath에 UIButton사용하여 런타임 주입입니다.

1) 가져 오기시 런타임이 필요합니다

2) 정적 상수 추가

3) NSIndexPath다음을 사용하여 런타임에 버튼에 추가 하십시오.

(void) setMetaData : (id) 대상 withObject : (id) newObj

4) 버튼을 누르면 메타 데이터를 얻습니다.

(id) 메타 데이터 : (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
    }

1
테이블이 재정렬되거나 행이 삭제되면 작동하지 않습니다.
— 닐

5

(@Vladimir)의 답변은 Swift입니다.

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

확인 indexPath != nil하면 손가락이 나옵니다 ... "NSIndexPath는 NSString의 하위 유형이 아닙니다"


5

Swift 4.2 및 iOS 12에서는 문제를 해결하기 위해 다음 예제 5 가지 중 하나를 선택할 수 있습니다 .


#1. 사용 UIView의 convert(_:to:)와 UITableView의indexPathForRow(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. 사용 UIView의 convert(_:to:)와 UITableView의 indexPathForRow(at:)(대체)

이 예제 nil는의 target매개 변수에 전달하는 이전 예제의 대안 입니다 addTarget(_:action:for:). 이렇게하면 첫 번째 응답자가 조치를 구현하지 않으면 적절한 구현이 발견 될 때까지 응답자 체인의 다음 응답자에게 전송됩니다.

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

}

#삼. 사용 UITableView의 indexPath(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. 사용 UITableView의 indexPath(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. 사용 UITableViewCell의 accessoryType와 UITableViewDelegate의tableView(_:accessoryButtonTappedForRowWith:)

당신의 버튼이 경우 UITableViewCell의 표준 부속품 제어, 그것의 모든 탭에 대한 호출을 트리거 UITableViewDelegate의 ' tableView(_: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
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

태그 속성을 사용하여 태그를 다음과 같이 설정했습니다.

[button setTag:indexPath.row];

그런 다음 buttonPressedAction 안에 태그를 가져옵니다.

((UIButton *)sender).tag

또는

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

5
이 방법은 섹션이있는 테이블에서 완전히 손상되었습니다.
— ohhorob

아니요, 간단한 기능을 사용하여 섹션을 태그에 넣을 수도 있습니다.
— ACBurk

2
tag정수입니다. 인덱스 경로를 뷰 태그로 인코딩 / 디코딩하는 것이 약간 어색한 것 같습니다.
— ohhorob

맞습니다.하지만 섹션이있는 경우 사용하지는 않지만 해결책입니다. 내가 말하려고하는 것은이 방법을 사용하여 수행 할 수 있다는 것입니다. 더 좋고 더 복잡한 버전은 UITableView 내부의 버튼 위치에서 색인 경로를 결정합니다. 그러나 rein은 섹션이없는 5 개의 셀 만 가지고 있다고 말했기 때문에 아마도 그 방법이 복잡하고 초기 주석 과이 전체 주석 스레드가 무의미합니다.
— ACBurk

3

태그 방식이 마음에 들지만 ... 어떤 이유로 든 태그를 사용하지 않으려면 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
}

2

섹션 다루기-NSIndexPath를 사용자 정의 UITableViewCell에 저장했습니다.

CLKIndexPricesHEADERTableViewCell.xib에서

IN IB XIB에 UIButton 추가-DONT 추가 조치!

콘센트 @property 추가 (비 원자 유지) IBOutlet UIButton * buttonIndexSectionClose;

IB에서 작업을 CTRL + DRAG하지 마십시오 (아래 코드에서 수행).

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

viewForHeaderInSection에서 (테이블이 섹션이 하나 인 경우 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);
    }
}

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

deleteRowsAtIndexPaths를 호출하면 셀의 indexPath가 변경 될 수 있기 때문에 약간 버그가 있습니다.
— John Gibb

deleteRowsAtIndexPaths는 cellForRowAtIndexPath가 다시 호출되게합니다. 그런 다음 버튼에는 새로운 올바른 indexPath가 있습니다.
— mmmanishs 22

1

감사합니다 @Cocoanut

셀의 indexPath에 대한 참조를 얻기 위해 superview의 superview를 사용하는 방법이 완벽하게 작동한다는 것을 알았습니다. 팁 링크 텍스트를위한 iphonedevbook.com (macnsmith)에게 감사합니다

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

}

0

태그 패턴을 사용할 수 있습니다.

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

단일 셀에 여러 개의 컨트롤이있는 경우 컨트롤에 태그를 어떻게 추가합니까?
— rein

셀이 행 # 1에 대해 작성되면 태그 1을 얻게됩니다. 행 # 3에 대해 큐에서 제외되면 여전히 3이 아닌 1의 태그를 갖습니다.
— rein

당신이 두 번째 의견에 맞다고 생각합니다. 내 잘못이야. 가장 좋은 해결책은 UIButton을 서브 클래 싱하고 다른 속성이나 자신의 속성 중 하나 또는 두 개를 추가 한 다음 적절한 경우에 설정 / 사용하는 것입니다 (코드에있는 태그 : 1을 사용하십시오)
— Nir Levy

0

뭔가 빠졌습니까? 발신자를 사용하여 버튼을 식별 할 수는 없습니다. 발신자가 다음과 같은 정보를 제공합니다.

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

그런 다음 버튼의 속성을 변경하려면 발신자에게 알려주는 배경 이미지를 말하십시오.

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

태그가 필요하면 ACBurk의 방법이 좋습니다.


1
그들은 버튼과 관련된 "객체"를 찾고 있습니다
— ohhorob

0
// 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

대상 동작 설정을 조정하려면 메서드에 이벤트 매개 변수를 포함시킨 다음 해당 이벤트의 터치를 사용하여 터치 좌표를 확인할 수 있습니다. 터치 뷰 범위에서 좌표를 여전히 해결해야하지만 일부 사람들에게는 더 쉬운 것처럼 보일 수 있습니다.


0

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

}
}

0

버튼이 테이블의 바닥 글에있을 때 Coconutts의 약간의 변형 (이 문제를 해결하는 데 도움이 됨)은 '클릭 된 셀'을 찾지 못하게합니다.

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

나는 항상 태그를 사용합니다.

서브 클래 싱하고 UITableviewCell거기서부터 버튼 누름을 처리해야합니다.


나는 어떻게 이해하지 못한다. 태그 속성은 셀 생성 중에 설정됩니다.이 셀은 동일한 식별자를 가진 각 행에 재사용 할 수 있습니다. 이 태그는 일반 재사용 가능 셀의 제어에 고유합니다. 이 태그를 사용하여 일반적인 방식으로 만들어진 셀의 버튼을 구별하려면 어떻게해야합니까? 코드를 게시 할 수 있습니까?
— 09:10에 rein

0

간단 해; 사용자 정의 셀을 만들고 버튼의 콘센트를 가져 가십시오.

- (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를 수행하여 어떤 버튼을 탭하고 있는지 값을 얻을 수 있습니다.


0

필요한 값을 저장하기 위해 버튼을 서브 클래 싱하고 프로토콜 (ControlWithData 등)을 생성하십시오. 버튼을 테이블보기 셀에 추가 할 때 값을 설정하십시오. 터치 업 이벤트에서 발신자가 프로토콜을 준수하고 데이터를 추출하는지 확인하십시오. 일반적으로 테이블 뷰 셀에 렌더링되는 실제 객체에 대한 참조를 저장합니다.


0

스위프트 2 업데이트

다음은 어떤 버튼이 탭되었는지 알아 내고 그 버튼에서 다른 ViewController로 데이터를 보내는 방법 indexPath.row입니다. 가장 중요한 점이라고 가정합니다!

@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를 추가 한 사람들을 위해 TableViewController 대신 ViewController를 사용하고 있으므로 액세스하기 위해 tableView를 수동으로 추가했습니다.

해당 버튼을 탭하고 셀을 전달할 때 다른 VC에 데이터를 전달하는 코드는 다음과 같습니다. 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

여기 에이 코드가 완벽하게 작동하는 맞춤 셀을 사용하고 있습니다.

 @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

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

0

이 문제는 두 부분으로 구성됩니다.

1) UITableViewCell포함 된 색인 경로 가져 오기UIButton

다음과 같은 제안이 있습니다.

  • 업데이트는 UIButton의 tag에 cellForRowAtIndexPath:인덱스 경로의 사용 방법 row값입니다. tag지속적으로 업데이트해야 하며 둘 이상의 섹션이있는 테이블보기에서는 작동하지 않으므로 이는 좋은 솔루션 이 아닙니다.

  • 가산 NSIndexPath사용자 정의 셀 속성을하고 대신에 그것을 업데이트 UIButton의 ' tag에 cellForRowAtIndexPath:방법. 이렇게하면 여러 섹션 문제가 해결되지만 항상 업데이트해야하므로 여전히 좋지 않습니다.

  • UITableView사용자 지정 셀에서 부모 에 대한 약한 참조를 유지 하면서 생성 indexPathForCell:하고 인덱스 경로를 얻는 방법을 사용 합니다. 조금 더 나아 보이고, cellForRowAtIndexPath:메소드에서 아무것도 업데이트 할 필요는 없지만, 사용자 정의 셀을 만들 때 여전히 약한 참조를 설정해야합니다.

  • cell superView속성을 사용 하여 parent 참조를 가져옵니다 UITableView. 사용자 정의 셀에 특성을 추가 할 필요가 없으며 작성 / 나중에 아무것도 설정 / 업데이트 할 필요가 없습니다. 그러나 셀 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. 따라서이 버튼 클릭 이벤트는 didSelectRowAtIndexPath:UITableView 델리게이트의 메소드 와 유사한 추상화 및 논리 레벨에서 처리 될 수 있습니다 .

이를 위해 두 가지 접근 방식을 사용할 수 있습니다.

a) 위임 : 사용자 정의 셀은 delegate속성을 가질 수 있으며 프로토콜을 정의 할 수 있습니다. 버튼을 누르면 delegate속성 에 대리자 메서드 만 수행 됩니다. 그러나이 delegate특성은 각 사용자 정의 셀을 작성할 때 설정해야합니다. 대안으로, 커스텀 셀은 부모 테이블 뷰 delegate에서도 델리게이트 메소드를 수행하도록 선택할 수 있습니다 .

b) 알림 센터 : 사용자 정의 셀은 사용자 정의 알림 이름을 정의하고이 알림을 userInfo오브젝트에 제공된 색인 경로 및 상위 테이블보기 정보와 함께 게시 할 수 있습니다 . 각 셀에 대해 아무것도 설정할 필요가 없으며 사용자 정의 셀의 알림에 관찰자를 추가하는 것으로 충분합니다.


0

하위 클래스의 솔루션을 사용하고 UIButtonSwift의 코드를 공유해야한다고 생각했습니다.

class ButtonWithIndexPath : UIButton {
    var indexPath:IndexPath?
}

그런 다음 indexPath를 업데이트해야합니다. 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
}

버튼의 이벤트에 응답하면 다음과 같이 사용할 수 있습니다.

func cellButtonPressed(_ sender:UIButton) {
    if sender is ButtonWithIndexPath {
        let button = sender as! ButtonWithIndexPath
        print(button.indexPath)
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.