8
votes

I've implemented a paged scroll according to this technique ( iOS develop. How to extend UIScrollView's scroll event responding area? ) and it works just as intended.

The view that I'm scrolling is containing a couple of buttons and I want to be able to click not only those that are centered/paged into the scrollview but also those to the left and to the right of it. I cannot find any way to solve this but I'm not really an iOS-Jedi yet, hoping one of you are though :)

My xib, with the UIScrollView at the center

So as you can see from the screenie the UIScrollView is about a third of the width of the window, the contentsize of the UIScrollView is much larger: about 1500px and contains a lot of buttons added programmatically. The cool thing with this solution, and the part that actually works, is that the buttons: 1) are paged into the scrollview 2) are visible outside the scrollview (since "clip subviews" is unchecked for the scrollview) 3) the buttons are clickable when visible inside the uiscrollview.

BUT what doesn't work is simply this: - the buttons currently being outside of the window does not receive "their" clicks when clicking on them, the events are instead forwarded to the underlaying (the white part of the window) view.

3
Just to make sure I understand your question correctly, are you saying you want to be able to get clicks from outside of the scroll view bounds & frame?Michael Dautermann
Michael: Yes I do believe that is what I want. The goal is to make the components within the scroll view - but not in it's paged area - clickable. I'm sorry for not being especially good at explaining myself.tommys
it might be worthwhile to include a screenshot. If you have buttons outside of your scrollview but still part of the same xib file, you can assign actions to fire when clicking on those buttons.Michael Dautermann
Michael: The buttons are added programmatically to the UIScrollView and not in a nib and even if so, the problem is that the event never reaches the buttons displaying outside the uiscrollview.tommys
Thanks for including that screenshot! Ahhh, so you mean the buttons are added to the UIScrollView's contentView but outside of the UIScrollView's visible rectangle (and bounds)?Michael Dautermann

3 Answers

16
votes

So,

I finally managed to solve this puzzle and the solution is divided into two parts. The problem was, as you way recall, that the click events did not travel to the buttons that were(visible) outside the UIScrollView. It turned out that the clicks were captured by the underlying view and that it is possible to manipulate their way to finding their target by bending the rules a bit regarding who got hit and thereby tricking the events to get passed where you want them. Not really sure if this is how it should be done but it solved my problem.. . :)

1) First one must override the following method in the bottom view so that it returns the scrollview instead of itself when appropriate.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{
    UIView *view = [super hitTest:point withEvent:event];

    if (view == self) 
        return [self scrollView];

    return view;
}

2) The scrollView must override TWO methods to hand over the clicks to its contained objects.

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
    {
       UIView *view = [super hitTest:point withEvent:event];
       // Always return us.
       return view ;    
     }

and

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    // We want EVERYTHING!
    return YES;
}

Thanks a lot for you comments and willingness to help. I ho

2
votes

Inspired by the answer @tommys mentioned, it turns out that by overriding the hinTest method of a UIView and return the scrollView instead, you actually can detach the swiping of this UIView to the scrollView.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{
    UIView *view = [super hitTest:point withEvent:event];

    // Doing this make you detached the swiping to the scrollView
    if (view == self) 
        return [self scrollView];

    return view;
}

So this UIView is acting like an extension scroll area of the scrollView, the idea is here. If you make the UIView mask over the scrollView and same size of the window, then swiping anywhere inside the window makes the scrollView scroll.

Here is the example, ExtensionScrollArea

0
votes

Here's my version:

  1. hit test in container

    - (UIView *) hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
        if ( CGRectContainsPoint( self.frame, point ) && ! self.hidden ) // <-- *
        {
            if ( ! CGRectContainsPoint( scrollView.frame, point ) )
                return scrollView;
        }
        return [super hitTest:point withEvent:event];
    }
    

(*) This marked line is important if you are moving about or otherwise hiding your view, for instance if you have multiple views, each with their own scrollviews. If you don't have this line, you may be directing all your touches to an off-screen scrollview!

  1. override in scrollview

    - (BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event
    {
        return YES;
    }
    

(in the hitTest of the container, you can exclude additional frames within the if statement for default behaviour) :)