iOS 关于UITableView Group移动的那些事儿(tableview section move)
来源:互联网 发布:网络播放平台 编辑:程序博客网 时间:2024/05/21 09:10
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; } }}
以上是长按手势的响应,在里面我们需要做是如何实现tableView配合section的移动。
使用到apple的API
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection NS_AVAILABLE_IOS(5_0);
在过程中我们要如何获得我们的点击是那一个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;}
这里用了一个比较笨的方式去算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;}
里面具体的方式可以参考一下Demo中实现。
Demo中包含了section的一下相关操作,比如:展开,移动,删除,添加等等。这里仅仅为大家提供一种解决方案,不代表最优解决方案。
- iOS 关于UITableView Group移动的那些事儿(tableview section move)
- iOS 设置group类型tableview的section间距
- iOS开发-进阶:设置group类型tableview的section间距
- ios tableView那些事 (八) tableview的插入移动
- ios tableView那些事 (八) tableview的插入移动
- ios tableView那些事 (八) tableview的插入移动
- ios tableView那些事 (八) tableview的插入移动
- ios tableView那些事 (八) tableview的插入移动
- ios tableView那些事 (十七) UITableView的下拉刷新
- IOS开发(28)UITableView之移动Section和Cell
- UITableView优化的那些事儿
- ios关于通讯录的那些事儿
- 关于iOS本地推送的那些事儿
- 关于iOS UIAlertController自定义的那些事儿
- 关于iOS NSOperation 自定义的那些事儿
- 关于tableView的section悬浮
- ios TableView那些事(三十 五)TableView 单选操作使用Autolayout实现UITableView的Cell动态布局和高度动态改变
- ios TableView那些事(三十 五)TableView 单选操作使用Autolayout实现UITableView的Cell动态布局和高度动态改变
- APK大小的瘦身的总结:
- android、ios音频兼容解决方法
- MathType公式编辑器中怎么输入千分号
- iOS多个第三方库的.a中.o重复定义解决方法
- HR是这样筛选简历的,不知道就完蛋!
- iOS 关于UITableView Group移动的那些事儿(tableview section move)
- SVN中的trunk(主线) branch(分支) tags(标记)介绍
- fastjson解析大驼峰
- Android NDK: From Elementary to Expert Episode 5
- Linux关闭回显
- containsKey方法——判断是否包含指定的键名
- 2-网络编程学习环境搭建
- 分支限界法
- 各类距离的意义与Python实现