Integra Trasmetti nella tua app per iOS

Questa guida per gli sviluppatori descrive come aggiungere il supporto di Google Cast sul tuo dispositivo iOS dell'app del mittente utilizzando l'SDK Sender per iOS.

Il dispositivo mobile o il laptop è il mittente che controlla la riproduzione e Il dispositivo Google Cast è il ricevitore che mostra i contenuti sulla TV.

Il framework del mittente si riferisce al file binario della libreria di classi Cast e al file associato risorse presenti in fase di runtime sul mittente. L'app mittente o l'app Cast si riferisce a un'app in esecuzione anche sul mittente. L'app Web receiver si riferisce all'applicazione HTML in esecuzione sul ricevitore web.

Il framework del mittente utilizza un design di callback asincrono per informare il mittente di eventi e la transizione tra i vari stati dell'attività dell'app Google Cast. ciclo di lancio di Android.

Flusso di app

I passaggi seguenti descrivono il flusso di esecuzione tipico di alto livello per un mittente App per iOS:

  • Viene avviato il framework Cast GCKDiscoveryManager in base alle proprietà fornite GCKCastOptions a inizia la ricerca dei dispositivi.
  • Quando l'utente fa clic sul pulsante Trasmetti, la struttura presenta la con l'elenco dei dispositivi di trasmissione rilevati.
  • Quando l'utente seleziona un dispositivo di trasmissione, il framework tenta di avviare lo strumento App Web Ricevitore sul dispositivo di trasmissione.
  • Il framework richiama i callback nell'app del mittente per confermare che È stata lanciata l'app WebRicevitore.
  • Il framework crea un canale di comunicazione tra il mittente e App WebRicevitore.
  • Il framework utilizza il canale di comunicazione per caricare e controllare i contenuti multimediali la riproduzione sul ricevitore web.
  • Il framework sincronizza lo stato di riproduzione dei contenuti multimediali tra il mittente e WebRicevitore: quando l'utente esegue azioni nell'interfaccia utente del mittente, il framework supera le richieste di controllo multimediale al ricevitore web e quando quest'ultimo invia aggiornamenti sullo stato dei contenuti multimediali, il framework aggiorna lo stato dell'interfaccia utente del mittente.
  • Quando l'utente fa clic sul pulsante Trasmetti per disconnettersi dal dispositivo di trasmissione, il framework disconnetterà l'app del mittente dal ricevitore web.

Per risolvere i problemi del mittente, devi abilitare il logging.

Per un elenco completo di tutti i corsi, i metodi e gli eventi in Google Cast framework iOS, consulta API Google Cast per iOS Riferimento. I passaggi sono descritti nelle sezioni seguenti per integrare Google Cast nella tua app per iOS.

Metodi di chiamata dal thread principale

Inizializzare il contesto Cast

Il framework Cast ha un oggetto singleton globale, GCKCastContext, che coordina tutte le attività del framework. Questo oggetto deve essere inizializzato all'inizio del ciclo di vita dell'applicazione, in genere metodo -[application:didFinishLaunchingWithOptions:] del delegato dell'app, quindi il ripristino automatico della sessione al riavvio dell'app del mittente possa attivarsi correttamente.

Un GCKCastOptions durante l'inizializzazione di GCKCastContext. Questa classe contiene opzioni che influiscono sul comportamento del framework. Il più di questi è importante l'ID applicazione Web receiver, usato per filtrare dei risultati del rilevamento e avviare l'app Web Ricevitore quando viene avviata una sessione di trasmissione. a iniziare.

Anche il metodo -[application:didFinishLaunchingWithOptions:] è una buona soluzione per configurare un delegato di logging che riceva i messaggi di logging dal framework. Queste possono essere utili per il debug e la risoluzione dei problemi.

Rapido
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate {
  let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID
  let kDebugLoggingEnabled = true

  var window: UIWindow?

  func applicationDidFinishLaunching(_ application: UIApplication) {
    let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
    let options = GCKCastOptions(discoveryCriteria: criteria)
    GCKCastContext.setSharedInstanceWith(options)

    // Enable logger.
    GCKLogger.sharedInstance().delegate = self

    ...
  }

  // MARK: - GCKLoggerDelegate

  func logMessage(_ message: String,
                  at level: GCKLoggerLevel,
                  fromFunction function: String,
                  location: String) {
    if (kDebugLoggingEnabled) {
      print(function + " - " + message)
    }
  }
}
Obiettivo-C

