为 UITableViewCell 的 accoryView 使用自定义映像并让它响应 UITableView氏委托

我正在使用一个自定义绘制的 UITableViewCell,其中包括该单元的 accessoryView。我对 accoryView 的设置是这样的:

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次)没有被触发。这位代表肯定已经准备好了。

到底少了什么?

谢谢大家。

89200 次浏览

遗憾的是,除非点击使用预定义类型时提供的内部按钮类型,否则不会调用该方法。要使用你自己的,你必须创建你的附件作为一个按钮或其他 UIControl 子类(我建议一个按钮使用 -buttonWithType:UIButtonTypeCustom和设置按钮的图像,而不是使用 UIImageView)。

下面是我在 Outpost 中使用的一些东西,它定制了足够多的标准窗口小部件(只是稍微符合我们的青绿色) ,我最终使用我自己的 UITableViewController 中间子类来保存实用程序代码,以供所有其他表视图使用(它们现在子类 OPTableViewController)。

首先,这个函数返回一个新的详细信息披露按钮,使用我们的自定义图形:

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


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


return ( button );
}

按钮完成后会调用这个例程,然后为附件按钮提供标准的 UITableViewgenerate 例程:

- (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];
}

此函数通过从按钮提供的事件中获取触摸的表视图中的位置,并请求表视图获取该行在该点的索引路径来定位该行。

您必须使用 UIControl来正确地获取事件分派(例如 UIButton) ,而不是简单的 UIView/UIImageView

我发现这个网站很有帮助: 自定义附件视图为您的 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];
}
}

我的方法是创建一个 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

对于扬琴科的方法,我不得不加上: [accBtn setFrame:CGRectMake(0, 0, 20, 20)];

如果您使用 xib 文件来定制 tableCell,那么 initWithStyle: reuseIdentifier: wont 得到调用。

取而代之的是:

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


[super awakeFromNib];


}
  1. 为按钮标签定义一个宏:

    #define AccessoryViewTagSinceValue 100000 // (AccessoryViewTagSinceValue * sections + rows) must be LE NSIntegerMax
    
  2. Create button and set the cell.accessoryView when creating a cell

    UIButton *accessoryButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
    accessoryButton.frame = CGRectMake(0, 0, 30, 30);
    [accessoryButton addTarget:self action:@selector(accessoryButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
    cell.accessoryView = accessoryButton;
    
  3. Set cell.accessoryView.tag by indexPath in UITableViewDataSource method -tableView:cellForRowAtIndexPath:

    cell.accessoryView.tag = indexPath.section * AccessoryViewTagSinceValue + indexPath.row;
    
  4. Event handler for buttons

    - (void) accessoryButtonTapped:(UIButton *)button {
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:button.tag % AccessoryViewTagSinceValue
    inSection:button.tag / AccessoryViewTagSinceValue];
    
    
    [self.tableView.delegate tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
    }
    
  5. Implement the UITableViewDelegate method

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

当点击该按钮时,您可以让它调用 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]];


}

对吉姆•多维(Jim Dovey)上述答案的延伸:

在 UITableView 中使用 UISearchBarController 时要小心。在这种情况下,您需要检查 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];
}

从 iOS 3.2开始,你可以避免使用其他人推荐的按钮,而是使用带有点击手势识别器的 UIImageView。一定要启用用户交互,这在 UIImageView 中是默认关闭的。

Swift 5

这种方法使用 UIButton.tag来存储使用基本位移的索引路径。该方法将在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
}