1

I'm using UIScroll View to make a gallery-like ui with paging functionality. Basically like this:

enter image description here

Since I need paging, so I set the width of scrollview equals to the width of a single page, in my example, the width of the pink rectangular.

But I want two extra things:

  1. Tapping the yellow or blue area will bring the corresponding rectangular to the center.
  2. One can scroll/swipe on yellow or blue area (out of the scrollview), which means the entire width of the screen is scrollable.

I followed this thread and added - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event. BUT by doing so, I can only achieve my second goal. When I set selector or delegate handling tapping reaction of yellow and blue, it does't work. Any idea about it?

Community
  • 1
  • 1
mlin956
  • 345
  • 1
  • 5
  • 13

1 Answers1

1

That answer you referenced is one of my old favorites. It doesn't contemplate your first requirement, but I think it can handle it very neatly with just the addition of a tap gesture recognizer.

Create it on your "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
}

Challenge 1) is easy thanks to the gesture recognizer, which answer locationInView:

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

For challenge 2) we need to work out what page within your scroll view was tapped. That can be done with pretty simple arithmetic given the page width.

// 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);

Now, challenge 3) is easy now because we can change a page to it's scroll position straight-forwardly...

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

Or, all in one chunk of code...

- (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];
}

EDIT

You may also want to exclude the "current page" area from the gesture recognizer. That's simply done by qualifying the test in the tap method.

The only trick is to get the tap position in the same coordinate system as the scroll view's frame, that is, the clip view...

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

And the SDK provides a nice method to test...

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

So...

- (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];
    }
}
Community
  • 1
  • 1
danh
  • 62,181
  • 10
  • 95
  • 136
  • Thank you so much! This perfectly solves my problem and the answer is so well-designed. – mlin956 Aug 12 '16 at 00:38
  • Is there a way to restrict the area that the tap gesture recognizer is observing? like I only want the yellow and blue area observe it. I know there is a way that I can calculate the location in step 2... – mlin956 Aug 12 '16 at 00:47
  • Awesome! One last thing.. since we call setContentOffset: animated:YES with animation on, I noticed that weird thing happens if tapping during the animation. So any way to disable the gesture recognizer while the animation is on going? – mlin956 Aug 12 '16 at 01:21
  • There is. In outline, you can disable user interaction on the clip view when you setContentOffset:animated. To re-enable it after, you should become your scroll view's delegate and implement `- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView`. In that method, set userInteractionEnabled to YES on the clipView – danh Aug 12 '16 at 01:27
  • I realize that's a bit icky. Going deeper on it probably warrants a new question. Feel free to post a link here. – danh Aug 12 '16 at 01:28