【问题标题】:Expand UIScrollView interactive area and differentiate swiping and tapping扩展 UIScrollView 交互区域,区分滑动和点击
【发布时间】:2016-08-11 22:13:48
【问题描述】:

我正在使用 UIScroll View 制作具有分页功能的类似画廊的 ui。基本上是这样的:

由于我需要分页,所以我将scrollview的宽度设置为单个页面的宽度,在我的示例中为粉红色矩形的宽度。

但我想要两件额外的东西:

  1. 点击黄色或蓝色区域会将相应的矩形带到中心。
  2. 可以在黄色或蓝色区域(滚动视图外)滚动/滑动,这意味着屏幕的整个宽度都是可滚动的。

我关注了this thread 并添加了- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event但是这样做,我只能实现我的第二个目标。当我设置选择器或委托处理黄色和蓝色的点击反应时,它不起作用。有什么想法吗?

【问题讨论】:

    标签: ios objective-c uiscrollview scroll-paging


    【解决方案1】:

    That answer you referenced 是我以前的最爱之一。它没有考虑您的第一个要求,但我认为只需添加一个轻击手势识别器,它就可以非常巧妙地处理它。

    在您的“ClipView”上创建它:

    UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
    [self.myClipView addGestureRecognizer:tapGR];
    // myClipView is the view that contains the paging scroll view
    
    - (void)tap: (UITapGestureRecognizer *)gr {
        // there are a few challenges here:
        // 1) get the tap location in the correct coordinate system
        // 2) convert that to which "page" was tapped
        // 3) scroll to that page
    }
    

    挑战 1) 很容易感谢手势识别器,它回答了locationInView:

    CGPoint location = [gr locationInView:self.scrollView]; 
    

    对于挑战 2),我们需要确定您的滚动视图中的哪个页面被点击了。考虑到页面宽度,这可以通过非常简单的算术来完成。

    // assuming you have something like this
    #define kPAGE_WIDTH    // some float
    
    // page is just how many page-width's are represented by location.y
    NSInteger page = floor(location.y/kPAGE_WIDTH);
    

    现在,挑战 3) 现在很容易了,因为我们可以直接将页面更改为其滚动位置...

    CGFloat y = page * kPAGE_WIDTH;
    [self.scrollView setContentOffset:CGPointMake(y, 0.0f) animated:YES];
    

    或者,全部在一块代码中......

    - (void)tap: (UITapGestureRecognizer *)gr {
        CGPoint location = [gr locationInView:self.scrollView]; 
        NSInteger page = floor(location.y/kPAGE_WIDTH);
        CGFloat y = page * kPAGE_WIDTH;
        [self.scrollView setContentOffset:CGPointMake(y, 0.0f) animated:YES];
    }
    

    编辑

    您可能还想从手势识别器中排除“当前页面”区域。这只需通过在 tap 方法中限定测试即可完成。

    唯一的窍门是让点击位置在与滚动视图的框架相同的坐标系中,即剪辑视图...

    CGPoint locationInClipper = [gr locationInView:gr.view]; 
    

    而且 SDK 提供了一个很好的测试方法……

    BOOL inScrollView = [self.scrollView pointInside:locationInClipper withEvent:nil];
    

    所以...

    - (void)tap: (UITapGestureRecognizer *)gr {
        CGPoint locationInClipper = [gr locationInView:gr.view]; 
        BOOL inScrollView = [self.scrollView pointInside:locationInClipper withEvent:nil];
    
        if (!inScrollView) {
            CGPoint location = [gr locationInView:self.scrollView]; 
            NSInteger page = floor(location.y/kPAGE_WIDTH);
            CGFloat y = page * kPAGE_WIDTH;
            [self.scrollView setContentOffset:CGPointMake(y, 0.0f) animated:YES];
        }
    }
    

    【讨论】:

    • 非常感谢!这完美解决了我的问题,而且答案设计得非常好。
    • 有没有办法限制点击手势识别器正在观察的区域?就像我只希望黄色和蓝色区域观察它。我知道有一种方法可以在步骤 2 中计算位置...
    • 太棒了!最后一件事.. 因为我们调用 setContentOffset: animated:YES 动画,我注意到如果在动画期间点击会发生奇怪的事情。那么有什么方法可以在动画播放时禁用手势识别器?
    • 有。概括地说,您可以在 setContentOffset:animated 时禁用剪辑视图上的用户交互。之后要重新启用它,您应该成为滚动视图的委托并实现- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView。在该方法中,在 clipView 上将 userInteractionEnabled 设置为 YES
    • 我意识到这有点恶心。深入研究它可能需要一个新问题。欢迎在此处发布链接。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-07
    • 2012-01-10
    • 2011-08-28
    • 1970-01-01
    • 2021-01-29
    • 2012-11-15
    • 1970-01-01
    相关资源
    最近更新 更多