编辑:这在 iOS 4.0 上完全被破坏了。一旦我找出问题所在,我会更新此内容。
由于 UIWebView 不能很好地配合 3.0 中引入的自动嵌套滚动,并且不再支持向 UIScrollView 发送 touchesBegin/Moved/Ended,这就是我想出的。
我在所有其他视图之上添加了一个透明的 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;
}
}
还有一些东西要添加,让它感觉更原生,但这应该是一个很好的起点。