Cast in iOS-App einbinden

In diesem Entwicklerleitfaden wird beschrieben, wie Sie Ihrer iOS-Absender-App mit dem iOS Sender SDK Google Cast-Unterstützung hinzufügen.

Das Mobilgerät oder der Laptop ist der Absender, der die Wiedergabe steuert, und das Google Cast-Gerät ist der Empfänger, der die Inhalte auf dem Fernseher anzeigt.

Das Absender-Framework bezieht sich auf das Binärprogramm der Cast-Klassenbibliothek und die zugehörigen Ressourcen, die auf dem Absender zur Laufzeit vorhanden sind. Die Absender-App oder Cast-App bezieht sich auf eine App, die ebenfalls auf dem Absender ausgeführt wird. Die Web Receiver App bezieht sich auf die HTML-Anwendung, die auf dem Web Receiver ausgeführt wird.

Das Sender-Framework verwendet ein asynchrones Callback-Design, um die Sender-App über Ereignisse zu informieren und zwischen verschiedenen Zuständen des Lebenszyklus der Cast-App zu wechseln.

Anwendungsfluss

Die folgenden Schritte beschreiben den typischen allgemeinen Ausführungsablauf für eine Absender-iOS-App:

  • Das Cast-Framework startet GCKDiscoveryManager anhand der in GCKCastOptions bereitgestellten Attribute, um mit dem Scannen nach Geräten zu beginnen.
  • Wenn der Nutzer auf das Cast-Symbol klickt, öffnet das Framework das Cast-Dialogfeld mit der Liste der erkannten Übertragungsgeräte.
  • Wenn der Nutzer ein Übertragungsgerät auswählt, versucht das Framework, die Web Receiver App auf dem Übertragungsgerät zu starten.
  • Das Framework ruft Callbacks in der Absenderanwendung auf, um zu bestätigen, dass die Webempfängeranwendung gestartet wurde.
  • Das Framework erstellt einen Kommunikationskanal zwischen den Sender- und Web-Receiver-Anwendungen.
  • Das Framework verwendet den Kommunikationskanal, um die Medienwiedergabe auf dem Webempfänger zu laden und zu steuern.
  • Das Framework synchronisiert den Status der Medienwiedergabe zwischen Sender und Webempfänger: Wenn der Nutzer UI-Aktionen des Absenders ausführt, leitet das Framework diese Mediensteuerungsanfragen an den Webempfänger weiter. Wenn der Webempfänger Aktualisierungen des Medienstatus sendet, aktualisiert das Framework den Status der Sender-UI.
  • Wenn der Nutzer auf das Cast-Symbol klickt, um die Verbindung zum Übertragungsgerät zu trennen, trennt das Framework die Absender-App vom Webempfänger.

Aktivieren Sie die Protokollierung, um Fehler bei Ihrem Absender zu beheben.

Eine umfassende Liste aller Klassen, Methoden und Ereignisse im Google Cast iOS-Framework finden Sie in der Referenz zur Google Cast iOS API. In den folgenden Abschnitten wird beschrieben, wie du Cast in deine iOS-App integrieren kannst.

Aufrufmethoden aus dem Hauptthread

Streaming-Kontext initialisieren

Das Cast-Framework hat ein globales Singleton-Objekt, GCKCastContext, das alle Aktivitäten des Frameworks koordiniert. Dieses Objekt muss früh im Lebenszyklus der Anwendung initialisiert werden, in der Regel in der Methode -[application:didFinishLaunchingWithOptions:] des Anwendungsdelegats, damit die automatische Sitzungswiederaufnahme beim Neustart der Absenderanwendung ordnungsgemäß ausgelöst werden kann.

Beim Initialisieren von GCKCastContext muss ein GCKCastOptions-Objekt angegeben werden. Diese Klasse enthält Optionen, die das Verhalten des Frameworks beeinflussen. Die wichtigste davon ist die Web Receiver-Anwendungs-ID. Sie wird zum Filtern von Erkennungsergebnissen und zum Starten der Web Receiver-App beim Start einer Streaming-Sitzung verwendet.

Die Methode -[application:didFinishLaunchingWithOptions:] eignet sich auch gut, um einen Logging-Delegaten einzurichten, der die Logging-Nachrichten vom Framework empfängt. Diese können bei der Fehlerbehebung hilfreich sein.

