This seems like it should be pretty straight-forward but I'm having trouble finding a working example, good documentation, or even many StackOverflow posts that are helpful.
I have a custom view which contains an AVPlayer, like so:
@implementation
{
@private
AVPlayerViewController *controller
}
- (id) init
{
self = [super init];
if (self)
{
controller = [[AVPlayerViewController alloc] init];
controller.view.frame = self.view.bounds;
[self.view addSubview:controller.view];
}
return self;
}
@end
(I have a few other views like a message that overlays the player, a poster that I display while swapping videos, etc - but this is the basic setup)
When I integrated the IMA SDK, I started to have issues. If you press the pause button on the remote control during an ad, it pauses the ad just fine. But if you press the pause button again it doesn't unpause the ad, but instead unpauses my content player behind the ad. I don't hear any audio, but I know the content player was unpaused because I have ID3 metadata in my video and an NSLog()
statement when I hit it, and I begin to see these logs. If I press the pause button again, the logs pause. I press it a fourth time, the logs start up again.
To try and fix this I wanted to bind a listener to the remote's play/pause button and make sure that if I was playing an ad then the ad was resumed, not the content. So I tried adding the following to my init
method on my view:
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self.view action:@selector(tapped:)];
[tapRecognizer setAllowedPressTypes:@[ [NSNumber numberWithInt:UIPressTypePlayPause] ]];
[self.view addGestureRecognizer:tapRecognizer];
I then created the following method:
- (void) tapped: (UITapGestureRecognizer *) sender
{
NSLog(@"Tapped");
}
This isn't being called. I'm pretty confident I made a simple mistake, but the documentation isn't very clear, so I'm not sure what I should be doing instead. The official documentation on detecting button presses uses Swift and says:
let tapRecognizer = UITapGestureRecognizer(target: self, action: "tapped:")
tapRecognizer.allowedPressTypes = [NSNumber(integer: UIPressType.PlayPause.rawValue)];
self.view.addGestureRecognizer(tapRecognizer)
I believe I translated those three lines well. The documentation then doesn't show what the tapped
method should look like, but instead goes on a tangent about working with low-level event handling. So to get the appropriate method signature I looked at the documentation on UITagGestureRecognizer which had the following (Swift) example for writing a handler:
func handleTap(sender: UITapGestureRecognizer) {
if sender.state == .ended {
// handling code
}
}
This is why I went with - (void) tapped: (UITapGestureRecognizer *) sender
Still, after all of that, it's not working.
I tried replacing:
initWithTarget:self.view
With:
initWithTarget:controller.view
And:
self.view addGestureRecognizer
With:
controller.view addGestureRecognizer
And this time it looks like something actually happened when I pressed the play/pause button. The app crashed and Xcode gave me the following error:
2019-12-17 12:16:50.937007-0500 Example tvOS App[381:48776] -[_AVPlayerViewControllerContainerView tapped:]: unrecognized selector sent to instance 0x10194e060
So it seems like (correct me if I'm wrong):
So I guess an alternative question to my original would be: How do I allow my class to handle a gesture on some other class (e.g. AVPlayerViewController)?
The target does not need to equal the view that the gesture recognizer is attached to. You can set the target to MyView
but still attach the gesture recognizer to controller.view
To work around the unrecognized selector
crash, you need to make sure you're providing the correct object as the target
for your gesture recognizer. The way UIGestureRecognizer
works is that when the gesture is recognized, it will invoke the given selector
on the given target
object. In other words, when gesture fires, it's going to perform the equivalent of:
[target selector:self];
(You seem to be treating the target
as the view that the gesture will be attached to, which isn't how it works)
So if you implemented tapped:
on your class MyView
, then the target
you pass to the gesture recognizer initializer should be an instance of MyView
. You probably want to provide self
, not self.view
.