UIScrollView水平分页就像Mobile Safari标签一样

时间:2009-08-03 01:10:17

标签: ios iphone cocoa-touch uiscrollview

Mobile Safari允许您通过输入一种UIScrollView水平分页视图来切换页面,页面控件位于底部。

我正在尝试复制这种特殊行为,其中水平可滚动的UIScrollView显示下一个视图的内容。

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

如何让UIScrollView显示移动Safari的下一个视图的某些内容?

10 个答案:

答案 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
}
相关问题