• iOS 利用长按手势移动 Table View Cells


    本文译自:Cookbook: Moving Table View Cells with a Long Press Gesture

    目录:

    • 你需要什么?
    • 如何做?
    • 如何将其利用至UICollectionView上?
    • 何去何从?

    本次的 cookbook-style 教程中介绍如何通过长按手势来移动 table view中的cell,这种操作方式就像苹果自家的天气 App 一样。

    你可以直接把本文中的到吗添加到你的工程中,或者将其添加到我为你创建好的 starter project 中,也可以下载本文的完整示例工程

    你需要什么?

    • UILongGestureRecognizer
    • UITableView (可以用 UICollectionView 替代之)
    • UITableViewController (可以用 UIViewController 或 UICollectionViewController 替代之)
    • 5 分钟。

    如何做?

    首先给 table view 添加一个 UILongGestureRecognizer。可以在 table view controller 的 viewDidLoad 方法中添加。

    1
    2
    3
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]
    initWithTarget:self action:@selector(longPressGestureRecognized:)];
    [self.tableView addGestureRecognizer:longPress];

    记者为 gesture recognizer 添加 action 方法。该方法首先应该获取到在 table view 中长按的位置,然后找出这个位置对应的 cell 的 index。记住:这里获取到的 index path 有可能为 nil(例如,如果用户长按在 table view的section header上)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - (IBAction)longPressGestureRecognized:(id)sender {
     
    UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender;
    UIGestureRecognizerState state = longPress.state;
     
    CGPoint location = [longPress locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
     
    // More coming soon...
    }

    接着你需要处理UIGestureRecognizerStateBegan分支。如果获取到一个有效的 index path(non-nil),就去获取对应的 UITableViewCell,并利用一个 helper 方法获取这个 table view cell 的 snapshot view。然后将这个 snapshot view 添加到 table view 中,并将其 center 到对应的 cell上。

    为了更好的用户体验,以及更自然的效果,在这里我把原始 cell 的背景设置为黑色,并给 snapshot view 增加淡入效果,让 snapshot view 比 原始 cell 稍微大一点,将它的Y坐标偏移量与手势的位置的Y轴对齐。这样处理之后,cell 就像从 table view 中跳出,然后浮在上面,并捕捉到用户的手指。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    static UIView *snapshot = nil; ///< A snapshot of the row user is moving.
    static NSIndexPath *sourceIndexPath = nil; ///< Initial index path, where gesture begins.
     
    switch (state) {
    case UIGestureRecognizerStateBegan: {
    if (indexPath) {
    sourceIndexPath = indexPath;
     
    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
     
    // Take a snapshot of the selected row using helper method.
    snapshot = [self customSnapshotFromView:cell];
     
    // Add the snapshot as subview, centered at cell's center...
    __block CGPoint center = cell.center;
    snapshot.center = center;
    snapshot.alpha = 0.0;
    [self.tableView addSubview:snapshot];
    [UIView animateWithDuration:0.25 animations:^{
     
    // Offset for gesture location.
    center.y = location.y;
    snapshot.center = center;
    snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05);
    snapshot.alpha = 0.98;
     
    // Black out.
    cell.backgroundColor = [UIColor blackColor];
    } completion:nil];
    }
    break;
    }
    // More coming soon...
    }

    将下面的方法添加到 .m 文件的尾部。该方法会根据传入的 view,返回一个对应的 snapshot view。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    - (UIView *)customSnapshotFromView:(UIView *)inputView {
     
    UIView *snapshot = [inputView snapshotViewAfterScreenUpdates:YES];
    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;
    }

    当手势移动的时候,也就是UIGestureRecognizerStateChanged分支,此时需要移动 snapshot view(只需要设置它的 Y 轴偏移量即可)。如果手势移动的距离对应到另外一个 index path,就需要告诉 table view,让其移动 rows。同时,你需要对 data source 进行更新:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    case UIGestureRecognizerStateChanged: {
    CGPoint center = snapshot.center;
    center.y = location.y;
    snapshot.center = center;
     
    // Is destination valid and is it different from source?
    if (indexPath && ![indexPath isEqual:sourceIndexPath]) {
     
    // ... update data source.
    [self.objects exchangeObjectAtIndex:indexPath.row withObjectAtIndex:sourceIndexPath.row];
     
    // ... move the rows.
    [self.tableView moveRowAtIndexPath:sourceIndexPath toIndexPath:indexPath];
     
    // ... and update source so it is in sync with UI changes.
    sourceIndexPath = indexPath;
    }
    break;
    }
    // More coming soon...

    最后,当手势结束或者取消时,table view 和 data source 都是最新的。你所需要做的事情就是将 snapshot view 从 table view 中移除,并把 cell 的背景色还原为白色。

    为了提升用户体验,我们将 snapshot view 淡出,并让其尺寸变小至与 cell 一样。这样看起来就像把 cell 放回原处一样。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    default: {
    // Clean up.
    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:sourceIndexPath];
    [UIView animateWithDuration:0.25 animations:^{
     
    snapshot.center = cell.center;
    snapshot.transform = CGAffineTransformIdentity;
    snapshot.alpha = 0.0;
     
    // Undo the black-out effect we did.
    cell.backgroundColor = [UIColor whiteColor];
     
    } completion:^(BOOL finished) {
     
    [snapshot removeFromSuperview];
    snapshot = nil;
     
    }];
    sourceIndexPath = nil;
    break;
    }

    就这样,搞定了!编译并运行程序,现在可以通过长按手势对 tableview cells重新排序!

    你可以在 GitHub 上下载到完整的示例工程

    如何将其利用至UICollectionView上?

    假设你已经有一个示例工程使用了 UICollectionView,那么你可以很简单的就使用上本文之前介绍的代码。所需要做的事情就是用 self.collectionView替换掉 self.tableView,并更新一下获取和移动 UICollectionViewCell 的调用方法。

    这里有个练习,从 GitHub上 checkout 出 UICollectionView 的starter project,然后将 tap-和-hold 手势添加进去以对 cells 进行重排。这里可以下载到已经实现好了工程

  • 相关阅读:
    LeetCode——Length of Last Word
    Leetcode--Remove Duplicates from Sorted Array
    Cocos2d-x 精灵碰撞检測(方法二)
    hibernate中持久化对象的生命周期(三态:自由态,持久态,游离态 之间的转换)
    Jquery Ajax时 error处理 之 parsererror
    DropdownList绑定的两种方法
    mac 系统开发android,真机调试解决方式(无数的坑之后吐血总结)
    淘宝PK京东:哥刷的不是广告,刷的是存在
    历时一年,我的著作《第一行代码——Android》已出版!
    编程算法
  • 原文地址:https://www.cnblogs.com/jgCho/p/5262281.html
Copyright © 2020-2023  润新知