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 进行重排。这里可以下载到已经实现好了工程。
iOS 利用长按手势移动 Table View Cells的更多相关文章
- Table View Programming Guide for iOS---(六)---A Closer Look at Table View Cells
A Closer Look at Table View Cells A table view uses cell objects to draw its visible rows and then c ...
- Table View Programming Guide for iOS---(一)---About Table Views in iOS Apps
About Table Views in iOS Apps Table views are versatile user interface objects frequently found in i ...
- 创建一个Table View
在本课程中,您将创建应用程序FoodTracker的主屏幕.您将创建第二个,表视图为主场景,列出了用户的菜谱.你会设计定制表格单元格显示每一个菜谱,它是这样的: 学习目标 在课程结束时,你将能够: 创 ...
- How To Make A Swipeable Table View Cell With Actions – Without Going Nuts With Scroll Views
How To Make A Swipeable Table View Cell With Actions – Without Going Nuts With Scroll Views Ellen S ...
- Table View Programming Guide for iOS---(七)---Managing Selections
Managing Selections 管理选择 When users tap a row of a table view, usually something happens as a result ...
- Table View Programming Guide for iOS---(五)---Creating and Configuring a Table View
Creating and Configuring a Table View Your app must present a table view to users before it can mana ...
- Table View Programming Guide for iOS---(三)----Overview of the Table View API
Overview of the Table View API 表格视图API概述 The table view programming interface includes several UIKit ...
- Table View Programming Guide for iOS---(二)----Table View Styles and Accessory Views
Table View Styles and Accessory Views 表格视图的风格以及辅助视图 Table views come in distinctive styles that are ...
- iOS学习之Table View的简单使用
Table View简单描述: 在iPhone和其他iOS的很多程序中都会看到Table View的出现,除了一般的表格资料展示之外,设置的属性资料往往也用到Table View,Table View ...
随机推荐
- 2014 网选 广州赛区 hdu 5025 Saving Tang Monk(bfs+四维数组记录状态)
/* 这是我做过的一道新类型的搜索题!从来没想过用四维数组记录状态! 以前做过的都是用二维的!自己的四维还是太狭隘了..... 题意:悟空救师傅 ! 在救师父之前要先把所有的钥匙找到! 每种钥匙有 k ...
- sql order by 排序多个字段
order by 多个字段,每个字段后面都有排序方式,默认ASC 例如:select table a order by a.time1 ,a.time2 desc,a.time3 asc
- 使用Html5+C#+微信 开发移动端游戏详细教程 :(二)准备工作&开发环境
C#开发环境:VS2013; H5开发环境:WebStorm; 数据库:mysql+navicat管理工具: 操作系统:win7: 调试:chrome浏览器 如果想在微信端上线运营游戏请做好以下准备工 ...
- doctrine2到底是个什么玩意
之前和最近一个项目用到了Doctrine,由于是别人搭建的,自己没有很了解,最近又开始做的时候发现拙荆见肘,于是看了一下doctrine教程,本文就是加上自己理解的doctrine教程文档笔记了. D ...
- springMVC源码分析之拦截器
一个东西用久了,自然就会从仅使用的层面上升到探究其原理的层面,在javaweb中springmvc更是如此,越是优秀的框架,其底层实现代码更是复杂,而在我看来,一个优秀程序猿就相当于一名武林高手,不断 ...
- 因用了NeatUpload大文件上传控件而导致Nonfile portion > 4194304 bytes错误的解决方法
今天遇到一个问题,就是“NeatUpload大文件上传控件而导致Nonfile portion > 4194304 bytes错误”,百度后发现了一个解决方法,跟大家分享下: NeatUploa ...
- 【循序渐进学Python】6.Python中的函数
1. 创建函数 一个函数代表一个行为并且返回一个结果(包括None),在Python中使用def关键字来定义一个函数,如下: def hello(name): print 'hello,' + nam ...
- 关于迭代器中IEnumerable与IEnumerator的区别
首先是IEnumerable与IEnumerator的定义: 1.IEnumerable接口允许使用foreach循环,包含GetEnumerator()方法,可以迭代集合中的项. 2.IEnumer ...
- floyd离散,最小环
Description 杭州有N个景区,景区之间有一些双向的路来连接,现在8600想找一条旅游路线,这个路线从A点出发并且最后回到A点,假设经过的路线为V1,V2,....VK,V1,那么必须满足K& ...
- php中的字符串常用函数(一) strpos() 子字符首次出现的位置
strpos($str, $needle); 1.返回$needle在$str首次出现的位置.(大小写敏感). 2.从php5开始$needle支持多字符.php4只能用单个字符. 3.能找到$nee ...