Passing swipe touches from UIView to underlying UI

2019-04-29 06:02发布

I have a situation similar to these two posts (1907297 AND 689684) and to describe my situation most concisely, I present this text/graphical layout (similar to what you'd see in IB, dots used to enforce indent levels)

UIView (MainView: 320x460)
. .UIScrollView (ScrollView: 320x460)
. .UIView (OverlayView: 320x40)
. . . .UIButton (ArbitraryButton1)
. . . .UILabel (ArbitraryLabel1)
. . . .UILabel (ArbitraryLabel2)

The goal here is for the OverlayView to serve as a unified, transparent container to position and display some arbitrary buttons/labels on top of the ScrollView. These buttons/labels should remain stationary while the content in the ScrollView beneath moves with user swipes. The buttons/labels may sometimes be hidden/unhidden/scaled in unison (with animation) which is what makes it handy to have them all grouped in the single OverlayView.

The trouble is that, while taps on the OverlayView seem to transmit right through to the underlying ScrollView just nicely, swiping motions have no effect. I can detect/intercept the swipes by overriding the

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

method in the OverlayView, however I haven't yet found a way to properly pass those along to the ScrollView in a way that makes it scroll. Evidently the touchesMoved method is not what UIScrollView uses to detect/interpret swipes?

All the other similar posts I've researched have either found a different solution that wouldn't work in my case or have just gone unsolved. I've also seen mention of employing touchesShouldBegin / touchesShouldCancel though I don't grasp how that would be implemented. Anyhow, still hopeful that there's some insight from the community that can allow me to come up with an elegant solution for this - any sample code would be fantastic.

Thanks in advance, Joel.

P.S. - I should also mention that I need to make this compatible with iOS 3.0 so I think trying to use UIGestureRecognizers is out.

4条回答
Bombasti
2楼-- · 2019-04-29 06:52

I needed to so something similar and after some research and reading through Apple's documentation, I created this really simple and safe open source library that will solve your (MobileJoel) problem. The demo project uses a scrollview so it directly relates:
https://github.com/natrosoft/NATouchThroughView

So your view hierarchy would look like this:

UIView (MainView: 320x460)    
. .UIScrollView (ScrollView: 320x460)
. .UIView (OverlayView: 320x40)  --> change this view to class NARootTouchThroughView in I.B.
. . . .UIView (transparent UIView that you change to class NATouchThroughView in I.B.)
. . . .UIButton (ArbitraryButton1)
. . . .UILabel (ArbitraryLabel1)
. . . .UILabel (ArbitraryLabel2)

So basically your overlay view will forward its touches below it which will then get received by the UIScrollview. If your UILabels are huge and are intercepting touches, then place another NATouchThroughView on top of them so that the final result looks like this:

UIView (MainView: 320x460)    
. .UIScrollView (ScrollView: 320x460)
. .NARootTouchThroughView (OverlayView: 320x40)
. . . .NATouchThroughView (transparent)
. . . .UIButton (ArbitraryButton1)
. . . .UILabel (ArbitraryLabel1)
. . . .UILabel (ArbitraryLabel2)
. . . .NATouchThroughView (transparent and covers the labels but not the UIButton)
查看更多
Rolldiameter
3楼-- · 2019-04-29 06:54

You should subclass UIScrollView and override the touchesShouldCancelInContentView: method

-(BOOL)touchesShouldCancelInContentView:(UIView *)view
{

    if ([view isKindOfClass:[UIButton class]]) {//or whatever class you want to be able to scroll in
        return YES;
    }

    if ([view isKindOfClass:[UIControl class]]) {
        return NO;
    }

    return YES;
}
查看更多
做个烂人
4楼-- · 2019-04-29 06:55

Here's an easier solutions that worked well for me:

In the OverlayView (the view on top of UIScrollView), make the width and height both 0 (so that the view is no longer technically on top of the scrollview) and set clipsToBounds = NO (so that the contents of the OverlayView still show up on top of the scrollview). It worked like a charm for me.

 self.OverlayView.clipsToBounds = NO;
 CGRect frame = self.OverlayView.frame;
 self.OverlayView.frame = CGRectMake(frame.origin.x, frame.origin.y, 0, 0);

Note that if OverlayView contains interactive controls (like the button above) then they will no longer work. You'll need to move it into it's own view above the UIScrollView.

查看更多
【Aperson】
5楼-- · 2019-04-29 06:57

How about, at runtime in viewDidLoad you take the buttons out of the container view and place them in the view as subviews directly (and get rid of the container view)? Then there's no container view to intercept swipes but you can still use a view to group things in IB.

Also potentially you could put the container view in as a subview of the scroll view instead, and in the scroll view delegate keep re-positioning the view whenever the user scrolls. That would seem to have a high potential for being jittery but may be worth a try.

Also if the containing view is a visual container and you need to see it, you could instead use a CALayer that was placed in the superview on top of the CALayer for rendering the scroll view, since CALayers have nothing to do with input and would not each touches.

查看更多
登录 后发表回答