模仿iOS8.0郵件左劃刪除的功能

最近自己看了一下iOS 8.0中的郵件刪除的功能,在iOS8.0中蘋(píng)果給tableView新增了一個(gè)在Cell上側(cè)滑,右邊彈出按鈕進(jìn)行便捷操作的一個(gè)新的代理方法。但是細(xì)心的話你會(huì)發(fā)現(xiàn),這個(gè)方法只提供類似QQ、微信中的那種單調(diào)的彈出菜單,并不能實(shí)現(xiàn)類似郵件中的從右一直往左拉到一定程度可以直接刪除的快捷操作。所以自己就想試著寫(xiě)一個(gè)這樣的小Demo,如果你不經(jīng)意間看到了,歡迎指證,不喜勿噴...

其實(shí)仔細(xì)的思考一下這個(gè)東西,并不是太有技術(shù)含量(ps:好吧, 承認(rèn)自己程度沒(méi)那么高,所以也想通過(guò)這種方式提高自己)。我大概的把要做的工作分了以下幾步:

  1. 首先自定義自己的Cell,Cell要提供兩個(gè)代理方法
  • 獲取每一行Cell右邊將要顯示的按鈕集合
  • 每一行Cell右邊按鈕被點(diǎn)擊后的回調(diào)
  1. 其次就是在Cell內(nèi)部要實(shí)現(xiàn)touchesBegan等一系列方法來(lái)監(jiān)測(cè)我們的拖拽手勢(shì)
  2. 在touchesMoved里面根據(jù)用戶拖拽手勢(shì)的點(diǎn)來(lái)動(dòng)態(tài)改變Cell中contentView和右邊按鈕的frame

下面我們首先創(chuàng)建自定義的一個(gè)Cell

#import <UIKit/UIKit.h>
#import "AGTableViewRowAction.h"
 
@protocol AGTableViewCellDelegate <NSObject>
@optional
/*!
 * @brief  獲取每一行Cell對(duì)應(yīng)的按鈕集合
 *
 * @param tableView 父級(jí)tableView
 * @param indexPath 索引
 *
 * @return 該行Cell的按鈕集合
 */
- (NSArray *)AGTableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath;
 
/*!
 * @brief  每一行Cell的動(dòng)作觸發(fā)回調(diào)
 *
 * @param tableView 父級(jí)tableView
 * @param index     點(diǎn)擊按鈕集合的動(dòng)作索引
 * @param indexPath 索引
 */
- (void)AGTableView:(UITableView *)tableView didSelectActionIndex:(NSInteger)index forRowAtIndexPath:(NSIndexPath *)indexPath;
@end
 
 
@interface AGTableViewCell : UITableViewCell
 
/*!
 * @brief  滑動(dòng)過(guò)程中刷新動(dòng)畫(huà)的時(shí)間間隔,默認(rèn)值是0.2s
 */
@property (nonatomic, assign) CGFloat dragAnimationDuration;
 
