当用户在表格视图中滑动单元格时,如何创建一个“更多”按钮(就像ios 7中的邮件应用程序)
我一直在这里和Cocoa Touch论坛上寻找这些信息,但我似乎找不到答案,我希望比我更聪明的人能给我一个解决方案。
我希望当用户滑动一个表格视图单元格时,显示多个编辑按钮(默认是删除按钮)。 在iOS 7的邮件应用程序中,你可以滑动删除,但会出现一个“更多”按钮。
当用户在表格视图中滑动单元格时,如何创建一个“更多”按钮(就像ios 7中的邮件应用程序)
我一直在这里和Cocoa Touch论坛上寻找这些信息,但我似乎找不到答案,我希望比我更聪明的人能给我一个解决方案。
我希望当用户滑动一个表格视图单元格时,显示多个编辑按钮(默认是删除按钮)。 在iOS 7的邮件应用程序中,你可以滑动删除,但会出现一个“更多”按钮。
当前回答
这里有一种有点脆弱的方法,它不涉及私有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
其他回答
这里有一个简单的解决办法。它能够在UITableViewCell中显示和隐藏自定义UIView。 显示逻辑包含在从UITableViewCell, BaseTableViewCell扩展的类中。
BaseTableViewCell.h
#import <UIKit/UIKit.h>
@interface BaseTableViewCell : UITableViewCell
@property(nonatomic,strong)UIView* customView;
-(void)showCustomView;
-(void)hideCustomView;
@end
BaseTableViewCell。米
#import "BaseTableViewCell.h"
@interface BaseTableViewCell()
{
BOOL _isCustomViewVisible;
}
@end
@implementation BaseTableViewCell
- (void)awakeFromNib {
// Initialization code
}
-(void)prepareForReuse
{
self.customView = nil;
_isCustomViewVisible = NO;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
-(void)showCustomView
{
if(nil != self.customView)
{
if(!_isCustomViewVisible)
{
_isCustomViewVisible = YES;
if(!self.customView.superview)
{
CGRect frame = self.customView.frame;
frame.origin.x = self.contentView.frame.size.width;
self.customView.frame = frame;
[self.customView willMoveToSuperview:self.contentView];
[self.contentView addSubview:self.customView];
[self.customView didMoveToSuperview];
}
__weak BaseTableViewCell* blockSelf = self;
[UIView animateWithDuration:.5 animations:^(){
for(UIView* view in blockSelf.contentView.subviews)
{
CGRect frame = view.frame;
frame.origin.x = frame.origin.x - blockSelf.customView.frame.size.width;
view.frame = frame;
}
}];
}
}
}
-(void)hideCustomView
{
if(nil != self.customView)
{
if(_isCustomViewVisible)
{
__weak BaseTableViewCell* blockSelf = self;
_isCustomViewVisible = NO;
[UIView animateWithDuration:.5 animations:^(){
for(UIView* view in blockSelf.contentView.subviews)
{
CGRect frame = view.frame;
frame.origin.x = frame.origin.x + blockSelf.customView.frame.size.width;
view.frame = frame;
}
}];
}
}
}
@end
要获得此功能,只需从BaseTableViewCell扩展您的表视图单元格。
接下来, 在UIViewController中,它实现了UITableViewDelegate,创建了两个手势识别器来处理左右滑动。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.tableView registerNib:[UINib nibWithNibName:CUSTOM_CELL_NIB_NAME bundle:nil] forCellReuseIdentifier:CUSTOM_CELL_ID];
UISwipeGestureRecognizer* leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleLeftSwipe:)];
leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[self.tableView addGestureRecognizer:leftSwipeRecognizer];
UISwipeGestureRecognizer* rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightSwipe:)];
rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[self.tableView addGestureRecognizer:rightSwipeRecognizer];
}
然后添加两个滑动处理程序
- (void)handleLeftSwipe:(UISwipeGestureRecognizer*)recognizer
{
CGPoint point = [recognizer locationInView:self.tableView];
NSIndexPath* index = [self.tableView indexPathForRowAtPoint:point];
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:index];
if([cell respondsToSelector:@selector(showCustomView)])
{
[cell performSelector:@selector(showCustomView)];
}
}
- (void)handleRightSwipe:(UISwipeGestureRecognizer*)recognizer
{
CGPoint point = [recognizer locationInView:self.tableView];
NSIndexPath* index = [self.tableView indexPathForRowAtPoint:point];
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:index];
if([cell respondsToSelector:@selector(hideCustomView)])
{
[cell performSelector:@selector(hideCustomView)];
}
}
现在,在UITableViewDelegate的cellForRowAtIndexPath中,你可以创建自定义UIView并将它附加到dequeuedcell中。
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCellTableViewCell* cell = (CustomCellTableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"CustomCellTableViewCell" forIndexPath:indexPath];
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"CellCustomView"
owner:nil
options:nil];
CellCustomView* customView = (CellCustomView*)[ nibViews objectAtIndex: 0];
cell.customView = customView;
return cell;
}
当然,这种加载自定义UIView的方式只适用于这个例子。按照您的意愿管理它。
我使用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)
}
为了改进Johnny的回答,现在可以使用下面的公共API来完成:
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "More", handler:{action, indexpath in
print("MORE•ACTION");
});
moreRowAction.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0);
let deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "Delete", handler:{action, indexpath in
print("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
斯威夫特4
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = UIContextualAction(style: .destructive, title: "Delete") { (action, sourceView, completionHandler) in
print("index path of delete: \(indexPath)")
completionHandler(true)
}
let rename = UIContextualAction(style: .normal, title: "Edit") { (action, sourceView, completionHandler) in
print("index path of edit: \(indexPath)")
completionHandler(true)
}
let swipeActionConfig = UISwipeActionsConfiguration(actions: [rename, delete])
swipeActionConfig.performsFirstActionWithFullSwipe = false
return swipeActionConfig
}