Oferecer compatibilidade com várias janelas e dobráveis

Os aplicativos que oferecem suporte a várias janelas no iPad ou que são executados em dispositivos dobráveis, como o iPhone Duo, podem mostrar a interface em tamanhos de janela e configurações de tela dinâmicos. No iPad, os usuários podem interagir com várias cópias simultâneas da interface de um app e redimensionar janelas. Em dispositivos dobráveis, o estado de dobra determina a tela ativa. Quando aberto, o dispositivo usa a tela interna maior. Quando dobrado, o dispositivo usa a tela externa menor.

Este guia aborda o carregamento e a renderização de anúncios em ambientes de várias janelas e telas dobráveis.

Pré-requisitos

Antes de começar, faça o seguinte:

Definir a cena da janela em uma solicitação de anúncio

Para receber um anúncio que se ajuste a uma janela ou tela ativa específica, atribua o objeto UIWindowScene ativo à propriedade scene no objeto GADRequest. Google Mobile Ads SDK retorna um anúncio dimensionado para essa cena. No SwiftUI, consulte as cenas conectadas para encontrar a cena da janela ativa em primeiro plano.

O exemplo a seguir configura a cena da janela, carrega um anúncio intersticial em caso de sucesso e registra uma mensagem de erro em caso de falha:

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;
      }];
}

No modo de teste, as solicitações de anúncio falham com o seguinte erro se o app de várias cenas solicitar um anúncio sem transmitir uma cena:

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

No modo de produção, a solicitação de anúncio é preenchida, mas a apresentação do anúncio falha se ele é apresentado em uma janela menor que o tamanho do conteúdo do anúncio:

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

Criar a solicitação de anúncio

Ao enviar solicitações de anúncio, os aplicativos com várias cenas exigem uma cena de janela válida. Onde a cena da janela é preenchida, crie solicitações de anúncio usando o método viewDidAppear. Recomendamos esse processo porque uma visualização ainda não foi adicionada a uma janela no método viewDidLoad.

Embora seja possível chamar o método viewDidAppear mais de uma vez no ciclo de vida de um app, é necessário incluir a solicitação de anúncio em uma flag booleana para que ela seja executada apenas uma vez.

O exemplo a seguir inicializa a solicitação de anúncio no método 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;
  }
}

Processar o redimensionamento da janela

As dimensões da janela mudam quando os usuários redimensionam cenas no iPad ou dobram e desdobram um dispositivo dobrável, como o iPhone Duo. Quando o tamanho da janela mudar, verifique se os anúncios carregados se ajustam às dimensões atualizadas e recarregue-os, se necessário.

Para verificar se os anúncios carregados se ajustam às dimensões atualizadas, use um dos seguintes métodos:

Anúncios de tela cheia

Para verificar se um anúncio de tela cheia carregado continua válido para o tamanho de janela ou estado de dobra atualizado, use o método canPresentFromRootViewController:error:. Se a verificação falhar, carregue um anúncio dimensionado para a cena atualizada.

O exemplo a seguir verifica se um anúncio intersticial pode ser veiculado após uma transição de tamanho e recarrega o anúncio, se necessário:

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];
                        }
                      }];
}

Processar o redimensionamento de janelas e as transições de dobra de forma semelhante à rotação do dispositivo. Limite as visualizações de banner à propriedade safeAreaLayoutGuide. Esse processo permite que os anúncios fiquem livres de dobradiças, recortes e barras de sistema do dispositivo. Esse processo também recalcula o tamanho do banner adaptativo fixo usando a largura atualizada da área segura.

O exemplo a seguir carrega um banner adaptativo fixo com a largura de janela atualizada durante uma transição de tamanho:

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];
  });
}

Anúncios nativos

Você controla o layout dos anúncios nativos. Ao contrário dos banners ou anúncios de tela cheia, você não precisa recarregar os anúncios nativos quando a janela é redimensionada. Use restrições padrão de layout automático ou layouts do SwiftUI para que a visualização do anúncio nativo se adapte às mudanças de tamanho.