Swift
@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)
    }
  }
}
Objective 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

Die Cast UX-Widgets

Das Cast iOS SDK bietet folgende Widgets, die der Checkliste für das Cast-Design entsprechen:

  • Einleitendes Overlay: Die Klasse GCKCastContext hat die Methode presentCastInstructionsViewControllerOnceWithCastButton, mit der das Cast-Symbol hervorgehoben werden kann, sobald ein Webempfänger verfügbar ist. Die Absender-App kann den Text, die Position des Titeltexts und die Schaltfläche "Schließen" anpassen.

  • Cast-Schaltfläche: Ab Version 4.6.0 des Cast iOS Sender SDK ist das Cast-Symbol immer sichtbar, wenn das Gerät des Absenders mit einem WLAN verbunden ist. Wenn der Nutzer nach dem ersten Start der App zum ersten Mal auf das Cast-Symbol tippt, wird ein Berechtigungsdialogfeld angezeigt, über das der Nutzer der App Zugriff auf das lokale Netzwerk von Geräten im Netzwerk gewähren kann. Wenn der Nutzer anschließend auf das Cast-Symbol tippt, wird ein Streaming-Dialogfeld mit den erkannten Geräten angezeigt. Wenn der Nutzer auf das Cast-Symbol tippt, während das Gerät verbunden ist, werden die aktuellen Medienmetadaten (z. B. Titel, Name des Aufnahmestudios und eine Miniaturansicht) angezeigt oder der Nutzer kann die Verbindung zum Übertragungsgerät trennen. Wenn der Nutzer auf das Cast-Symbol tippt, während keine Geräte verfügbar sind, wird ein Bildschirm mit Informationen dazu angezeigt, warum die Geräte nicht gefunden werden und wie der Fehler behoben werden kann.

  • Mini Controller: Wenn der Nutzer Inhalte streamt und von der aktuellen Inhaltsseite oder dem erweiterten Controller zu einem anderen Bildschirm in der Sender-App gewechselt ist, wird der Mini-Controller unten auf dem Bildschirm angezeigt, damit der Nutzer die aktuell gestreamten Medienmetadaten sehen und die Wiedergabe steuern kann.

  • Erweiterter Controller: Wenn der Nutzer Inhalte streamt, wird der maximierte Controller gestartet, wenn er auf die Medienbenachrichtigung oder den Mini-Controller klickt. Er zeigt die Metadaten der aktuell wiedergegebenen Medien an und bietet mehrere Schaltflächen zum Steuern der Medienwiedergabe.

Cast-Symbol hinzufügen

Das Framework stellt eine Cast-Schaltflächenkomponente als UIButton-Unterklasse bereit. Es kann der Titelleiste der App hinzugefügt werden, indem es in ein UIBarButtonItem eingeschlossen wird. In einer typischen UIViewController-Unterklasse kann ein Cast-Symbol so installiert werden:

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

Wenn Sie auf die Schaltfläche tippen, wird standardmäßig das vom Framework bereitgestellte Cast-Dialogfeld geöffnet.

GCKUICastButton kann auch direkt zum Storyboard hinzugefügt werden.

Geräteerkennung konfigurieren

Im Framework erfolgt die Geräteerkennung automatisch. Der Erkennungsprozess muss nicht explizit gestartet oder beendet werden, es sei denn, Sie implementieren eine benutzerdefinierte UI.

Die Erkennung im Framework wird von der Klasse GCKDiscoveryManager verwaltet, die eine Property von GCKCastContext ist. Das Framework stellt eine standardmäßige Cast-Dialogkomponente zur Geräteauswahl und -steuerung zur Verfügung. Die Geräteliste ist lexikografisch nach gerätefreundlichen Namen sortiert.

So funktioniert die Sitzungsverwaltung

Das Cast SDK führt das Konzept einer Cast-Sitzung ein. Dabei werden die Schritte zum Verbinden mit einem Gerät, Starten (oder Beitritt) einer Web-Receiver-App, Verbinden mit dieser App und Initialisieren eines Mediensteuerungskanals kombiniert. Weitere Informationen zu Übertragungssitzungen und dem Lebenszyklus des Webempfängers finden Sie in der Anleitung zum Lebenszyklus von Anwendungen für den Web Receiver.

