РЕДАКТИРОВАТЬ: это полностью не работает на 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;
}
}
Есть еще несколько вещей, которые нужно добавить, чтобы он казался более естественным, но это должно стать хорошей отправной точкой.