AppDelegate.h

@interface AppDelegate () <GCKLoggerDelegate>
@end

AppDelegate.m

@implementation AppDelegate

static NSString *const kReceiverAppID = @"AABBCCDD";
static const BOOL kDebugLoggingEnabled = YES;

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc]
                                    initWithApplicationID:kReceiverAppID];
  GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria:criteria];
  [GCKCastContext setSharedInstanceWithOptions:options];

  // Enable logger.
  [GCKLogger sharedInstance].delegate = self;

  ...

  return YES;
}

...

#pragma mark - GCKLoggerDelegate

- (void)logMessage:(NSString *)message
           atLevel:(GCKLoggerLevel)level
      fromFunction:(NSString *)function
          location:(NSString *)location {
  if (kDebugLoggingEnabled) {
    NSLog(@"%@ - %@, %@", function, message, location);
  }
}

@end

I widget UX di Cast

L'SDK Cast per iOS fornisce questi widget conformi al design Cast Elenco di controllo:

  • Overlay introduttivo: La classe GCKCastContext ha un metodo, presentCastInstructionsViewControllerOnceWithCastButton, che può essere usato per mettere in evidenza il pulsante Trasmetti la prima volta che un ricevitore web è disponibile. L'app del mittente può personalizzare il testo e la posizione del titolo e il pulsante Ignora.

  • Pulsante Trasmetti: A partire dall'SDK Cast iOS 4.6.0, il pulsante Trasmetti è sempre visibile quando il dispositivo del mittente è connesso alla rete Wi-Fi. La prima volta che l'utente tocca il dispositivo. sul pulsante Trasmetti dopo l'avvio iniziale dell'app, viene visualizzata una finestra di dialogo per consentire all'utente di concedere all'app l'accesso alla rete locale sui dispositivi in rete. In seguito, quando l'utente tocca il pulsante Trasmetti, viene avviata che mostra l'elenco dei dispositivi rilevati. Quando l'utente tocca sul pulsante Trasmetti mentre il dispositivo è connesso, viene visualizzata la I metadati multimediali (come titolo, nome dello studio di registrazione e una miniatura) immagine) o consente all'utente di disconnettersi dal dispositivo di trasmissione. Quando l'utente tocca il pulsante Trasmetti quando non ci sono dispositivi disponibili, viene visualizzata una schermata con informazioni sul motivo per cui il dispositivo non è stato trovato e come risolvere i problemi.

  • Mini controller: Quando l'utente sta trasmettendo contenuti ed è uscito dalla versione corrente pagina dei contenuti o controller espanso su un altro schermo nell'app del mittente, il mini controller viene mostrato nella parte inferiore dello schermo per consentire all'utente di vedere i metadati multimediali attualmente trasmessi e controllare la riproduzione.

  • Controller espanso: Quando l'utente trasmette contenuti, se fa clic sulla notifica multimediale o mini controller, si avvia il controller espanso, che mostra sono attualmente in riproduzione metadati multimediali e include diversi pulsanti per controllare la riproduzione di contenuti multimediali.

Aggiungi un pulsante Trasmetti

Il framework fornisce un componente Pulsante Trasmetti come sottoclasse UIButton. Può essere aggiunto alla barra del titolo dell'app racchiudendolo in un UIBarButtonItem. Un tipico UIViewController sottoclasse può installare un pulsante Trasmetti nel seguente modo:

Rapido
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
castButton.tintColor = UIColor.gray
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
Obiettivo-C
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
castButton.tintColor = [UIColor grayColor];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];

Per impostazione predefinita, se tocchi il pulsante si aprirà la finestra di dialogo Trasmetti fornita dall' il modello di machine learning.

GCKUICastButton può anche essere aggiunto direttamente allo storyboard.

Configura il rilevamento dei dispositivi

Nel framework, il rilevamento dei dispositivi avviene automaticamente. Non è necessario avviare o interrompere esplicitamente il processo di rilevamento, a meno che non implementi una UI personalizzata.

