6

Similar to this thread and many others, I've got a view controller with a NIB file which has this layout...

  • a. UIView (480 x 320) - stores a background image
  • b. UIScrollView (480 x 220) - Is a level selector scrollview
  • c. UIView (480 x 320) - contains a foreground animated graphic

All three items above are subviews of the main View in the NIB. UIView (c) is the full size of the iPhone screen and on top of the hierarchy. On it, I've placed a character which animates based on the current touch position in that view. However the issue is that with this view receiving touches, I cannot get the touches to the ScrollView (b) below it. I still need to use the touches at (c) but also need to pass relevant touches / swipes to the UIScrollView below afterwards.

Can anyone advise how this can be done? I've read various posts on using hittest but don't want to offset the touches completely, I just need to forward them after so that the scrollview still works as normal.

Thanks,

Community
  • 1
  • 1
Simon
  • 8,981
  • 2
  • 26
  • 32

4 Answers4

1

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

In view C (the foreground view that is above the 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 view C still show up on top of the scrollview). It worked like a charm for me.

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

Note that if view C contains interactive controls then they will no longer work.

bbrame
  • 18,031
  • 10
  • 35
  • 52
1

If it were me, I would set the scrollView as the top view's delegate. This, of course, would require that the top view is a custom sublcass of UIView, so you can set

id delegate; 

and

@property (nonatomic, assign) id delegate;

in the header file. You would then set the scrollView as the delegate with something along the lines of

[topView setDelegate:scrollView];

With this in place, you can then send messages (in your case, touch events) to the scrollView when they are necessary. If, for example, you want to send all touchBegan events, you would have this:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self.delegate touchesBegan:touches withEvent:event];
}

Of course, if you only want certain events passed, you would set up your parameters in the methods to say when to call [self.delegate ....] or [super ....]. The latter will perform the action for the view rather than in the scrollView. I hope this helps

justin
  • 5,811
  • 3
  • 29
  • 32
  • The problem with this, is that swipes aren't detected in the scrollview - I tried this method earlier before I got to the 'state' I'm in now. lol... and it IS a state! However you did get me thinking about passing the information required to animate, but without the touch events. I shall look into this! – Simon Sep 20 '11 at 17:24
  • Haha, I know that feeling all too well. But I am glad this got you thinking of new ways to go about the problem. This is usually my default for issues like that. Basically just set up the conditions in which to send events to the delegate and go from there, whether it be gestures or touches or something else. If you need more control, you can subclass the scrollView and adopt a custom protocol from the top view that specifies what methods to call and how to implement them. I hope that isn't confusing, but if that's something you might want to try, I'd be more than happy to help – justin Sep 20 '11 at 17:30
  • Yeah, this is certainly a design issue - I always know when I'm doing something a silly way because it 'feels' like bad programming! Appreciate your suggestions though. – Simon Sep 20 '11 at 17:36
  • No problem. I'm always glad to help. I hope you get everything fixed up soon. – justin Sep 20 '11 at 17:45
  • Try adding this: [super touchesBegan:touches withEvent:event]; Also be aware that if you are overriding one of the touches handling methods, you need to override all four: touchesBegan, touchesMoved, touchesEnded AND touchesCancelled. – Ash Oct 02 '13 at 08:13
0

Few more options:

  1. Consider setting isUserinteractionEnabled to false for the views hiding/=above the scrollview. I think this is for "c. UIView (480x320)" See answer here https://stackoverflow.com/a/52054954/4970749. If false, touches get skipped and handed down to subviews below.

  2. The animation on top "c. UIView (480x320)" isExclusiveTouch might be set to true. Set it to the default false value. Apple documentation: https://developer.apple.com/documentation/uikit/uiview/1622453-isexclusivetouch

  3. Gesture recognizers also allow multiple touches to happen. UIGestureRecognizerDelegate has func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool

  4. Gesture recognizers part 2 -- func gestureRecognizerShouldBegin(_) https://stackoverflow.com/a/11968812/4970749

bubbaspike
  • 101
  • 6
-2

Subclass UIScrollVeiw and override the touchesShouldCancelInContentView: method like this:

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

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

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

    return YES;
}
Frane Poljak
  • 2,315
  • 23
  • 25
  • Probably because view C is not a subview of the scrollview, and so will not be included in the `touchesShouldCancelInContentView` test. (I'm guessing that's why someone down voted you) – colinta Mar 28 '14 at 02:29