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 zur Laufzeit auf dem Absender vorhanden sind. Die Absender-App oder Cast-App bezieht sich auf eine App, die ebenfalls auf dem Absender ausgeführt wird. Die Web Receiver-Anwendung 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 Status 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, zeigt das Framework das Streaming-Dialogfeld mit der Liste der erkannten Übertragungsgeräte an.
  • 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 Sender-App auf, um zu bestätigen, dass die Web Receiver-App 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 Sender-App vom Web Receiver.

Damit Sie Fehler bei Ihrem Absender beheben können, müssen Sie die Protokollierung aktivieren.

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 erfahren Sie, wie Sie Cast in Ihre iOS-App einbinden.

Aufrufmethoden aus dem Hauptthread

Cast-Kontext initialisieren

Das Cast-Framework hat ein globales Singleton-Objekt, das 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 Senderanwendung 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.

Mit der Methode -[application:didFinishLaunchingWithOptions:] lässt sich außerdem ein Logging-Delegate einrichten, um die Logging-Nachrichten vom Framework zu erhalten. 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, wenn ein Webempfänger zum ersten Mal verfügbar ist. Die Sender-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 Sendergerät mit einem WLAN verbunden ist. Wenn der Nutzer nach dem ersten Start der App zum ersten Mal auf das Cast-Symbol tippt, erscheint ein Berechtigungsdialogfeld, über das der Nutzer der App Zugriff auf Geräte 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 ein Thumbnail) angezeigt oder er 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 er Fehler beheben kann.

  • Mini-Controller: Wenn der Nutzer Inhalte streamt und von der aktuellen Inhaltsseite oder dem maximierten 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 und auf die Medienbenachrichtigung oder den Mini-Controller klickt, wird der maximierte Controller gestartet. Dabei werden die Metadaten der aktuell wiedergegebenen Medien sowie mehrere Schaltflächen zur Steuerung der Medienwiedergabe angezeigt.

Cast-Symbol hinzufügen

Das Framework stellt eine Cast-Schaltflächenkomponente als UIButton-Unterklasse bereit. Sie können es der Titelleiste der App hinzufügen, indem Sie es in ein UIBarButtonItem einfügen. In einer typischen abgeleiteten Klasse UIViewController 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 dem Storyboard hinzugefügt werden.

Geräteerkennung konfigurieren

In diesem 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 Eigenschaft von GCKCastContext ist. Das Framework stellt eine standardmäßige Komponente des Cast-Dialogfelds für die Geräteauswahl und -steuerung bereit. Die Geräteliste ist lexikografisch nach dem gerätefreundlichen Namen sortiert.

So funktioniert die Sitzungsverwaltung

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

Sitzungen werden von der Klasse GCKSessionManager verwaltet, die ein Attribut 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 über die Property currentCastSession von GCKSessionManager auf die aktuell aktive Übertragungssitzung (falls vorhanden) zugreifen.

Die Schnittstelle GCKSessionManagerListener kann zum Überwachen von Sitzungsereignissen wie der Erstellung, Sperrung, Wiederaufnahme und Beendigung von Sitzungen verwendet werden. Das Framework hält Sitzungen automatisch an, wenn die Sender-App in den Hintergrund geht, und versucht, sie fortzusetzen, wenn die App in den Vordergrund zurückkehrt (oder neu gestartet wird, nachdem die App während einer aktiven Sitzung abnormal/abrupt beendet wurde).

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

Wenn die Anwendung als Reaktion auf Lebenszyklusereignisse der Sitzung eine besondere Verarbeitung ausführen muss, kann sie eine oder mehrere GCKSessionManagerListener-Instanzen bei der 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 mithilfe von Sprachbefehlen, der Google Home App oder Smart Displays auf andere Geräte verschieben können. Die Wiedergabe von Medien 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 findest du 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 Sonderfällen automatisch zu handhaben, z. B.:

  • Wiederherstellung nach vorübergehendem WLAN-Ausfall
  • Aus dem Geräte-Ruhemodus wiederherstellen
  • App aus Hintergrundmodus wiederherstellen
  • Wiederherstellung nach Absturz der App

So funktioniert die Mediensteuerung

Wenn eine Streamingsitzung mit einer Web Receiver-App eingerichtet wird, die den Medien-Namespace unterstützt, wird vom Framework automatisch eine Instanz von GCKRemoteMediaClient erstellt. Sie kann über das Attribut remoteMediaClient 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 wird erwartet, dass die Instanz von GCKRemoteMediaClient von mehreren Teilen der App gemeinsam genutzt wird und dass einige interne Komponenten des Frameworks wie das Cast-Dialogfeld und die Mini-Mediensteuerelemente gemeinsam genutzt werden. 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 Titel, Untertitel, 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]];

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

