iOS 的侧拉菜单功能已经深入人心了,所以侧拉是开发过程中经常会遇到的,好在UITableView的代理方法中已经自带了侧拉,用起来也比较方便,但是弊端就是不好自定义,今天这篇文章就来详解一下侧拉功能,从原生到多种自定义。
首先写一个最基本的列表
#import "ViewController.h"
@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
@property(nonatomic,strong)UITableView *tableView;
@property(nonatomic,strong)NSMutableArray *datas;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_datas = [[NSMutableArray alloc]init];
_tableView=[[UITableView alloc]initWithFrame:self.view.frame style:UITableViewStylePlain];
_tableView.delegate=self;
_tableView.dataSource=self;
[self.view addSubview:_tableView];
for (int i=0; i<20; i++) {
[_datas addObject:[NSString stringWithFormat:@"%d",i]];
}
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _datas.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Cell *cell=[tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (!cell) {
cell=[[[NSBundle mainBundle]loadNibNamed:@"Cell" owner:nil options:nil]firstObject];
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
1.如果是侧拉一个菜单 ,写这三个代理方法-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath{
return @"删除";
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(@"回掉在这里");
}
下面是效果图
2.如果想侧拉出多个菜单,则需要写这个代理方法
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath{
NSMutableArray *btnArray = [NSMutableArray array];
// 添加一个删除按钮
UITableViewRowAction *deleteRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:@"删除" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
// 1. 移除一行
[self.datas removeObjectAtIndex:indexPath.row];
// 2. 更新UI
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}];
// 设置背景颜色
deleteRowAction.backgroundColor = [UIColor blueColor];
// 添加一个编辑按钮
UITableViewRowAction *editRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"编辑" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
NSLog(@"点击了编辑");
// 一个动画
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
}];
// 设置背景颜色
editRowAction.backgroundColor = [UIColor greenColor];
// 将按钮们加入数组
[btnArray addObject:deleteRowAction];
[btnArray addObject:editRowAction];
return btnArray;
}
3.难点来了,如果是想自定义字体,颜色,或者是加图片,这就要在cell里面添加这个方法了
//在删除按钮将要出现的时候,改变按钮
-(void)willTransitionToState:(UITableViewCellStateMask)state{
//当删除按钮将要出来的时候,改变按钮
if (state == UITableViewCellStateShowingDeleteConfirmationMask) {
UITableView *tableView = [self valueForKey:@"_tableView"];
if (![tableView.delegate respondsToSelector:@selector(tableView:editActionsForRowAtIndexPath:)]) {
return;//如果没有删除按钮,就不要继续进行 要return掉
}
//时候要创建一个线程,并且马上执行修改动作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.001 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIView *swipeToDeleteConfirmationView = [self valueForKey:@"_swipeToDeleteConfirmationView"];//这里先要把按钮的父View找出来,然后一个一个的修改
for (UIButton *deleteButton in swipeToDeleteConfirmationView.subviews) {
[deleteButton setTitleColor:[UIColor greenColor] forState:UIControlStateNormal];
[deleteButton setImage:[UIImage imageNamed:@"zhanwei2"] forState:UIControlStateNormal];
deleteButton.titleLabel.font=[UIFont systemFontOfSize:14];
return;//这里可以修改一个,也可以都修改,看情况而而定
}
});
}
}
但是这样做虽然可以,但是这么做未眠有点low,如果把这些功能做一个分类提出来的话,就会更好一点,以下是进阶篇
首先在刚刚创建多个菜单的时候加一些需要的字段
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath{
NSMutableArray *btnArray = [NSMutableArray array];
// 添加一个删除按钮
UITableViewRowAction *deleteRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:@"删除"handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
// 1. 移除一行
[self.datas removeObjectAtIndex:indexPath.row];
// 2. 更新UI
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}];
deleteRowAction.image=[UIImage imageNamed:@"zhanwei2"];//这里加了一张图片
// 设置背景颜色
deleteRowAction.backgroundColor = [UIColor blueColor];
// 添加一个编辑按钮
UITableViewRowAction *editRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"编辑"handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
NSLog(@"点击了编辑");
// 一个动画
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
}];
editRowAction.font=14;//这里加了字体大小
// 设置背景颜色
editRowAction.backgroundColor = [UIColor greenColor];
// 将按钮们加入数组
[btnArray addObject:deleteRowAction];
[btnArray addObject:editRowAction];
return btnArray;
}
然后间一个UITableViewRowAction的分类
.h文件中
#import <UIKit/UIKit.h>
@interface UITableViewRowAction (Extension)
@property (nonatomic, strong, nullable) UIImage *image;//是不是带图片
@property (nonatomic, assign) BOOL enabled;//是不是有事件
@property (nonatomic, assign) NSInteger font;//是不是要改字体
@end
.m文件中
#import "UITableViewRowAction+Extension.h"
#import <objc/runtime.h>
@implementation UITableViewRowAction (Extension)
/*
*这里面实现各个属性的setter getter 方法
*/
- (void)setImage:(UIImage *)image {
objc_setAssociatedObject(self, @selector(image), image, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setEnabled:(BOOL)enabled {
objc_setAssociatedObject(self, @selector(enabled), @(enabled), OBJC_ASSOCIATION_ASSIGN);
}
- (void)setFont:(NSInteger)font {
objc_setAssociatedObject(self, @selector(font), @(font), OBJC_ASSOCIATION_ASSIGN);
}
- (UIImage *)image {
return objc_getAssociatedObject(self, _cmd);
}
- (NSInteger)font {
NSNumber *number=objc_getAssociatedObject(self, _cmd);
return number.integerValue;
}
- (BOOL)enabled {
id enabled = objc_getAssociatedObject(self, _cmd);
return enabled ? [enabled boolValue] : true;
}
@end
然后间一个 UITableViewCell 的分类.文件中
#import "UITableViewCell+Extension.h"
#import <objc/runtime.h>
#import "UITableViewRowAction+Extension.h"
@implementation UITableViewCell (Extension)
+ (void)load {
[super load];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method willTransitionToState = class_getInstanceMethod(self, @selector(willTransitionToState:));
Method __willTransitionToState = class_getInstanceMethod(self, @selector(__willTransitionToState:));
method_exchangeImplementations(willTransitionToState, __willTransitionToState);
//这里最好加点运行时的代码,在willTransitionToState出来之前运行替换的函数,切记一定要替换一次
});
}
//在删除按钮将要出现的时候,改变按钮
- (void)__willTransitionToState:(UITableViewCellStateMask)state {
[self __willTransitionToState:state];
//当删除按钮将要出来的时候,改变按钮
if (state == UITableViewCellStateShowingDeleteConfirmationMask) {
UITableView *tableView = [self valueForKey:@"_tableView"];
if (![tableView.delegate respondsToSelector:@selector(tableView:editActionsForRowAtIndexPath:)]) {
return;//如果没有删除按钮,就不要继续进行 要return掉
}
//时候要创建一个线程,并且马上执行修改动作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.001 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIView *swipeToDeleteConfirmationView = [self valueForKey:@"_swipeToDeleteConfirmationView"];//这里先要把按钮的父View找出来,然后一个一个的修改
for (UIButton *deleteButton in swipeToDeleteConfirmationView.subviews) {
UITableViewRowAction *rowAction = [deleteButton valueForKey:@"_action"];
if (rowAction.backgroundColor) {
deleteButton.backgroundColor = rowAction.backgroundColor;
}
/*
*下面就通过创建时候set的参数来修改每个按钮的状态
*/
deleteButton.enabled = rowAction.enabled;
[deleteButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
if (rowAction.image) {
NSTextAttachment *imageAtt = [[NSTextAttachment alloc] init];
imageAtt.image = rowAction.image;
[deleteButton setAttributedTitle:[NSAttributedString attributedStringWithAttachment:imageAtt] forState:UIControlStateNormal];
}
if (rowAction.font) {
deleteButton.titleLabel.font=[UIFont systemFontOfSize:rowAction.font];
}
}
return;
});
}
}
//用运行时的代码将rowActions找到
- (void)setRowActions:(NSArray *)rowActions {
objc_setAssociatedObject(self, @selector(rowActions), rowActions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSArray *)rowActions {
return objc_getAssociatedObject(self, _cmd);
}
@end
//经过一天的时间终于把各种侧拉菜单都整理好了,欢迎大家阅读,交流,批评指正。