iOS 关于UITableView Group移动的那些事儿(tableview section move)

转自: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

猜你喜欢

转载自blog.csdn.net/zhanglizhi111/article/details/80167498