移动Safari允许您通过输入一种UIScrollView水平分页视图(底部带有页面控件)来切换页面。
我试图复制这种特殊的行为,其中水平可滚动的UIScrollView显示下一个视图的某些内容。
苹果提供的示例:PageControl显示了如何使用UIScrollView进行水平分页,但是所有视图都占据整个屏幕宽度。
如何获得UIScrollView来显示下一个视图的某些内容(如移动Safari一样)?
移动Safari允许您通过输入一种UIScrollView水平分页视图(底部带有页面控件)来切换页面。
我试图复制这种特殊的行为,其中水平可滚动的UIScrollView显示下一个视图的某些内容。
苹果提供的示例:PageControl显示了如何使用UIScrollView进行水平分页,但是所有视图都占据整个屏幕宽度。
如何获得UIScrollView来显示下一个视图的某些内容(如移动Safari一样)?
Answers:
UIScrollView
启用了分页的A 将停止在其帧宽度(或高度)的倍数处。因此,第一步是弄清楚您希望页面的宽度。使宽度为UIScrollView
。然后,根据需要设置子视图的大小,然后根据UIScrollView
宽度的倍数设置其中心。
然后,因为要查看其他页,当然,设置clipsToBounds
以NO
作为mhjoy说明。现在,技巧部分是让用户在UIScrollView
框的范围之外开始拖动时可以滚动。我的解决方案(当我最近必须这样做时)如下:
创建将包含和的子视图的子UIView
类(即ClipView
)UIScrollView
。本质上,它应该具有您UIScrollView
在正常情况下会假设的框架。将放置UIScrollView
在的中心ClipView
。确保ClipView
的clipsToBounds
设定为YES
如果其宽度小于其父视图。另外,ClipView
需要参考UIScrollView
。
最后一步是重写- (UIView *)hitTest:withEvent:
里面ClipView
。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
return [self pointInside:point withEvent:event] ? scrollView : nil;
}
基本上UIScrollView
,这正是您所需要的将触摸区域扩展到其父视图的框架。
另一个选择是子类化UIScrollView
并重写其- (BOOL)pointInside:(CGPoint) point withEvent:(UIEvent *) event
方法,但是您仍将需要容器视图来进行裁剪,并且可能YES
仅基于UIScrollView
的帧来确定何时返回的难度很大。
注意: 如果子视图用户交互存在问题,您还应该查看Juri Pakaste的hitTest:withEvent:修改。
UIScrollView
子类来延迟加载视图(在中layoutSubviews
),并使用clippingRect
进行了pointInside:withEvent:
测试。效果很好,不需要其他容器视图。
UIScrollView
@ohhorob之类的子类,但具有用于裁剪和返回[super pointInside:CGPointMake(self.contentOffset.x + self.frame.size.width/2, self.contentOffset.y + self.frame.size.height/2) withEvent:event];
in 的容器视图pointInside:withEvent:
。这非常有效,并且@JuriPakaste的答案中没有提到用户交互问题。
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
在iOS5中将其添加到UIScrollViewDelegate意味着您不必再经历这个麻烦了。
ClipView
上面的解决方案对我有用,但是我必须执行不同的-[UIView hitTest:withEvent:]
实现。埃德·马蒂(Ed Marty)的版本无法在水平滚动条中进行垂直滚动视图的情况下进行用户交互。
以下版本对我有用:
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* child = nil;
if ((child = [super hitTest:point withEvent:event]) == self)
return self.scrollView;
return child;
}
将scrollView的框架大小设置为页面大小:
[self.view addSubview:scrollView];
[self.view addGestureRecognizer:mainScrollView.panGestureRecognizer];
现在,您可以平移self.view
,并且scrollView 上的内容将被滚动。
也可scrollView.clipsToBounds = NO;
用于防止剪切内容。
我自己决定使用自定义UIScrollView,因为它对我来说是最快,最简单的方法。但是,我没有看到任何确切的代码,因此我想分享一下。我需要的是内容较小的UIScrollView,因此UIScrollView本身很小,无法实现页面调度效果。如该帖子所述,您不能滑动。但是现在可以了。
创建一个类CustomScrollView和子类UIScrollView。然后,您需要做的就是将其添加到.m文件中:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return (point.y >= 0 && point.y <= self.frame.size.height);
}
这使您可以从一侧滚动到另一侧(水平)。相应地调整范围以设置滑动/滚动触摸区域。请享用!
我做了另一个实现,可以自动返回scrollview。因此,它不需要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;
}
对于ClipView hitTest实现,我还有另一个可能有用的修改。我不想提供对ClipView的UIScrollView引用。我在下面的实现允许您重新使用ClipView类来扩展任何内容的点击测试范围,而不必为其提供引用。
- (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;
}
我实现了上面建议的建议,但是UICollectionView
我使用的是超出屏幕范围的任何东西。这导致附近的单元仅在用户向其滚动时才超出范围,这并不理想。
我最终要做的是通过将以下方法添加到委托(或UICollectionViewLayout
)中来模拟滚动视图的行为。
- (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解决方案的一个变体。已进行了清理以提高可读性,并结合了Bartserk的错误修复。我曾考虑过编辑user1856273的答案,但此更改太大了。)
我的版本按下滚动条上的按钮-work =)
- (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]必须是滚动
这是一个基于Ed Marty的答案的Swift答案,还包括Juri Pakaste所做的修改,允许在滚动视图内点击按钮等。
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
return view == self ? scrollView : view
}