Supportare più finestre e dispositivi pieghevoli

Le applicazioni che supportano più finestre su iPad o che vengono eseguite su dispositivi pieghevoli, come iPhone Duo, possono visualizzare l'interfaccia utente in dimensioni delle finestre e configurazioni dello schermo dinamiche. Su iPad, gli utenti possono interagire con più copie simultanee della UI di un'app e ridimensionare le finestre. Sui dispositivi pieghevoli, lo stato di piegatura del dispositivo determina lo schermo attivo. Quando è aperto, il dispositivo utilizza il display interno più grande. Quando è piegato, il dispositivo utilizza il display esterno più piccolo.

Questa guida illustra il caricamento e il rendering degli annunci in ambienti multifinestra e display pieghevoli.

Prerequisiti

Prima di iniziare, completa i seguenti passaggi:

Imposta la scena della finestra in una richiesta di annuncio

Per ricevere un annuncio adatto a una finestra specifica o a un display attivo, assegna l'oggetto UIWindowScene attivo alla proprietà scene nell'oggetto GADRequest. Google Mobile Ads SDK restituisce un annuncio di dimensioni adatte alla scena. In SwiftUI, esegui una query sulle scene connesse per la scena della finestra in primo piano attiva.

L'esempio seguente configura la scena della finestra, carica un annuncio interstitial in caso di esito positivo e registra un messaggio di errore in caso di esito negativo:

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

In modalità di test, le richieste di annunci non vanno a buon fine con il seguente errore se l'app con più scene richiede un annuncio senza passare una scena:

<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 modalità di produzione, la richiesta di annuncio viene soddisfatta, ma la presentazione dell'annuncio non va a buon fine se l'annuncio viene presentato in una finestra più piccola delle dimensioni dei contenuti dell'annuncio:

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

Crea la richiesta di annuncio

Quando inviano richieste di annunci, le applicazioni con più scene richiedono una scena della finestra valida. Se la scena della finestra è compilata, crea le richieste di annunci utilizzando il metodo viewDidAppear. Consigliamo questa procedura perché una visualizzazione non è ancora stata aggiunta a una finestra nel metodo viewDidLoad.

Sebbene sia possibile chiamare il metodo viewDidAppear più di una volta nel ciclo di vita di un'app, devi inserire la richiesta di annuncio in un flag booleano per eseguirla una sola volta.

L'esempio seguente inizializza la richiesta di annuncio nel metodo 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;
  }
}

Gestisci il ridimensionamento della finestra

Le dimensioni della finestra cambiano quando gli utenti ridimensionano le scene su iPad o aprono e chiudono un dispositivo pieghevole come iPhone Duo. Quando le dimensioni della finestra cambiano, verifica che gli annunci caricati si adattino alle dimensioni aggiornate e ricaricali se necessario.

Per verificare che gli annunci caricati si adattino alle dimensioni aggiornate, utilizza uno dei seguenti metodi:

Annunci a schermo intero

Per verificare se un annuncio a schermo intero caricato rimane valido per le dimensioni della finestra o lo stato di piegatura aggiornati, utilizza il metodo canPresentFromRootViewController:error:. Se il controllo non va a buon fine, carica un annuncio di dimensioni adatte alla scena aggiornata.

L'esempio seguente verifica se un annuncio interstitial può essere mostrato dopo una transizione delle dimensioni e ricarica l'annuncio, se necessario:

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

Gestisci il ridimensionamento della finestra e le transizioni di apertura/chiusura in modo simile alla rotazione del dispositivo. Limita le visualizzazioni del banner alla proprietà safeAreaLayoutGuide. In questo modo, gli annunci non vengono coperti da cerniere, ritagli e barre di sistema del dispositivo. Questo processo ricalcola anche le dimensioni del banner adattivo ancorato utilizzando la larghezza dell'area sicura aggiornata.

L'esempio seguente carica un banner adattivo ancorato con la larghezza della finestra aggiornata durante una transizione delle dimensioni:

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

Annunci nativi

Sei tu a controllare il layout degli annunci nativi. A differenza dei banner o degli annunci a schermo intero, non è necessario ricaricare gli annunci nativi quando la finestra viene ridimensionata. Utilizza i vincoli di layout automatico standard o i layout SwiftUI per adattare la visualizzazione dell'annuncio nativo alle modifiche delle dimensioni.