Il rilevamento nel framework è gestito dalla classe GCKDiscoveryManager, che è una proprietà GCKCastContext. La fornisce un componente predefinito della finestra di dialogo di trasmissione per la selezione dei dispositivi e controllo. L'elenco dei dispositivi è ordinato lessicograficamente in base al nome semplice per i dispositivi.

Come funziona la gestione delle sessioni

L'SDK Cast introduce il concetto di sessione di trasmissione, il la cui creazione combina i passaggi per connettersi a un dispositivo, avviare (o accedere) a un App ricevitore, connessione all'app e inizializzazione di un canale di controllo multimediale. Guarda il ricevitore web Guida al ciclo di vita dell'applicazione per ulteriori informazioni sulle sessioni di trasmissione e sul ciclo di vita del ricevitore web.

Le sessioni sono gestite dalla classe GCKSessionManager, che è una proprietà GCKCastContext. Le singole sessioni sono rappresentate dalle sottoclassi della classe GCKSession: ad esempio, GCKCastSession rappresenta le sessioni con dispositivi di trasmissione. Puoi accedere alla trasmissione attualmente attiva sessione (se presente), come proprietà currentCastSession di GCKSessionManager.

La GCKSessionManagerListener può essere usata per monitorare gli eventi della sessione, come la creazione di sessioni, sospensione, ripresa e risoluzione. Il framework sospende automaticamente sessioni quando l'app del mittente è in background e tenta di riprenderla quando l'app torna in primo piano (o viene riavviata dopo chiusura anomala/improvvisa dell'app mentre una sessione era attiva).

Se è in uso la finestra di dialogo Trasmetti, le sessioni vengono create e rimosse automaticamente in risposta ai gesti dell'utente. Altrimenti, l'app può iniziare e terminare in modo esplicito tramite metodi GCKSessionManager

Se l'app deve eseguire un'elaborazione speciale in risposta al ciclo di vita della sessione può registrare una o più istanze GCKSessionManagerListener con GCKSessionManager. GCKSessionManagerListener è un protocollo che definisce per eventi quali inizio della sessione, fine della sessione e così via.

Trasferimento dello streaming

La conservazione dello stato della sessione è alla base del trasferimento dello streaming, dove gli utenti possono spostare gli stream audio e video esistenti su più dispositivi con i comandi vocali e Google Home all'app o a smart display. La riproduzione dei contenuti multimediali si interrompe su un dispositivo (l'origine) e continua su un altro (lo destinazione). Qualsiasi dispositivo di trasmissione con il firmware più recente può fungere da sorgente o destinazione in un trasferimento dello streaming.

Per ricevere il nuovo dispositivo di destinazione durante il trasferimento dello streaming, utilizza GCKCastSession#device durante [sessionManager:didResumeCastSession:] di Google.

Consulta Trasferimento dello streaming sul ricevitore web per ulteriori informazioni.

Riconnessione automatica

Il framework Cast aggiunge una logica di riconnessione per gestire automaticamente la riconnessione in alcuni casi d'angolo, come ad esempio:

  • Recuperare dopo una perdita temporanea del Wi-Fi
  • Ripristina dalla sospensione del dispositivo
  • Recuperare l'app in background
  • Ripristinare se l'app ha avuto un arresto anomalo

Come funziona il controllo multimediale

Se viene stabilita una sessione di trasmissione con un'app WebRicevitore che supporta i contenuti multimediali un'istanza di Compute Engine, GCKRemoteMediaClient verrà creato automaticamente dal framework; è possibile accedervi come remoteMediaClient della proprietà GCKCastSession in esecuzione in un'istanza Compute Engine.

Tutti i metodi su GCKRemoteMediaClient che inviano richieste al ricevitore web restituirà un GCKRequest che possono essere utilizzati per monitorare la richiesta. R GCKRequestDelegate può essere assegnato all'oggetto per ricevere notifiche sull'eventuale il risultato dell'operazione.

È previsto che l'istanza di GCKRemoteMediaClient possono essere condivisi da più parti dell'app e alcuni componenti interni del framework, come la finestra di dialogo Trasmetti e i controlli multimediali mini condividono in esecuzione in un'istanza Compute Engine. A questo scopo, GCKRemoteMediaClient supporta la registrazione di più GCKRemoteMediaClientListener.

