当用户在表格视图中滑动单元格时,如何创建一个“更多”按钮(就像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
其他回答
Swift 3版本代码,不使用任何库:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.tableFooterView = UIView(frame: CGRect.zero) //Hiding blank cells.
tableView.separatorInset = UIEdgeInsets.zero
tableView.dataSource = self
tableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)
return cell
}
//Enable cell editing methods.
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let more = UITableViewRowAction(style: .normal, title: "More") { action, index in
//self.isEditing = false
print("more button tapped")
}
more.backgroundColor = UIColor.lightGray
let favorite = UITableViewRowAction(style: .normal, title: "Favorite") { action, index in
//self.isEditing = false
print("favorite button tapped")
}
favorite.backgroundColor = UIColor.orange
let share = UITableViewRowAction(style: .normal, title: "Share") { action, index in
//self.isEditing = false
print("share button tapped")
}
share.backgroundColor = UIColor.blue
return [share, favorite, more]
}
}
我正在寻找添加相同的功能到我的应用程序,经过这么多不同的教程(raywenderlich是最好的DIY解决方案),我发现苹果有自己的UITableViewRowActionclass,这是非常方便的。
你必须把Tableview的boilerpoint方法改成这样:
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {
// 1
var shareAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Share" , handler: { (action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in
// 2
let shareMenu = UIAlertController(title: nil, message: "Share using", preferredStyle: .ActionSheet)
let twitterAction = UIAlertAction(title: "Twitter", style: UIAlertActionStyle.Default, handler: nil)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
shareMenu.addAction(twitterAction)
shareMenu.addAction(cancelAction)
self.presentViewController(shareMenu, animated: true, completion: nil)
})
// 3
var rateAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Rate" , handler: { (action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in
// 4
let rateMenu = UIAlertController(title: nil, message: "Rate this App", preferredStyle: .ActionSheet)
let appRateAction = UIAlertAction(title: "Rate", style: UIAlertActionStyle.Default, handler: nil)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
rateMenu.addAction(appRateAction)
rateMenu.addAction(cancelAction)
self.presentViewController(rateMenu, animated: true, completion: nil)
})
// 5
return [shareAction,rateAction]
}
你可以在这个网站上找到更多信息。苹果自己的文档对于改变背景颜色非常有用:
操作按钮的背景色。 声明OBJECTIVE-C @property(nonatomic, copy) UIColor 使用此属性指定按钮的背景色。如果没有指定值 属性中的值为UIKit分配一个默认颜色 样式属性。 可用性iOS 8.0及以上版本支持。
如果你想改变按钮的字体,这就有点棘手了。我看过另一个关于SO的帖子。为了提供代码和链接,这里是他们在那里使用的代码。你必须改变按钮的外观。你必须对tableviewcell做一个特定的引用,否则你会在整个应用程序中改变按钮的外观(我不想那样,但你可能会,我不知道:))
Objective - C:
+ (void)setupDeleteRowActionStyleForUserCell {
UIFont *font = [UIFont fontWithName:@"AvenirNext-Regular" size:19];
NSDictionary *attributes = @{NSFontAttributeName: font,
NSForegroundColorAttributeName: [UIColor whiteColor]};
NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString: @"DELETE"
attributes: attributes];
/*
* We include UIView in the containment hierarchy because there is another button in UserCell that is a direct descendant of UserCell that we don't want this to affect.
*/
[[UIButton appearanceWhenContainedIn:[UIView class], [UserCell class], nil] setAttributedTitle: attributedTitle
forState: UIControlStateNormal];
}
迅速:
//create your attributes however you want to
let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(UIFont.systemFontSize())] as Dictionary!
//Add more view controller types in the []
UIButton.appearanceWhenContainedInInstancesOfClasses([ViewController.self])
恕我直言,这是最简单、最流畅的版本。希望能有所帮助。
更新:这是Swift 3.0版本:
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
var shareAction:UITableViewRowAction = UITableViewRowAction(style: .default, title: "Share", handler: {(action, cellIndexpath) -> Void in
let shareMenu = UIAlertController(title: nil, message: "Share using", preferredStyle: .actionSheet)
let twitterAction = UIAlertAction(title: "Twitter", style: .default, handler: nil)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
shareMenu.addAction(twitterAction)
shareMenu.addAction(cancelAction)
self.present(shareMenu,animated: true, completion: nil)
})
var rateAction:UITableViewRowAction = UITableViewRowAction(style: .default, title: "Rate" , handler: {(action, cellIndexpath) -> Void in
// 4
let rateMenu = UIAlertController(title: nil, message: "Rate this App", preferredStyle: .actionSheet)
let appRateAction = UIAlertAction(title: "Rate", style: .default, handler: nil)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
rateMenu.addAction(appRateAction)
rateMenu.addAction(cancelAction)
self.present(rateMenu, animated: true, completion: nil)
})
// 5
return [shareAction,rateAction]
}
约翰尼的答案是正确的。我只是在objective-c中添加了下面的代码,以便初学者(以及那些拒绝学习Swift语法的人:)
确保你声明了uitableviewdelegate并拥有以下方法:
-(NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewRowAction *button = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 1" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
{
NSLog(@"Action to perform with Button 1");
}];
button.backgroundColor = [UIColor greenColor]; //arbitrary color
UITableViewRowAction *button2 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 2" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
{
NSLog(@"Action to perform with Button2!");
}];
button2.backgroundColor = [UIColor blueColor]; //arbitrary color
return @[button, button2]; //array with all the buttons you want. 1,2,3, etc...
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// you need to implement this method too or nothing will work:
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES; //tableview must be editable or nothing will work...
}
这是一个(相当可笑的)私有API。
下面两个方法是私有的,并且被发送给UITableView的委托:
-(NSString *)tableView:(UITableView *)tableView titleForSwipeAccessoryButtonForRowAtIndexPath:(NSIndexPath *)indexPath;
-(void)tableView:(UITableView *)tableView swipeAccessoryButtonPushedForRowAtIndexPath:(NSIndexPath *)indexPath;
它们不言自明。
这里有一个简单的解决办法。它能够在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的方式只适用于这个例子。按照您的意愿管理它。