複数のウィンドウと折りたたみ式デバイスに対応する

iPad でマルチ ウィンドウをサポートするアプリや、iPhone Duo などの折りたたみ式デバイスで動作するアプリは、動的なウィンドウ サイズと画面構成で UI を表示できます。iPad では、ユーザーはアプリの UI の複数のコピーを同時に操作したり、ウィンドウのサイズを変更したりできます。折りたたみ式デバイスでは、デバイスの折りたたみ状態によってアクティブな画面が決まります。開いた状態では、デバイスはより大きなインナー ディスプレイを使用します。折りたたむと、デバイスは小さい外側のディスプレイを使用します。

このガイドでは、マルチウィンドウ環境と折りたたみ式ディスプレイで広告を読み込んでレンダリングする方法について説明します。

前提条件

始める前に、次のことを行います。

広告リクエストにウィンドウ シーンを設定する

特定のウィンドウまたはアクティブなディスプレイに適合する広告を受け取るには、アクティブな UIWindowScene オブジェクトを GADRequest オブジェクトの scene プロパティに割り当てます。Google Mobile Ads SDK は、そのシーンに適したサイズの広告を返します。SwiftUI で、アクティブなフォアグラウンド ウィンドウ シーンの接続済みシーンをクエリします。

次の例では、ウィンドウ シーンを構成し、成功した場合はインタースティシャル広告を読み込み、失敗した場合はエラー メッセージをログに記録します。

Swift

func loadInterstitial() async {
  let request = Request()
  request.scene = view.window?.windowScene

  do {
    interstitial = try await InterstitialAd.load(
      with: "ca-app-pub-3940256099942544/4411468910", request: request)
  } catch {
    print("Failed to load interstitial ad with error: \(error.localizedDescription)")
  }
}

SwiftUI

func loadInterstitial() async {
  let request = Request()
  request.scene =
    UIApplication.shared.connectedScenes
    .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene

  do {
    let _ = try await InterstitialAd.load(
      with: "ca-app-pub-3940256099942544/4411468910", request: request)
  } catch {
    print("Failed to load interstitial ad with error: \(error.localizedDescription)")
  }
}

Objective-C

- (void)loadInterstitial {
  GADRequest *request = [GADRequest request];
  request.scene = self.view.window.windowScene;

  [GADInterstitialAd
       loadWithAdUnitID:@"ca-app-pub-3940256099942544/4411468910"
                request:request
      completionHandler:^(GADInterstitialAd *ad, NSError *error) {
        if (error) {
          NSLog(@"Failed to load interstitial ad with error: %@", [error localizedDescription]);
          return;
        }
        self.interstitial = ad;
      }];
}

テストモードでは、マルチシーン アプリがシーンを渡さずに広告をリクエストすると、広告リクエストは次のエラーで失敗します。

<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.

本番環境モードでは、広告リクエストは処理されますが、広告コンテンツのサイズよりも小さいウィンドウに広告が表示されると、広告の表示に失敗します。

<Google> Ad cannot be presented. The full screen ad content size exceeds the current window size.

広告リクエストを作成する

マルチシーン アプリケーションで広告リクエストを送信するには、有効なウィンドウ シーンが必要です。ウィンドウ シーンが入力されている場合は、viewDidAppear メソッドを使用して広告リクエストを作成します。viewDidLoad メソッドでは、ビューがまだウィンドウに追加されていないため、このプロセスをおすすめします。

アプリのライフサイクルで viewDidAppear メソッドを複数回呼び出すことはできますが、広告リクエストをブール値フラグでラップして 1 回だけ実行する必要があります。

次の例では、viewDidAppear メソッドで広告リクエストを初期化しています。

Swift

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)
  if !requestInitialized {
    Task {
      await loadInterstitial()
    }
    requestInitialized = true
  }
}

Objective-C

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  if (!_requestInitialized) {
    [self loadInterstitial];
    _requestInitialized = YES;
  }
}

ウィンドウのサイズ変更を処理する

ウィンドウのサイズは、ユーザーが iPad でシーンのサイズを変更したり、iPhone Duo などの折りたたみ式デバイスを折りたたんだり開いたりすると変化します。ウィンドウ サイズが変更されたら、読み込まれた広告が更新後のサイズに適合していることを確認し、必要に応じて広告を再読み込みします。

読み込まれた広告が更新されたサイズに適合していることを確認するには、次のいずれかの方法を使用します。

フルスクリーン広告

