UIScrollView水平分页,如移动Safari选项卡

移动Safari允许您切换页面,方法是在底部input一个UIScrollView水平分页视图,页面控件位于页面底部。

我试图复制这个特定的行为,水平滚动的UIScrollView显示下一个视图的内容。

Apple提供的例子:PageControl显示了如何使用UIScrollView进行水平分页,但是所有的视图都占用了整个屏幕的宽度。

我如何获得一个UIScrollView来显示移动Safari浏览器的下一个视图的一些内容?

启用了分页function的UIScrollView将以其帧宽(或高度)的倍数停止。 所以,第一步是弄清楚你想要的页面有多宽。 使那个UIScrollView的宽度。 然后,设置你的子视图的大小,然后根据UIScrollView的宽度倍数设置它们的中心。

然后,因为你想看到其他的页面,当然,请将clipsToBounds设置为NO 。 现在的技巧部分是让用户在UIScrollView框架范围之外开始拖动时进行滚动。 我的解决scheme(最近我不得不这样做)如下:

创build一个包含UIScrollView和它的子视图的UIView子类(即ClipView )。 从本质上讲,它应该具有你在一般情况下会假设UIScrollView的框架。 将UIScrollView放置在ClipView的中心。 如果其宽度小于其父视图,请确保ClipViewclipsToBounds设置为YES 。 此外, ClipView需要对UIScrollView的引用。

最后一步是重写- (UIView *)hitTest:withEvent:ClipView

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { return [self pointInside:point withEvent:event] ? scrollView : nil; } 

这基本上将UIScrollView的触摸区域扩展到其父视图的框架,正是您所需要的。

另一个select是- (BOOL)pointInside:(CGPoint) point withEvent:(UIEvent *) event UIScrollView并覆盖它的- (BOOL)pointInside:(CGPoint) point withEvent:(UIEvent *) event方法,但是仍然需要一个容器视图来完成剪裁,并且可能很难确定何时仅基于UIScrollView的框架返回YES

注意: 如果您遇到与子视图用户交互的问题,您还应该查看Juri Pakaste的hitTest:withEvent:修改 。

上面的ClipView解决scheme为我工作,但我不得不做一个不同的-[UIView hitTest:withEvent:]实现。 埃德马蒂的版本没有得到与垂直滚动视图的用户交互工作我有横向的内部。

以下版本适用于我:

 -(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event { UIView* child = nil; if ((child = [super hitTest:point withEvent:event]) == self) return self.scrollView; return child; } 

我自定义了UIScrollView,因为这是我看来最快,最简单的方法。 但是,我没有看到任何确切的代码,所以我想分享。 我的需求是一个UIScrollView有小内容,因此UIScrollView本身很小,以实现分页的影响。 正如post所述,你无法滑过。 但现在你可以。

创build一个类CustomScrollView和子类UIScrollView。 然后你需要做的就是把这个添加到.m文件中:

 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { return (point.y >= 0 && point.y <= self.frame.size.height); } 

这允许您从一边到另一边(水平)滚动。 相应地调整边界以设置您的轻扫/滚动触摸区域。 请享用!

我做了另一个可以自动返回滚动视图的实现。 所以它不需要有一个IBOutlet来限制项目中的重用。

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if ([self pointInside:point withEvent:event]) { for (id view in [self subviews]) { if ([view isKindOfClass:[UIScrollView class]]) { return view; } } } return nil; } 

设置scrollView的框架大小为您的页面大小将是:

 [self.view addSubview:scrollView]; [self.view addGestureRecognizer:mainScrollView.panGestureRecognizer]; 

现在你可以平移self.view,scrollView上的内容将被滚动。
另外使用scrollView.clipsToBounds = NO; 以防止裁剪内容。

我有另一个可能有用的修改ClipView hitTest实现。 我不喜欢提供一个UIScrollView引用的ClipView。 我的下面的实现允许您重新使用ClipView类来扩展任何东西的命中testing区域,而不必提供参考。

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (([self pointInside:point withEvent:event]) && (self.subviews.count >= 1)) { // An extended-hit view should only have one sub-view, or make sure the // first subview is the one you want to expand the hit test for. return [self.subviews objectAtIndex:0]; } return nil; } 

我实现了上面的upvotedbuild议,但我使用的UICollectionView考虑了任何超出框架的屏幕。 这导致附近的单元格只在用户向他们滚动时渲染出界限,这并不理想。

我最终做的是通过将下面的方法添加到委托(或UICollectionViewLayout )来模拟scrollview的行为。

 - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity { if (velocity.x > 0) { proposedContentOffset.x = ceilf(self.collectionView.contentOffset.x / pageSize) * pageSize; } else { proposedContentOffset.x = floorf(self.collectionView.contentOffset.x / pageSize) * pageSize; } return proposedContentOffset; } 

这样就完全避免了滑动动作的授权,这也是一个好处。 UIScrollViewDelegate有一个类似的方法叫做scrollViewWillEndDragging:withVelocity:targetContentOffset:它可以用来页面UITableViews和UIScrollViews。

在支持此SO问题的技术的同时,在滚动视图的子视图上启用点击轻敲事件。 为了便于阅读,使用滚动视图(self.scrollView)的引用。

 - (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *hitView = nil; NSArray *childViews = [self.scrollView subviews]; for (NSUInteger i = 0; i < childViews.count; i++) { CGRect childFrame = [[childViews objectAtIndex:i] frame]; CGRect scrollFrame = self.scrollView.frame; CGPoint contentOffset = self.scrollView.contentOffset; if (childFrame.origin.x + scrollFrame.origin.x < point.x + contentOffset.x && point.x + contentOffset.x < childFrame.origin.x + scrollFrame.origin.x + childFrame.size.width && childFrame.origin.y + scrollFrame.origin.y < point.y + contentOffset.y && point.y + contentOffset.y < childFrame.origin.y + scrollFrame.origin.y + childFrame.size.height ){ hitView = [childViews objectAtIndex:i]; return hitView; } } hitView = [super hitTest:point withEvent:event]; if (hitView == self) return self.scrollView; return hitView; } 

将其添加到您的子视图以捕获触摸事件:

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 

(这是对user1856273解决scheme的一个变种,为了便于阅读而加以改进,并join了Bartserk的bug修复function,我想到了编辑user1856273的答案,但是这个改动太大了。

我的版本按下躺在滚动button – 工作=)

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView* child = nil; for (int i=0; i<[[[[self subviews] objectAtIndex:0] subviews] count];i++) { CGRect myframe =[[[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i] frame]; CGRect myframeS =[[[self subviews] objectAtIndex:0] frame]; CGPoint myscroll =[[[self subviews] objectAtIndex:0] contentOffset]; if (myframe.origin.x < point.x && point.x < myframe.origin.x+myframe.size.width && myframe.origin.y+myframeS.origin.y < point.y+myscroll.y && point.y+myscroll.y < myframe.origin.y+myframeS.origin.y +myframe.size.height){ child = [[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i]; return child; } } child = [super hitTest:point withEvent:event]; if (child == self) return [[self subviews] objectAtIndex:0]; return child; } 

但只有[[self subviews] objectAtIndex:0]必须是滚动