UITableView - cell拖動(dòng)

cell拖動(dòng)
  • UITableView - cell之間拖動(dòng) 下次在進(jìn)入APP 自動(dòng)記錄此次位置! 網(wǎng)絡(luò)請求數(shù)據(jù)進(jìn)行排序處理! (cell自定義)直接VC引用此View

核心代碼如下:
.h中

#import <UIKit/UIKit.h>


@class RTDragCellTableView;
@protocol RTDragCellTableViewDataSource <UITableViewDataSource>

@required
/**將外部數(shù)據(jù)源數(shù)組傳入悼院,以便在移動(dòng)cell數(shù)據(jù)發(fā)生改變時(shí)進(jìn)行修改重排*/
- (NSArray *)originalArrayDataForTableView:(RTDragCellTableView *)tableView;

@end

@protocol RTDragCellTableViewDelegate <UITableViewDelegate>

@required
/**將修改重排后的數(shù)組傳入抵皱,以便外部更新數(shù)據(jù)源*/
- (void)tableView:(RTDragCellTableView *)tableView newArrayDataForDataSource:(NSArray *)newArray;
@optional
/**選中的cell準(zhǔn)備好可以移動(dòng)的時(shí)候*/
- (void)tableView:(RTDragCellTableView *)tableView cellReadyToMoveAtIndexPath:(NSIndexPath *)indexPath;
/**選中的cell正在移動(dòng),變換位置,手勢尚未松開*/
- (void)cellIsMovingInTableView:(RTDragCellTableView *)tableView;
/**選中的cell完成移動(dòng),手勢已松開*/
- (void)cellDidEndMovingInTableView:(RTDragCellTableView *)tableView;

@end

@interface RTDragCellTableView : UITableView

@property (nonatomic, assign) id<RTDragCellTableViewDataSource> dataSource;
@property (nonatomic, assign) id<RTDragCellTableViewDelegate> delegate;

@end

.m中

#import "RTDragCellTableView.h"
typedef enum{
    RTSnapshotMeetsEdgeTop,
    RTSnapshotMeetsEdgeBottom,
}RTSnapshotMeetsEdge;

@interface RTDragCellTableView ()
/**對被選中的cell的截圖*/
@property (nonatomic, weak) UIView *snapshot;
/**被選中的cell的原始位置*/
@property (nonatomic, strong) NSIndexPath *originalIndexPath;
/**被選中的cell的新位置*/
@property (nonatomic, strong) NSIndexPath *relocatedIndexPath;
/**cell被拖動(dòng)到邊緣后開啟,tableview自動(dòng)向上或向下滾動(dòng)*/
@property (nonatomic, strong) CADisplayLink *autoScrollTimer;
/**記錄手指所在的位置*/
@property (nonatomic, assign) CGPoint fingerLocation;
/**自動(dòng)滾動(dòng)的方向*/
@property (nonatomic, assign) RTSnapshotMeetsEdge autoScrollDirection;

@end


@implementation RTDragCellTableView

@dynamic delegate;
@dynamic dataSource;

# pragma mark - initialization methods
/**在初始化時(shí)加入一個(gè)長按手勢*/
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style{
    self = [super initWithFrame:frame style:style];
    if (self) {
        UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressGestureRecognized:)];
        [self addGestureRecognizer:longPress];
    }
    return self;
}
/**在初始化時(shí)加入一個(gè)長按手勢*/
//- (instancetype)init{
//    self = [super init];
//    if (self) {
//        UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressGestureRecognized:)];
//        [self addGestureRecognizer:longPress];
//    }
//    return self;
//}

# pragma mark - Gesture methods

