การใช้อิมเมจที่กำหนดเองสำหรับอุปกรณ์เสริมของ UITableViewCell และให้มันตอบสนองต่อ UITableViewDelegate


139

ฉันใช้ UITableViewCell ที่วาดเองซึ่งรวมถึงเซลล์ของ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:UIButtonTypeCustomและตั้งค่ารูปภาพของปุ่มแทนที่จะใช้ UIImageView)

นี่คือบางสิ่งที่ฉันใช้ใน 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];
}

ฟังก์ชั่นนี้หาตำแหน่งของแถวโดยรับตำแหน่งในมุมมองตารางของการสัมผัสจากเหตุการณ์ที่จัดทำโดยปุ่มและขอมุมมองตารางสำหรับเส้นทางดัชนีของแถวที่จุดนั้น


ขอบคุณจิม นั่นเป็นความอัปยศที่ฉันใช้เวลามากกว่า 20 นาทีในการสงสัยว่าทำไมฉันถึงทำไม่ได้ด้วย imageView ที่กำหนดเอง ฉันเพิ่งเห็นวิธีการทำสิ่งนี้ในแอปอุปกรณ์ตัวอย่างของ Apple คำตอบของคุณได้รับการอธิบายอย่างดีและมีการบันทึกไว้แล้วดังนั้นฉันจึงทำเครื่องหมายและเก็บไว้รอบ ๆ ขอบคุณอีกครั้ง. :-)

จิมคำตอบที่ดี ปัญหาหนึ่งที่อาจเกิดขึ้น (อย่างน้อยในตอนท้ายของฉัน) - ฉันต้องเพิ่มบรรทัดต่อไปนี้เพื่อให้ได้สัมผัสเพื่อลงทะเบียนบนปุ่ม: button.userInteractionEnabled = YES;
Mike Laurence

11
สำหรับคนอื่นที่ดูคำตอบนี้คุณสามารถใส่แท็กบนปุ่มที่สอดคล้องกับแถว (ถ้าคุณมีหลายส่วนคุณต้องทำคณิตศาสตร์) แล้วดึงแท็กออกจากปุ่ม ฟังก์ชั่น. ฉันคิดว่ามันอาจจะเร็วกว่าการคำนวณแบบสัมผัสเล็กน้อย
RyanJM

3
self.tableViewนี้คุณจะต้องยากรหัส ถ้าคุณไม่รู้ว่า tableview บรรจุแถวอะไร
user102008

4
@RyanJM ฉันเคยคิดว่าการทำ hitTest นั้นเกินความจริงแล้วแท็กก็พอเพียงแล้ว อันที่จริงฉันใช้แนวคิดแท็กในบางรหัสของฉัน แต่วันนี้ฉันพบปัญหาที่ผู้ใช้สามารถเพิ่มแถวใหม่ วิธีนี้จะทำให้การแฮ็คโดยใช้แท็ก วิธีการแก้ปัญหาโดยจิม Dovey (และเท่าที่เห็นในตัวอย่างรหัสแอปเปิ้ล) เป็นวิธีการแก้ปัญหาทั่วไปและการทำงานในทุกสถานการณ์
srik

77

ฉันพบว่าเว็บไซต์นี้มีประโยชน์มาก: มุมมองอุปกรณ์เสริมที่กำหนดเองสำหรับ uitableview ของคุณใน iphone

ในระยะสั้นใช้สิ่งนี้ใน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
ฉันจะบอกว่า +1 นี่เป็นสิ่งที่ Apple แนะนำให้ทำในโค้ดตัวอย่างใน docs ของพวกเขา: developer.apple.com/library/ios/#samplecode/Accessory/Listings/…
cleverbit

การตั้งกรอบเป็นชิ้นส่วนที่ขาดหายไปสำหรับฉัน คุณสามารถตั้งค่าภาพ (แทนพื้นหลัง) ได้ตราบใดที่คุณไม่ต้องการข้อความ
Jeremy Hicks

1
ลิงก์เสียในคำตอบของ @ richarddas ลิงก์ใหม่: developer.apple.com/library/prerelease/ios/samplecode/Accessory/
......

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:...]มีไม่เพียงพอที่ปลอดภัย
นายหมิง

3

ส่วนขยายของคำตอบของ Jim Dovey ด้านบน:

ระวังเมื่อคุณใช้ UISearchBarController กับ UITableView ของคุณ ในกรณีที่คุณต้องการที่จะตรวจสอบself.searchDisplayController.activeและใช้แทนself.searchDisplayController.searchResultsTableView self.tableViewมิฉะนั้นคุณจะได้รับผลลัพธ์ที่ไม่คาดคิดเมื่อ searchDisplayController เปิดใช้งานโดยเฉพาะเมื่อผลลัพธ์การค้นหาถูกเลื่อน

ตัวอย่างเช่น:

- (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. ตั้งค่า cell.accessoryView.tag ตาม indexPath ในวิธี UITableViewDataSource -tableView: cellForRowAtIndexPath:

    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อย่างถูกต้องจะได้รับการจัดส่งเหตุการณ์ (ตัวอย่างเช่นUIButton) UIView/UIImageViewแทนง่าย


1

สวิฟท์ 5

วิธีการนี้ใช้UIButton.tagเพื่อเก็บ indexPath โดยใช้การเลื่อนบิตพื้นฐาน วิธีนี้จะใช้กับระบบ 32 และ 64 บิตตราบใดที่คุณไม่มีส่วนหรือแถวมากกว่า 65535

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.