代码之家  ›  专栏  ›  技术社区  ›  Sergey Mikhanov

在uiscrollview中拖动uiview

  •  21
  • Sergey Mikhanov  · 技术社区  · 16 年前

    我正在尝试用iphone的拖放功能来解决一个基本问题。这是我的设置:

    • 我有一个uiscrollview,它有一个大的内容子视图(我可以滚动和缩放它)
    • 内容子视图有几个小平铺作为子视图,应该在其中拖动。

    我的uiscrollview子类具有以下方法:

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
        UIView *tile = [contentView pointInsideTiles:[self convertPoint:point toView:contentView] withEvent:event];
        if (tile) {
            return tile;
        } else {
            return [super hitTest:point withEvent:event];
        }
    }
    

    内容子视图具有以下方法:

    - (UIView *)pointInsideTiles:(CGPoint)point withEvent:(UIEvent *)event {
        for (TileView *tile in tiles) {
            if ([tile pointInside:[self convertPoint:point toView:tile] withEvent:event])
                return tile;
        }
    
        return nil;
    }
    

    而tile视图具有以下方法:

    - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
        UITouch *touch = [touches anyObject];   
        CGPoint location = [touch locationInView:self.superview];
    
        self.center = location;
    }
    

    这是可行的,但不是完全正确的:在拖动过程中,瓷砖有时会“掉落”。更准确地说,它停止接收touchesmoved:invocations,而scroll view开始滚动。我注意到这取决于拖动速度:拖动速度越快,平铺“下落”越快。

    有什么办法让瓷砖粘在拖拽的手指上吗?

    4 回复  |  直到 8 年前
        1
  •  58
  •   frauen1    15 年前

    我在同一个问题上挣扎——我试图在软木板上与许多“卡”(UIVIEW子类)做接口,软木板区域可以滚动,但仍然能够拖放卡。我正在做上面的解决方案,但是一个苹果工程师问我为什么那样做。他们提出的更简单的解决办法如下:

    1)在uiscrollview类中,将cancancelcontenttouches的值设置为no-这将告诉uiscrollview类允许在子视图中(或者在本例中,在子视图的子视图中)进行触摸。

    2)在我的“card”类中,将exclusivetouch设置为yes-这将告诉子视图它拥有其内部的触摸。

    在这之后,我可以拖拽卡片,仍然滚动子视图。它比上面的hittest()解决方案简单、干净得多。

    (顺便说一句,如果您使用的是iOS 3.2或4.0或更高版本,请使用uipangestureRecognizer类来处理拖放逻辑-拖放动作比重写touchesStarted()/touchesMoved()/toucheSend()平滑得多。)

        2
  •  1
  •   Sergey Mikhanov    16 年前

    解决方案:结果发现,tile中还应该有touchesStarted:和touchesSend:实现(在我的例子中,有空方法的帮助),否则这个手势就开始传播到父视图,它们不知怎么截获了这个手势。对拖曳速度的依赖是虚构的。

        3
  •  0
  •   Tyler    16 年前

    基于您共享的代码,它看起来像 touchesMoved: 方法将只对平铺中的手势调用。在每次触摸时,您都会将互动程序移动到该触摸的中心位置,因此在手势退出互动程序之前,缓慢的移动都会在互动程序内提供更新,互动程序会用指尖“跟上”。但是,当一个手势速度更快时,(x,y)touchesMoved事件将相距更远,并且当一个(x,y)点距离最后一个点足够远且已经在平铺之外时,您将丢失该手势。

    你可以通过捕捉超视域中的运动来覆盖整个可拖曳区域,并控制超视界内的瓦片运动。

    顺便问一下,是否有理由重写hittest:method?可能会更容易(也可能更有效?)使用内置实现。

        4
  •  0
  •   codercat Piyush Dubey    8 年前

    首先,设置:

    scrollview.canCancelContentTouches = NO;
    
    yourSubView. exclusiveTouch = YES;
    

    然后在你的子视图手势处理函数中,

    - (void)handleSubviewMove:(UIPanGestureRecognizer *)gesture {
    
    if(gesture.state == UIGestureRecognizerStateBegan) {
        if(_parentScrollView != nil) {
            _parentScrollView.scrollEnabled = NO;
        }
    }
    if(gesture.state == UIGestureRecognizerStateEnded) {
        if(_parentScrollView != nil) {
            _parentScrollView.scrollEnabled = YES;
        }
    }
    
    CGPoint translation = [gesture translationInView:[self superview]];
    /* handle your view's movement here. */
    [gesture setTranslation:CGPointZero inView:[self superview]];
    }