- (void)longPressGestureRecognized:(id)sender{
    UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender;
    UIGestureRecognizerState longPressState = longPress.state;
    //手指在tableView中的位置
    _fingerLocation = [longPress locationInView:self];
    //手指按住位置對應(yīng)的indexPath,可能為nil
    _relocatedIndexPath = [self indexPathForRowAtPoint:_fingerLocation];
    switch (longPressState) {
        case UIGestureRecognizerStateBegan:{  //手勢開始,對被選中cell截圖纯路,隱藏原cell
            _originalIndexPath = [self indexPathForRowAtPoint:_fingerLocation];
            if (_originalIndexPath) {
                [self cellSelectedAtIndexPath:_originalIndexPath];
            }
            break;
        }
        case UIGestureRecognizerStateChanged:{//點(diǎn)擊位置移動(dòng),判斷手指按住位置是否進(jìn)入其它indexPath范圍寞忿,若進(jìn)入則更新數(shù)據(jù)源并移動(dòng)cell
            //截圖跟隨手指移動(dòng)
            CGPoint center = _snapshot.center;
            center.y = _fingerLocation.y;
            _snapshot.center = center;
            if ([self checkIfSnapshotMeetsEdge]) {
                [self startAutoScrollTimer];
            }else{
                [self stopAutoScrollTimer];
            }
            //手指按住位置對應(yīng)的indexPath驰唬,可能為nil
            _relocatedIndexPath = [self indexPathForRowAtPoint:_fingerLocation];
            if (_relocatedIndexPath && ![_relocatedIndexPath isEqual:_originalIndexPath]) {
                [self cellRelocatedToNewIndexPath:_relocatedIndexPath];
            }
            break;
        }
        default: {                             //長按手勢結(jié)束或被取消,移除截圖罐脊,顯示cell
            [self stopAutoScrollTimer];
            [self didEndDraging];
            break;
        }
    }
}

# pragma mark - timer methods
/**
 *  創(chuàng)建定時(shí)器并運(yùn)行
 */
- (void)startAutoScrollTimer{
    if (!_autoScrollTimer) {
        _autoScrollTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(startAutoScroll)];
        [_autoScrollTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    }
}
/**
 *  停止定時(shí)器并銷毀
 */
- (void)stopAutoScrollTimer{
    if (_autoScrollTimer) {
        [_autoScrollTimer invalidate];
        _autoScrollTimer = nil;
    }
}

# pragma mark - Private methods
/**修改數(shù)據(jù)源定嗓,通知外部更新數(shù)據(jù)源*/
- (void)updateDataSource{
    //通過DataSource代理獲得原始數(shù)據(jù)源數(shù)組
    NSMutableArray *tempArray = [NSMutableArray array];
    if ([self.dataSource respondsToSelector:@selector(originalArrayDataForTableView:)]) {
        [tempArray addObjectsFromArray:[self.dataSource originalArrayDataForTableView:self]];
    }
    //判斷原始數(shù)據(jù)源是否為嵌套數(shù)組
    if ([self nestedArrayCheck:tempArray]) {//是嵌套數(shù)組
        if (_originalIndexPath.section == _relocatedIndexPath.section) {//在同一個(gè)section內(nèi)
            [self moveObjectInMutableArray:tempArray[_originalIndexPath.section] fromIndex:_originalIndexPath.row toIndex:_relocatedIndexPath.row];
        }else{                                                          //不在同一個(gè)section內(nèi)
            id originalObj = tempArray[_originalIndexPath.section][_originalIndexPath.item];
            [tempArray[_relocatedIndexPath.section] insertObject:originalObj atIndex:_relocatedIndexPath.item];
            [tempArray[_originalIndexPath.section] removeObjectAtIndex:_originalIndexPath.item];
        }
    }else{                                  //不是嵌套數(shù)組
        [self moveObjectInMutableArray:tempArray fromIndex:_originalIndexPath.row toIndex:_relocatedIndexPath.row];
    }
    //將新數(shù)組傳出外部以更改數(shù)據(jù)源
    if ([self.delegate respondsToSelector:@selector(tableView:newArrayDataForDataSource:)]) {
        [self.delegate tableView:self newArrayDataForDataSource:tempArray];
    }
}

/**
 *  檢查數(shù)組是否為嵌套數(shù)組
 *  @param array 需要被檢測的數(shù)組
 *  @return 返回YES則表示是嵌套數(shù)組
 */
- (BOOL)nestedArrayCheck:(NSArray *)array{
    for (id obj in array) {
        if ([obj isKindOfClass:[NSArray class]]) {
            return YES;
        }
    }
    return NO;
}

/**
 *  cell被長按手指選中,對其進(jìn)行截圖萍桌,原cell隱藏
 */
