Register custom video controls (iOS v3)

Register custom controls as friendly obstructions when using OMID with Google IMA ads in your iOS app.



If you use the IAB Open Measurement Interface Definition (OMID) and run Google IMA ads, all custom video controls that overlay the media element must be registered as friendly obstructions. Friendly obstructions are views -- such as video controls -- that are essential to the user’s experience but do not impact viewability.

The JWFriendlyAdObstructions class enables you to register fully transparent overlays or small buttons. Once registered, your custom video controls are excluded from the ad viewability measurements that the Open Measurement SDK calculates.

The following table lists the views that can and cannot be registered.

PermittedNot permitted
β€’ Transparent overlay used to capture user taps
β€’ Transient buttons such as the following:
Β Β Β β—¦ Pause
Β Β Β β—¦ Play
Β Β Β β—¦ Fullscreen
Β Β Β β—¦ Cast/AirPlay
Β Β Β β—¦ Collapse
Β Β Β β—¦ Progress/Seek
Β Β Β β—¦ Other playback-related actions
β€’ Watermarks
β€’ Pop-ups
β€’ Dialogs
β€’ Non-transient buttons
β€’ Other obscuring views


Register a custom control

Use the following steps and code sample to register a custom video control as a friendly ad obstruction:

  1. Create a JWFriendlyAdObstructions object and name it, for example, adObstructionsManager.
  2. During initialization, associate the JWFriendlyAdObstructions object (adObstructionsManager) with the player.
  3. Add a custom view to your app and name it, for example, button.
  4. Use registerView to register the custom view with the JWFriendlyAdObstructions (adObstructionsManager) list.
@property (nonatomic) JWPlayerController *player;
@property (nonatomic, weak) IBOutlet UIView *playerContainerView;
@property (nonatomic) JWFriendlyAdObstructions *adObstructionsManager;

@end

@implementation ObjCViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    JWAdBreak *adBreak = [JWAdBreak adBreakWithTag:@"https://www.domain.com/adtag.xml" offset:@"pre"];

    JWConfig *config = [JWConfig configWithContentURL:@"http://example.com/hls.m3u8"];
    config.advertising = [[JWAdConfig alloc] init];
    config.advertising.client = JWAdClientGoogima;
    config.advertising.schedule = @[adBreak];
    self.player = [[JWPlayerController alloc] initWithConfig:config];
    self.adObstructionsManager = [[JWFriendlyAdObstructions alloc] initWithPlayer:self.player];

}

- (void)viewDidAppear {
    [super viewDidAppear];
    [self.view addSubview:self.player.view];
    [self setupPlaybackButton];
}

- (void)setupPlaybackButton {
    UIButton *button = [UIButton systemButtonWithImage:[UIImage systemImageNamed:@"play"] target:self action:@selector(togglePlayback:)];
    CGPoint origin = CGPointMake(self.playerContainerView.center.x - 22, self.playerContainerView.center.y - 22);
    button.frame = CGRectMake(origin.x, origin.y, 44, 44);
    button.backgroundColor = [UIColor whiteColor];
    
    [self.playerContainerView insertSubview:button aboveSubview:self.player.view];
    [self.adObstructionsManager registerView:button];
}

- (void)togglePlayback:(UIButton *)button {
    if (self.player.state == JWPlayerStatePlaying) {
        [self.player pause];
        [button setImage:[UIImage systemImageNamed:@"play"] forState:UIControlStateNormal];
    } else {
        [self.player play];
        [button setImage:[UIImage systemImageNamed:@"pause"] forState:UIControlStateNormal];
    }
}
class ViewController: UIViewController {
    @IBOutlet weak var playerContainerView: UIView!
    var player: JWPlayerController?
    var adObstructionsManager: JWFriendlyAdObstructions?

    override func viewDidLoad() {
        super.viewDidLoad()

        var adBreak = JWAdBreak(tag: "https://www.domain.com/adtag.xml", offset: "pre")

        var config = JWConfig(contentURL: "http://example.com/hls.m3u8")
        config.advertising = JWAdConfig()
        config.advertising.client = .googima
        config.advertising.schedule = [adBreak]
        player = JWPlayerController(config: config)

        if let player = player {
            adObstructionsManager = JWFriendlyAdObstructions(player: player)
        }
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        playerContainerView.addSubview(player!.view)
        setupPlaybackButton()
    }

    func setupPlaybackButton() {
        let origin = CGPoint(x: playerContainerView.center.x - 22, y: playerContainerView.center.y - 22)
        let playbackButton = UIButton(frame: CGRect(origin: origin, size: CGSize(width: 44, height: 44)))
        
        playbackButton.setImage(UIImage(systemName: "play"), for: .normal)
        playbackButton.backgroundColor = UIColor.white
        playbackButton.addTarget(self, action: #selector(self.togglePlayback(_:)), for: .touchUpInside)
        
        if let playerView = player?.view {
            playerContainerView.insertSubview(playbackButton, aboveSubview: playerView)
            adObstructionsManager?.registerView(playbackButton)
        }
    }
    
    @objc func togglePlayback(_ button: UIButton) {
        if player?.state == JWPlayerState.playing {
            player?.pause()
            button.setImage(UIImage(systemName: "play"), for: .normal)
        } else {
            player?.play()
            button.setImage(UIImage(systemName: "pause"), for: .normal)
        }
    }
}