Mobile Safari允许您通过输入一种UIScrollView水平分页视图来切换页面,页面控件位于底部。
我正在尝试复制这种特殊行为,其中水平可滚动的UIScrollView显示下一个视图的内容。
Apple提供的示例:PageControl显示如何使用UIScrollView进行水平分页,但所有视图占用整个屏幕宽度。
如何让UIScrollView显示移动Safari的下一个视图的某些内容?
答案 0 :(得分:260)
启用分页的UIScrollView
将停止在其帧宽(或高度)的倍数处。因此,第一步是确定您希望页面的宽度。将其设为UIScrollView
的宽度。然后,将子视图的大小设置为您需要的大小,并根据UIScrollView
宽度的倍数设置其中心。
然后,由于你想要查看其他页面,当然要将clipsToBounds
设置为NO
,因为mhjoy说。现在的技巧部分是当用户在UIScrollView
的帧范围之外开始拖动时使其滚动。我的解决方案(当我最近必须这样做时)如下:
创建一个UIView
子类(即ClipView
),其中包含UIScrollView
及其子视图。从本质上讲,它应该具有UIScrollView
在正常情况下所具有的框架。将UIScrollView
放在ClipView
的中心。如果ClipView
的{{1}}的宽度小于其父视图的宽度,请确保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
方法,但是你仍然需要一个容器视图来进行剪切,并且可能很难确定何时返回{{1仅基于YES
的框架。
注意: 如果您遇到子视图用户互动问题,还应该查看Juri Pakaste的hitTest:withEvent: modification。
答案 1 :(得分:69)
上面的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;
}
答案 2 :(得分:8)
设置scrollView的帧大小,因为您的页面大小为:
[self.view addSubview:scrollView];
[self.view addGestureRecognizer:mainScrollView.panGestureRecognizer];
现在您可以平移self.view
,滚动滚动内容
同时使用scrollView.clipsToBounds = NO;
来阻止裁剪内容。
答案 3 :(得分:5)
我自己选择使用自定义UIScrollView,因为它是我看来最快捷,最简单的方法。但是,我没有看到任何确切的代码,所以我想分享。我的需求是UIScrollView具有较小的内容,因此UIScrollView本身很小,以实现分页效果。正如帖子所说,你无法滑过。但现在你可以。
创建一个类CustomScrollView和子类UIScrollView。然后你需要做的就是将它添加到.m文件中:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return (point.y >= 0 && point.y <= self.frame.size.height);
}
这允许您从一侧滚动到另一侧(水平)。相应地调整边界以设置滑动/滚动触摸区域。享受!
答案 4 :(得分:4)
我做了另一个可以自动返回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;
}
答案 5 :(得分:3)
我对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;
}
答案 6 :(得分:3)
我实施了上面提出的建议,但我使用的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。
答案 7 :(得分:3)
在支持此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
(这是对用户1856273解决方案的一种变体。为了便于阅读而进行了清理并整合了Bartserk的错误修复。我想到了编辑user1856273的答案,但要做的改变太大了。)
答案 8 :(得分:2)
我的版本 按下滚动上的按钮 - 工作=)
- (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]必须是滚动
答案 9 :(得分:0)
这是一个基于Ed Marty的答案的快速答案,还包括Juri Pakaste的修改,允许在滚动视图内点击按钮等。
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
return view == self ? scrollView : view
}