Imposta metadati multimediali

La GCKMediaMetadata rappresenta informazioni su un elemento multimediale che vuoi trasmettere. Le seguenti esempio crea una nuova istanza GCKMediaMetadata di un film e imposta il titolo, un sottotitolo, il nome dello studio di registrazione e due immagini.

Rapido
let metadata = GCKMediaMetadata()
metadata.setString("Big Buck Bunny (2008)", forKey: kGCKMetadataKeyTitle)
metadata.setString("Big Buck Bunny tells the story of a giant rabbit with a heart bigger than " +
  "himself. When one sunny day three rodents rudely harass him, something " +
  "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon " +
  "tradition he prepares the nasty rodents a comical revenge.",
                   forKey: kGCKMetadataKeySubtitle)
metadata.addImage(GCKImage(url: URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg")!,
                           width: 480,
                           height: 360))
Obiettivo-C
GCKMediaMetadata *metadata = [[GCKMediaMetadata alloc]
                                initWithMetadataType:GCKMediaMetadataTypeMovie];
[metadata setString:@"Big Buck Bunny (2008)" forKey:kGCKMetadataKeyTitle];
[metadata setString:@"Big Buck Bunny tells the story of a giant rabbit with a heart bigger than "
 "himself. When one sunny day three rodents rudely harass him, something "
 "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon "
 "tradition he prepares the nasty rodents a comical revenge."
             forKey:kGCKMetadataKeySubtitle];
[metadata addImage:[[GCKImage alloc]
                    initWithURL:[[NSURL alloc] initWithString:@"https://commondatastorage.googleapis.com/"
                                 "gtv-videos-bucket/sample/images/BigBuckBunny.jpg"]
                    width:480
                    height:360]];

Consulta la sezione Selezione delle immagini e Memorizzazione nella cache sull'uso delle immagini con i metadati multimediali.

Carica contenuti multimediali

Per caricare un elemento multimediale, crea un GCKMediaInformation utilizzando i metadati del file multimediale. Quindi ottieni lo stato attuale GCKCastSession e utilizza i suoi GCKRemoteMediaClient per caricare i contenuti multimediali sull'app del ricevitore. Puoi quindi utilizzare GCKRemoteMediaClient per controllare un'app di media player in esecuzione sul ricevitore, ad esempio per riprodurre, mettere in pausa e interrompere.

Rapido
let url = URL.init(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")
guard let mediaURL = url else {
  print("invalid mediaURL")
  return
}

let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL)
mediaInfoBuilder.streamType = GCKMediaStreamType.none;
mediaInfoBuilder.contentType = "video/mp4"
mediaInfoBuilder.metadata = metadata;
mediaInformation = mediaInfoBuilder.build()

guard let mediaInfo = mediaInformation else {
  print("invalid mediaInformation")
  return
}

if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInfo) {
  request.delegate = self
}
Obiettivo-C
GCKMediaInformationBuilder *mediaInfoBuilder =
  [[GCKMediaInformationBuilder alloc] initWithContentURL:
   [NSURL URLWithString:@"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"]];
mediaInfoBuilder.streamType = GCKMediaStreamTypeNone;
mediaInfoBuilder.contentType = @"video/mp4";
mediaInfoBuilder.metadata = metadata;
self.mediaInformation = [mediaInfoBuilder build];

GCKRequest *request = [self.sessionManager.currentSession.remoteMediaClient loadMedia:self.mediaInformation];
if (request != nil) {
  request.delegate = self;
}

Vedi anche la sezione su utilizzando tracce multimediali.

Formato video 4K

Per determinare il formato video dei tuoi contenuti multimediali, utilizza la proprietà videoInfo di GCKMediaStatus per ottenere l'istanza corrente GCKVideoInfo. Questa istanza contiene il tipo di formato TV HDR e l'altezza e la larghezza in pixel. Le varianti del formato 4K sono indicate nella proprietà hdrType tramite enum valori GCKVideoInfoHDRType.

Aggiungi mini controller

In base al design del cast elenco di controllo, l'app mittente deve fornire un controllo permanente noto come mini un controller che dovrebbe essere visualizzata quando l'utente esce dalla pagina dei contenuti corrente. Il mini controller fornisce accesso immediato e un promemoria visibile per sessione di trasmissione corrente.

