UITableViewCell의 accessoryView에 대한 사용자 정의 이미지 사용 및 UITableViewDelegate에 응답


139

셀에 대해 동일한 것을 포함하여 사용자 정의 UIUIViewViewCell을 사용하고 accessoryView있습니다. accessoryView에 대한 내 설정은 다음과 같은 방식으로 발생합니다.

UIImage *accessoryImage = [UIImage imageNamed:@"accessoryDisclosure.png"];
UIImageView *accImageView = [[UIImageView alloc] initWithImage:accessoryImage];
accImageView.userInteractionEnabled = YES;
[accImageView setFrame:CGRectMake(0, 0, 28.0, 28.0)];
self.accessoryView = accImageView;
[accImageView release];

또한 셀이 초기화 될 때 initWithFrame:reuseIdentifier:다음을 사용하여 다음 속성을 설정했습니다.

self.userInteractionEnabled = YES;

불행히도 내 UITableViewDelegate에서 내 tableView:accessoryButtonTappedForRowWithIndexPath:메소드 (10 회 반복 시도)가 트리거되지 않습니다. 델리게이트가 올바르게 연결되었습니다.

무엇을 놓칠 수 있습니까?

모두 감사합니다.

답변:


228

슬프게도 미리 정의 된 유형 중 하나를 사용할 때 제공된 내부 버튼 유형을 누르지 않으면 해당 메소드가 호출되지 않습니다. 직접 사용하려면 액세서리를 버튼 또는 다른 UIControl 서브 클래스로 만들어야합니다 ( -buttonWithType:UIButtonTypeCustomUIImageView를 사용하는 대신 버튼 이미지를 사용 하고 설정 하는 버튼을 권장합니다 ).

다음은 Outpost에서 사용하는 몇 가지 사항입니다. 표준 위젯을 약간 커스터마이징하여 (청록색과 일치하도록) UITableViewController 중개 하위 클래스를 사용하여 다른 모든 테이블보기에서 사용할 유틸리티 코드를 보유했습니다 (이제 하위 클래스). OPTableViewController).

먼저이 함수는 사용자 정의 그래픽을 사용하여 새로운 세부 정보 공개 버튼을 반환합니다.

- (UIButton *) makeDetailDisclosureButton
{
    UIButton * button = [UIButton outpostDetailDisclosureButton];

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

    return ( button );
}

버튼은 완료되면이 루틴을 호출 한 다음 액세서리 버튼에 표준 UITableViewDelegate 루틴을 제공합니다.

- (void) accessoryButtonTapped: (UIControl *) button withEvent: (UIEvent *) event
{
    NSIndexPath * indexPath = [self.tableView indexPathForRowAtPoint: [[[event touchesForView: button] anyObject] locationInView: self.tableView]];
    if ( indexPath == nil )
        return;

    [self.tableView.delegate tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath];
}

이 기능은 버튼으로 제공된 이벤트에서 터치 테이블 뷰의 위치를 ​​가져 와서 해당 시점에서 행의 인덱스 경로에 대한 테이블 뷰를 요청하여 행을 찾습니다.


고마워요 커스텀 imageView로 왜 그것을 할 수 없는지 궁금해하는 데 20 분 이상이 걸렸습니다. 방금 Apple의 샘플 액세서리 앱에서이 작업을 수행하는 방법을 보았습니다. 귀하의 답변은 잘 설명되고 문서화되어 있으므로 표시하고 유지하고 있습니다. 다시 감사합니다. :-)

짐, 좋은 대답입니다. 하나의 잠재적 인 문제 (적어도 내 말미에)-버튼에 터치를 등록하려면 다음 줄을 추가해야했습니다. button.userInteractionEnabled = YES;
Mike Laurence

11
이 답변을보고있는 다른 사람들을 위해 행에 해당하는 버튼에 태그를 넣을 수도 있습니다 (여러 섹션이있는 경우 수학을 수행해야합니다). 함수. 터치를 계산하는 것보다 조금 빠를 수도 있습니다.
RyanJM

