Ios 为UITableViewCell使用自定义图像';s accessoryView并使其响应UITableViewDelegate

Ios 为UITableViewCell使用自定义图像';s accessoryView并使其响应UITableViewDelegate,ios,cocoa-touch,uitableview,uikit,Ios,Cocoa Touch,Uitableview,Uikit,我使用的是一个自定义绘制的UITableViewCell,包括该单元格的附件视图。我对accessoryView的设置方式如下: UIImage *accessoryImage = [UIImage imageNamed:@"accessoryDisclosure.png"]; UIImageView *accImageView = [[UIImageView alloc] initWithImage:accessoryImage]; accImageView.userInteractionEn

我使用的是一个自定义绘制的UITableViewCell,包括该单元格的
附件视图
。我对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次)。该代表显然已正确连接

可能遗漏了什么


谢谢大家。

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

下面是我在Outpost中使用的一些东西,它定制了足够多的标准小部件(只是稍微有点,以匹配我们的teal颜色),我最终完成了自己的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];
}

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

您必须使用
UIControl
正确获取事件调度(例如
ui按钮
)我发现这个网站非常有用,而不是简单的
UIView/UIImageView

简而言之,在
单元格中使用此选项,如rowatindexpath:

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

对于yanchenko方法,我必须添加:
[accBtn setFrame:CGRectMake(0,0,20,20)]

如果您使用xib文件定制tableCell,那么initWithStyle:reuseIdentifier:将不会被调用

而是覆盖:

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

[super awakeFromNib];

}
  • 为按钮的标记定义宏:

    #define AccessoryViewTagSinceValue 100000 // (AccessoryViewTagSinceValue * sections + rows) must be LE NSIntegerMax
    
  • 创建按钮,并在创建单元格时设置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;
    
  • 在UITableViewDataSource方法-tableView:cellForRowAtIndexPath中通过indexPath设置cell.accessoryView.tag:

    cell.accessoryView.tag = indexPath.section * AccessoryViewTagSinceValue + indexPath.row;
    
  • 按钮的事件处理程序

    - (void) accessoryButtonTapped:(UIButton *)button {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:button.tag % AccessoryViewTagSinceValue
                                                    inSection:button.tag / AccessoryViewTagSinceValue];
    
        [self.tableView.delegate tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
    }
    
  • 实现UITableViewDelegate方法

    - (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
    来存储使用基本位移位的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
    }
    
    public func tableView(\utableview:UITableView,cellForRowAt indexath:indexPath)->UITableView单元格{
    let cell=tableView.dequeueReusableCell(带有标识符:“cellId”)
    让accessoryButton=UIButton(类型:。自定义)
    setImage(UIImage(名为:“imageName”),用于:。正常)
    accessoryButton.sizeToFit()
    addTarget(self,action:#选择器(handleAccessoryButton(sender:)),for:.touchUpInside)
    让标记=(indexPath.section>16
    让行=sender.tag&0xFFFF
    //做事
    }
    
    谢谢Jim。真遗憾,我花了20多分钟想知道为什么不能使用自定义imageView。我刚刚在Apple的示例附件应用程序上看到了如何做到这一点。虽然你的答案解释得很好,也有文档记录,但我正在标记并保存它。再次感谢:-)Jim,回答得很好。一个潜在的问题(至少在我这方面)-我必须添加以下行才能在按钮上注册触摸:button.userInteractionEnabled=YES;对于其他看到这个答案的人,你也可以在对应于行的按钮上放置一个标记(如果你有多个部分,你需要做一些数学运算),然后从函数中的按钮中拉出标记。我认为这可能比计算触摸速度快一点。这需要您硬编码
    self.tableView
    。如果您不知道哪个tableview包含该行怎么办?@RyanJM我过去认为进行命中测试是过分的,标记就足够了。事实上,我在我的一些代码中使用了标记的思想。但是今天我遇到了一个问题,用户可以添加新行。这将杀死使用标签的黑客。Jim Dovey建议的解决方案(如苹果的示例代码所示)是一个通用解决方案,适用于所有情况。我认为+1适用于这一点,因为苹果在其文档的示例代码中建议这样做:设置框架是我缺少的一部分。你也可以只设置图像(而不是背景),只要你不需要任何文本