在iOS应用程序中拖动UITableViewCells时自动滚动UITableView

在iOS应用程序中拖动UITableViewCells时自动滚动UITableView,ios,objective-c,uitableview,scroll,Ios,Objective C,Uitableview,Scroll,我已经实现了一个在UITableView中对单元格重新排序的系统。一切正常,只是我不能将单元格重新排序到iPhone屏幕上没有显示的位置 所以我实现了一个条件来检查我是否必须滚动 NSArray *indexVisibles = [_tableView indexPathsForVisibleRows]; NSInteger indexForObject = [indexVisibles indexOfObject:indexPath]; if (indexForObject == NSNotF

我已经实现了一个在UITableView中对单元格重新排序的系统。一切正常,只是我不能将单元格重新排序到iPhone屏幕上没有显示的位置

所以我实现了一个条件来检查我是否必须滚动

NSArray *indexVisibles = [_tableView indexPathsForVisibleRows];
NSInteger indexForObject = [indexVisibles indexOfObject:indexPath];
if (indexForObject == NSNotFound){
     [_tableView scrollToRowAtIndexPath:indexPath
                                          atScrollPosition:UITableViewScrollPositionTop
                                                  animated:YES];
}
我的问题是,动画不是甜蜜和干净

我认为检查一个单元格是否显示的操作对我的系统来说是非常大的,并且在移动单元格时会有一个小的延迟,但是我也不知道为什么隐藏单元格时滚动会如此困难

我已经将
UITableViewScrollPositionTop
更改为
UITableViewScrollPositionMiddle
,现在更好了,但速度非常快,所以总是滚动到我的UITableView的顶部

我想慢慢来

其他失败尝试:

备选案文1:

[UIView animateWithDuration:0.2
                                             animations:^{_tableView.contentOffset = CGPointMake(0.0, _tableView.contentOffset.y - 50);}
                                             completion:^(BOOL finished){ }];
但这有两个问题:

  • 活动仍然很繁忙
  • 拖动到第一个元素时,该元素处于中间隐藏状态
  • 备选案文2:

    [UIView animateWithDuration: 1.0
                                                 animations: ^{
                                                     [_tableView scrollToRowAtIndexPath:indexPath
                                                                       atScrollPosition:UITableViewScrollPositionMiddle
                                                                               animated:NO];
                                                 }completion: ^(BOOL finished){
    
                                                 }
                                 ];
    

    我还在寻找一种通过长按tableview单元格来渲染它们的方法。 我为此找到了一个存储库。如果您不介意将第三方库放到您的项目中,请查看它!:)


    我个人将此代码用于我的应用程序Cheetah Note。他们工作得很有魅力!强烈推荐

    当拖动的单元格位于顶部时,使用选项1尝试滚动到拖动的单元格位置-1:

    NSArray *indexVisibles = [_tableView indexPathsForVisibleRows];
    NSInteger indexForObject = [indexVisibles indexOfObject:indexPath];
    if (indexForObject == 0){
         [_tableView scrollToRowAtIndexPath:indexPathForCellBefore
                                              atScrollPosition:UITableViewScrollPositionTop
                                                      animated:YES];
    }
    
    要平滑滚动tableview,请使用以下代码段:

     [UIView animateWithDuration: 1.0 animations: ^{
         [_tableView scrollToRowAtIndexPath:indexPathForCellBefore atScrollPosition:UITableViewScrollPositionTop animated:NO];
     } completion: ^(BOOL finished){
     }];
    

    当然,当你向下滚动时,你需要修改它才能工作。

    我已经用一个非常漂亮的解决方案解决了我的问题,所以我将用三个简单的步骤来解释如何做到这一点

    我从中获得了一些灵感,并将其分享到我自己的知识库中

    A.管理手势识别

    长按手势识别:

    scrollTableWithCell:

    当您处于表格的限制(上下)时,调用它进行滚动移动的方法

    customSnapshoFromView:

    返回给定视图的自定义快照*/

    - (UIView *)customSnapshoFromView:(UIView *)inputView {
    
        // Make an image from the input view.
        UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, NO, 0);
        [inputView.layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        // Create an image view.
        snapshot = [[UIImageView alloc] initWithImage:image];
        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;
    }
    
    更新NapshotWithPosition:

    给定一个CGPoint,它会更改快照位置,以显示正在移动的单元格在_tableView的正确位置

    -(void)updateSnapshotWithPosition:(CGPoint)location{
        CGPoint center = snapshot.center;
        center.y = location.y;
        snapshot.center = center;
    }
    
    -(void)deleteSnapshotForRowAtIndexPath:(NSIndexPath *)sourceIndexPath{
        UITableViewCell *cell = [_tableView cellForRowAtIndexPath:sourceIndexPath];
        cell.hidden = NO;
        cell.alpha = 0.0;
    
        [UIView animateWithDuration:0.25 animations:^{
    
            snapshot.center = cell.center;
            snapshot.transform = CGAffineTransformIdentity;
            snapshot.alpha = 0.0;
            cell.alpha = 1.0;
    
        } completion:^(BOOL finished) {
            [snapshot removeFromSuperview];
        }];
    }
    
    DeleteSnapshotForRow索引路径:

    拖动完成后,需要从_tableView中删除快照

    -(void)updateSnapshotWithPosition:(CGPoint)location{
        CGPoint center = snapshot.center;
        center.y = location.y;
        snapshot.center = center;
    }
    
    -(void)deleteSnapshotForRowAtIndexPath:(NSIndexPath *)sourceIndexPath{
        UITableViewCell *cell = [_tableView cellForRowAtIndexPath:sourceIndexPath];
        cell.hidden = NO;
        cell.alpha = 0.0;
    
        [UIView animateWithDuration:0.25 animations:^{
    
            snapshot.center = cell.center;
            snapshot.transform = CGAffineTransformIdentity;
            snapshot.alpha = 0.0;
            cell.alpha = 1.0;
    
        } completion:^(BOOL finished) {
            [snapshot removeFromSuperview];
        }];
    }
    
    计算滚动

    声明使用上述代码所需的变量

    @implementation YourClassName{
    
        CADisplayLink *_scrollDisplayLink;
        CGFloat _scrollRate;
        UIView *snapshot; // A snapshot of the row user is moving.
        NSIndexPath *sourceIndexPath; // Initial index path, where gesture begins.
    }
    

    这就是解决我的问题所需的一切。

    我正在使用Objective-C,但在该存储库的描述中,我发现了这一点。我将测试它。我无法编译该项目。是的,我很抱歉听到这一消息。。存储库已经很长时间没有更新了。您可以在本教程中使用LPRTableView。在本教程中,您可以找到将swift类导入对象文件的方法:)请检查此存储库,希望这对您有用。这似乎是正确的,但它有一些错误,它不适用于我的项目。但是我正在研究您的代码,以找到错误并修复错误。这对我没有帮助,因为
    滚动箭头路径:atScrollPosition:Animated
    的动画非常强大,我希望平滑地滚动UITableView是,它解决了我的一个问题,那就是当你将单元格拖动到顶部时,如何将其保持在0的位置,但移动并不平滑,这是我所期望的。我只是想确认一下。我将尝试检查其他问题。我已经尝试了
    if(indexForObject==0&&indexPath.row>0){[\u tableView scrollToRowAtIndexPath:[NSIndexPath indexPath indexPathForRow:indexPath.row-1片段:0]atScrollPosition:UITableViewScrollPositionMiddle动画:是];}
    我不知道UITableViewScrollPositionMiddle和UITableViewScrollPositionTop的区别。但中间,它走得相当快(我都不想要),但它更真实的动画请检查此存储库希望这是你的工作。github.com/hpique/hpreOrderTableViews您是否可以共享视图控制器的所有源代码,或者您是否可以显示什么是“_elementsOffset”(它是如何计算的)和其他辅助方法,例如“calculateScroll”elementsOffset是可选的,如果您的表顶部有要锁定的元素,您可以使用它。我在这个存储库中分享了我的课程。如果您有任何问题,请尽管问我;)希望它能帮助你!
    -(void)deleteSnapshotForRowAtIndexPath:(NSIndexPath *)sourceIndexPath{
        UITableViewCell *cell = [_tableView cellForRowAtIndexPath:sourceIndexPath];
        cell.hidden = NO;
        cell.alpha = 0.0;
    
        [UIView animateWithDuration:0.25 animations:^{
    
            snapshot.center = cell.center;
            snapshot.transform = CGAffineTransformIdentity;
            snapshot.alpha = 0.0;
            cell.alpha = 1.0;
    
        } completion:^(BOOL finished) {
            [snapshot removeFromSuperview];
        }];
    }
    
    -(void)calculateScroll:(UIGestureRecognizer *)gestureRecognizer{
    
        const CGPoint location = [gestureRecognizer locationInView:_tableView];
    
        CGRect rect = _tableView.bounds;
        // adjust rect for content inset as we will use it below for calculating scroll zones
        rect.size.height -= _tableView.contentInset.top;
    
        //[self updateCurrentLocation:gestureRecognizer];
    
        // tell us if we should scroll and which direction
        CGFloat scrollZoneHeight = rect.size.height / 6;
        CGFloat bottomScrollBeginning = _tableView.contentOffset.y + _tableView.contentInset.top + rect.size.height - scrollZoneHeight;
        CGFloat topScrollBeginning = _tableView.contentOffset.y + _tableView.contentInset.top  + scrollZoneHeight;
    
        // we're in the bottom zone
        if (location.y >= bottomScrollBeginning)
        {
            _scrollRate = (location.y - bottomScrollBeginning) / scrollZoneHeight;
        }
        // we're in the top zone
        else if (location.y <= topScrollBeginning)
        {
            _scrollRate = (location.y - topScrollBeginning) / scrollZoneHeight;
        }
        else
        {
            _scrollRate = 0;
        }
    
    }
    
        _reorderGestureRecognizer = [[UILongPressGestureRecognizer alloc]
                                                   initWithTarget:self action:@selector(longPressGestureRecognized:)];
    
        [_tableView addGestureRecognizer:_reorderGestureRecognizer];
    
    @implementation YourClassName{
    
        CADisplayLink *_scrollDisplayLink;
        CGFloat _scrollRate;
        UIView *snapshot; // A snapshot of the row user is moving.
        NSIndexPath *sourceIndexPath; // Initial index path, where gesture begins.
    }