编辑:这在 iOS 4.0 上完全被破坏了。一旦我弄清楚出了什么问题,我就会更新这个。
由于 UIWebView 与 3.0 中引入的自动嵌套滚动效果不太好,并且不再支持将 TouchBegin/Moved/Ended 发送到 UIScrollView,这就是我的想法。
我在所有其他视图之上添加了一个透明的 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;
}
}
还有一些东西需要添加,以使其感觉更原生,但这应该是一个很好的起点。