Detecting clicks outside of UIScrollView

2019-04-24 10:45发布

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条回答
ら.Afraid
2楼-- · 2019-04-24 11:23

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) :)

查看更多
手持菜刀,她持情操
3楼-- · 2019-04-24 11:33

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

查看更多
Animai°情兽
4楼-- · 2019-04-24 11:38

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

查看更多
登录 后发表回答