ios - 交互式过渡,如谷歌地图 (iOS)

标签 ios google-maps uiscrollview draggable transitions

我想实现与 Google map (iOS) 非常接近的东西,但我有一些疑问。

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

------------ 答案------------

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

https://github.com/vCrespoP/VCSlidingView

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

  • 您点击 map 内的一个点,一个 View 从底部进入,但是当您与该摘要 View 交互时:
  • 请注意,只需拉动一点,导航栏就已经设置好。
  • 滚动到顶部后,您可以继续滚动,内部 ScrollView 将继续滚动。
  • 当您“反转”关闭 View 的操作时,PanGesture 不会与内部 scrollView 混淆(另一种方式相同,scrollView VS 关闭)

这是实际操作: enter image description here

疑问:

  • 我尝试将其作为交互式过渡 (UIPercentDrivenInteractiveTransition) 并在 2 个 Controller 中将 Map 与 Details 分开,但我遇到了 UIPanGesture 干扰 scrollView 的问题。
  • 也许最好将它作为一个 subview 来处理那里的所有事情?或多或少像 MBPullDownController(虽然它在 iOS8 上有一些问题)-> https://github.com/matej/MBPullDownController

那么,有人知道任何框架,已经使用过,或者知道如何以好的方式做到这一点吗?

谢谢你的时间:D

最佳答案

通过我的实现来看,以下内容似乎是正确的:

  1. 我有一个 UIViewController 的子类,它是 View Controller
  2. 我有一个 UIView 的子类,它是覆盖层(此后称为“覆盖层”)(实际上对我来说这是一个 UIScrollView 因为它需要也横向移动,但我会尝试过滤掉不必要的代码)
  3. 我有另一个加载覆盖内容的 UIView 子类(“内容包装器”)
  4. 内容包装器有一个 UIScrollView 属性,其中加载了所有其他 View (“内容 View ”)

View Controller 负责初始化叠加层,设置它的初始框架(高度是屏幕的高度)并将内容传递给它,仅此而已。

通过它的 -initWithFrame 方法,叠加层使用 UIDynamicItemBehavior 自行设置。它还创建了一些 UICollisionBehavior 对象:一个在屏幕顶部,一个在屏幕底部下方,正好位于正确的 y 位置,以便叠加层的顶部部分可见(如GIF 的第一帧)。还设置了一个 UIGravityBehavior 以保持叠加层位于较低的碰撞边界上。当然,_animator = [[UIDynamicAnimator alloc...也设置好了。

最后:

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

overlay 类还有一些其他有用的方法,例如改变重力的方向,这样 overlay 就可以捕捉到屏幕的顶部或底部。

_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];
        }
    }

}

内容包装器监听内容 View 产生的滚动事件:

- (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 发送到内容包装器的委托(delegate)(覆盖层) . overlay 像这样实现委托(delegate)方法:

- (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];
}

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

关于ios - 交互式过渡,如谷歌地图 (iOS),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26604395/

相关文章:

ios - 根据滚动逐渐改变背景颜色

javascript - 谷歌地图与谷歌 MDL 标签问题

ios - UIScrollView 的缩放替代品?

ios - VoiceLayer iOS obj c

ios - 在 Objective C 中将 Hex NSData 转换为 int

ios - 关闭共享扩展自定义 View Controller

google-maps - 按英国邮政编码向 Google map 添加多个图钉

javascript - 谷歌地图在 IE7 中不工作

iPhone:将按钮添加到 ScrollView 会使按钮无法交互

ios - 如何按顺序将 subview 添加到 UIScrollView