Il framework Cast offre una barra di controllo, GCKUIMiniMediaControlsViewController, che può essere aggiunto alle scene in cui vuoi mostrare il mini controller.

Quando l'app del mittente riproduce un live streaming video o audio, l'SDK mostra automaticamente un pulsante di riproduzione/interruzione al posto del pulsante di riproduzione/pausa nel mini controller.

Vedi Personalizzare l'UI mittente di iOS per informazioni L'app del mittente può configurare l'aspetto dei widget Trasmetti.

Esistono due modi per aggiungere il mini controller a un'app mittente:

  • Lascia che il framework Cast gestisca il layout del mini controller eseguendo il wrapping il tuo controller di visualizzazione esistente con il suo controller di visualizzazione.
  • Gestisci autonomamente il layout del widget del mini controller aggiungendolo al tuo un controller di visualizzazione esistente fornendo una visualizzazione secondaria nello storyboard.

Esegui il wrapping con GCKUICastContainerViewController

Il primo modo è utilizzare GCKUICastContainerViewController che aggrega un altro controller di visualizzazione e aggiunge GCKUIMiniMediaControlsViewController in basso. Questo approccio è limitato in quanto non è possibile personalizzare dell'animazione e non può configurare il comportamento del controller della vista container.

Questo primo metodo viene in genere fatto Metodo -[application:didFinishLaunchingWithOptions:] del delegato dell'app:

Rapido
func applicationDidFinishLaunching(_ application: UIApplication) {
  ...

  // Wrap main view in the GCKUICastContainerViewController and display the mini controller.
  let appStoryboard = UIStoryboard(name: "Main", bundle: nil)
  let navigationController = appStoryboard.instantiateViewController(withIdentifier: "MainNavigation")
  let castContainerVC =
          GCKCastContext.sharedInstance().createCastContainerController(for: navigationController)
  castContainerVC.miniMediaControlsItemEnabled = true
  window = UIWindow(frame: UIScreen.main.bounds)
  window!.rootViewController = castContainerVC
  window!.makeKeyAndVisible()

  ...
}
Obiettivo-C
- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  // Wrap main view in the GCKUICastContainerViewController and display the mini controller.
  UIStoryboard *appStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
  UINavigationController *navigationController =
          [appStoryboard instantiateViewControllerWithIdentifier:@"MainNavigation"];
  GCKUICastContainerViewController *castContainerVC =
          [[GCKCastContext sharedInstance] createCastContainerControllerForViewController:navigationController];
  castContainerVC.miniMediaControlsItemEnabled = YES;
  self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
  self.window.rootViewController = castContainerVC;
  [self.window makeKeyAndVisible];
  ...

}
.
Swift
var castControlBarsEnabled: Bool {
  set(enabled) {
    if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController {
      castContainerVC.miniMediaControlsItemEnabled = enabled
    } else {
      print("GCKUICastContainerViewController is not correctly configured")
    }
  }
  get {
    if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController {
      return castContainerVC.miniMediaControlsItemEnabled
    } else {
      print("GCKUICastContainerViewController is not correctly configured")
      return false
    }
  }
}
Obiettivo-C

AppDelegate.h

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, assign) BOOL castControlBarsEnabled;

@end

AppDelegate.m

@implementation AppDelegate

...

- (void)setCastControlBarsEnabled:(BOOL)notificationsEnabled {
  GCKUICastContainerViewController *castContainerVC;
  castContainerVC =
      (GCKUICastContainerViewController *)self.window.rootViewController;
  castContainerVC.miniMediaControlsItemEnabled = notificationsEnabled;
}

- (BOOL)castControlBarsEnabled {
  GCKUICastContainerViewController *castContainerVC;
  castContainerVC =
      (GCKUICastContainerViewController *)self.window.rootViewController;
  return castContainerVC.miniMediaControlsItemEnabled;
}

...

@end

Incorpora in un controller di visualizzazione esistente

Il secondo modo è aggiungere il mini controller direttamente alla visualizzazione esistente. un controller usando il controller createMiniMediaControlsViewController per creare un GCKUIMiniMediaControlsViewController e poi aggiungerla al controller della vista container come vista secondaria.

Configura il controller di visualizzazione nel delegato dell'app:

