为UITableViewCell的accessoryView使用自定义图像并让它响应UITableViewDelegate

问题描述 投票:0回答:11

我正在使用自定义绘制的 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 次)没有被触发。代表肯定已正确连接。

可能缺少什么?

谢谢大家。

ios cocoa-touch uitableview uikit
11个回答
229
投票

遗憾的是,除非点击使用预定义类型之一时提供的内部按钮类型,否则不会调用该方法。要使用您自己的附件,您必须将附件创建为按钮或其他 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];
}

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


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

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

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时设置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方法中通过indexPath设置cell.accessoryView.tag -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.
    }
    

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
使用基本位移位来存储索引路径。只要您的节或行不超过 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 中默认处于关闭状态。


0
投票

有一个非常有用的扩展:

public extension UIResponder {
    func next<T>(of type: T.Type) -> T? {
        return (next as? T) ?? next?.next(of: type)
    }
    func closest<T>(of type: T.Type) -> T? {
        return (self as? T) ?? next?.closest(of: type)
    }
}

cell.accessoryView
设置
UIButton
:

let button = UIButton(frame: CGRect(x: 0.0, y: 0.0, width: 26.0, height: 26.0), primaryAction: UIAction { action in
    if let button = action.sender as? UIButton,
       let cell = button.next(of: UITableViewCell.self),
       let tableView = cell.next(of: UITableView.self),
       let indexPath = tableView.indexPath(for: cell) {
        tableView.delegate?.tableView?(tableView, accessoryButtonTappedForRowWith: indexPath)
    }
})
let image = UIImage(systemName: "chevron.forward.circle")?
    .withTintColor(.systemGray, renderingMode: .alwaysOriginal)
    .applyingSymbolConfiguration(UIImage.SymbolConfiguration(pointSize: UIFont.systemFontSize + 2.0, weight: .light, scale: .large))
button.setImage(image, for: .normal)
cell.accessoryView = button

然后分别点击

cell
cell.accessoryView
时将调用这两个委托方法:

extension YourTableViewController: UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        print("didSelectRowAt: \(indexPath)")
    }
    
    func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
        print("accessoryButtonTappedForRowWith: \(indexPath)")
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.