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