3
을 하드 코딩해야합니다 self.tableView. 행을 포함하는 테이블 뷰를 모르는 경우 어떻게해야합니까?
user102008

4
@RyanJM 나는 hitTest를하는 것이 과잉이며 태그만으로 충분하다고 생각했었다. 실제로 내 코드 중 일부에서 태그 아이디어를 사용했습니다. 그러나 오늘 사용자가 새 행을 추가 할 수있는 문제가 발생했습니다. 이것은 태그를 사용하여 핵을 죽입니다. Jim Dovey가 제안한 솔루션 (Apple의 샘플 코드에서 볼 수있는)은 일반적인 솔루션이며 모든 상황에서 작동합니다.
srik

77

이 웹 사이트가 매우 유용하다는 것을 알았습니다 .iphone에서 uitableview에 대한 사용자 정의 액세서리보기

요컨대, 이것을 다음에서 사용하십시오 cellForRowAtIndexPath::

UIImage *image = (checked) ? [UIImage imageNamed:@"checked.png"] : [UIImage imageNamed:@"unchecked.png"];

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
button.frame = frame;
[button setBackgroundImage:image forState:UIControlStateNormal];

[button addTarget:self action:@selector(checkButtonTapped:event:)  forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor clearColor];
cell.accessoryView = button;

그런 다음이 방법을 구현하십시오.

- (void)checkButtonTapped:(id)sender event:(id)event
{
    NSSet *touches = [event allTouches];
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];

    if (indexPath != nil)
    {
        [self tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath];
    }
}

4
Apple이 문서의 샘플 코드에서 권장하는 방식으로 +1이라고 말하고 싶습니다. developer.apple.com/library/ios/#samplecode/Accessory/Listings/…
cleverbit

프레임을 설정하는 것은 저에게 없어진 부분이었습니다. 텍스트를 원하지 않는 한 배경 대신 이미지를 설정할 수도 있습니다.
Jeremy Hicks

1
@richarddas의 답변에서 링크가 끊어졌습니다. 새로운 링크 : developer.apple.com/library/prerelease/ios/samplecode/Accessory/…
delavega66

7

내 접근 방식은 UITableViewCell하위 클래스 를 만들고 그 UITableViewDelegate안에 일반적인 메소드를 호출하는 논리를 캡슐화하는 것입니다.

// CustomTableViewCell.h
@interface CustomTableViewCell : UITableViewCell

- (id)initForIdentifier:(NSString *)reuseIdentifier;

@end

// CustomTableViewCell.m
@implementation CustomTableViewCell

- (id)initForIdentifier:(NSString *)reuseIdentifier;
{
    // the subclass specifies style itself
    self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
    if (self) {
        // get the button elsewhere
        UIButton *accBtn = [ViewFactory createTableViewCellDisclosureButton];
        [accBtn addTarget: self
                   action: @selector(accessoryButtonTapped:withEvent:)
         forControlEvents: UIControlEventTouchUpInside];
        self.accessoryView = accBtn;
    }
    return self;
}

#pragma mark - private

