当用户在表格视图中滑动单元格时,如何创建一个“更多”按钮(就像ios 7中的邮件应用程序)

我一直在这里和Cocoa Touch论坛上寻找这些信息,但我似乎找不到答案,我希望比我更聪明的人能给我一个解决方案。

我希望当用户滑动一个表格视图单元格时,显示多个编辑按钮(默认是删除按钮)。 在iOS 7的邮件应用程序中,你可以滑动删除,但会出现一个“更多”按钮。


当前回答

如何实施

看起来iOS 8打开了这个API。在Beta 2中有这样的功能提示。

为了让某些东西工作,在你的UITableView的委托上实现以下两个方法以获得所需的效果(参见示例的要点)。

- tableView:editActionsForRowAtIndexPath:
- tableView:commitEditingStyle:forRowAtIndexPath:

已知的问题

文档说tableView:commitEditingStyle:forRowAtIndexPath是:

使用uitableviewwroaction的编辑操作不会被调用——操作的处理程序将被调用。

然而,没有它,滑动就不能工作。即使方法存根是空白的,它现在仍然需要它。这显然是beta 2中的一个bug。

来源

https://twitter.com/marksands/status/481642991745265664 https://gist.github.com/marksands/76558707f583dbb8f870

原答案:https://stackoverflow.com/a/24540538/870028

更新:

示例代码与此工作(在Swift中):http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip

示例代码在MasterViewController.swift中包含了这个易于遵循的方法,通过这个方法你可以得到OP截图中显示的行为:

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {

    var moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "More", handler:{action, indexpath in
        println("MORE•ACTION");
    });
    moreRowAction.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0);

    var deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete", handler:{action, indexpath in
        println("DELETE•ACTION");
    });

    return [deleteRowAction, moreRowAction];
}

其他回答

如何实施

看起来iOS 8打开了这个API。在Beta 2中有这样的功能提示。

为了让某些东西工作,在你的UITableView的委托上实现以下两个方法以获得所需的效果(参见示例的要点)。

- tableView:editActionsForRowAtIndexPath:
- tableView:commitEditingStyle:forRowAtIndexPath:

已知的问题

文档说tableView:commitEditingStyle:forRowAtIndexPath是:

使用uitableviewwroaction的编辑操作不会被调用——操作的处理程序将被调用。

然而,没有它,滑动就不能工作。即使方法存根是空白的,它现在仍然需要它。这显然是beta 2中的一个bug。

来源

https://twitter.com/marksands/status/481642991745265664 https://gist.github.com/marksands/76558707f583dbb8f870

原答案:https://stackoverflow.com/a/24540538/870028

更新:

示例代码与此工作(在Swift中):http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip

示例代码在MasterViewController.swift中包含了这个易于遵循的方法,通过这个方法你可以得到OP截图中显示的行为:

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {

    var moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "More", handler:{action, indexpath in
        println("MORE•ACTION");
    });
    moreRowAction.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0);

    var deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete", handler:{action, indexpath in
        println("DELETE•ACTION");
    });

    return [deleteRowAction, moreRowAction];
}

这里有一种有点脆弱的方法,它不涉及私有api或构造自己的系统。你在对冲赌注,苹果不会打破这一点,希望他们会发布一个API,你可以用它来替换这几行代码。

KVO self.contentView.superview.layer.sublayer. Do this in init. This is the UIScrollView's layer. You can't KVO 'subviews'. When subviews changes, find the delete confirmation view within scrollview.subviews. This is done in the observe callback. Double the size of that view and add a UIButton to the left of its only subview. This is also done in the observe callback. The only subview of the delete confirmation view is the delete button. (optional) The UIButton event should look up self.superview until it finds a UITableView and then call a datasource or delegate method you create, such as tableView:commitCustomEditingStyle:forRowAtIndexPath:. You may find the indexPath of the cell by using [tableView indexPathForCell:self].

这还要求您实现标准表视图编辑委托回调。

static char kObserveContext = 0;

