UIScrollView与UIWebViews [英] UIScrollView with UIWebViews

查看:103
本文介绍了UIScrollView与UIWebViews的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

stackoverflow似乎有很多问题在这里讨论这个问题,但没有人触及3.0的更新。在捣烂了几个小时后,我终于发现,嵌套的滚动视图(在我的情况下Web视图滚动视图)完全支持,但是示例在 http://developer.apple.com/iphone/library/documentation/WindowsViews/Conceptual/UIScrollView_pg/Introduction/

There seem to be many questions asked about this subject here on stackoverflow, but none of them touch on the updates made in 3.0. After mucking around for hours on end I finally found out, that nested scroll views (in my case web views inside a scroll view) are fully supported, however the example given at http://developer.apple.com/iphone/library/documentation/WindowsViews/Conceptual/UIScrollView_pg/Introduction/Introduction.html is pretty basic.

我有一个主滚动视图,启用了分页,将Web视图布置为子视图,以便我可以向左

I have a main scroll view with paging enabled, with web views laid out as subviews, so that I can page left and right changing different web views, but also scroll up and down inside the subviews.

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

In essence this seems to work fine, however what I can't figure out is how to stop the parent scroll view from paging left or right once the user has already started scrolling the web view. Essentially I'd like to lock the scrolling to whichever direction it started with. Funnily enough, this works fine if I start paging first, but if I start scrolling up or down first it also lets page at the same time (during the same began-moved-ended cycle).

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

The stocks app for example locks the scrolling properly.

推荐答案

编辑:在iOS 4.0上。

由于UIWebView在3.0中引入的自动嵌套滚动并不能很好地发挥,并且发送touchesBegin /移动/结束到一个UIScrollView不再支持这里的我想出了。

Since UIWebView doesn't play very nicely with the automagical nested scrolling introduced in 3.0 and sending touchesBegin/Moved/Ended to a UIScrollView is no longer supported here's what I came up with.

我添加了一个透明的UIView子类在所有其他视图的顶部,并使其捕获所有接触。
当触摸开始我转发到当前活动的UIWebView和启动一个短的定时器(或者更确切地说,另一个线程使用usleep()休眠一点点 - 在我的经验,主线程可以被锁定很多的触摸事件进入,计时器可以离开)。

I added a transparent UIView subclass on top of all of the other views and made it catch all touches. When touches begin I forward that to the currently active UIWebView and start a short timer (or rather another thread that uses usleep() to sleep for a little bit - in my experience the main thread can get locked up when a lot of touch events are incoming and timers can go way off).

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

In touchesMoved I check if the timer I started hasn't expired - if it hasn't and fabs(location.x - lastLocation.x) > fabs(location.y - lastLocation.y) then it looks like the user is trying to page (this could be adjusted with a multiplier, but for now this seems to be just the sweet spot). If it's been determined that the user is trying to page I send the web view touchesCancelled and start adjusting the scroll view's contentOffset.x accordingly.

在touchesEnded中,如果确定用户是分页,我应用一些牛顿物理学来检查触摸,以及滚动的动力学延续是否会已经足够跨越到下一页(下一页的一半以上是可见的)。如果是这样,我会根据滚动的速度为滚动添加动画。

In touchesEnded, if it's been determined that the user was paging, I apply some Newtonian physics to see if the touch, along with some kinetic continuation of the scrolling would've been enough to cross over to the next page (more than half of the next page is visible). If so I animate the scroll to continue based on the speed of the scroll.

警告:这段代码是可怕的,因为尝试了十亿种不同的东西。我还没有机会清理它。希望这有助于某人。

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

还有几件事要补充更原生,但这应该是一个很好的起点。

There's a few more things to add to make it feel more native, but this should be a good starting point.

这篇关于UIScrollView与UIWebViews的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