Beginning with iOS 13, applications can support multiple windows on iPad, meaning users can interact with multiple concurrent copies of an app's UI. Each window can be created in different sizes and can be resized at anytime, which has implications for how ads are loaded and presented.
This guide is intended to show you the best practices for rendering ads correctly in an iPad multi-window scenario.
Prerequisites
- Google Mobile Ads SDK 7.53.0 or higher
- Enable scene support in your project
- Implement at least one ad format
Set the scene in an ad request
In order to receive an ad that fits a specific window, you pass the view's
windowScene
to the ad request. The Google Mobile Ads SDK returns an ad with
valid size for that scene.
Swift
func loadInterstitial() { let request = GADRequest() request.scene = view.window?.windowScene GADInterstitialAd.load(withAdUnitID: "[AD_UNIT_ID]", request: request) { ad, error in } }
Objective-C
- (void)loadInterstitial { GADRequest *request = [GADRequest request]; request.scene = self.view.window.windowScene; [GADInterstitialAd loadWithAdUnitID:@"[AD_UNIT_ID]" request:request completionHandler:^(GADInterstitialAd *ad, NSError *error) {}]; }
In test mode, ad requests will fail with the following error if your multiscene app requests an ad without passing a scene:
<Google> Invalid Request. The GADRequest scene property should be set for
applications that support multi-scene. Treating the unset property as an error
while in test mode.
In production mode, the ad request fills, but presenting the ad will fail if the ad is to be presented on a non full screen window. The error message in this case is:
<Google> Ad cannot be presented. The full screen ad content size exceeds the current window size.
Build the ad request in viewDidAppear:
The case of multi-window introduces a requirement of having a window scene for
sending ad requests. Since a view has not yet been added to a window in
viewDidLoad:
, you should instead build ad requests in viewDidAppear:
where the window scene is set by that point.
Note that viewDidAppear:
can get called more than once during an app's
lifecycle. We recommend that you wrap the ad request initialization code in a
flag that indicates whether it has been done already.
Swift
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if !requestInitialized { loadInterstitial() requestInitialized = true } }
Objective-C
- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (!_requestInitialized) { [self loadInterstitial]; _requestInitialized = YES; } }
Handle resizing
Users can drag scenes around at anytime, changing window sizes after an ad
request has been made. It's up to you to request a new ad when resizing happens.
The sample code below uses
viewWillTransitionToSize:withTransitionCoordinator:
to get notified when the root view controller's window rotates or resizes, but
you can also listen to
windowScene:didUpdateCoordinateSpace:interfaceOrientation:traitCollection:
for window scene specific changes.
Interstitial and Rewarded Ad
The Google Mobile Ads SDK provides the method
canPresentFromViewController:error:
to determine if an interstitial or a
rewarded ad is valid or not, giving you the ability to check if any fullscreen
ad needs to be refreshed whenever window size changes.
Swift
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) coordinator.animate(alongsideTransition: nil) { [self] context in do { try interstitial?.canPresent(fromRootViewController: self) } catch { loadInterstitial() } } }
Objective-C
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition:nil completion:^(id _Nonnull context) { if (![self.interstitial canPresentFromRootViewController:self error:nil]) { [self loadInterstitial]; } }]; }
Banner
You can handle window resizing in the same way you do for window rotation. Your app is responsible for ensuring the banner ad fits the new window size.
The example below creates a new adaptive banner with the new window width:
Swift
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) coordinator.animate(alongsideTransition: nil) { [self] context in loadBanner() } } func loadBanner() { let bannerWidth = view.frame.size.width bannerView.adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(bannerWidth) let request = GADRequest() request.scene = view.window?.windowScene bannerView.load(request) }
Objective-C
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition:nil completion:^(id _Nonnull context) { [self loadBannerAd]; }]; } - (void)loadBannerAd { CGFloat bannerWidth = self.view.frame.size.width; self.bannerView.adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(bannerWidth); GADRequest *request = [GADRequest request]; request.scene = self.view.window.windowScene; [self.bannerView loadRequest:request]; }
Native ad
You are in control of rendering native ads and are responsible for ensuring the native ad is rendered inside a resized view, similar to the rest of your app content.
Known issues
Currently multi-window and split-screen ads are supported only in portrait mode. You will get the following log message when requesting an ad in landscape mode.
<Google> Ad cannot be presented. The full screen ad content size exceeds the
current window size.