iPhone UITableView - 删除按钮

27

我正在使用UITableView的“滑动删除”功能。

问题是我正在使用自定义的UITableViewCell,并且是逐个项目地创建的。

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

我需要更改删除按钮的位置(仅需将其向左移动约10像素),我该怎么做?

这是我用于创建单元格的现有代码:

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"cellForRowAtIndexPath");
#if USE_CUSTOM_DRAWING
    const NSInteger TOP_LABEL_TAG = 1001;
    const NSInteger BOTTOM_LABEL_TAG = 1002;
    UILabel *topLabel;
    UILabel *bottomLabel;
#endif

    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
        //
        // Create the cell.
        //
        cell =
        [[[UITableViewCell alloc]
          initWithFrame:CGRectZero
          reuseIdentifier:CellIdentifier]
         autorelease];

#if USE_CUSTOM_DRAWING


        const CGFloat LABEL_HEIGHT = 20;
        UIImage *image = [UIImage imageNamed:@"trans_clock.png"];

        //
        // Create the label for the top row of text
        //
        topLabel =
        [[[UILabel alloc]
          initWithFrame:
          CGRectMake(
                     image.size.width + 2.0 * cell.indentationWidth,
                     0.5 * (aTableView.rowHeight - 2 * LABEL_HEIGHT),
                     aTableView.bounds.size.width -
                     image.size.width - 4.0 * cell.indentationWidth
                     ,
                     LABEL_HEIGHT)]
         autorelease];
        [cell.contentView addSubview:topLabel];

        //
        // Configure the properties for the text that are the same on every row
        //
        topLabel.tag = TOP_LABEL_TAG;
        topLabel.backgroundColor = [UIColor clearColor];
        topLabel.textColor = fontColor;
        topLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
        topLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];

        //
        // Create the label for the top row of text
        //
        bottomLabel =
        [[[UILabel alloc]
          initWithFrame:
          CGRectMake(
                     image.size.width + 2.0 * cell.indentationWidth,
                     0.5 * (aTableView.rowHeight - 2 * LABEL_HEIGHT) + LABEL_HEIGHT,
                     aTableView.bounds.size.width -
                     image.size.width - 4.0 * cell.indentationWidth
                     ,
                     LABEL_HEIGHT)]
         autorelease];
        [cell.contentView addSubview:bottomLabel];

        //
        // Configure the properties for the text that are the same on every row
        //
        bottomLabel.tag = BOTTOM_LABEL_TAG;
        bottomLabel.backgroundColor = [UIColor clearColor];
        bottomLabel.textColor = fontColor;
        bottomLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
        bottomLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize] - 2];

        //
        // Create a background image view.
        //
        cell.backgroundView =
        [[[UIImageView alloc] init] autorelease];
        cell.selectedBackgroundView =
        [[[UIImageView alloc] init] autorelease];
#endif
    }

#if USE_CUSTOM_DRAWING
    else
    {
        topLabel = (UILabel *)[cell viewWithTag:TOP_LABEL_TAG];
        bottomLabel = (UILabel *)[cell viewWithTag:BOTTOM_LABEL_TAG];
    }
    topLabel.text  = @"Example Text";
    topLabel.textColor = fontColor;

    bottomLabel.text = @"More Example Text";
    bottomLabel.textColor = fontColor;

    //
    // Set the background and selected background images for the text.
    // Since we will round the corners at the top and bottom of sections, we
    // need to conditionally choose the images based on the row index and the
    // number of rows in the section.
    //
    UIImage *rowBackground;
    UIImage *selectionBackground;
    NSInteger sectionRows = [aTableView numberOfRowsInSection:[indexPath section]];
    NSInteger row = [indexPath row];
    if (row == 0 && row == sectionRows - 1)
    {
        rowBackground = [UIImage imageNamed:@"topAndBottomRow.png"];
        selectionBackground = [UIImage imageNamed:@"topAndBottomRowSelected.png"];
    }
    else if (row == 0)
    {
        rowBackground = [UIImage imageNamed:@"topRow.png"];
        selectionBackground = [UIImage imageNamed:@"topRowSelected.png"];
    }
    else if (row == sectionRows - 1)
    {
        rowBackground = [UIImage imageNamed:@"bottomRow.png"];
        selectionBackground = [UIImage imageNamed:@"bottomRowSelected.png"];
    }
    else
    {
        rowBackground = [UIImage imageNamed:@"middleRow.png"];
        selectionBackground = [UIImage imageNamed:@"middleRowSelected.png"];
    }
    ((UIImageView *)cell.backgroundView).image = rowBackground;
    ((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;

    cell.imageView.image = [UIImage imageNamed:@"Example_Image.png"];
#else
    cell.text = @"Example";
#endif
    return cell;
}
6个回答

58

对我来说,解决这个问题的最好方法是在MyCell:UITableViewCell中重写-(void)layoutSubviews方法。

在这里,你不仅可以自定义删除按钮的位置,还可以重新定位编辑模式下的编辑和重新排序控件。

- (void)layoutSubviews
{
    [super layoutSubviews];

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:0.0f];

    for (UIView *subview in self.subviews) {


        if ([NSStringFromClass([subview class]) isEqualToString:@"UITableViewCellDeleteConfirmationControl"]) { 
            CGRect newFrame = subview.frame;
            newFrame.origin.x = 200;
            subview.frame = newFrame;
        }
        else if ([NSStringFromClass([subview class]) isEqualToString:@"UITableViewCellEditControl"]) {             
            CGRect newFrame = subview.frame;
            newFrame.origin.x = 100;
            subview.frame = newFrame;
        }
        else if ([NSStringFromClass([subview class]) isEqualToString:@"UITableViewCellReorderControl"]) {             
            CGRect newFrame = subview.frame;
            newFrame.origin.x = 200;
            subview.frame = newFrame;
        }
    }
    [UIView commitAnimations];
}

