如何在 UIScrollView 中同时拖动和移动某些东西并滚动

Posted

技术标签:

【中文标题】如何在 UIScrollView 中同时拖动和移动某些东西并滚动【英文标题】:How to drag and move something and scroll at the same time in UIScrollView 【发布时间】:2011-07-24 08:45:39 【问题描述】:

我正在编写一个具有书架视图的应用程序,例如 iBooks。

现在的问题是:我可以将一本书从一个地方拖放到另一个地方。但是当我将书拖到滚动视图的底部时,如何让这些同时发生:

    让滚动视图向下滚动 让书跟着我的手指走 将其他书籍放置在正确的位置,并带有动画,例如在跳板中移动应用程序

我知道Github里有一个AQGridView,但是跳板demo好像不支持同时滚动和移动。(我已经把scrollEnable设置为YES了)

【问题讨论】:

【参考方案1】:

我会给你我的解决方案,但由于整个事情很大,我只会给你相关的 sn-ps。

另外,请注意,我使用手势识别器进行拖动 (UILongPressGestureRecognizer),因为这是用户在我的应用程序中通过将手指按住对象来启动拖动的方式。因此,您可以拖动的每个子视图都有自己的 UILongPressGestureRecognizer 分配给它,并且该识别器的目标/选择器位于另一个管理滚动视图和子视图的类中。

这里是手势识别器的目标:

-(void)dragged:(UILongPressGestureRecognizer *)panRecog

    if (panRecog.state == UIGestureRecognizerStateBegan)
    
        UIView * pannedView = panRecog.view;

        dragView = pannedView;
        dragView.center = [panRecog locationInView:scrollView];

        [scrollView bringSubviewToFront:dragView];

        [self startDrag]; // Not important, changes some stuff on screen to show the user he is dragging 
        return;
    

    if (panRecog.state == UIGestureRecognizerStateChanged)
    
        int xDelta  = dragView.center.x - [panRecog locationInView:scrollView].x;
        dragView.center = [panRecog locationInView:scrollView];

        [self scrollIfNeeded:[panRecog locationInView:scrollView.superview] withDelta:xDelta];

        return;
    

    if (panRecog.state == UIGestureRecognizerStateEnded)
    
        [self endDrag]; // Not important, changes some stuff on screen to show the user he is not dragging anymore
    

与你相关的事情:

开始拖动时,我将拖动哪个视图存储在 dragView 中,并将其中心设置为您的手指相对于它所在的滚动视图的位置。 startDrag 对你来说并不重要,它会改变屏幕上的一些内容以向用户显示他正在拖动 当我们实际移动我们的对象 (UIGestureRecognizerStateChanged) 时,我会得到用户移动手指的点数,并使用该增量与用户的手指位置一起检查是否需要在 scrollIfNeeded 中移动滚动视图。

这是代码

-(void)scrollIfNeeded:(CGPoint)locationInScrollSuperview withDelta:(int)xDelta

    UIView * scrollSuperview = scrollView.superview;
    CGRect bounds = scrollSuperview.bounds;
    CGPoint scrollOffset = scrollView.contentOffset;
    int xOfs = 0;
    int speed = 10;

    if ((locationInScrollSuperview.x > bounds.size.width * 0.7) && (xDelta < 0))
    
        xOfs = speed * locationInScrollSuperview.x/bounds.size.width;
    

    if ((locationInScrollSuperview.x < bounds.size.width * 0.3) && (xDelta > 0))
    
        xOfs = -speed * (1.0f - locationInScrollSuperview.x/bounds.size.width);
    

    if (xOfs < 0)
    
        if (scrollOffset.x == 0)    return;
        if (xOfs < -scrollOffset.x) xOfs = -scrollOffset.x;
    
    scrollOffset.x += xOfs;
    CGRect rect = CGRectMake(scrollOffset.x, 0, scrollView.bounds.size.width, scrollView.bounds.size.height);
    [scrollView scrollRectToVisible:rect animated:NO];
    CGPoint center = dragView.center;
    center.x += xOfs;
    dragView.center=center;

这个东西只做水平滚动,但处理垂直将非常相似。它的作用是:

检查是否拖入了滚动视图的边界区域(我使用左右 30% 的边框作为滚动应该发生的区域)。但前提是用户朝那个方向移动(xDelta 0 代表右侧) 接下来,我使用速度常数计算滚动视图的移动量,并根据用户与滚动视图实际边框的距离来缩放它,因此速度与用户到边缘的距离成正比。 最后一步是使用非动画 scrollRectToVisible 滚动滚动视图。当然 您拖动的视图会随之移动,因此我们会通过其中心的相反偏移量来补偿它。

希望这可以帮助你。

【讨论】:

这很好用!但是,您的 xDelta 计算应该是当前位置减去最后一个位置。最终值减去初始值,因此正 xDelta 减去用户向右拖动,负 xDelta 表示他们向左拖动。但它也适用于你。 :)

以上是关于如何在 UIScrollView 中同时拖动和移动某些东西并滚动的主要内容,如果未能解决你的问题,请参考以下文章

如何拖动 UIView 元素并更改位置?

如何在 UIScrollView 中实现可拖动的 UIView 子类?

如何在 d3.js 中同时自动移动节点和链接

拖动时如何让 UIScrollView 离开屏幕

UIScrollView 在拖动和点击之间做出决定

检测用户何时中断 UIScrollView 动画