@implementation KZTableViewCell {
    UIScrollView *_contentScrollView;
    UIView *_confirmationView;
    UIButton *_editButton;
    UIButton *_deleteButton;
}

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        _contentScrollView = (id)self.contentView.superview;

        [_contentScrollView.layer addObserver:self
             forKeyPath:@"sublayers"
                options:0
                context:&kObserveContext];

        _editButton = [UIButton new];
        _editButton.backgroundColor = [UIColor lightGrayColor];
        [_editButton setTitle:@"Edit" forState:UIControlStateNormal];
        [_editButton addTarget:self
                        action:@selector(_editTap)
              forControlEvents:UIControlEventTouchUpInside];

    }
    return self;
}

-(void)dealloc {
    [_contentScrollView.layer removeObserver:self forKeyPath:@"sublayers" context:&kObserveContext];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if(context != &kObserveContext) {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        return;
    }
    if(object == _contentScrollView.layer) {
        for(UIView * view in _contentScrollView.subviews) {
            if([NSStringFromClass(view.class) hasSuffix:@"ConfirmationView"]) {
                _confirmationView = view;
                _deleteButton = [view.subviews objectAtIndex:0];
                CGRect frame = _confirmationView.frame;
                CGRect frame2 = frame;
                frame.origin.x -= frame.size.width;
                frame.size.width *= 2;
                _confirmationView.frame = frame;

                frame2.origin = CGPointZero;
                _editButton.frame = frame2;
                frame2.origin.x += frame2.size.width;
                _deleteButton.frame = frame2;
                [_confirmationView addSubview:_editButton];
                break;
            }
        }
        return;
    }
}

-(void)_editTap {
    UITableView *tv = (id)self.superview;
    while(tv && ![tv isKindOfClass:[UITableView class]]) {
        tv = (id)tv.superview;
    }
    id<UITableViewDelegate> delegate = tv.delegate;
    if([delegate respondsToSelector:@selector(tableView:editTappedForRowWithIndexPath:)]) {
        NSIndexPath *ip = [tv indexPathForCell:self];
        // define this in your own protocol
        [delegate tableView:tv editTappedForRowWithIndexPath:ip];
    }
}
@end

我使用tableViewCell来显示多个数据,在一个单元格上从右向左滑动()后,它将显示两个按钮批准和拒绝,有两个方法,第一个是ApproveFunc,它需要一个参数,另一个是RejectFunc,它也需要一个参数。

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
        let Approve = UITableViewRowAction(style: .normal, title: "Approve") { action, index in

            self.ApproveFunc(indexPath: indexPath)
        }
        Approve.backgroundColor = .green

        let Reject = UITableViewRowAction(style: .normal, title: "Reject") { action, index in

            self.rejectFunc(indexPath: indexPath)
        }
        Reject.backgroundColor = .red



        return [Reject, Approve]
    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func ApproveFunc(indexPath: IndexPath) {
        print(indexPath.row)
    }
    func rejectFunc(indexPath: IndexPath) {
        print(indexPath.row)
    }

这是一个(相当可笑的)私有API。

下面两个方法是私有的,并且被发送给UITableView的委托:

-(NSString *)tableView:(UITableView *)tableView titleForSwipeAccessoryButtonForRowAtIndexPath:(NSIndexPath *)indexPath;
-(void)tableView:(UITableView *)tableView swipeAccessoryButtonPushedForRowAtIndexPath:(NSIndexPath *)indexPath;

它们不言自明。

使用标准SDK是不可能实现的。然而,有各种各样的第三方解决方案或多或少地模仿了Mail.app中的行为。其中一些(例如MCSwipeTableViewCell, DAContextMenuTableViewController, RMSwipeTableViewCell)使用手势识别器检测滑动,其中一些(例如SWTableViewCell)在标准UITableViewCellScrollView (UITableViewCell的私有子视图)下面放置第二个UISScrollView,其中一些修改UITableViewCellScrollView的行为。

我最喜欢最后一种方法,因为触摸操作感觉最自然。具体来说,MSCMoreOptionTableViewCell很好。你的选择可能会根据你的具体需求而有所不同(你是否也需要从左到右平移,是否需要iOS 6兼容性等等)。还要注意,大多数这些方法都有一个负担:如果苹果在UITableViewCell子视图层次结构中做了改变,它们很容易在未来的iOS版本中被打破。