Questa guida per gli sviluppatori descrive come aggiungere il supporto di Google Cast all'app del mittente iOS utilizzando l'SDK del mittente di iOS.
Il dispositivo mobile o laptop è il mittente che controlla la riproduzione, mentre il dispositivo Google Cast è il destinatario che mostra i contenuti sulla TV.
Il framework del mittente si riferisce al programma binario della libreria di classi di trasmissione e alle risorse associate presenti in fase di esecuzione sul mittente. L'app mittente o l'app Trasmetti si riferisce a un'app in esecuzione anche sul mittente. L'app Ricevitore web si riferisce all'applicazione HTML in esecuzione sul ricevitore web.
Il framework del mittente utilizza un design asincrono di callback per informare l'app del mittente degli eventi e per passare da uno stato all'altro del ciclo di vita dell'app di trasmissione.
Flusso dell'app
I seguenti passaggi descrivono il flusso di esecuzione tipico di alto livello per un'app per iOS del mittente:
- Il framework di Cast viene avviato
GCKDiscoveryManager
in base alle proprietà fornite inGCKCastOptions
per iniziare la scansione dei dispositivi. - Quando l'utente fa clic sul pulsante Trasmetti, il framework presenta la finestra di dialogo Trasmetti con l'elenco dei dispositivi di trasmissione rilevati.
- Quando l'utente seleziona un dispositivo di trasmissione, il framework tenta di avviare l'app Ricevitore web sul dispositivo di trasmissione.
- Il framework richiama i callback nell'app del mittente per confermare che l'app Ricevitore web è stata avviata.
- Il framework crea un canale di comunicazione tra le app del mittente e del ricevitore web.
- Il framework utilizza il canale di comunicazione per caricare e controllare la riproduzione dei contenuti multimediali sul ricevitore web.
- Il framework sincronizza lo stato di riproduzione dei contenuti multimediali tra il mittente e il ricevitore web: quando l'utente esegue azioni dell'interfaccia utente del mittente, il framework trasmette le richieste di controllo dei contenuti multimediali al ricevitore web e, quando quest'ultimo invia gli aggiornamenti dello 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 disconnette 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 nel framework Google Cast per iOS, consulta il riferimento API Google Cast per iOS. Le seguenti sezioni illustrano la procedura per integrare Cast nell'app per iOS.
Metodi di chiamata dal thread principale
Inizializza il contesto di trasmissione
Il framework Cast ha un oggetto singleton globale, GCKCastContext
, che coordina tutte le attività del framework. Questo oggetto deve essere inizializzato
in anticipo nel ciclo di vita dell'applicazione, in genere nel
metodo -[application:didFinishLaunchingWithOptions:]
della delega dell'app, in modo che
la ripresa automatica della sessione al riavvio dell'app del mittente possa attivarsi correttamente.
Quando si inizializza GCKCastContext
, è necessario specificare un oggetto GCKCastOptions
.
Questa classe contiene opzioni che influiscono sul comportamento del framework. Il più
importante di questi è l'ID applicazione web Ricevitore, che viene utilizzato per filtrare
i risultati del rilevamento e per avviare l'app Web Receiver all'avvio della
sessione di trasmissione.
Il metodo -[application:didFinishLaunchingWithOptions:]
è una buona soluzione anche per configurare un delegato di logging in modo che riceva i messaggi di logging dal framework.
Questi possono essere utili per il debug e la risoluzione dei problemi.
@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) } } }
Delegato-App
@interface AppDelegate () <GCKLoggerDelegate> @end
DelegaApp
@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 trasmissione
L'SDK Cast per iOS fornisce i seguenti widget conformi all'elenco di controllo di Cast Design:
Overlay di introduzione: La classe
GCKCastContext
ha un metodo,presentCastInstructionsViewControllerOnceWithCastButton
, che può essere utilizzato per mettere in evidenza il pulsante Trasmetti la prima volta che è disponibile un ricevitore web. L'app mittente può personalizzare il testo, la posizione del testo del titolo e il pulsante Ignora.Pulsante Trasmetti: a partire dall'SDK del mittente Google Cast 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 pulsante Trasmetti dopo aver avviato inizialmente l'app, viene visualizzata una finestra di dialogo delle autorizzazioni che consente all'utente di concedere alla rete locale l'accesso ai dispositivi della rete. Successivamente, quando l'utente tocca il pulsante Trasmetti, viene visualizzata una finestra di dialogo che elenca i dispositivi rilevati. Quando l'utente tocca il pulsante Trasmetti mentre il dispositivo è connesso, visualizza i metadati multimediali correnti (come titolo, nome dello studio di registrazione e un'immagine in miniatura) o consente all'utente di disconnettersi dal dispositivo di trasmissione. Quando l'utente tocca il pulsante Trasmetti mentre non ci sono dispositivi disponibili, viene visualizzata una schermata che fornisce informazioni sul motivo per cui i dispositivi non vengono trovati e su come risolvere i problemi.
Mini controller: quando l'utente trasmette contenuti ed è uscito dalla pagina dei contenuti corrente o da un altro controller espanso a un'altra schermata dell'app del mittente, il mini controller viene visualizzato nella parte inferiore dello schermo per consentire all'utente di visualizzare i metadati multimediali trasmessi attualmente e per controllare la riproduzione.
Controller espanso: Quando l'utente trasmette contenuti, se fa clic sulla notifica multimediale o sul mini controller, viene avviato il controller espanso, che mostra i metadati multimediali attualmente in riproduzione e dispone di diversi pulsanti per controllare la riproduzione dei contenuti multimediali.
Aggiungere un pulsante Trasmetti
Il framework fornisce un componente del pulsante Trasmetti come sottoclasse UIButton
. Può essere aggiunto alla barra del titolo dell'app inserendolo in un UIBarButtonItem
. Una tipica sottoclasse UIViewController
può installare un pulsante Trasmetti come segue:
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24)) castButton.tintColor = UIColor.gray navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)]; castButton.tintColor = [UIColor grayColor]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];
Per impostazione predefinita, toccando il pulsante si apre la finestra di dialogo Trasmetti fornita dal framework.
GCKUICastButton
puoi anche aggiungerlo direttamente allo storyboard.
Configura il rilevamento del dispositivo
Nel framework, il rilevamento dei dispositivi avviene automaticamente. Non è necessario avviare o interrompere esplicitamente il processo di rilevamento, a meno che non implementi un'interfaccia utente personalizzata.
Il rilevamento nel framework è gestito dalla classe
GCKDiscoveryManager
,
che è una proprietà di
GCKCastContext
. Il
framework fornisce un componente predefinito della finestra di dialogo Trasmetti per la selezione e
il controllo del dispositivo. L'elenco dei dispositivi è ordinato in ordine alfabetico in base al nome.
Come funziona la gestione delle sessioni
L'SDK Cast introduce il concetto di sessione Google Cast, il cui scopo consiste nel combinare i passaggi per connettersi a un dispositivo, lanciare (o partecipare) a un'app Ricevitore web, connettersi a quell'app e inizializzare un canale di controllo dei contenuti multimediali. Consulta la 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à di GCKCastContext
.
Le singole sessioni sono rappresentate da sottoclassi della classe
GCKSession
: ad esempio,
GCKCastSession
rappresenta le sessioni con dispositivi di trasmissione. Puoi accedere alla sessione di trasmissione attualmente attiva (se disponibile) come proprietà currentCastSession
di GCKSessionManager
.
L'interfaccia
GCKSessionManagerListener
può essere utilizzata per monitorare gli eventi della sessione, come la creazione,
la sospensione, la ripresa e la terminazione della sessione. Il framework sospende automaticamente le sessioni quando l'app del mittente entra in background e tenta di ripristinarle quando l'app torna in primo piano (o viene riavviata dopo la terminazione anomala/improvvisa mentre una sessione era attiva).
Se la finestra di dialogo Trasmetti è in uso, le sessioni vengono create e eliminate automaticamente in risposta ai gesti dell'utente. In caso contrario, l'app può avviare e terminare
le sessioni in modo esplicito tramite metodi su
GCKSessionManager
.
Se l'app deve eseguire un'elaborazione speciale in risposta agli eventi del ciclo di vita della sessione, può registrare una o più istanze GCKSessionManagerListener
con GCKSessionManager
. GCKSessionManagerListener
è un protocollo che definisce i callback per eventi come l'inizio, la fine e così via della sessione.
Trasferimento dello streaming
La conservazione dello stato della sessione è la base del trasferimento dello streaming, in cui gli utenti possono spostare gli stream audio e video esistenti sui vari dispositivi utilizzando i comandi vocali, l'app Google Home o smart display. La riproduzione dei contenuti multimediali viene interrotta su un dispositivo (l'origine) e continua su un altro (la destinazione). Qualsiasi dispositivo di trasmissione con il firmware più recente può fungere da origine o destinazione in un trasferimento di streaming.
Per ottenere il nuovo dispositivo di destinazione durante il trasferimento dello streaming, utilizza la proprietà GCKCastSession#device
durante il callback [sessionManager:didResumeCastSession:]
.
Per ulteriori informazioni, consulta Trasferimento dello streaming sul ricevitore web.
Riconnessione automatica
Il framework di trasmissione aggiunge logica di riconnessione per gestire automaticamente la riconnessione in molti casi d'angolo sottili, ad esempio:
- Esegui il ripristino in seguito a una perdita temporanea del Wi-Fi
- Recupera dal sonno del dispositivo
- Recupera dall'app in background
- Ripristino in caso di arresto anomalo dell'app
Come funziona il controllo dei contenuti multimediali
Se viene stabilita una sessione di trasmissione con un'app web ricevitore che supporta lo spazio dei nomi multimediale, l'istanza di GCKRemoteMediaClient
verrà creata automaticamente dal framework; è possibile accedervi come proprietà remoteMediaClient
dell'istanza di GCKCastSession
.
Tutti i metodi su GCKRemoteMediaClient
che inviano richieste al ricevitore web restituiranno un oggetto GCKRequest
che può essere utilizzato per monitorare la richiesta. Puoi assegnare un oggetto GCKRequestDelegate
a questo oggetto per ricevere notifiche sull'eventuale risultato dell'operazione.
Prevediamo che l'istanza di GCKRemoteMediaClient
potrebbe essere condivisa da più parti dell'app e infatti alcuni componenti interni del framework, come la finestra di dialogo di trasmissione e i controlli mini-media, condividono l'istanza. A tal fine, GCKRemoteMediaClient
supporta la registrazione di più GCKRemoteMediaClientListener
.
Imposta metadati multimediali
La classe GCKMediaMetadata
rappresenta le informazioni su un elemento multimediale che vuoi trasmettere. L'esempio seguente crea una nuova istanza GCKMediaMetadata
di un film e imposta il titolo, il sottotitolo, il nome dello studio di registrazione e due immagini.
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))
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 e memorizzazione delle immagini nell'utilizzo delle immagini con metadati multimediali.
Carica contenuti multimediali
Per caricare un elemento multimediale, crea un'istanza GCKMediaInformation
utilizzando i metadati del contenuto multimediale. Quindi recupera l'attuale GCKCastSession
e utilizzala GCKRemoteMediaClient
per caricare i contenuti multimediali nell'app del ricevitore. Puoi quindi usare GCKRemoteMediaClient
per controllare un'app del lettore multimediale in esecuzione sul ricevitore, ad esempio per riprodurre, mettere in pausa e interrompere.
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 }
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; }
Consulta anche la sezione sull'utilizzo delle tracce multimediali.
Formato video 4K
Per determinare quale formato video sono i tuoi contenuti multimediali, utilizza la proprietà videoInfo
di GCKMediaStatus
per ottenere l'istanza attuale di 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
con valori enum GCKVideoInfoHDRType
.
Aggiungi mini controller
In base all'elenco di controllo di Cast Design, un'app mittente deve fornire un controllo permanente noto come mini controller che dovrebbe essere visualizzato quando l'utente esce dalla pagina dei contenuti corrente. Il controller mini fornisce accesso immediato e un promemoria visibile per la sessione di trasmissione corrente.
Il framework di trasmissione include una barra di controllo, GCKUIMiniMediaControlsViewController
, che può essere aggiunta alle scene in cui vuoi mostrare il mini controller.
Quando l'app del mittente sta riproducendo un video stream o un live streaming audio, l'SDK mostra automaticamente un pulsante di riproduzione/interruzione al posto del pulsante di riproduzione/pausa nel mini controller.
Vedi Personalizzare l'interfaccia utente del mittente iOS per scoprire come l'app del mittente può configurare l'aspetto dei widget Trasmetti.
Esistono due modi per aggiungere il mini controller a un'app mittente:
- Consenti al framework di trasmissione di gestire il layout del mini controller aggiungendo il controller della visualizzazione esistente al proprio.
- Gestisci il layout del widget mini controller aggiungendolo al controller vista esistente fornendo una visualizzazione secondaria nello storyboard.
Wrap utilizzando GCKUICastContainerViewController
Il primo è utilizzare GCKUICastContainerViewController
, che esegue il wrapping di un altro controller di visualizzazione e aggiunge una GCKUIMiniMediaControlsViewController
in basso. Questo approccio è limitato nel fatto che non puoi personalizzare l'animazione e non configurare il comportamento del controller visualizzazione contenitore.
In genere, il primo modo viene eseguito nel metodo -[application:didFinishLaunchingWithOptions:]
del delegato dell'app:
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() ... }
- (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]; ... }
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 } } }
Delegato-App
@interface AppDelegate : UIResponder <UIApplicationDelegate> @property (nonatomic, strong) UIWindow *window; @property (nonatomic, assign) BOOL castControlBarsEnabled; @end
DelegaApp
@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 nel controller visualizzazione esistente
Il secondo modo è aggiungere il mini controller direttamente al controller di visualizzazione esistente utilizzando
createMiniMediaControlsViewController
per creare un'istanza
GCKUIMiniMediaControlsViewController
e poi aggiungerla al controller visualizzazione contenitore come vista secondaria.
Configura il controller della vista nel delegato dell'app:
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 }
- (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 visualizzazione radice, crea un'istanza GCKUIMiniMediaControlsViewController
e aggiungila al controller della vista container come visualizzazione secondaria:
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) } } ...
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
GCKUIMiniMediaControlsViewControllerDelegate
indica al controller visualizzazione host quando il controller mini deve essere visibile:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
Aggiungi controller espanso
L'elenco di controllo di Google Cast Design richiede che un'app mittente fornisca un controller espanso per i contenuti multimediali trasmessi. Il controller espanso è una versione a schermo intero del mini controller.
Il controller espanso è una visualizzazione a schermo intero che offre il controllo completo della riproduzione di contenuti multimediali da remoto. Questa visualizzazione dovrebbe consentire a un'app di trasmissione di gestire ogni aspetto gestibile di una sessione di trasmissione, ad eccezione del controllo del volume del ricevitore web e del ciclo di vita della sessione (connessione/interruzione della trasmissione). Fornisce inoltre tutte le informazioni sullo stato della sessione multimediale (opera d'arte, titolo, sottotitolo e così via).
La funzionalità di questa visualizzazione è implementata dalla classe GCKUIExpandedMediaControlsViewController
.
La prima cosa da fare è attivare il controller espanso predefinito nel contesto della trasmissione. Modifica il delegato dell'app per abilitare il controller espanso predefinito:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (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:
func playSelectedItemRemotely() { GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls() ... // Load your media sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation) }
- (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 sta riproducendo un video stream o un live streaming audio, l'SDK visualizza automaticamente un pulsante di riproduzione/interruzione al posto del pulsante di riproduzione/pausa nel controller espanso.
Consulta la pagina Applicare stili personalizzati all'app per iOS per stabilire in che modo l'app del mittente può configurare l'aspetto dei widget Trasmetti.
Controllo del volume
Il framework di trasmissione gestisce automaticamente il volume dell'app del mittente. Il framework viene sincronizzato automaticamente con il volume del ricevitore web per i widget dell'interfaccia utente forniti. Per sincronizzare un dispositivo di scorrimento fornito dall'app, utilizza
GCKUIDeviceVolumeController
.
Controllo del volume del pulsante fisico
I pulsanti fisici del volume sul dispositivo del mittente possono essere utilizzati per modificare il volume della sessione di trasmissione sul ricevitore web utilizzando il flag physicalVolumeButtonsWillControlDeviceVolume
su GCKCastOptions
, che è impostato sul GCKCastContext
.
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) let options = GCKCastOptions(discoveryCriteria: criteria) options.physicalVolumeButtonsWillControlDeviceVolume = true GCKCastContext.setSharedInstanceWith(options)
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 decidano la risposta migliore per ogni fase del ciclo di vita di trasmissione. L'app può mostrare le finestre di dialogo di errore all'utente o decidere di terminare la sessione di trasmissione.
Logging
GCKLogger
è un singleton utilizzato per il logging dal framework. Utilizza
GCKLoggerDelegate
per personalizzare la gestione dei messaggi di log.
Utilizzando GCKLogger
, l'SDK produce l'output di logging sotto forma di messaggi di debug, errori e avvisi. Questi messaggi di log sono utili per il debug e sono utili per la risoluzione dei problemi e l'identificazione. Per impostazione predefinita, l'output del log viene soppresso, ma assegnando un valore GCKLoggerDelegate
, l'app del mittente può ricevere questi messaggi dall'SDK e registrarli nella console di sistema.
@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) } } }
Delegato-App
@interface AppDelegate () <GCKLoggerDelegate> @end
DelegaApp
@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 aver impostato il delegato (mostrato in precedenza):
let filter = GCKLoggerFilter.init() filter.minimumLevel = GCKLoggerLevel.verbose GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setMinimumLevel:GCKLoggerLevelVerbose]; [GCKLogger sharedInstance].filter = filter;
Puoi anche filtrare i messaggi di log prodotti da
GCKLogger
.
Imposta il livello minimo di logging per corso, ad esempio:
let filter = GCKLoggerFilter.init() filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton", "GCKUIImageCache", "NSMutableDictionary"]) GCKLogger.sharedInstance().filter = filter
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
.