Rapido
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  ...

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true
  window?.clipsToBounds = true

  let rootContainerVC = (window?.rootViewController as? RootContainerViewController)
  rootContainerVC?.miniMediaControlsViewEnabled = true

  ...

  return true
}
Obiettivo-C
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  self.window.clipsToBounds = YES;

  RootContainerViewController *rootContainerVC;
  rootContainerVC =
      (RootContainerViewController *)self.window.rootViewController;
  rootContainerVC.miniMediaControlsViewEnabled = YES;

  ...

  return YES;
}

Nel controller della vista root, crea un GCKUIMiniMediaControlsViewController e aggiungerla al controller della vista container come vista secondaria:

Rapido
let kCastControlBarsAnimationDuration: TimeInterval = 0.20

@objc(RootContainerViewController)
class RootContainerViewController: UIViewController, GCKUIMiniMediaControlsViewControllerDelegate {
  @IBOutlet weak private var _miniMediaControlsContainerView: UIView!
  @IBOutlet weak private var _miniMediaControlsHeightConstraint: NSLayoutConstraint!
  private var miniMediaControlsViewController: GCKUIMiniMediaControlsViewController!
  var miniMediaControlsViewEnabled = false {
    didSet {
      if self.isViewLoaded {
        self.updateControlBarsVisibility()
      }
    }
  }

  var overriddenNavigationController: UINavigationController?

  override var navigationController: UINavigationController? {

    get {
      return overriddenNavigationController
    }

    set {
      overriddenNavigationController = newValue
    }
  }
  var miniMediaControlsItemEnabled = false

  override func viewDidLoad() {
    super.viewDidLoad()
    let castContext = GCKCastContext.sharedInstance()
    self.miniMediaControlsViewController = castContext.createMiniMediaControlsViewController()
    self.miniMediaControlsViewController.delegate = self
    self.updateControlBarsVisibility()
    self.installViewController(self.miniMediaControlsViewController,
                               inContainerView: self._miniMediaControlsContainerView)
  }

  func updateControlBarsVisibility() {
    if self.miniMediaControlsViewEnabled && self.miniMediaControlsViewController.active {
      self._miniMediaControlsHeightConstraint.constant = self.miniMediaControlsViewController.minHeight
      self.view.bringSubview(toFront: self._miniMediaControlsContainerView)
    } else {
      self._miniMediaControlsHeightConstraint.constant = 0
    }
    UIView.animate(withDuration: kCastControlBarsAnimationDuration, animations: {() -> Void in
      self.view.layoutIfNeeded()
    })
    self.view.setNeedsLayout()
  }

  func installViewController(_ viewController: UIViewController?, inContainerView containerView: UIView) {
    if let viewController = viewController {
      self.addChildViewController(viewController)
      viewController.view.frame = containerView.bounds
      containerView.addSubview(viewController.view)
      viewController.didMove(toParentViewController: self)
    }
  }

  func uninstallViewController(_ viewController: UIViewController) {
    viewController.willMove(toParentViewController: nil)
    viewController.view.removeFromSuperview()
    viewController.removeFromParentViewController()
  }

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "NavigationVCEmbedSegue" {
      self.navigationController = (segue.destination as? UINavigationController)
    }
  }

...
Obiettivo-C

RootContainerViewController.h

static const NSTimeInterval kCastControlBarsAnimationDuration = 0.20;

@interface RootContainerViewController () <GCKUIMiniMediaControlsViewControllerDelegate> {
  __weak IBOutlet UIView *_miniMediaControlsContainerView;
  __weak IBOutlet NSLayoutConstraint *_miniMediaControlsHeightConstraint;
  GCKUIMiniMediaControlsViewController *_miniMediaControlsViewController;
}

@property(nonatomic, weak, readwrite) UINavigationController *navigationController;

@property(nonatomic, assign, readwrite) BOOL miniMediaControlsViewEnabled;
@property(nonatomic, assign, readwrite) BOOL miniMediaControlsItemEnabled;

@end

RootContainerViewController.m

@implementation RootContainerViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  GCKCastContext *castContext = [GCKCastContext sharedInstance];
  _miniMediaControlsViewController =
      [castContext createMiniMediaControlsViewController];
  _miniMediaControlsViewController.delegate = self;

  [self updateControlBarsVisibility];
  [self installViewController:_miniMediaControlsViewController
              inContainerView:_miniMediaControlsContainerView];
}

