UIScrollView与UIWebViews

时间:2010-03-08 13:22:35

标签: cocoa-touch uiwebview uiscrollview nested

在stackoverflow上似乎有很多关于这个问题的问题,但没有一个涉及3.0中的更新。经过几个小时的讨论后我终于发现,嵌套滚动视图(在我的情况下是滚动视图中的Web视图)完全支持,但是http://developer.apple.com/iphone/library/documentation/WindowsViews/Conceptual/UIScrollView_pg/Introduction/Introduction.html给出的示例非常基本。

我有一个启用分页的主滚动视图,其中Web视图作为子视图布局,因此我可以左右翻页以更改不同的Web视图,还可以在子视图中向上和向下滚动。

本质上这似乎工作正常,但是我无法弄清楚的是,一旦用户已经开始滚动Web视图,如何停止向左或向右分页的父滚动视图。基本上我想将滚动锁定到它开始的任何方向。有趣的是,如果我首先开始分页,这可以正常工作,但是如果我先开始向上或向下滚动,它也会同时允许页面(在同一个开始移动结束的循环期间)。

股票应用程序例如正确锁定滚动。

1 个答案:

答案 0 :(得分:0)

编辑:这在iOS 4.0上完全被破坏了。一旦弄清楚什么是错的,我会更新这个。

由于UIWebView在3.0中引入的自动嵌套滚动效果不佳,并且不再支持将touchesBegin / Moved / Ended发送到UIScrollView,这就是我想到的。

我在所有其他视图的顶部添加了一个透明的UIView子类,并使其捕获所有触摸。 当触摸开始时,我将它转发到当前活动的UIWebView并启动一个短计时器(或者更确切地说使用usleep()的另一个线程稍微睡一觉 - 根据我的经验,当很多触摸事件发生时,主线程可能会被锁定传入和计时器可以一路走下去。)

在touchesMoved中,我检查我启动的计时器是否尚未过期 - 如果它没有和fabs(location.x - lastLocation.x)> fabs(location.y - lastLocation.y)然后它看起来像用户试图寻呼(这可以用乘数调整,但现在这似乎只是最佳点)。 如果确定用户正在尝试寻呼,则发送web视图touchesCancelled并相应地开始调整滚动视图的contentOffset.x。

在touchesEnded中,如果确定用户正在进行分页,我会应用一些牛顿物理来查看触摸是否与滚动的动态延续相当于是否足以跨越到下一页(超过下一页的一半是可见的)。如果是这样的话,我会根据滚动的速度为滚动设置动画。

警告:由于尝试了十亿种不同的东西,此代码非常糟糕。我还没有机会清理它。希望这有助于某人。

-(void)timer {
    usleep(250000);
    expired = YES;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    paging = NO;
    expired = NO;
    determined = NO;


    if(navManager == nil) {
        navManager = [[[[UIApplication sharedApplication] delegate] viewController] navManager];
    }

    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self];

    touchStartX = location.x;
    touchStarted = touch.timestamp;

    [NSThread detachNewThreadSelector:@selector(timer) toTarget:self withObject:nil];

    [[navManager.currentNavItem.webView hitTest:CGPointZero withEvent:event] touchesBegan:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    if(!paging) {
        [[navManager.currentNavItem.webView hitTest:CGPointZero withEvent:event] touchesCancelled:touches withEvent:event];
    }

}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if(!paging) {
        [[navManager.currentNavItem.webView hitTest:CGPointZero withEvent:event] touchesEnded:touches withEvent:event];
    }
    else {
        UITouch *touch = [touches anyObject];
        CGPoint location = [touch locationInView:self];
        NSTimeInterval touchLasted = touch.timestamp - touchStarted;
        CGFloat touchLen = location.x - touchStartX;
        float dir = touchLen/fabs(touchLen);
        float touchSpeed = touchLen/touchLasted;
        float deAccelRate = -3000.0;
        float timeToDeAccel = (-touchSpeed) / deAccelRate;
        float averageVelocity = touchSpeed / 2.0;
        float couldTravel = averageVelocity*timeToDeAccel;

        if(couldTravel > navManager.scrollView.frame.size.width/2.0) {
            couldTravel = navManager.scrollView.frame.size.width/2.0;
        }
        couldTravel = dir*couldTravel;

        NSLog(@"could travel: %f, touchSpeed: %f, timeToDeAccel = %f, averageVelocity: %f", couldTravel, touchSpeed, timeToDeAccel, averageVelocity);

        int page = round((navManager.scrollView.contentOffset.x - couldTravel) / navManager.scrollView.frame.size.width);
        if(page < 0) 
            page = 0;
        else if(page > round(navManager.scrollView.contentSize.width / navManager.scrollView.frame.size.width) - 1) 
            page = round(navManager.scrollView.contentSize.width / navManager.scrollView.frame.size.width) - 1;

        CGPoint newOffset = CGPointMake(page*navManager.scrollView.frame.size.width, navManager.scrollView.contentOffset.y);
        float needToMove = fabs(newOffset.x - navManager.scrollView.contentOffset.x);
        float timeToAnimate = needToMove / averageVelocity;

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDelegate:nil];
        [UIView setAnimationDuration:timeToAnimate];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
        navManager.scrollView.contentOffset = newOffset;
        [UIView commitAnimations];
    }
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self];
    CGPoint lastLocation = [touch previousLocationInView:self];

    if(!determined && !expired) {

        if(fabs(location.x - lastLocation.x) > fabs(location.y - lastLocation.y)) {
            NSLog(@"PAGE!!");
            paging = YES;
            [[navManager.currentNavItem.webView hitTest:CGPointZero withEvent:event] touchesCancelled:touches withEvent:event];
        }
        else
            [navManager.scrollView touchesCancelled:touches withEvent:event];

        determined = YES;
    }

    if(!paging) 
        [[navManager.currentNavItem.webView hitTest:CGPointZero withEvent:event] touchesMoved:touches withEvent:event];

    else {
        float xScroll = navManager.scrollView.contentOffset.x-(location.x - lastLocation.x);

        CGPoint newOffset = CGPointMake(xScroll, navManager.scrollView.contentOffset.y);
        navManager.scrollView.contentOffset = newOffset;
    }
}

还有一些要添加的东西让它感觉更原生,但这应该是一个很好的起点。