/*!
 * @brief  重置動(dòng)畫(huà)的時(shí)長(zhǎng),默認(rèn)值是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

這個(gè)類的實(shí)例化方法除了接收tableView的Cell的樣式以及復(fù)用標(biāo)示之外,還把其所屬的tableView作為氣的一個(gè)屬性傳過(guò)來(lái),目的是為了在用戶拖動(dòng)Cell的時(shí)候限制tableView的滾動(dòng)。

頭文件中的兩個(gè)設(shè)置動(dòng)畫(huà)時(shí)長(zhǎng)的屬性以及Cell的預(yù)設(shè)值需要在初始化方法中進(jìn)行如下設(shè)置

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;

這里我使用了兩個(gè)臨時(shí)的局部變量_isMoving _hasMoved來(lái)在Cell被拖動(dòng)的時(shí)候標(biāo)示其狀態(tài),使用self.isEditing來(lái)判斷在當(dāng)前Cell上是應(yīng)該響應(yīng)一般touch事件還是響應(yīng)它的tableView的didSelectRowAtIndexPath事件。然后我們要在Cell內(nèi)部擁有一個(gè)可變的數(shù)組來(lái)存儲(chǔ)該行Cell右邊的action集合,然后最好在layoutSubviews的時(shí)候通過(guò)代理方法拿到這個(gè)集合。

- (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],通常我們習(xí)慣在self.contentView上添加子View,但是記得不要把右邊的action添加到contentView上去。這樣的話在移動(dòng)contentView的時(shí)候會(huì)帶著右邊的action一同移動(dòng)。

這些工作做完接下來(lái)就是核心的處理邏輯,監(jiān)聽(tīng)用戶觸摸的手勢(shì):
首先要touchesBegan方法內(nèi)判斷touches.count是否為1,若不為1則調(diào)用父類的方法 [super touchesBegan:touches withEvent:event] ,若為1則記錄用戶開(kāi)始接觸到屏幕是的點(diǎn)的水平(x)軸的坐標(biāo),以備使用。
然后要在touchesMoved方法內(nèi)針對(duì)用戶觸摸的點(diǎn)與開(kāi)始觸摸屏幕是的點(diǎn)進(jìn)行對(duì)比并對(duì)當(dāng)期Cell的所有子View的frame進(jìn)行更改。
最后在touchesEnded方法中獲取到用戶最后觸摸的點(diǎn),然后來(lái)判定Cell當(dāng)前該執(zhí)行哪一種操作
1. 刪除自己
2. 恢復(fù)到展示右邊actions的狀態(tài)
3. 恢復(fù)到隱藏右邊actions的最初始的狀態(tài)

下面是touchesMoved中動(dòng)態(tài)改變Cell內(nèi)部子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上拖動(dòng)的長(zhǎng)度,之所以在后邊乘以一個(gè)1.1,這其實(shí)是模擬一個(gè)彈性系數(shù),讓Cell的移動(dòng)距離稍稍的大于用戶手指拖動(dòng)的距離,而1.1的系數(shù)在真實(shí)顯示的情況下并不是很明顯,你可以適當(dāng)?shù)男薷倪@個(gè)數(shù)值達(dá)到自己理想的效果,從而獲得更好的用戶體驗(yàn)度。

我們用beganPointX減去currentPointX這樣得到的值若是大于0則證明用戶是在向左滑動(dòng),這樣從開(kāi)始滑動(dòng)就要開(kāi)始設(shè)置contentView的origin.x=-distance。然后遍歷右邊的actions數(shù)組,由于右邊所有按鈕總的初始寬度我預(yù)設(shè)的是當(dāng)前屏幕的一把,這樣也就是說(shuō),當(dāng)用戶滑動(dòng)的距離超過(guò)屏幕寬度一般的時(shí)候就要開(kāi)始修改每一個(gè)action的width。

那么在這里我設(shè)置的是當(dāng)用戶手指拉倒距離屏幕左邊距離小于50,并且總的拖動(dòng)長(zhǎng)度大于屏幕的一半時(shí),瞬間修改最右方的action的origin.x為用戶手指當(dāng)前位置的x軸上的位置,寬度增長(zhǎng)到當(dāng)前拖動(dòng)的長(zhǎng)度,然后當(dāng)用戶手指離開(kāi)屏幕的時(shí)候,根據(jù)contentView的origin.x的位置來(lái)選擇該如何重置或是刪除當(dāng)前Cell。

最后,關(guān)于在touchesEnded中如何做處理,以及其他的一些細(xì)節(jié)就不在此贅述了。如果你想看到更詳細(xì)的內(nèi)容可以去下載該項(xiàng)目的demo源碼。


注:此文章首發(fā)在簡(jiǎn)書(shū)轉(zhuǎn)載請(qǐng)說(shuō)明出處。
如果你想看到完整的代碼,可以去這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,310評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,951評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,796評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,566評(píng)論 6 407
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,055評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,303評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,799評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,683評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,899評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,135評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,520評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,757評(píng)論 1 282
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,528評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,844評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容