読み込まれたフルスクリーン広告が、更新されたウィンドウ サイズまたは折りたたみ状態でも有効かどうかを確認するには、canPresentFromRootViewController:error: メソッドを使用します。チェックが失敗した場合は、更新されたシーンのサイズに合わせて広告を読み込みます。

次の例では、サイズ変更後にインタースティシャル広告を表示できるかどうかを確認し、必要に応じて広告を再読み込みしています。

Swift

override func viewWillTransition(
  to size: CGSize,
  with coordinator: UIViewControllerTransitionCoordinator
) {
  super.viewWillTransition(to: size, with: coordinator)

  coordinator.animate(alongsideTransition: nil) { [weak self] _ in
    guard let self else { return }
    do {
      try self.interstitial?.canPresent(from: self)
    } catch {
      Task {
        await self.loadInterstitial()
      }
    }
  }
}

Objective-C

- (void)viewWillTransitionToSize:(CGSize)size
       withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
  [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

  [coordinator
      animateAlongsideTransition:nil
                      completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
                        if (![self.interstitial canPresentFromRootViewController:self error:nil]) {
                          [self loadInterstitial];
                        }
                      }];
}

ウィンドウのサイズ変更と折りたたみ遷移を、デバイスの回転と同様に処理します。バナービューを safeAreaLayoutGuide プロパティに制限します。このプロセスにより、広告がデバイスのヒンジ、カットアウト、システムバーに重ならないようにすることができます。このプロセスでは、更新されたセーフエリアの幅を使用して、アンカー アダプティブ バナーのサイズも再計算されます。

次の例では、サイズ変更中に更新されたウィンドウ幅を使用してアンカー アダプティブ バナーを読み込みます。

Swift

override func viewWillTransition(
  to size: CGSize,
  with coordinator: UIViewControllerTransitionCoordinator
) {
  super.viewWillTransition(to: size, with: coordinator)

  coordinator.animate(alongsideTransition: nil) { [weak self] _ in
    self?.loadBannerAd()
  }
}

@MainActor
func loadBannerAd() {
  let frame = view.frame.inset(by: view.safeAreaInsets)
  let viewWidth = frame.size.width

  bannerView.adSize = largeAnchoredAdaptiveBanner(width: viewWidth)

  let request = Request()
  request.scene = view.window?.windowScene
  bannerView.load(request)
}

SwiftUI

struct MultiSceneBannerView: View {
  var body: some View {
    GeometryReader { geometry in
      let adSize = largeAnchoredAdaptiveBanner(width: geometry.size.width)
      MultiSceneBannerViewContainer(adSize: adSize)
        .frame(width: adSize.size.width, height: adSize.size.height)
    }
  }
}

private struct MultiSceneBannerViewContainer: UIViewRepresentable {
  let adSize: AdSize

  func makeUIView(context: Context) -> BannerView {
    let bannerView = BannerView(adSize: adSize)
    let request = Request()
    request.scene =
      UIApplication.shared.connectedScenes
      .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene
    bannerView.load(request)
    return bannerView
  }

  func updateUIView(_ uiView: BannerView, context: Context) {
    if uiView.adSize.size != adSize.size {
      uiView.adSize = adSize
      let request = Request()
      request.scene = uiView.window?.windowScene
      uiView.load(request)
    }
  }
}

Objective-C

- (void)viewWillTransitionToSize:(CGSize)size
       withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
  [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

  [coordinator
      animateAlongsideTransition:nil
                      completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
                        [self loadBannerAd];
                      }];
}

- (void)loadBannerAd {
  dispatch_async(dispatch_get_main_queue(), ^{
    CGRect frame = UIEdgeInsetsInsetRect(self.view.frame, self.view.safeAreaInsets);
    CGFloat viewWidth = frame.size.width;

    self.bannerView.adSize = GADLargeAnchoredAdaptiveBannerAdSizeWithWidth(viewWidth);

    GADRequest *request = [GADRequest request];
    request.scene = self.view.window.windowScene;
    [self.bannerView loadRequest:request];
  });
}

ネイティブ広告

ネイティブ広告のレイアウトは、パブリッシャー様が管理します。バナー広告やフルスクリーン広告とは異なり、ウィンドウのサイズが変更されたときにネイティブ広告を再読み込みする必要はありません。ネイティブ広告ビューで標準の Auto Layout 制約または SwiftUI レイアウトを使用して、サイズの変更に対応します。