Cocoa touch 带UIWebView的UIScrollView

Cocoa touch 带UIWebView的UIScrollView,cocoa-touch,uiwebview,uiscrollview,nested,Cocoa Touch,Uiwebview,Uiscrollview,Nested,在stackoverflow上似乎有很多关于这个主题的问题,但是没有一个涉及到3.0中的更新。经过几个小时的反复研究,我终于发现,我的案例中嵌套的滚动视图完全支持滚动视图中的web视图,但是在中给出的示例非常基本 我有一个启用分页功能的主滚动视图,web视图作为子视图进行布局,这样我可以左右翻页更改不同的web视图,但也可以在子视图内上下滚动 从本质上讲,这似乎工作得很好,但是我不明白的是,一旦用户已经开始滚动web视图,如何阻止父滚动视图向左或向右分页。基本上,我希望将滚动锁定到它开始的任何方

在stackoverflow上似乎有很多关于这个主题的问题,但是没有一个涉及到3.0中的更新。经过几个小时的反复研究,我终于发现,我的案例中嵌套的滚动视图完全支持滚动视图中的web视图,但是在中给出的示例非常基本

我有一个启用分页功能的主滚动视图,web视图作为子视图进行布局,这样我可以左右翻页更改不同的web视图,但也可以在子视图内上下滚动

从本质上讲,这似乎工作得很好,但是我不明白的是,一旦用户已经开始滚动web视图,如何阻止父滚动视图向左或向右分页。基本上,我希望将滚动锁定到它开始的任何方向。有趣的是,如果我先开始分页,这很好,但如果我先开始向上或向下滚动,它也会让页面在相同的开始-结束周期中同时移动

例如,stocks应用程序会正确锁定滚动。

编辑:这在iOS 4.0上完全被破坏。一旦我发现问题所在,我会更新这个

由于UIWebView不能很好地使用3.0中引入的自动嵌套滚动功能,因此不再支持将TouchBegin/Moved/End发送到UIScrollView,下面是我的想法

我在所有其他视图的顶部添加了一个透明的UIView子类,并使其能够捕捉所有内容。 当触摸开始时,我将其转发到当前活动的UIWebView,并启动一个短计时器,或者更确切地说,启动另一个使用usleep睡眠一点的线程——根据我的经验,当大量触摸事件进入时,主线程可能会被锁定,计时器可能会关闭

在TouchsMoved中,我检查我启动的计时器是否未过期-如果未过期,则检查fabslocation.x-lastLocation.x>fabslocation.y-lastLocation.y,然后用户似乎正在尝试分页,这可以通过倍增进行调整,但目前这似乎只是一个最佳点。 如果确定用户正在尝试翻页,我将发送web视图touchesCancelled并开始相应地调整滚动视图的contentOffset.x

在touchesEnded中,如果确定用户正在分页,我会应用一些牛顿物理来看看触摸以及滚动的一些动态延续是否足以跨越到下一页下一页的一半以上是可见的。如果是这样,我将根据滚动的速度设置滚动动画以继续

警告:由于尝试了十亿种不同的方法,此代码非常可怕。我还没有机会把它清理干净。希望这对别人有帮助

还有一些东西需要添加,以使其更具本地性,但这应该是一个很好的起点。

编辑:这在iOS 4.0上完全被打破。一旦我发现问题所在,我会更新这个

由于UIWebView不能很好地使用3.0中引入的自动嵌套滚动功能,因此不再支持将TouchBegin/Moved/End发送到UIScrollView,下面是我的想法

我在所有其他视图的顶部添加了一个透明的UIView子类,并使其能够捕捉所有内容。 当触摸开始时,我将其转发到当前活动的UIWebView,并启动一个短计时器,或者更确切地说,启动另一个使用usleep睡眠一点的线程——根据我的经验,当大量触摸事件进入时,主线程可能会被锁定,计时器可能会关闭

在TouchsMoved中,我检查我启动的计时器是否未过期-如果未过期,则检查fabslocation.x-lastLocation.x>fabslocation.y-lastLocation.y,然后用户似乎正在尝试分页,这可以通过倍增进行调整,但目前这似乎只是一个最佳点。 如果确定用户正在尝试翻页,我将发送web视图touchesCancelled并开始相应地调整滚动视图的contentOffset.x

在touchesEnded中,如果确定用户正在分页,我会应用一些牛顿物理来看看触摸以及滚动的一些动态延续是否足以跨越到下一页下一页的一半以上是可见的。如果是这样,我将根据滚动的速度设置滚动动画以继续

警告:由于尝试了十亿种不同的方法,此代码非常可怕。我还没有机会把它清理干净。希望这对别人有帮助


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

在这一点上,我还尝试对最顶层的UIScollView进行子类化,以返回no-in-BOOLtouchesShouldCancelInContentView:UIView*查看是否有一个计时器与ScrollView内部计时器同时过期,以确定是否需要转发触摸。问题是,这并不总是在正确的时间触发,因为触摸事件似乎锁定了主线程
通过让我提到的计时器在另一个线程上运行来获得定向锁。现在,如果我能弄清楚如何修改分页/滚动的阈值,这在默认情况下似乎过于倾向于分页,那么我将是gold。在这一点上,我还尝试将最顶层的UIScollView子类化,以返回no-in-BOOLtouchesShouldCancelInContentView:UIView*视图,如果计时器与ScrollView内部视图同时过期用于确定是否需要转发触摸的计时器。问题是,这并不总是在正确的时间触发,因为触摸事件似乎锁定了主线程。我已经设法通过让我提到的计时器在另一个线程上运行来实现方向锁定。现在,如果我能弄清楚如何修改分页/滚动的阈值,这在默认情况下似乎过于倾向于分页,我会很高兴。
-(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;
    }
}