- (void)setMiniMediaControlsViewEnabled:(BOOL)miniMediaControlsViewEnabled {
  _miniMediaControlsViewEnabled = miniMediaControlsViewEnabled;
  if (self.isViewLoaded) {
    [self updateControlBarsVisibility];
  }
}

- (void)updateControlBarsVisibility {
  if (self.miniMediaControlsViewEnabled &&
      _miniMediaControlsViewController.active) {
    _miniMediaControlsHeightConstraint.constant =
        _miniMediaControlsViewController.minHeight;
    [self.view bringSubviewToFront:_miniMediaControlsContainerView];
  } else {
    _miniMediaControlsHeightConstraint.constant = 0;
  }
  [UIView animateWithDuration:kCastControlBarsAnimationDuration
                   animations:^{
                     [self.view layoutIfNeeded];
                   }];
  [self.view setNeedsLayout];
}

- (void)installViewController:(UIViewController *)viewController
              inContainerView:(UIView *)containerView {
  if (viewController) {
    [self addChildViewController:viewController];
    viewController.view.frame = containerView.bounds;
    [containerView addSubview:viewController.view];
    [viewController didMoveToParentViewController:self];
  }
}

- (void)uninstallViewController:(UIViewController *)viewController {
  [viewController willMoveToParentViewController:nil];
  [viewController.view removeFromSuperview];
  [viewController removeFromParentViewController];
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  if ([segue.identifier isEqualToString:@"NavigationVCEmbedSegue"]) {
    self.navigationController =
        (UINavigationController *)segue.destinationViewController;
  }
}

...

@end

La GCKUIMiniMediaControlsViewControllerDelegate indica al controller della vista host quando il mini controller dovrebbe essere visibile:

Rapido
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Obiettivo-C
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

Aggiungi controller espanso

L'elenco di controllo per la progettazione di Google Cast richiede che un'app del mittente fornisca un un controller per i contenuti multimediali trasmessi. Il controller espanso è una versione a schermo intero il mini controller.

Il controller espanso è in modalità a schermo intero e offre il pieno controllo la riproduzione remota di contenuti multimediali. Questa visualizzazione dovrebbe consentire a un'app di trasmissione di gestire ogni l'aspetto gestibile di una sessione di trasmissione, con l'eccezione del volume del ricevitore web del controllo e del ciclo di vita delle sessioni (connessione/interruzione della trasmissione). Offre inoltre tutte le le informazioni sullo stato della sessione multimediale (artwork, titolo, sottotitolo e così via ).

La funzionalità di questa vista è implementata dalla GCKUIExpandedMediaControlsViewController .

Per prima cosa devi abilitare il controller espanso predefinito il contesto della trasmissione. Modifica il delegato dell'app per abilitare il controller espanso predefinito:

Rapido
func applicationDidFinishLaunching(_ application: UIApplication) {
  ..

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

  ...
}
Obiettivo-C
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

Aggiungi il seguente codice al controller di visualizzazione per caricare il controller espanso Quando l'utente inizia a trasmettere un video:

Rapido
func playSelectedItemRemotely() {
  GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()

  ...

  // Load your media
  sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation)
}
Obiettivo-C
- (void)playSelectedItemRemotely {
  [[GCKCastContext sharedInstance] presentDefaultExpandedMediaControls];

  ...

  // Load your media
  [self.sessionManager.currentSession.remoteMediaClient loadMedia:mediaInformation];
}

Il controller espanso verrà avviato automaticamente anche quando l'utente tocca il mini controller.

Quando l'app del mittente riproduce un live streaming video o audio, l'SDK mostra automaticamente un pulsante di riproduzione/interruzione al posto del pulsante di riproduzione/pausa nel controller espanso.

Consulta la pagina Applicare stili personalizzati al proprio dispositivo iOS App per informazioni su come l'app del mittente può configurare l'aspetto dei widget di trasmissione.

Controllo del volume

Il framework Cast gestisce automaticamente il volume dell'app del mittente. La si sincronizza automaticamente con il volume WebRicevir per widget UI forniti. Per sincronizzare un cursore fornito dall'app, usa GCKUIDeviceVolumeController

