ios 为 UITableViewCell 的附件视图使用自定义图像并让它响应 UITableViewDelegate

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/869421/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 15:56:05  来源:igfitidea点击:

Using a custom image for a UITableViewCell's accessoryView and having it respond to UITableViewDelegate

ioscocoa-touchuitableviewuikit

提问by Jim Dovey

I'm using a custom drawn UITableViewCell, including the same for the cell's accessoryView. My setup for the accessoryView happens by the way of something like this:

我正在使用自定义绘制的 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];

Also when the cell is initialized, using initWithFrame:reuseIdentifier:I ensured to set the following property:

同样在单元格初始化时,使用initWithFrame:reuseIdentifier:我确保设置以下属性:

self.userInteractionEnabled = YES;

Unfortunately in my UITableViewDelegate, my tableView:accessoryButtonTappedForRowWithIndexPath:method (try repeating that 10 times) is not getting triggered. The delegate is definitely wired up properly.

不幸的是,在我的 UITableViewDelegate 中,我的tableView:accessoryButtonTappedForRowWithIndexPath:方法(尝试重复 10 次)没有被触发。代表绝对接线正确。

What can be possibly missing?

可能缺少什么?

Thanks all.

谢谢大家。

回答by Jim Dovey

Sadly that method doesn't get called unless the internal button type provided when you use one of the predefined types is tapped. To use your own, you'll have to create your accessory as a button or other UIControl subclass (I'd recommend a button using -buttonWithType:UIButtonTypeCustomand setting the button's image, rather than using a UIImageView).

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

Here's some things I use in Outpost, which customizes enough of the standard widgets (just slightly, to match our teal colouring) that I wound up doing my own UITableViewController intermediary subclass to hold utility code for all other table views to use (they now subclass OPTableViewController).

这是我在 Outpost 中使用的一些东西,它自定义了足够多的标准小部件(只是稍微匹配我们的青色),我最终做了自己的 UITableViewController 中间子类来保存所有其他要使用的表视图的实用程序代码(它们现在子类化OPTableViewController)。

Firstly, this function returns a new detail disclosure button using our custom graphic:

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

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

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

    return ( button );
}

The button will call this routine when it's done, which then feeds the standard UITableViewDelegate routine for accessory buttons:

按钮将在完成后调用此例程,然后为附件按钮提供标准 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];
}

This function locates the row by getting the location in the table view of a touch from the event provided by the button and asking the table view for the index path of the row at that point.

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

回答by Jon

I found this website to be very helpful: custom accessory view for your uitableview in iphone

我发现这个网站非常有帮助: 在 iphone 中为您的 uitableview 自定义附件视图

In short, use this in cellForRowAtIndexPath::

简而言之,在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;

then, implement this method:

然后,实现这个方法:

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

回答by yanchenko

My approach is to create a UITableViewCellsubclass and encapsulate the logic that will call the usual UITableViewDelegate's method within it.

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

回答by Zaggo

An extension to Jim Dovey's answer above:

上面 Jim Dovey 回答的扩展:

Be careful when you use a UISearchBarController with your UITableView. In that case you want to check for self.searchDisplayController.activeand use self.searchDisplayController.searchResultsTableViewinstead of self.tableView. Otherwise you'll get unexpected results when the searchDisplayController is active, especially when the search results are scrolled.

在 UITableView 中使用 UISearchBarController 时要小心。在这种情况下,您要检查self.searchDisplayController.active并使用self.searchDisplayController.searchResultsTableView而不是self.tableView. 否则,当 searchDisplayController 处于活动状态时,您会得到意想不到的结果,尤其是在滚动搜索结果时。

For example:

例如:

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

回答by Mr. Ming

  1. Define a macro for tags of buttons:

    #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.
    }
    
  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.
    }
    

回答by Eric Welander

When the button is tapped, you could have it call the following method inside a UITableViewCell subclass

当按钮被点击时,你可以让它在 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]];

 }

回答by Toydor

With yanchenko approach I had to add: [accBtn setFrame:CGRectMake(0, 0, 20, 20)];

使用 yanchenko 方法,我必须添加: [accBtn setFrame:CGRectMake(0, 0, 20, 20)];

If you're using xib file to customise your tableCell then initWithStyle:reuseIdentifier: wont get called.

如果您使用 xib 文件来自定义您的 tableCell,则 initWithStyle:reuseIdentifier: 不会被调用。

Instead override:

而是覆盖:

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

[super awakeFromNib];

}

回答by ikarius

You must use a UIControlto properly get event dispatch (for instance a UIButton) instead of simple UIView/UIImageView.

您必须使用 aUIControl来正确获取事件调度(例如 a UIButton)而不是 simple UIView/UIImageView

回答by Brody Robertson

Swift 5

斯威夫特 5

This approach uses the UIButton.tagto store the indexPath using basic bit-shifting. The approach will work on 32 & 64 bit systems as long as you don't have more than 65535 sections or rows.

这种方法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
}

回答by aeu

As of iOS 3.2 you can avoid the buttons that others here are recommending and instead use your UIImageView with a tap gesture recognizer. Be sure to enable user interaction, which is off by default in UIImageViews.

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