4
这个解决方案比已接受的更好,因为这里没有人工延迟按钮动画。 - Morrowless
很棒的东西,需要注意的一点是动画似乎在这里并不必要... - Nick Hingston
3
太棒了!非常感谢,这是最好的答案,无需再寻找其他答案了。 - n.evermind
你不应该在 layoutSubviews 中进行动画。 - titaniumdecoy
1
正如我在另一个答案的评论中所提到的:请注意类名可能会在未来的iOS版本中更改为不同的内容,比如:UITableViewCellDeleteConfirmationControl_Legacy。最好使用以下方式进行判断:if([NSStringFromClass([subview class]) rangeOfString:@"Delete"].location != NSNotFound)。 - Lukasz
显示剩余7条评论

24

Iwat的代码对我无效,但是这个有效。

- (void)willTransitionToState:(UITableViewCellStateMask)state {
[super willTransitionToState:state];
if ((state & UITableViewCellStateShowingDeleteConfirmationMask) == UITableViewCellStateShowingDeleteConfirmationMask) {
for (UIView *subview in self.subviews) {
if ([NSStringFromClass([subview class]) isEqualToString:@"UITableViewCellDeleteConfirmationControl"]) {
subview.hidden = YES; subview.alpha = 0.0; } } } }
- (void)didTransitionToState:(UITableViewCellStateMask)state {
[super didTransitionToState:state];
if (state == UITableViewCellStateShowingDeleteConfirmationMask || state == UITableViewCellStateDefaultMask) { for (UIView *subview in self.subviews) {
if ([NSStringFromClass([subview class]) isEqualToString:@"UITableViewCellDeleteConfirmationControl"]) { // 获取删除按钮视图并向左移动20个像素 UIView *deleteButtonView = (UIView *)[subview.subviews objectAtIndex:0]; CGRect f = deleteButtonView.frame; f.origin.x -= 20; deleteButtonView.frame = f; subview.hidden = NO;
[UIView beginAnimations:@"anim" context:nil]; subview.alpha = 1.0; [UIView commitAnimations]; } } } }

1
抱歉,我是stackoverflow的新手。我只想让这段代码加粗。当我点击加粗按钮时,“**”会被添加到末尾。 - nonamelive
5
我想补充一点,这是依赖于私有类名的。尽管私有符号被隐藏在编译器和链接器中,但它们仍然被使用。 - Jasarien
8
UITableViewCellDeleteConfirmationControl 不是一个公开的类。 - Jasarien
@nonamelive:那么,至少你应该在你的答案中清楚地注明。像这样的东西(字符串比较到私有类名,摆弄你自己无法控制的类的子视图([subview.subviews objectAtIndex:0]))在面向提交/生产使用的应用程序中永远不应该被执行! - Daniel Rinser
即使@nonamelive并不完全正确,因为类名可能会在未来的iOS版本中更改为不同的名称,比如:UITableViewCellDeleteConfirmationControl_Legacy;-)最好使用:if([NSStringFromClass([subview class]) rangeOfString:@"Delete"].location != NSNotFound) - Lukasz
显示剩余7条评论

2

我无法改变删除按钮的实际外观,但您可以更改文本。也许您可以使用此方法来获得所需的效果?

请查看以下UITableViewDelegate协议成员:

- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath

此外,您可以通过子类化UITableViewCell并覆盖以下内容来检测删除按钮的显示:
- (void)willTransitionToState:(UITableViewCellStateMask)state
- (void)didTransitionToState:(UITableViewCellStateMask)state

