转自:https://blog.csdn.net/qq_27339239/article/details/69224228
section的移动
github:https://github.com/Avanlanching/QJYTableViewSectionMove/tree/master/QJYTableViewSectionMove
具体的demo放在该路径下。
很多时候,我们都会遇到TableView cell 移动的问题,这个问题比较容易解决,apple官方给出了比较好的解决方案,iOS版的QQ也做了类似的方案。
这里分享一下以为大神的cell移动的方法,我也是从他的方法里面获取到的思路:http://blog.csdn.net/u012399689/article/details/45370567
在之前的项目里面有一个比较有趣的需求,在列表中去移动section,从而达到分组排序的效果。
这里与移动cell不同的是移动的过程中如何处理数据的问题。我这里是移动了section,在移动的同时还要保持结构完整。可以说,我在上面那位大神的基础上进行了改进。
- (void)longPressGestureRecognized:(UILongPressGestureRecognizer *)longPress {
UIGestureRecognizerState state = longPress.state;
CGPoint location = [longPress locationInView:self.tableView];
// 判断长按手势的坐标落在哪一个section上面,如果不想写这个代码,可以将长按手势添加在section上
NSIndexPath *indexPath = [self.tableView indexPathForSectionAtPoint:location numberOfSecton:self.dataArray.count];
// 这里是快照 我们拖动不是真正的UITableViewHeaderFooterView 而是一张快照
static UIView *snapshot = nil;
// 记录section的初始的行号
static NSIndexPath *initialLocation = nil;
switch (state) {
case UIGestureRecognizerStateBegan: {
if (indexPath) {
initialLocation = indexPath;
self.moveSection = [self.tableView headerViewForSection:indexPath.section];
// 创建section的一个快照
snapshot = [self customSnapshoFromView:self.moveSection];
// 添加快照至tableView中
__block CGPoint center = self.moveSection.center;
snapshot.center = center;
snapshot.alpha = 0.0;
[self.tableView addSubview:snapshot];
// 按下的瞬间执行动画 这里最终目的是为了隐藏选中的Section
[UIView animateWithDuration:0.25 animations:^{
center.y = location.y;
snapshot.center = center;
// 稍微设置一下快照的样式
snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05);
snapshot.alpha = 0.98;
self.moveSection.alpha = 0.0;
} completion:^(BOOL finished) {
self.moveSection.hidden = YES;
}];
}
break;
}
case UIGestureRecognizerStateChanged: {
// 保持位置数组存有前后两个坐标点
[self.locations addObject:[NSValue valueWithCGPoint:location]];
if (self.locations.count > 2) {
[self.locations removeObjectAtIndex:0];
}
// 移动快照
CGPoint center = snapshot.center;
center.y = location.y;
CGPoint firstPoint = [[self.locations firstObject] CGPointValue];
CGPoint lastPoint = [[self.locations lastObject] CGPointValue];
// 注意这里是中心点,而不是快照的frame.origin.x
CGFloat moveX = lastPoint.x - firstPoint.x;
center.x += moveX;
// 如果section的移动在有效范围执行动画
if (center.y + SECTION_HEIGHT / 2 <= self.tableView.contentSize.height && center.y - SECTION_HEIGHT / 2 >= 0) {
snapshot.center = center;
// 对比新的indexPath和初始的是否一致,
// 这里使用NSIndexPath是因为,在某些场景中可能出现垃圾数值,使用对象可以进行nil的判断
if (indexPath && ![indexPath isEqual:initialLocation]) {
// 这里使用try 是为了避免数据源错误造成的崩溃,
// 常规情况下OC比较少用 try catch,因为OC是动态语言一下野指针和空指针是在运行时才产生的
@try {
// 更新数组中的内容
[self.dataArray exchangeObjectAtIndex:
indexPath.section withObjectAtIndex:initialLocation.section];
// 把section移动至指定行 这里是从API中找出来的方法,apple没有像处理cell那样给定了一个编辑模式的样式
// 这里参考cell的移动
/**
*- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;
*
*- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;
*/
[self.tableView moveSection:initialLocation.section toSection:indexPath.section];
// 这里判断是否滚动了屏幕的顶部或者底部,这里是指相对坐标。
[self scrollTableViewToTargetView:indexPath];
} @catch (NSException *exception) {
NSLog(@"row is error");
} @finally {
// 存储改变后indexPath的值,以便下次比较
initialLocation = indexPath;
}
} else {
// 判断屏幕上的最上方一个和最下方一个,滚动tableView
CGRect targetViewFrame = [self.tableView rectForSection:indexPath.section];
if (self.moveSection.center.y < snapshot.center.y) {
// 向下滚
targetViewFrame.origin.y += SECTION_HEIGHT;
[self.tableView scrollRectToVisible:targetViewFrame animated:YES];
} else if (self.moveSection.center.y > snapshot.center.y) {
// 向上滚
targetViewFrame.origin.y -= SECTION_HEIGHT;
[self.tableView scrollRectToVisible:targetViewFrame animated:YES];
}
}
}
break;
}
// 长按手势取消状态
default: {
// 清空数组
[self.locations removeAllObjects];
self.moveSection.hidden = NO;
self.moveSection.alpha = 0.0;
// 将快照恢复到初始状态
[UIView animateWithDuration:0.25 animations:^{
snapshot.center = self.moveSection.center;
snapshot.transform = CGAffineTransformIdentity;
snapshot.alpha = 0.0;
self.moveSection.alpha = 1.0;
} completion:^(BOOL finished) {
[snapshot removeFromSuperview];
snapshot = nil;
// 这个地方必须加上这一句,否则在响应长按手势的瞬间取消手势,section将无法显示
self.moveSection.hidden = NO;
}];
break;
}
}
}
- 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
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
以上是长按手势的响应,在里面我们需要做是如何实现tableView配合section的移动。
使用到apple的API
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection NS_AVAILABLE_IOS(5_0);
- 1
在过程中我们要如何获得我们的点击是那一个section,根据长按手势的响应点去获取,这里其实可以把长按手势添加到section上面,但是考虑复用和多次添加的问题,最终添加在了tableview上面
- (nullable NSIndexPath *)indexPathForSectionAtPoint:(CGPoint)point numberOfSecton:(NSInteger)count {
static NSIndexPath *indexPath = nil;
indexPath = nil;
/** 使用indexPath可以为空 点击在外面 */
for (NSInteger i = 0; i < count ; i++) {
UIView *targetView = [self headerViewForSection:i];
CGRect rect = targetView.frame;
if (point.y <= rect.origin.y + 44) {
indexPath = [NSIndexPath indexPathForRow:0 inSection:i];
break;
}
}
return indexPath;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
这里用了一个比较笨的方式去算section的行数, 44为一个section的高度。
自定义section的响应方法
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
SectionModel *model = self.dataArray[section];
NSString *key = model.title;
CustomHeadView * sectionView = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier:key];
if (!sectionView) {
sectionView = [[CustomHeadView alloc] initWithReuseIdentifier:key];
}
__weak TestViewController *weakSelf = self;
[sectionView setModelToSectionWithModel:self.dataArray[section] editSectionBlock:^(CustomHeadView *targetView) {
if (!weakSelf.editAlertView) {
weakSelf.editAlertView = [[UIAlertView alloc] initWithTitle:@"修改标题" message:@"" delegate:weakSelf cancelButtonTitle:@"cannel" otherButtonTitles:@"confit", nil];
weakSelf.editAlertView.alertViewStyle = UIAlertViewStylePlainTextInput;
}
weakSelf.editAlertView.tag = [weakSelf.tableView indexPathForSectionAtPoint:targetView.center numberOfSecton:weakSelf.dataArray.count].section;
[weakSelf.editAlertView show];
} deleteSectionBlock:^(CustomHeadView *targetView) {
/** section删除按钮的回调 */
NSIndexPath *indexPath = [self.tableView indexPathForSectionAtPoint:targetView.center numberOfSecton:weakSelf.dataArray.count];
[weakSelf.dataArray removeObjectAtIndex:indexPath.section];
[weakSelf.tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationTop];
} spreadSectionBlock:^(CustomHeadView *targetView) {
/** section展开效果的回调 */
NSIndexPath *indexPath = [self.tableView indexPathForSectionAtPoint:targetView.center numberOfSecton:weakSelf.dataArray.count];
SectionModel *model = targetView.strongModel;
model.sectionSwitch = !model.sectionSwitch;
[weakSelf.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
} longpressSectionBlock:^(UILongPressGestureRecognizer *longPress, CustomHeadView *targetView) {
/** section长按的回调 */
[weakSelf longPressGestureRecognized:longPress];
}];
return sectionView;
}
- 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
- 35
- 36
- 37
- 38
- 39