- (void)accessoryButtonTapped:(UIControl *)button withEvent:(UIEvent *)event
{
    UITableViewCell *cell = (UITableViewCell*)button.superview;
    UITableView *tableView = (UITableView*)cell.superview;
    NSIndexPath *indexPath = [tableView indexPathForCell:cell];
    [tableView.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
}

@end

이것이 최선의 대답입니다. 그러나 button.superview, cell.superview[tableView.delegate tableView:...]안전 충분하지 않습니다.
Mr. Ming

3

위의 Jim Dovey의 답변에 대한 확장 :

UITableView와 함께 UISearchBarController를 사용할 때주의하십시오. 이 경우 대신 확인 self.searchDisplayController.active하고 사용 하고 싶습니다 . 그렇지 않으면 searchDisplayController가 활성화되어있을 때, 특히 검색 결과가 스크롤 될 때 예기치 않은 결과가 발생합니다.self.searchDisplayController.searchResultsTableViewself.tableView

예를 들면 다음과 같습니다.

- (void) accessoryButtonTapped:(UIControl *)button withEvent:(UIEvent *)event
{
    UITableView* tableView = self.tableView;
    if(self.searchDisplayController.active)
        tableView = self.searchDisplayController.searchResultsTableView;

    NSIndexPath * indexPath = [tableView indexPathForRowAtPoint:[[[event touchesForView:button] anyObject] locationInView:tableView]];
    if(indexPath)
       [tableView.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
}

2
  1. 버튼 태그에 대한 매크로를 정의하십시오.

    #define AccessoryViewTagSinceValue 100000 // (AccessoryViewTagSinceValue * sections + rows) must be LE NSIntegerMax
  2. 셀을 만들 때 만들기 버튼을 누르고 cell.accessoryView를 설정하십시오.

    UIButton *accessoryButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
    accessoryButton.frame = CGRectMake(0, 0, 30, 30);
    [accessoryButton addTarget:self action:@selector(accessoryButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
    cell.accessoryView = accessoryButton;
  3. UITableViewDataSource 메소드 -tableView : cellForRowAtIndexPath에서 indexPath로 cell.accessoryView.tag를 설정하십시오.

    cell.accessoryView.tag = indexPath.section * AccessoryViewTagSinceValue + indexPath.row;
  4. 버튼의 이벤트 핸들러

    - (void) accessoryButtonTapped:(UIButton *)button {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:button.tag % AccessoryViewTagSinceValue
                                                    inSection:button.tag / AccessoryViewTagSinceValue];
    
        [self.tableView.delegate tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
    }
  5. UITableViewDelegate 메소드 구현

    - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
        // do sth.
    }

1
tag절대적으로 필요한 경우가 아니면 다른 솔루션을 찾아 보지 마십시오 .
Lifely

2

버튼을 누르면 UITableViewCell 서브 클래스 내에서 다음 메소드를 호출 할 수 있습니다.

 -(void)buttonTapped{
     // perform an UI updates for cell

     // grab the table view and notify it using the delegate
     UITableView *tableView = (UITableView *)self.superview;
     [tableView.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:[tableView indexPathForCell:self]];

 }

1

yanchenko 접근 방식으로 다음을 추가해야했습니다. [accBtn setFrame:CGRectMake(0, 0, 20, 20)];

xib 파일을 사용하여 tableCell을 사용자 정의하는 경우 initWithStyle : reuseIdentifier :가 호출되지 않습니다.

대신 재정의하십시오.

-(void)awakeFromNib
{
//Put your code here 

[super awakeFromNib];

}

1

간단한 대신 UIControl이벤트 디스패치 (예 : a UIButton) 를 올바르게 가져 오려면 a 를 사용해야합니다 UIView/UIImageView.


1

스위프트 5

이 방법은 UIButton.tag기본 비트 시프 팅을 사용하여 indexPath를 저장하는 데 사용합니다 . 이 방법은 65535 개 이상의 섹션이나 행이없는 한 32 비트 및 64 비트 시스템에서 작동합니다.

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "cellId")
    let accessoryButton = UIButton(type: .custom)
    accessoryButton.setImage(UIImage(named: "imageName"), for: .normal)
    accessoryButton.sizeToFit()
    accessoryButton.addTarget(self, action: #selector(handleAccessoryButton(sender:)), for: .touchUpInside)

    let tag = (indexPath.section << 16) | indexPath.row
    accessoryButton.tag = tag
    cell?.accessoryView = accessoryButton

}

@objc func handleAccessoryButton(sender: UIButton) {
    let section = sender.tag >> 16
    let row = sender.tag & 0xFFFF
    // Do Stuff
}

0

iOS 3.2부터는 다른 사람들이 권장하는 버튼을 피하고 대신 탭 제스처 인식기로 UIImageView를 사용하십시오. UIImageViews에서 기본적으로 해제되어있는 사용자 상호 작용을 활성화하십시오.

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