利用長按手勢移動 Table View Cells

本次的 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重新排序!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章