Sitzungen werden von der Klasse GCKSessionManager verwaltet, die eine Property von GCKCastContext ist. Einzelne Sitzungen werden durch abgeleitete Klassen der Klasse GCKSession repräsentiert. So repräsentiert GCKCastSession beispielsweise Sitzungen mit Übertragungsgeräten. Sie können auf die aktuell aktive Streaming-Sitzung (falls vorhanden) als currentCastSession-Property von GCKSessionManager zugreifen.

Die Schnittstelle GCKSessionManagerListener kann zum Überwachen von Sitzungsereignissen wie der Erstellung, Sperrung, Wiederaufnahme und Beendigung von Sitzungen verwendet werden. Das Framework sperrt Sitzungen automatisch, wenn die Absenderanwendung in den Hintergrund wechselt, und versucht, sie fortzusetzen, wenn die Anwendung wieder in den Vordergrund wechselt (oder nach einer abnormalen/abrupten Beendigung einer Anwendung während einer Sitzung neu gestartet wird).

Bei Verwendung des Dialogfelds „Streamen“ werden Sitzungen automatisch als Reaktion auf Nutzergesten erstellt und gelöscht. Andernfalls kann die Anwendung Sitzungen explizit über Methoden in GCKSessionManager starten und beenden.

Wenn die Anwendung eine spezielle Verarbeitung als Reaktion auf Sitzungslebenszyklusereignisse ausführen muss, kann sie eine oder mehrere GCKSessionManagerListener-Instanzen bei GCKSessionManager registrieren. GCKSessionManagerListener ist ein Protokoll, das Callbacks für Ereignisse wie Sitzungsbeginn, Sitzungsende usw. definiert.

Stream-Übertragung

Das Beibehalten des Sitzungsstatus ist die Grundlage der Streamübertragung, bei der Nutzer vorhandene Audio- und Videostreams über Sprachbefehle, die Google Home App oder Smart Displays zwischen Geräten verschieben können. Die Medienwiedergabe wird auf einem Gerät (der Quelle) beendet und auf einem anderen (dem Ziel) fortgesetzt. Jedes Übertragungsgerät mit der neuesten Firmware kann als Quellen oder Ziele bei einer Streamübertragung dienen.

Um das neue Zielgerät während der Streamübertragung abzurufen, verwenden Sie während des [sessionManager:didResumeCastSession:]-Callbacks das Attribut GCKCastSession#device.

Weitere Informationen finden Sie unter Streamübertragung auf Web Receiver.

Automatische Wiederherstellung der Verbindung

Das Cast-Framework fügt eine Logik für die erneute Verbindung hinzu, um das erneute Herstellen einer Verbindung in vielen subtilen Eckfällen automatisch zu verarbeiten, z. B.:

  • Wiederherstellung nach einem vorübergehenden WLAN-Ausfall
  • Aus dem Ruhemodus wiederherstellen
  • App aus dem Hintergrund der App wiederherstellen
  • App-Absturz wiederherstellen

So funktioniert die Mediensteuerung

Wenn eine Cast-Sitzung mit einer Web Receiver-App eingerichtet wird, die den Medien-Namespace unterstützt, wird vom Framework automatisch eine Instanz von GCKRemoteMediaClient erstellt. Sie kann als remoteMediaClient-Attribut der GCKCastSession-Instanz aufgerufen werden.

Alle Methoden für GCKRemoteMediaClient, die Anfragen an den Webempfänger senden, geben ein GCKRequest-Objekt zurück, mit dem diese Anfrage verfolgt werden kann. Diesem Objekt kann ein GCKRequestDelegate zugewiesen werden, um Benachrichtigungen über das Endergebnis des Vorgangs zu erhalten.

Es ist zu erwarten, dass die Instanz von GCKRemoteMediaClient von mehreren Teilen der App gemeinsam genutzt werden kann und dass einige interne Komponenten des Frameworks wie das Cast-Dialogfeld und die Mini-Mediensteuerelemente die Instanz gemeinsam nutzen. Zu diesem Zweck unterstützt GCKRemoteMediaClient die Registrierung mehrerer GCKRemoteMediaClientListeners.

Medienmetadaten festlegen

Die Klasse GCKMediaMetadata stellt Informationen zu einem Medienelement dar, das Sie streamen möchten. Im folgenden Beispiel wird eine neue GCKMediaMetadata-Instanz eines Films erstellt und der Titel, der Untertitel, der Name des Aufnahmestudios und zwei Bilder festgelegt.

