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

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

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. 사용 UIViewconvert(_:to:)UITableViewindexPathForRow(at:)

import UIKit

private class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}
import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

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

    @objc func customCellButtonTapped(_ sender: UIButton) {
        let point = sender.convert(CGPoint.zero, to: tableView)
        guard let indexPath = tableView.indexPathForRow(at: point) else { return }
        print(indexPath)
    }

}

# 2. 사용 UIViewconvert(_:to:)UITableViewindexPathForRow(at:)(대체)

이 예제 nil는의 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)
    }

}

#삼. 사용 UITableViewindexPath(for:)위임 패턴을

이 예에서는 뷰 컨트롤러를 셀의 대리자로 설정했습니다. 셀의 단추를 누르면 해당 대리자의 적절한 메소드에 대한 호출이 트리거됩니다.

import UIKit

protocol CustomCellDelegate: AnyObject {
    func customCellButtonTapped(_ customCell: CustomCell)
}

class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)
    weak var delegate: CustomCellDelegate?

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @objc func buttonTapped(sender: UIButton) {
        delegate?.customCellButtonTapped(self)
    }

}
import UIKit

class TableViewController: UITableViewController, CustomCellDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

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

    // MARK: - CustomCellDelegate

    func customCellButtonTapped(_ customCell: CustomCell) {
        guard let indexPath = tableView.indexPath(for: customCell) else { return }
        print(indexPath)
    }

}

# 4. 사용 UITableViewindexPath(for:)위임에 대한 폐쇄

이것은 버튼 탭을 처리하기 위해 프로토콜 델리게이트 선언 대신 클로저를 사용하는 이전 예제의 대안입니다.

import UIKit

class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)
    var buttontappedClosure: ((CustomCell) -> Void)?

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @objc func buttonTapped(sender: UIButton) {
        buttontappedClosure?(self)
    }

}
import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        cell.buttontappedClosure = { [weak tableView] cell in
            guard let indexPath = tableView?.indexPath(for: cell) else { return }
            print(indexPath)
        }
        return cell
    }

}

# 5. 사용 UITableViewCellaccessoryTypeUITableViewDelegatetableView(_:accessoryButtonTappedForRowWith:)

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

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

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

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

  • 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.