21

This is my code:

_mediaPlayer = [[MPMoviePlayerController alloc] init];
_mediaPlayer.controlStyle = MPMovieControlStyleNone;
_mediaPlayer.shouldAutoplay = NO;
[_mediaPlayer.view setFrame: CGRectMake(5, 5, 600,400)];
[playerHolder addSubview: _mediaPlayer.view];
//
[self prepareScreenContentToPlay];
//
UITapGestureRecognizer *singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleRollTap:)];
singleFingerTap.numberOfTapsRequired = 1;
[_mediaPlayer.view addGestureRecognizer:singleFingerTap];
[singleFingerTap release];

And action method for gesture recognizer:

-(void)handleRollTap:(UITapGestureRecognizer*)sender{
    NSLog(@"%@", @"touch");
}

MPMoviePlayerController works fine. In addition I want to handle touch on MPMoviePlayerController view but handleRollTap never called. Why MPMoviePlayerController's view not works with UITapGestureRecognizer?


OK. If singleFingerTap.numberOfTapsRequired = 2; then all works fine as well. But nothing for single tap..


beryllium
  • 29,669
  • 15
  • 106
  • 125

7 Answers7

63

Actually, answer to this is simple:

  • set yourself as UIGestureRecognizer delegate
  • return YES for delegate methods:

e.g.

UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture)];
tapGestureRecognizer.delegate = self;

and somewhere else in the code:

#pragma mark - gesture delegate
// this allows you to dispatch touches
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}
// this enables you to handle multiple recognizers on single view
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
mdomans
  • 1,305
  • 12
  • 18
  • 7
    Yes. Normaly a gesture recognizer assumes it should do least damage possible. But in rare cases you want to add custom touch recognizers to a view that has his own recognizers, you can set a delegate that can perform resolution when recognizers should work together. Such delegate can decide on every recognizer added to the view. It's a very usefull patern alleviating a lot of pains caused by previous approaches utilising hacking into view internals. – mdomans Dec 11 '12 at 22:19
  • simply B R I L L I A N T !!! I don't understand why your answer is was not accepted as so. – Duck Jan 22 '13 at 06:28
  • This didn't seem to work for me. I've added a different solution for people who are still stuck – Toad May 16 '13 at 20:41
  • Works very well. And my belief is, this has to do with the fact that MPMoviePlayerController most probably adds its own tap recogniser (to show/hide player controls for instance) so this delegate pattern allows both to work simultaneously – Ege Akpinar May 03 '14 at 17:44
  • @EgeAkpinar exactly, MPMoviePlayerController and it views use multiple gesture recognisers – mdomans Jul 26 '14 at 15:15
  • @onmyway133 that's usually the case when your delegate gets deallocated – mdomans Jul 26 '14 at 15:15
21

MPMoviePlayerController has a subview that takes up its entire bounds, and that subview has 3 gesture recognizers on it (in iOS 4.3).

mp = [[MPMoviePlayerController alloc] initWithURL:movieURL];
mp.frame = aRectangle;

for (UIGestureRecognizer *g in ((UIView *)[mp.view.subviews objectAtIndex:0]).gestureRecognizers) {
    NSLog(@"g %@", g);
}

will output:

g <MPTapGestureRecognizer: 0x6224c30; baseClass = UIGestureRecognizer; state = Possible; cancelsTouchesInView = NO; view = <MPSwipableView 0x6416100>; target= <(action=_tapGestureRecognized:, target=<MPSwipableView 0x6416100>)>>
g <UIPinchGestureRecognizer: 0x6224710; state = Possible; cancelsTouchesInView = NO; delaysTouchesEnded = NO; view = <MPSwipableView 0x6416100>; target= <(action=_pinchGestureRecognized:, target=<MPSwipableView 0x6416100>)>>
g <MPActivityGestureRecognizer: 0x6224640; baseClass = UIGestureRecognizer; state = Possible; cancelsTouchesInView = NO; delaysTouchesEnded = NO; view = <MPSwipableView 0x6416100>; target= <(action=_activityGestureRecognized:, target=<MPSwipableView 0x6416100>)>>

