适配多窗口模式与可折叠设备

在 iPad 上支持多窗口或在可折叠设备(例如 iPhone Duo)上运行的应用可以跨动态窗口大小和屏幕配置显示界面。在 iPad 上,用户可以同时与某个应用的多个界面互动,并调整窗口大小。在可折叠设备上,设备的折叠状态决定了活动屏幕。展开后,设备会使用较大的内屏。折叠后,设备会使用较小的外屏。

本指南介绍了如何在多窗口模式环境和可折叠显示屏中加载和呈现广告。

前提条件

在开始之前,请完成以下步骤:

在广告请求中设置窗口场景

如要确保接收到的广告适合特定窗口或有效屏幕的大小,请将有效的 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 方法,但您必须将广告请求封装在布尔值标志中,以便仅运行一次。

以下示例在 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];
  });
}

原生广告

原生广告的布局由您掌控。与横幅广告或全屏广告不同,当窗口大小调整时,您无需重新加载原生广告。为原生广告视图使用标准自动布局限制条件或 SwiftUI 布局,以适应尺寸变化。