无论哪种情况,当删除按钮被显示时,状态掩码将是UITableViewCellStateShowingDeleteConfirmationMask。希望这些提示能指引您朝正确的方向前进。

2

我使用的解决方案乍一看似乎有点破解的味道,但确实能够解决问题,并且不依赖于未记录的API:

/**
 * Transition to state
 */
-(void)willTransitionToState:(UITableViewCellStateMask)state {

    if(state & UITableViewCellStateShowingDeleteConfirmationMask)
        _deleting = YES;
    else if(!(state & UITableViewCellStateShowingDeleteConfirmationMask))
        _deleting = NO;
    [super willTransitionToState:state];

}

/**
 * Reset cell transformations
 */
-(void)resetCellTransform {

    self.transform = CGAffineTransformIdentity;
    _background.transform = CGAffineTransformIdentity;
    _content.transform = CGAffineTransformIdentity;
}

/**
 * Move cell around if we are currently in delete mode
 */
-(void)updateWithCurrentState {

    if(_deleting) {

        float x = -20;
        float y = 0;
        self.transform = CGAffineTransformMakeTranslation(x, y);
        _background.transform = CGAffineTransformMakeTranslation(-x, -y);
        _content.transform = CGAffineTransformMakeTranslation(-x, -y);
    }
}

-(void)setFrame:(CGRect)frame {

    [self resetCellTransform];
    [super setFrame:frame];
    [self updateWithCurrentState];
}

-(void)layoutSubviews {

    [self resetCellTransform];
    [super layoutSubviews];
    [self updateWithCurrentState];
}

这实际上是移动单元格的位置并重新调整可见部分。WillTransitionToState 只是设置一个实例变量,指示单元格是否处于删除模式。重写 setFrame 是为了支持将手机旋转到横向和纵向。_background 是我单元格的背景视图,而 _content 是添加到单元格的 contentView 上的一个视图,包含所有标签等。


1

我不知道有什么方法可以“将删除按钮向左移动10像素”。但是,您可以通过监听UITableViewCell子类的willTransitionToState:消息来围绕不可移动的删除按钮的静态位置动画自定义表格单元格的内容,详情请参见这里

引用文档:

UITableViewCell的子类可以实现此方法以在更改状态时对单元格进行其他动画更改。每当单元格从正常状态(默认)转换为编辑模式等状态时,UITableViewCell都会调用此方法。自定义单元格可以设置和定位任何出现在新状态下的新视图。然后,单元格接收一个layoutSubviews消息(UIView),在其中可以将这些新视图定位到其新状态的最终位置。子类在重写此方法时必须始终调用super。

你要找的是 UITableViewCellStateShowingDeleteConfirmationMask 值。这是创建 UITableViewCell 子类可能更好的时候之一,因此从控制器中创建自定义单元格的实例即可。然后,单元格可以像 animateLeftanimateRight 一样自我调整并处理调整其自身子视图的内部工作。

我一开始考虑使用UITableViewCell的子类,但最终决定通过代码实现的原因是我不想管理3个自定义的UITableViewCell对象(顶部、中间和底部);而现在为了满足我的需求,需要创建4个(额外的一个用于删除),除非我非常错误? - Mick Walker

0

我不知道最终的解决方案,但是根据我的尝试,以下代码可能会有用。

// subclass UITableViewCell

- (void)willTransitionToState:(UITableViewCellStateMask)state
{
    [super willTransitionToState:state];

    if ((state & UITableViewCellStateShowingDeleteConfirmationMask) == UITableCellStateShowingDeleteConfirmationMask)
    {
        for (UIView *subview in self.subviews)
        {
            if ([NSStringFromClass([subview class]) isEqualToString:@"UITableViewCellDeleteConfirmationControl"])
            {
                subview.hidden = YES;
                subview.alpha = 0;
            }
        }
    }
}

- (void)didTransitionToState:(UITableViewCellStateMask)state
{
    [super willTransitionToState:state];

    if ((state & UITableViewCellStateShowingDeleteConfirmationMask) == UITableCellStateShowingDeleteConfirmationMask)
    {
        for (UIView *subview in self.subviews)
        {
            if ([NSStringFromClass([subview class]) isEqualToString:@"UITableViewCellDeleteConfirmationControl"])
            {
                subview.frame = CGRectMake(subview.frame.origin.x - 10, subview.frame.origin.y, subview.frame.size.width, subview.frame.size.height);
                subview.hidden = NO;

                [UIView beginAnimations:@"anim" context:nil];
                subview.alpha = 1;
                [UIView commitAnimations];
            }
        }
    }
}

如何在Swift中使用这个? - Janak Thakkar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接