So there is already a GestureRecognizer that handles a single tap, but it isn't a UITapGestureRecognizer, but an MPTapGestureRecognizer (a custom recognizer for the movie player).

If you create a generic view and add it to the movie player view hierarchy, you can add touches to it, but it blocks the touches to the movie player (so a single tap won't make the controls disappear).

e.g.

UIView *aView = [[UIView alloc] initWithFrame:mp.view.bounds];
[aView addGestureRecognizer:tapGesture];
[mp.view addSubview:aView];

This will get your tap, but you break the controls. There may still be a way to allow it to interact with the other gestures.

devdavid
  • 1,571
  • 10
  • 14
  • Just realized you're setting your controlStyle to NONE already, so I guess this should work for you! – devdavid Sep 22 '11 at 22:34
8

Just to share my different approach for adding a custom UISwipeGestureRecognizer to the player view without adding a custom view:

[[player.view.subviews objectAtIndex:0] addGestureRecognizer:swipeGesture];

This is to replace the conventional method of calling

[player.view addGestureRecognizer:swipeGesture];

as it only works on iPad non-fullscreen mode and on iPhone. When the player goes to fullscreen mode on iPad, the gesture does not work

Kent Nguyen
  • 3,042
  • 2
  • 20
  • 15
  • [[anyview.subviews objectAtIndex:0] addGestureRecognizer:swipeGesture]; Works like a champ... Bro. well done. – Siten Oct 26 '15 at 10:10
  • trick is you giving the hint to add a `swipeGesture`! So it preserves the tap geusture to keep it available the Player controls! Bravo! (Y) – Randika Vishman Jun 24 '16 at 18:39
1

You can use UIView UITouch event for this. Take a UIView and put MPMoviePlayerController inside that:

theMovie = [MPMoviePlayerController new];
theMovie.view.frame = CGRectMake(0, 0, 1024, 768);
[theMovie setContentURL:theURL];
[theMovie setScalingMode:MPMovieScalingModeAspectFit];
[theMovie setCurrentPlaybackTime:0.2];
[theMovie setFullscreen:YES animated:YES];
[self addSubview:theMovie.view];
[viewMovie addSubview:theMovie.view];

Use UIView touch delegate methods

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"touchesBegan");
    UITouch *touch = [touches anyObject];
    startPosition = [touch locationInView:self];
    [viewMovie touchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"touchesMoved");
    UITouch *touch = [touches anyObject];
    CGPoint endPosition = [touch locationInView:self];

    if (startPosition.x < endPosition.x)
    {
        NSLog(@"Left to Right");
    }
    else
    {
        NSLog(@"Right to Left");
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{

}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{

}

Where startPosition is a CGPoint declared in .h file.

Vaibhav Saran
  • 12,848
  • 3
  • 65
  • 75
0

The root cause is the new gesture recognizer should be added on MPVideoBackgroundView, that is a subview of MPMoviePlayerController view.

Horky
  • 90
  • 7
0

With the latest variant of the MPMoviePlayerController the accepted solution still did't work for me.

I also tried 'hacking the UIView stack', and overlaying my own UIView with gesture recognizers. But it all didn't work.

I finally implemented this solution: 'Overriding UIAplication', which seems an over the top way to do it, but in any case it works. Just add some kind of flag or delegate to it when you detect a touch event, and you can then stop the movie.

Community
  • 1
  • 1
Toad
  • 15,593
  • 16
  • 82
  • 128
0

devdavid has the right idea, but his solution didn't work for me. I guess in newer versions of the player there are other views above the movie player view.

What I did instead was add a UIView above the movie player view. This will prevent all gestures from ever getting through to the player, but that was the whole point as I didn't want any controls visible.

Eran Goldin
  • 980
  • 12
  • 21