- (void)cellSelectedAtIndexPath:(NSIndexPath *)indexPath{
    //HomePageTableViewCell.h
    UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath];
    UIView *snapshot = [self customSnapshotFromView:cell];
    [self addSubview:snapshot];
    _snapshot = snapshot;
    cell.hidden = YES;
    CGPoint center = _snapshot.center;
    center.y = _fingerLocation.y;
    [UIView animateWithDuration:0.2 animations:^{
        _snapshot.transform = CGAffineTransformMakeScale(1.03, 1.03);
        _snapshot.alpha = 0.98;
        _snapshot.center = center;
    }];
}
/**
 *  截圖被移動(dòng)到新的indexPath范圍宵溅,這時(shí)先更新數(shù)據(jù)源,重排數(shù)組上炎,再將cell移至新位置
 *  @param indexPath 新的indexPath
 */
- (void)cellRelocatedToNewIndexPath:(NSIndexPath *)indexPath{
    //更新數(shù)據(jù)源并返回給外部
    [self updateDataSource];
    //交換移動(dòng)cell位置
    [self moveRowAtIndexPath:_originalIndexPath toIndexPath:indexPath];
    //更新cell的原始indexPath為當(dāng)前indexPath
    _originalIndexPath = indexPath;
}
/**
 *  拖拽結(jié)束恃逻,顯示cell雏搂,并移除截圖
 */
- (void)didEndDraging{
    UITableViewCell *cell = [self cellForRowAtIndexPath:_originalIndexPath];
    cell.hidden = NO;
    cell.alpha = 0;
    [UIView animateWithDuration:0.2 animations:^{
        _snapshot.center = cell.center;
        _snapshot.alpha = 0;
        _snapshot.transform = CGAffineTransformIdentity;
        cell.alpha = 1;
    } completion:^(BOOL finished) {
        cell.hidden = NO;
        [_snapshot removeFromSuperview];
        _snapshot = nil;
        _originalIndexPath = nil;
        _relocatedIndexPath = nil;
    }];
}



/** 返回一個(gè)給定view的截圖. */
- (UIView *)customSnapshotFromView:(UIView *)inputView {
    
    // Make an image from the input view.
    UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, NO, 0);
    [inputView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    // Create an image view.
    UIView *snapshot = [[UIImageView alloc] initWithImage:image];
    snapshot.center = inputView.center;
    snapshot.layer.masksToBounds = NO;
    snapshot.layer.cornerRadius = 0.0;
    snapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0);
    snapshot.layer.shadowRadius = 5.0;
    snapshot.layer.shadowOpacity = 0.4;
    
    return snapshot;
}
/**
 *  將可變數(shù)組中的一個(gè)對象移動(dòng)到該數(shù)組中的另外一個(gè)位置
 *  @param array     要變動(dòng)的數(shù)組
 *  @param fromIndex 從這個(gè)index
 *  @param toIndex   移至這個(gè)index
 */
- (void)moveObjectInMutableArray:(NSMutableArray *)array fromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex{
    if (fromIndex < toIndex) {
        for (NSInteger i = fromIndex; i < toIndex; i ++) {
            [array exchangeObjectAtIndex:i withObjectAtIndex:i + 1];
        }
    }else{
        for (NSInteger i = fromIndex; i > toIndex; i --) {
            [array exchangeObjectAtIndex:i withObjectAtIndex:i - 1];
        }
    }
}

/**
 *  檢查截圖是否到達(dá)邊緣,并作出響應(yīng)
 */
- (BOOL)checkIfSnapshotMeetsEdge{
    CGFloat minY = CGRectGetMinY(_snapshot.frame);
    CGFloat maxY = CGRectGetMaxY(_snapshot.frame);
    if (minY < self.contentOffset.y) {
        _autoScrollDirection = RTSnapshotMeetsEdgeTop;
        return YES;
    }
    if (maxY > self.bounds.size.height + self.contentOffset.y) {
        _autoScrollDirection = RTSnapshotMeetsEdgeBottom;
        return YES;
    }
    return NO;
}
/**
 *  開始自動(dòng)滾動(dòng)
 */