Swift
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))
Objective 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]];

Weitere Informationen zur Verwendung von Bildern mit Medienmetadaten finden Sie im Abschnitt Image-Auswahl und -Caching.

Medien laden

Erstellen Sie zum Laden eines Medienelements eine GCKMediaInformation-Instanz. Rufen Sie dann den aktuellen GCKCastSession ab und verwenden Sie GCKRemoteMediaClient, um die Medien in die Empfänger-App zu laden. Mit GCKRemoteMediaClient können Sie dann eine auf dem Empfänger ausgeführte Mediaplayer-App steuern, z. B. für Wiedergabe, Pause und Stopp.

Swift
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
}
Objective 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;
}

Weitere Informationen finden Sie auch im Abschnitt zur Verwendung von Medientracks.

4K-Videoformat

Um das Videoformat Ihrer Medien zu ermitteln, verwenden Sie das Attribut videoInfo von GCKMediaStatus, um die aktuelle Instanz von GCKVideoInfo abzurufen. Diese Instanz enthält den Typ des HDR-TV-Formats sowie die Höhe und Breite in Pixeln. Varianten des 4K-Formats werden im Attribut hdrType durch die ENUM-Werte GCKVideoInfoHDRType gekennzeichnet.

Mini-Controller hinzufügen

Gemäß der Checkliste für Cast-Design sollte eine Sender-App eine dauerhafte Steuerung, den sogenannten Minicontroller, bieten. Diese sollte angezeigt werden, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Der Mini-Controller bietet sofortigen Zugriff und eine sichtbare Erinnerung für die aktuelle Sitzung.

Das Cast-Framework bietet eine Steuerleiste (GCKUIMiniMediaControlsViewController), die den Szenen hinzugefügt werden kann, in denen Sie den Mini-Controller zeigen möchten.

Wenn deine Sender-App einen Video- oder Audio-Livestream wiedergibt, zeigt das SDK automatisch eine Wiedergabe-/Stopp-Schaltfläche anstelle der Wiedergabe-/Pause-Taste auf dem Mini-Controller an.

Unter iOS-Sender-UI anpassen erfahren Sie, wie Ihre Absender-App das Aussehen der Cast-Widgets konfigurieren kann.

Es gibt zwei Möglichkeiten, den Mini-Controller einer Sender-App hinzuzufügen:

  • Lassen Sie das Layout des Mini-Controllers vom Cast-Framework verwalten, indem Sie Ihren vorhandenen Ansichts-Controller mit einem eigenen Ansichts-Controller verbinden.
  • Du kannst das Layout des Mini-Controller-Widgets selbst verwalten, indem du es deinem vorhandenen Ansichts-Controller hinzufügst, indem du eine Unteransicht im Storyboard bereitstellst.

Umschließen mit GCKUICastContainerViewController

Die erste Möglichkeit besteht darin, GCKUICastContainerViewController zu verwenden, das einen weiteren Ansichts-Controller umschließt und ein GCKUIMiniMediaControlsViewController-Element am unteren Rand hinzufügt. Bei diesem Ansatz können Sie weder die Animation anpassen noch das Verhalten des Containeransichts-Controllers konfigurieren.

Diese erste Möglichkeit wird in der Regel in der Methode -[application:didFinishLaunchingWithOptions:] des App-Delegaten ausgeführt:

Swift
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()

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

In vorhandenem Ansichts-Controller einbetten

Die zweite Möglichkeit besteht darin, den Mini-Controller direkt dem vorhandenen Ansichts-Controller hinzuzufügen. Dazu verwenden Sie createMiniMediaControlsViewController, um eine GCKUIMiniMediaControlsViewController-Instanz zu erstellen, und fügen sie dann als Teilansicht dem Container View Controller hinzu.

So richten Sie den Ansichts-Controller im App-Delegaten ein:

Swift
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
}
Objective 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;
}

Erstellen Sie in Ihrem Root-Ansicht-Controller eine GCKUIMiniMediaControlsViewController-Instanz und fügen Sie sie dem Container-Ansicht-Controller als Unteransicht hinzu:

Swift
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)
    }
  }

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

Mit GCKUIMiniMediaControlsViewControllerDelegate wird dem Host View Controller mitgeteilt, wann der Mini-Controller sichtbar sein soll:

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