Medien laden

Erstellen Sie zum Laden eines Medienelements eine GCKMediaInformation-Instanz. Verwenden Sie dazu die Metadaten des Mediums. Rufen Sie dann den aktuellen GCKCastSession ab und verwenden Sie GCKRemoteMediaClient, um die Medien in die Receiver-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 Ihres Mediums 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 Aufzählungswerte GCKVideoInfoHDRType angegeben.

Mini-Controller hinzufügen

Gemäß der Checkliste für das Streamingdesign sollte eine Sender-App eine dauerhafte Steuerung, den sogenannten Minicontroller, bieten. Diese sollte angezeigt werden, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Über den Mini-Controller hast du sofortigen Zugriff und es wird eine sichtbare Erinnerung für die aktuelle Streamingsitzung angezeigt.

Das Cast-Framework bietet die Steuerleiste GCKUIMiniMediaControlsViewController, die den Szenen hinzugefügt werden kann, in denen der Mini-Controller angezeigt werden soll.

Wenn die Sender-App einen Video- oder Audio-Livestream wiedergibt, zeigt das SDK automatisch eine Wiedergabe-/Stopp-Schaltfläche anstelle der Wiedergabe-/Pause-Schaltfläche 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 Cast-Framework das Layout des Mini-Controllers verwalten, indem Sie den vorhandenen Ansicht-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.

Zusammenfassen mit GCKUICastContainerViewController

Die erste Möglichkeit besteht darin, GCKUICastContainerViewController zu verwenden, um einen weiteren Ansichts-Controller zu umschließen und unten ein GCKUIMiniMediaControlsViewController-Element hinzuzufügen. Dieser Ansatz ist insofern eingeschränkt, als Sie die Animation nicht anpassen und das Verhalten des Containeransichts-Controllers nicht konfigurieren können.

Die erste Methode 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 in Ihren vorhandenen Ansichts-Controller einzufügen. Dazu erstellen Sie mit createMiniMediaControlsViewController eine GCKUIMiniMediaControlsViewController-Instanz und fügen sie dann als Teilansicht dem Container View-Controller hinzu.

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

Der GCKUIMiniMediaControlsViewControllerDelegate teilt dem Host View-Controller mit, 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 Design von Google Cast 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. In dieser Ansicht sollte eine Streaming-App alle zu verwaltenden Aspekte einer Streamingsitzung verwalten können, mit Ausnahme der Lautstärkeregelung für Web Receiver und des Sitzungslebenszyklus (Streaming verbinden/beenden). Außerdem enthält er alle Statusinformationen zur Mediensitzung (z. B. Artwork, Titel, Untertitel).

Die Funktionen dieser Ansicht werden durch die Klasse GCKUIExpandedMediaControlsViewController implementiert.

Als Erstes müssen Sie den erweiterten Standard-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 beginnt, ein Video zu streamen:

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 Ihre Sender-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 Ihre Sender-App die Darstellung der Cast-Widgets konfigurieren kann, finden Sie unter Benutzerdefinierte Stile auf iOS-App anwenden.

Lautstärkeregelung

Das Cast-Framework verwaltet automatisch das Volume für die Sender-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 kann die Lautstärke der Streamingsitzung auf dem Web Receiver geändert werden. Dazu wird das Flag physicalVolumeButtonsWillControlDeviceVolume für GCKCastOptions verwendet, 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

Es ist sehr wichtig, dass Absender-Apps alle Fehler-Callbacks verarbeiten und die beste Antwort für jede Phase des Cast-Lebenszyklus bestimmen. Die App kann dem Nutzer Dialoge zu Fehlern anzeigen oder die Streaming-Sitzung beenden.

Logging

GCKLogger ist ein Singleton-Element, das vom Framework für das Logging verwendet wird. Mit GCKLoggerDelegate können Sie die Verarbeitung von Logeinträgen anpassen.

Mithilfe von GCKLogger generiert das SDK Logging-Ausgaben in Form von Debug-Nachrichten, Fehlern und Warnungen. Diese Lognachrichten unterstützen die Fehlerbehebung und sind nützlich für die Fehlerbehebung und Identifizierung von Problemen. 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

Um sowohl die Fehlerbehebung als auch ausführliche Meldungen zu aktivieren, fügen Sie diese Zeile in den Code ein, nachdem Sie den Bevollmächtigten 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 Logmeldungen 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.