- (void)startAutoScroll{
    CGFloat pixelSpeed = 4;
    if (_autoScrollDirection == RTSnapshotMeetsEdgeTop) {//向下滾動(dòng)
        if (self.contentOffset.y > 0) {//向下滾動(dòng)最大范圍限制
            [self setContentOffset:CGPointMake(0, self.contentOffset.y - pixelSpeed)];
            _snapshot.center = CGPointMake(_snapshot.center.x, _snapshot.center.y - pixelSpeed);
        }
    }else{                                               //向上滾動(dòng)
        if (self.contentOffset.y + self.bounds.size.height < self.contentSize.height) {//向下滾動(dòng)最大范圍限制
            [self setContentOffset:CGPointMake(0, self.contentOffset.y + pixelSpeed)];
            _snapshot.center = CGPointMake(_snapshot.center.x, _snapshot.center.y + pixelSpeed);
        }
    }
    
    /*  當(dāng)把截圖拖動(dòng)到邊緣寇损,開始自動(dòng)滾動(dòng)凸郑,如果這時(shí)手指完全不動(dòng),則不會(huì)觸發(fā)‘UIGestureRecognizerStateChanged’矛市,對應(yīng)的代碼就不會(huì)執(zhí)行芙沥,導(dǎo)致雖然截圖在tableView中的位置變了,但并沒有移動(dòng)那個(gè)隱藏的cell浊吏,用下面代碼可解決此問題而昨,cell會(huì)隨著截圖的移動(dòng)而移動(dòng)
     */
    _relocatedIndexPath = [self indexPathForRowAtPoint:_snapshot.center];
    if (_relocatedIndexPath && ![_relocatedIndexPath isEqual:_originalIndexPath]) {
        [self cellRelocatedToNewIndexPath:_relocatedIndexPath];
    }
}
@end

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市找田,隨后出現(xiàn)的幾起案子歌憨,更是在濱河造成了極大的恐慌,老刑警劉巖墩衙,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件务嫡,死亡現(xiàn)場離奇詭異,居然都是意外死亡漆改,警方通過查閱死者的電腦和手機(jī)心铃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來籽懦,“玉大人于个,你說我怎么就攤上這事∧核常” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵秀存,是天一觀的道長捶码。 經(jīng)常有香客問我,道長或链,這世上最難降的妖魔是什么惫恼? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮澳盐,結(jié)果婚禮上祈纯,老公的妹妹穿的比我還像新娘。我一直安慰自己叼耙,他們只是感情好腕窥,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筛婉,像睡著了一般簇爆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天入蛆,我揣著相機(jī)與錄音响蓉,去河邊找鬼。 笑死哨毁,一個(gè)胖子當(dāng)著我的面吹牛枫甲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扼褪,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼想幻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了迎捺?” 一聲冷哼從身側(cè)響起举畸,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凳枝,沒想到半個(gè)月后抄沮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岖瑰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年叛买,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹋订。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡率挣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出露戒,到底是詐尸還是另有隱情椒功,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布智什,位于F島的核電站动漾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏荠锭。R本人自食惡果不足惜旱眯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望证九。 院中可真熱鬧删豺,春花似錦、人聲如沸愧怜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叫搁。三九已至赔桌,卻和暖如春供炎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疾党。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工音诫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人雪位。 一個(gè)月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓竭钝,卻偏偏與公主長得像,于是被迫代替她去往敵國和親雹洗。 傳聞我的和親對象是個(gè)殘疾皇子香罐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

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

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,111評論 1 32
  • 朋友圈看到一則知乎分享庇茫,題目是《摧毀一個(gè)熊孩子有多困難》。無盡的長度螃成,往下展開全文了好多次都沒到底旦签。 里面貢獻(xiàn)了無...
    白露Lin閱讀 484評論 0 0
  • 一、喜之郎果凍: 本來:長大我要當(dāng)太空人寸宏,爺爺奶奶可高興了宁炫,我喜歡愛吃的喜之郎果凍。 被改一:長大我要當(dāng)喜之...
    zy呵呵呵閱讀 379評論 0 2
  • 鹽 藕 味千拉面
    般若迷閱讀 196評論 0 0