UIScrollView с UIWebViews - PullRequest
       22

UIScrollView с UIWebViews

2 голосов
/ 08 марта 2010

Похоже, здесь много вопросов об этой теме в stackoverflow, но ни один из них не затрагивает обновления, сделанные в 3.0. После долгих часов слоняясь, я наконец-то обнаружил, что вложенные представления прокрутки (в моем случае веб-представления внутри представления прокрутки) полностью поддерживаются, однако пример, приведенный в http://developer.apple.com/iphone/library/documentation/WindowsViews/Conceptual/UIScrollView_pg/Introduction/Introduction.html, довольно прост.

У меня есть основное представление прокрутки с включенной подкачкой страниц, с веб-представлениями, размеченными как подпредставления, так что я могу перелистывать страницы влево и вправо, изменяя различные веб-представления, а также прокручивать вверх и вниз внутри подпредставлений.

По сути, это, кажется, работает нормально, но я не могу понять, как остановить просмотр родительской прокрутки влево или вправо, когда пользователь уже начал прокручивать веб-представление. По сути, я бы хотел заблокировать прокрутку в любом направлении, с которого он начинался. Как ни странно, это работает нормально, если я сначала начинаю пейджинг, но если я начинаю сначала прокручивать вверх или вниз, это также позволяет одновременно выполнять перелистывание страниц (в течение того же цикла «начался, двигался и заканчивался»).

Например, приложение для акций правильно блокирует прокрутку.

1 Ответ

0 голосов
/ 17 марта 2010

РЕДАКТИРОВАТЬ: это полностью не работает на iOS 4.0. Я обновлю это, как только выясню, что не так.

Поскольку UIWebView не очень хорошо работает с автоматическими вложенной прокруткой, введенной в 3.0, и отправка touchesBegin / Moved / Ended в UIScrollView больше не поддерживается, вот что я придумал.

Я добавил прозрачный подкласс UIView поверх всех остальных представлений и сделал так, чтобы он улавливал все прикосновения. Когда касания начинаются, я пересылаю это в текущий активный UIWebView и запускаю короткий таймер (или, скорее, другой поток, который использует usleep (), чтобы немного поспать - по моему опыту, основной поток может быть заблокирован, когда много событий касания входящие и таймеры могут отключиться).

В touchesMoved я проверяю, не истек ли запущенный мной таймер - если нет, и fabs (location.x - lastLocation.x)> fabs (location.y - lastLocation.y), тогда он выглядит как пользователь пытается на странице (это может быть скорректировано с помощью множителя, но сейчас это, кажется, просто сладкое пятно). Если определено, что пользователь пытается создать страницу, я отправляю веб-представление touchchesCancelled и начинаю соответствующим образом корректировать 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;
    }
}

Есть еще несколько вещей, которые нужно добавить, чтобы он казался более естественным, но это должно стать хорошей отправной точкой.

...