模仿iOS8.0邮件左划删除的功能

最近自己看了一下iOS 8.0中的邮件删除的功能,在iOS8.0中苹果给tableView新增了一个在Cell上侧滑,右边弹出按钮进行便捷操作的一个新的代理方法。但是细心的话你会发现,这个方法只提供类似QQ、微信中的那种单调的弹出菜单,并不能实现类似邮件中的从右一直往左拉到一定程度可以直接删除的快捷操作。所以自己就想试着写一个这样的小Demo,如果你不经意间看到了,欢迎指证,不喜勿喷...

其实仔细的思考一下这个东西,并不是太有技术含量(ps:好吧, 承认自己程度没那么高,所以也想通过这种方式提高自己)。我大概的把要做的工作分了以下几步:

  1. 首先自定义自己的Cell,Cell要提供两个代理方法
  • 获取每一行Cell右边将要显示的按钮集合
  • 每一行Cell右边按钮被点击后的回调
  1. 其次就是在Cell内部要实现touchesBegan等一系列方法来监测我们的拖拽手势
  2. 在touchesMoved里面根据用户拖拽手势的点来动态改变Cell中contentView和右边按钮的frame

下面我们首先创建自定义的一个Cell

#import <UIKit/UIKit.h>
#import "AGTableViewRowAction.h"
 
@protocol AGTableViewCellDelegate <NSObject>
@optional
/*!
 * @brief  获取每一行Cell对应的按钮集合
 *
 * @param tableView 父级tableView
 * @param indexPath 索引
 *
 * @return 该行Cell的按钮集合
 */
- (NSArray *)AGTableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath;
 
/*!
 * @brief  每一行Cell的动作触发回调
 *
 * @param tableView 父级tableView
 * @param index     点击按钮集合的动作索引
 * @param indexPath 索引
 */
- (void)AGTableView:(UITableView *)tableView didSelectActionIndex:(NSInteger)index forRowAtIndexPath:(NSIndexPath *)indexPath;
@end
 
 
@interface AGTableViewCell : UITableViewCell
 
/*!
 * @brief  滑动过程中刷新动画的时间间隔,默认值是0.2s
 */
@property (nonatomic, assign) CGFloat dragAnimationDuration;
 
/*!
 * @brief  重置动画的时长,默认值是0.3s
 */
@property (nonatomic, assign) CGFloat resetAnimationDuration;
 
@property (nonatomic, assign) BOOL isEditing;
@property (nonatomic, weak) id<AGTableViewCellDelegate> delegate;
 
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier inTableView:(UITableView *)tableView;
@end

这个类的实例化方法除了接收tableView的Cell的样式以及复用标示之外,还把其所属的tableView作为气的一个属性传过来,目的是为了在用户拖动Cell的时候限制tableView的滚动。

头文件中的两个设置动画时长的属性以及Cell的预设值需要在初始化方法中进行如下设置

self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
 
if (self) {
    [self.contentView setBackgroundColor:[UIColor grayColor]];
    self.tableView = tableView;
    self.touchBeganPointX = 0.0f;
    self.dragAnimationDuration = 0.2f;
    self.resetAnimationDuration = 0.3f;
    self.isEditing = NO;
    _isMoving = NO;
    _hasMoved = NO;
}
return self;

这里我使用了两个临时的局部变量_isMoving _hasMoved来在Cell被拖动的时候标示其状态,使用self.isEditing来判断在当前Cell上是应该响应一般touch事件还是响应它的tableView的didSelectRowAtIndexPath事件。然后我们要在Cell内部拥有一个可变的数组来存储该行Cell右边的action集合,然后最好在layoutSubviews的时候通过代理方法拿到这个集合。