Maximierten Controller hinzufügen

Gemäß der Checkliste für das Google Cast-Design muss eine Sender-App einen erweiterten Controller für die gestreamten Medien bereitstellen. Der maximierte Controller ist eine Vollbildversion des Mini-Controllers.

Der maximierte Controller ist eine Vollbildansicht, die volle Kontrolle über die Remote-Medienwiedergabe bietet. Diese Ansicht sollte es einer Streaming-App ermöglichen, alle verwaltbaren Aspekte einer Streamingsitzung zu verwalten, mit Ausnahme der Lautstärkeregelung für Web Receiver und des Sitzungslebenszyklus (Streaming verbinden/beenden). Außerdem enthält er alle Statusinformationen zur Mediensitzung (Artwork, Titel, Untertitel usw.).

Die Funktionalität dieser Ansicht wird durch die Klasse GCKUIExpandedMediaControlsViewController implementiert.

Als Erstes müssen Sie den standardmäßigen erweiterten Controller im Cast-Kontext aktivieren. Ändern Sie den App-Delegaten, um den standardmäßigen erweiterten Controller zu aktivieren:

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

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

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

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

Fügen Sie Ihrem Ansichts-Controller den folgenden Code hinzu, damit der maximierte Controller geladen wird, wenn der Nutzer mit dem Streamen eines Videos beginnt:

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

  ...

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

  ...

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

Der maximierte Controller wird automatisch gestartet, wenn der Nutzer auf den Mini-Controller tippt.

Wenn die Absender-App einen Video- oder Audio-Livestream wiedergibt, zeigt das SDK im maximierten Controller automatisch eine Wiedergabe-/Stopp-Schaltfläche anstelle der Wiedergabe-/Pause-Schaltfläche an.

Informationen dazu, wie die Absender-App die Darstellung der Cast-Widgets konfigurieren kann, finden Sie unter Benutzerdefinierte Stile auf Ihre iOS-App anwenden.

Lautstärkeregelung

Das Cast-Framework verwaltet automatisch das Volume für die Absender-App. Das Framework wird automatisch mit dem Web Receiver-Volume für die bereitgestellten UI-Widgets synchronisiert. Verwenden Sie GCKUIDeviceVolumeController, um einen von der App bereitgestellten Schieberegler zu synchronisieren.

Lautstärkeregelung über physische Taste

Mit den physischen Lautstärketasten auf dem Sendergerät können Sie die Lautstärke der Streamingsitzung auf dem Web Receiver ändern. Verwenden Sie dazu das Flag physicalVolumeButtonsWillControlDeviceVolume für GCKCastOptions, das auf der GCKCastContext festgelegt ist.

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

Fehler verarbeiten

Für Sender-Apps ist es sehr wichtig, alle Fehler-Callbacks zu verarbeiten und die beste Antwort für jede Phase des Cast-Lebenszyklus zu bestimmen. Die App kann dem Nutzer Fehlerdialoge anzeigen oder die Streamingsitzung beenden.

Logging

GCKLogger ist ein Singleton, das vom Framework für das Logging verwendet wird. Mit GCKLoggerDelegate können Sie anpassen, wie mit Lognachrichten umgegangen wird.

Mithilfe von GCKLogger generiert das SDK Logging-Ausgaben in Form von Meldungen zur Fehlerbehebung, Fehlern und Warnungen. Diese Lognachrichten unterstützen die Fehlerbehebung und sind nützlich, um Probleme zu beheben und zu identifizieren. Standardmäßig wird die Logausgabe unterdrückt. Durch Zuweisen einer GCKLoggerDelegate kann die Absenderanwendung diese Nachrichten jedoch vom SDK empfangen und in der Systemkonsole protokollieren.

Swift
@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)
    }
  }
}
Objective 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

Damit auch die Fehlerbehebung und ausführliche Meldungen aktiviert werden, fügen Sie dem Code diese Zeile hinzu, nachdem Sie den Delegaten festgelegt haben (siehe oben):

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

Sie können auch die von GCKLogger generierten Logeinträge filtern. Legen Sie die minimale Logging-Ebene pro Klasse fest. Beispiel:

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

Die Klassennamen können entweder Literalnamen oder glob-Muster sein, z. B. GCKUI\* und GCK\*Session.