像谷歌地图(iOS)这样的互动转型

时间:2014-10-28 09:06:52

标签: ios google-maps uiscrollview draggable transitions

我希望获得与谷歌地图(iOS)非常接近的东西,我有一些疑问。

但首先,要考虑更好的解释和事项:

-------------- ANSWER --------------

感谢Jugale的贡献,这里有一个存储库,所以每个人都可以下载并测试所有内容。

https://github.com/vCrespoP/VCSlidingView

--------------原始问题-----------

  • 点击地图内的某个点,视图从底部进入,但是当您与该摘要视图进行交互时:
  • 请注意,只需稍微拉动一下,导航栏就已经设置好了。
  • 当您将其滚动到顶部时,您可以继续滚动,内部滚动视图将继续滚动。
  • 当你倒退时解散视图的动作,PanGesture并没有搞乱内部scrollView(相反的方式,scrollView VS解雇)

这里有效: enter image description here

疑惑:

  • 我尝试将其作为交互式转换(UIPercentDrivenInteractiveTransition)并将Map与2个控制器中的详细信息分开,但我遇到了UIPanGesture干扰scrollView的麻烦。
  • 或许最好将其作为子视图处理并处理其中的所有内容?或多或少像MBPullDownController(虽然iOS8有一些问题) - > https://github.com/matej/MBPullDownController

那么,任何人都知道任何框架,已经完成了,或者知道如何以一种好的方式做到这一点?

感谢您的时间:D

1 个答案:

答案 0 :(得分:3)

通过我的实现看来,以下情况似乎如下:

  1. 我有一个UIViewController的子类,它是视图控制器
  2. 我有一个UIView的子类,它是叠加层(以后称为"叠加层")(实际上对我来说这是UIScrollView,因为它需要我也会侧身,但我会尝试过滤掉不必要的代码)
  3. 我有另一个UIView的子类加载了叠加层的内容("内容包装器")
  4. 内容包装器具有UIScrollView属性,其中加载了所有其他视图("内容视图")
  5. 视图控制器负责初始化叠加层,设置它的初始帧(其中高度是屏幕的高度)并将内容传递给它,仅此而已。

    通过它的-initWithFrame方法,叠加层设置为UIDynamicItemBehavior。它还会创建一些UICollisionBehavior个对象:一个位于屏幕顶部,一个位于屏幕底部,位于右侧y位置,覆盖顶部部分可见(如第一帧中所示)你的GIF)。还设置了UIGravityBehavior以使叠加层保持在下部碰撞边界。当然,_animator = [[UIDynamicAnimator alloc...也已设置。

    最后:

    _pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan)];
    _pan.delegate = self;
    _pan.cancelsTouchesInView = FALSE;
    

    叠加层还有一些其他有用的方法,例如更改重力方向,以便叠加层可以显示为捕捉到屏幕的顶部或底部。

    _pan处理程序使用UISnapBehavior来保持叠加层在用户手指下方的屏幕上上下移动:

    - (void)handlePan
    {
        [self handlePanFromPanGestureRecogniser:_pan];
    }
    
    - (void)handlePanFromPanGestureRecogniser:(UIPanGestureRecognizer *)pan
    {
        CGFloat d = [pan velocityInView:self.superview.superview].y;
    
        CGRect r = self.frame;
        r.origin.y = r.origin.y + (d*0.057);
    
        if (r.origin.y < 20)
        {
            r.origin.y = 20;
        }
        else if (r.origin.y > [UIScreen mainScreen].bounds.size.height - PEEKING_HEIGHT)
        {
            r.origin.y = [UIScreen mainScreen].bounds.size.height - PEEKING_HEIGHT;
        }
    
        if (pan.state == UIGestureRecognizerStateEnded)
        {
            [self panGestureEnded];
        }
        else if (pan.state == UIGestureRecognizerStateBegan)
        {
            [self snapToBottom];
            [self removeGestureRecognizer:_tap];
        }
        else
        {
            [_animator removeBehavior:_findersnap];
            _findersnap = [[UISnapBehavior alloc] initWithItem:self snapToPoint:CGPointMake(CGRectGetMidX(r), CGRectGetMidY(r))];
            [_animator addBehavior:_findersnap];
        }
    }
    
    - (void)panGestureEnded
    {
        [_animator removeBehavior:_findersnap];
    
        CGPoint vel = [_dynamicSelf linearVelocityForItem:self];
        if (fabsf(vel.y) > 250.0)
        {
            if (vel.y < 0)
            {
                [self snapToTop];
            }
            else
            {
                [self snapToBottom];
            }
        }
        else
        {
            if (self.frame.origin.y > (self.superview.bounds.size.height/2))
            {
                [self snapToBottom];
            }
            else
            {
                [self snapToTop];
            }
        }
    
    }
    

    内容包装器侦听内容视图生成的滚动事件:

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        //this is our fancy way of getting the pan to work when the scrollview is in the way
        if (scrollView.contentOffset.y <= 0 && _dragging)
        {
            _shouldForwardScrollEvents = TRUE;
        }
    
        if (_shouldForwardScrollEvents)
        {
            if ([_delegate respondsToSelector:@selector(theContentWrapper:isForwardingGestureRecogniserTouches:)])
            {
                [_delegate theContentWrapper:self isForwardingGestureRecogniserTouches:scrollView.panGestureRecognizer];
            }
        }
    }
    
    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
    {
        _dragging = FALSE;//scrollviewdidscroll must not be called after this
    
        if (scrollView.contentOffset.y <= 0 || _shouldForwardScrollEvents)
        {
            if ([_delegate respondsToSelector:@selector(theContentWrapperStoppedBeingDragged:)])
            {
                [_delegate theContentWrapperStoppedBeingDragged:self];
            }
        }
    
        _shouldForwardScrollEvents = FALSE;
    }
    
    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
        _dragging = TRUE;
    }
    

    正如您所看到的,当bool shouldForwardScrollEventsTRUE时,我们会将scrollView.panGestureRecognizer发送给内容包装器的代理(覆盖)。 overlay实现了如下的委托方法:

    - (void)theContentWrapper:(TheContentWrapper *)contentWrapper isForwardingGestureRecogniserTouches:(UIPanGestureRecognizer *)contentViewPan
    {
        [self handlePanFromPanGestureRecogniser:contentViewPan];
    }
    
    - (void)theContentWrapperStoppedBeingDragged:(TheContentWrapper *)contentWrapper
    {
        //because the scrollview internal pan doesn't tell use when it's state == ENDED
        [self panGestureEnded];
    }
    

    希望至少其中一些对某人有用!