- (void)getActionsArray {
    self.indexPath = [self.tableView indexPathForCell:self];
     
    if ([self.delegate respondsToSelector:@selector(AGTableView:editActionsForRowAtIndexPath:)]) {
        self.actionButtons = [[self.delegate AGTableView:self.tableView editActionsForRowAtIndexPath:self.indexPath] mutableCopy];
        CGFloat buttonWidth = (SCREENWIDTH / 2.0f) / self.actionButtons.count;
        self.buttonWidth = buttonWidth;
        for (AGTableViewRowAction *action in self.actionButtons) {
            action.frame = CGRectMake(SCREENWIDTH, 0.0f, buttonWidth, self.height);
            [action addTarget:self action:@selector(rightActionDidSelected:) forControlEvents:UIControlEventTouchUpInside];
            [self addSubview:action];
        }
    }
}

注意最后[self addSubview:action],通常我们习惯在self.contentView上添加子View,但是记得不要把右边的action添加到contentView上去。这样的话在移动contentView的时候会带着右边的action一同移动。

这些工作做完接下来就是核心的处理逻辑,监听用户触摸的手势:
首先要touchesBegan方法内判断touches.count是否为1,若不为1则调用父类的方法 [super touchesBegan:touches withEvent:event] ,若为1则记录用户开始接触到屏幕是的点的水平(x)轴的坐标,以备使用。
然后要在touchesMoved方法内针对用户触摸的点与开始触摸屏幕是的点进行对比并对当期Cell的所有子View的frame进行更改。
最后在touchesEnded方法中获取到用户最后触摸的点,然后来判定Cell当前该执行哪一种操作
1. 删除自己
2. 恢复到展示右边actions的状态
3. 恢复到隐藏右边actions的最初始的状态

下面是touchesMoved中动态改变Cell内部子View的一段代码

CGFloat currentLocationX = [touch locationInView:self.tableView].x;
CGFloat distance = (self.touchBeganPointX - currentLocationX) * 1.1;
 
if (distance > 0) { // 向左拉
    CGFloat button_addWidth = (distance - (SCREENWIDTH / 2.0)) / self.actionButtons.count;
    [UIView animateWithDuration:self.dragAnimationDuration animations:^{
        self.contentView.left = -distance;
        CGFloat t_dis = distance;
         
        for (AGTableViewRowAction *action in self.actionButtons) {
            if (distance > SCREENWIDTH / 2.0f) {
                if (currentLocationX < 50) {
                    action.left = SCREENWIDTH - distance;
                    action.width = distance;
                } else {
                    action.left = SCREENWIDTH - t_dis;
                    action.width = self.buttonWidth + button_addWidth;
                }
            } else  {
                action.left = SCREENWIDTH - t_dis;
            }
            t_dis = t_dis - distance / self.actionButtons.count;
        }
    }];
} else { // 向右拉
     
}

在这里我把distance作为用户在Cell上拖动的长度,之所以在后边乘以一个1.1,这其实是模拟一个弹性系数,让Cell的移动距离稍稍的大于用户手指拖动的距离,而1.1的系数在真实显示的情况下并不是很明显,你可以适当的修改这个数值达到自己理想的效果,从而获得更好的用户体验度。

我们用beganPointX减去currentPointX这样得到的值若是大于0则证明用户是在向左滑动,这样从开始滑动就要开始设置contentView的origin.x=-distance。然后遍历右边的actions数组,由于右边所有按钮总的初始宽度我预设的是当前屏幕的一把,这样也就是说,当用户滑动的距离超过屏幕宽度一般的时候就要开始修改每一个action的width。

那么在这里我设置的是当用户手指拉倒距离屏幕左边距离小于50,并且总的拖动长度大于屏幕的一半时,瞬间修改最右方的action的origin.x为用户手指当前位置的x轴上的位置,宽度增长到当前拖动的长度,然后当用户手指离开屏幕的时候,根据contentView的origin.x的位置来选择该如何重置或是删除当前Cell。

最后,关于在touchesEnded中如何做处理,以及其他的一些细节就不在此赘述了。如果你想看到更详细的内容可以去下载该项目的demo源码。


注:此文章首发在简书转载请说明出处。
如果你想看到完整的代码,可以去这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容