使用自定义图像作为UITableViewCell的annexView并使其响应UITableViewDelegate


139

我正在使用自定义绘制的UITableViewCell,其中包括与单元格相同的UITableViewCell accessoryView。我对AccessoriesView的设置是通过以下方式进行的:

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次)。代表肯定已正确连接。

可能缺少什么?

谢谢大家

Answers:


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的示例Accessory应用程序中看到了如何执行此操作。您的答案已得到很好的解释和记录,因此,我将其标记出来并保留在周围。再次感谢。:-)

吉姆,很好的答案。一个潜在的问题(至少在我看来是这样)-我必须添加以下行才能在按钮上注册触摸:button.userInteractionEnabled = YES;
Mike Laurence

11
对于其他人,您也可以在对应于该行的按钮上放置一个标签(如果您有多个部分,则需要做一些数学运算),然后将标签从按钮中拉出功能。我认为这可能会比计算触摸速度快一点。
RyanJM 2010年

3
这需要您对进行硬编码self.tableView。如果您不知道哪个tableview包含该行怎么办?
user102008 2011年

4
@RyanJM我以前认为做一次hitTest是过分的,标签就足够了。实际上,我在某些代码中使用了标签概念。但是今天我遇到了一个问题,用户可以在其中添加新行。这使用标签杀死了黑客。吉姆·多维(Jim Dovey)提出的解决方案(如苹果的示例代码所示)是一种通用解决方案,适用于所有情况
2013年

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
我想说+1这个,因为它是苹果公司建议用户在他们的示例代码在做他们的文档:developer.apple.com/library/ios/#samplecode/Accessory/Listings/...
cleverbit

设置框架是我所缺少的。您也可以只设置setImage(而不是背景),只要您也不需要任何文本即可。
杰里米·希克斯

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.superviewcell.superview[tableView.delegate tableView:...]不够安全。
明先生

3

对上述问题的扩展:

当将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. 通过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除非绝对必要,否则任何人都不应使用,请查找其他解决方案。
2015年

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

您必须使用a 而不是simple UIControl来正确获取事件分配(例如a UIButtonUIView/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中此功能处于关闭状态。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.