Controllo del volume tramite tasto fisico

I pulsanti fisici del volume sul dispositivo del mittente possono essere utilizzati per modificare volume della sessione di trasmissione sul ricevitore web utilizzando physicalVolumeButtonsWillControlDeviceVolume in GCKCastOptions che è impostato GCKCastContext.

Rapido
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
options.physicalVolumeButtonsWillControlDeviceVolume = true
GCKCastContext.setSharedInstanceWith(options)
Obiettivo-C
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc]
                                          initWithApplicationID:kReceiverAppID];
GCKCastOptions *options = [[GCKCastOptions alloc]
                                          initWithDiscoveryCriteria :criteria];
options.physicalVolumeButtonsWillControlDeviceVolume = YES;
[GCKCastContext setSharedInstanceWithOptions:options];

Gestire gli errori

È molto importante che le app dei mittenti gestiscano tutti i callback di errore e stabiliscano la risposta migliore per ogni fase del ciclo di vita della trasmissione. L'app può visualizzare finestra di dialogo di errore per l'utente o l'utente può decidere di terminare la sessione di trasmissione.

Logging

GCKLogger è un singleton utilizzato per il logging dal framework. Utilizza la GCKLoggerDelegate per personalizzare la gestione dei messaggi di log.

Utilizzando l'GCKLogger, l'SDK produce l'output di log sotto forma di debug messaggi, errori e avvisi. Questi messaggi di log favoriscono il debug e sono utili per la risoluzione e l'identificazione dei problemi. Per impostazione predefinita, l'output di log è soppressa, ma assegnando un valore GCKLoggerDelegate l'app del mittente può ricevere questi messaggi dall'SDK e registrarli nella console di sistema.

Rapido
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate {
  let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID
  let kDebugLoggingEnabled = true

  var window: UIWindow?

  func applicationDidFinishLaunching(_ application: UIApplication) {
    ...

    // Enable logger.
    GCKLogger.sharedInstance().delegate = self

    ...
  }

  // MARK: - GCKLoggerDelegate

  func logMessage(_ message: String,
                  at level: GCKLoggerLevel,
                  fromFunction function: String,
                  location: String) {
    if (kDebugLoggingEnabled) {
      print(function + " - " + message)
    }
  }
}
Obiettivo-C

AppDelegate.h

@interface AppDelegate () <GCKLoggerDelegate>
@end

AppDelegate.m

@implementation AppDelegate

static NSString *const kReceiverAppID = @"AABBCCDD";
static const BOOL kDebugLoggingEnabled = YES;

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  // Enable logger.
  [GCKLogger sharedInstance].delegate = self;

  ...

  return YES;
}

...

#pragma mark - GCKLoggerDelegate

- (void)logMessage:(NSString *)message
           atLevel:(GCKLoggerLevel)level
      fromFunction:(NSString *)function
          location:(NSString *)location {
  if (kDebugLoggingEnabled) {
    NSLog(@"%@ - %@, %@", function, message, location);
  }
}

@end

Per attivare anche i messaggi di debug e dettagliati, aggiungi questa riga al codice dopo impostare il delegato (come mostrato in precedenza):

Rapido
let filter = GCKLoggerFilter.init()
filter.minimumLevel = GCKLoggerLevel.verbose
GCKLogger.sharedInstance().filter = filter
Obiettivo-C
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setMinimumLevel:GCKLoggerLevelVerbose];
[GCKLogger sharedInstance].filter = filter;

Puoi anche filtrare i messaggi di log generati GCKLogger Imposta il livello minimo di logging per classe, ad esempio:

Rapido
let filter = GCKLoggerFilter.init()
filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton",
                                                            "GCKUIImageCache",
                                                            "NSMutableDictionary"])
GCKLogger.sharedInstance().filter = filter
Obiettivo-C
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setLoggingLevel:GCKLoggerLevelVerbose
             forClasses:@[@"GCKUICastButton",
                          @"GCKUIImageCache",
                          @"NSMutableDictionary"
                          ]];
[GCKLogger sharedInstance].filter = filter;

I nomi delle classi possono essere nomi letterali o pattern glob, ad esempio GCKUI\* e GCK\*Session.