Für Google Cast optimierte iOS-App

1. Übersicht

Google Cast-Logo

In diesem Codelab erfahren Sie, wie Sie eine bestehende iOS-Video-App so anpassen, dass sie Inhalte auf ein für Google Cast optimiertes Gerät streamen kann.

Was ist Google Cast?

Mit Google Cast können Nutzer Inhalte von Mobilgeräten auf einen Fernseher streamen. Nutzer können dann ihr Mobilgerät als Fernbedienung für die Medienwiedergabe auf dem Fernseher verwenden.

Mit dem Google Cast SDK können Sie Ihre App so erweitern, dass sie Google Cast-fähige Geräte wie Fernseher oder Soundsysteme steuern kann. Mit dem Cast SDK können Sie die erforderlichen UI-Komponenten anhand der Checkliste für das Google Cast-Design hinzufügen.

Die Design-Checkliste für Google Cast soll die Nutzung von Cast auf allen unterstützten Plattformen einfach und vorhersehbar machen.

Ziele

Wenn Sie dieses Codelab abgeschlossen haben, haben Sie eine iOS-Video-App, mit der Sie Videos auf ein Google Cast-Gerät streamen können.

Lerninhalte

  • Hinzufügen des Google Cast SDK zu einer Beispiel-Video-App
  • Hier erfahren Sie, wie Sie das Cast-Symbol zur Auswahl eines Google Cast-Geräts hinzufügen.
  • Verbindung zu einem Übertragungsgerät herstellen und einen Medienempfänger starten
  • So streamen Sie ein Video.
  • So fügen Sie Ihrer App einen Cast Mini-Controller hinzu.
  • So fügst du einen erweiterten Controller hinzu.
  • So erstellst du ein einführendes Overlay.
  • Cast-Widgets anpassen
  • Cast Connect einbinden

Voraussetzungen

  • Die neueste Version von Xcode.
  • Ein Mobilgerät mit iOS 9 oder höher oder der Xcode Simulator
  • Ein USB-Datenkabel, um Ihr Mobilgerät mit dem Entwicklungscomputer zu verbinden (falls Sie ein Gerät verwenden).
  • Ein Google Cast-Gerät wie Chromecast oder Android TV mit Internetverbindung
  • Einen Fernseher oder Monitor mit HDMI-Eingang
  • Für den Test der Cast Connect-Integration ist Chromecast mit Google TV erforderlich, für den Rest des Codelabs ist es optional. Falls Sie keines haben, überspringen Sie den Schritt Cast Connect-Unterstützung hinzufügen gegen Ende dieser Anleitung.

Erfahrung

  • Sie müssen über Vorkenntnisse in der iOS-Entwicklung verfügen.
  • Außerdem benötigen Sie Vorkenntnisse in Bezug auf Fernsehen. :)

Wie möchten Sie diese Anleitung nutzen?

Nur bis zum Ende lesen Lies sie dir durch und absolviere die Übungen

Wie würden Sie Ihre Erfahrung mit der Entwicklung von iOS-Apps bewerten?

Neuling Leicht fortgeschritten Kompetent

Wie würden Sie Ihre Erfahrungen mit Fernsehen bewerten?

Neuling Leicht fortgeschritten Kompetent

2. Beispielcode abrufen

Sie können entweder den gesamten Beispielcode auf Ihren Computer herunterladen...

und entpacken Sie die heruntergeladene ZIP-Datei.

3. Beispiel-App ausführen

Apple iOS-Logo

Sehen wir uns zunächst an, wie die fertige Beispiel-App aussieht. Die App ist ein einfacher Videoplayer. Der Nutzer kann ein Video aus einer Liste auswählen und es dann lokal auf dem Gerät wiedergeben oder auf ein Google Cast-Gerät streamen.

Nachdem Sie den Code heruntergeladen haben, können Sie die fertige Beispielanwendung in Xcode öffnen und ausführen. Gehen Sie dazu so vor:

Häufig gestellte Fragen

CocoaPods-Einrichtung

Wenn Sie CocoaPods einrichten möchten, öffnen Sie die Konsole und installieren Sie die Anwendung mit dem unter macOS verfügbaren Standard Ruby:

sudo gem install cocoapods

Wenn Probleme auftreten, finden Sie in der offiziellen Dokumentation Informationen zum Herunterladen und Installieren des Abhängigkeitsmanagers.

Projekt einrichten

  1. Rufen Sie auf Ihrem Terminal das Codelab-Verzeichnis auf.
  2. Installieren Sie die Abhängigkeiten aus der Podfile-Datei.
cd app-done
pod update
pod install
  1. Öffnen Sie Xcode und wählen Sie Open another project... (Weiteres Projekt öffnen...) aus.
  2. Wählen Sie im Ordner mit dem Beispielcode im Verzeichnis Ordnersymbolapp-done die Datei CastVideos-ios.xcworkspace aus.

Anwendung ausführen

Wählen Sie das Ziel und den Simulator aus und führen Sie dann die App aus:

Symbolleiste des XCode-App-Simulators

Die Video-App sollte nach einigen Sekunden angezeigt werden.

Klicken Sie auf „Zulassen“, wenn die Benachrichtigung zum Akzeptieren eingehender Netzwerkverbindungen angezeigt wird. Das Symbol „Streamen“ wird nicht angezeigt, wenn diese Option nicht akzeptiert wird.

Bestätigungsdialogfeld mit Berechtigung zum Akzeptieren eingehender Netzwerkverbindungen

Klicken Sie auf das Cast-Symbol und wählen Sie Ihr Google Cast-Gerät aus.

Wähle ein Video aus und klicke auf die Wiedergabeschaltfläche.

Das Video wird auf Ihrem Google Cast-Gerät wiedergegeben.

Der maximierte Controller wird angezeigt. Mit der Wiedergabe-/Pausetaste kannst du die Wiedergabe steuern.

Gehe zurück zur Liste der Videos.

Unten auf dem Bildschirm wird jetzt ein Mini-Controller angezeigt.

Abbildung eines iPhones, auf der die CastVideos App ausgeführt wird, mit dem Mini-Controller unten

Drücke im Mini-Controller auf die Schaltfläche „Pause“, um das Video auf dem Receiver zu pausieren. Klicke im Mini-Controller auf die Wiedergabeschaltfläche, um die Wiedergabe des Videos fortzusetzen.

Klicken Sie auf das Cast-Symbol, um das Streamen auf das Google Cast-Gerät zu beenden.

4. Startprojekt vorbereiten

Abbildung eines iPhones, auf der die CastVideos App ausgeführt wird

Wir müssen der heruntergeladenen Start-App Unterstützung für Google Cast hinzufügen. Im Folgenden finden Sie einige Google Cast-Begriffe, die wir in diesem Codelab verwenden:

  • eine Absender-App auf einem Mobilgerät oder Laptop ausgeführt wird,
  • Eine Receiver-App wird auf dem Google Cast-Gerät ausgeführt.

Projekt einrichten

Jetzt können Sie mit Xcode auf dem Startprojekt aufbauen:

  1. Rufen Sie auf Ihrem Terminal das Codelab-Verzeichnis auf.
  2. Installieren Sie die Abhängigkeiten aus der Podfile-Datei.
cd app-start
pod update
pod install
  1. Öffnen Sie Xcode und wählen Sie Open another project... (Weiteres Projekt öffnen...) aus.
  2. Wählen Sie im Ordner mit dem Beispielcode im Verzeichnis Ordnersymbolapp-start die Datei CastVideos-ios.xcworkspace aus.

App-Design

Die App ruft eine Liste mit Videos von einem Remote-Webserver ab und stellt eine Liste zur Verfügung, die der Nutzer durchsuchen kann. Nutzer können ein Video auswählen, um die Details aufzurufen, oder das Video lokal auf einem Mobilgerät abspielen.

Die App besteht aus zwei Controllern für die Hauptansicht: MediaTableViewController und MediaViewController..

MediaTableViewController

Dieser UITableViewController zeigt eine Liste von Videos aus einer MediaListModel-Instanz an. Die Liste der Videos und die zugehörigen Metadaten werden auf einem Remoteserver als JSON-Datei gehostet. MediaListModel ruft diese JSON-Datei ab und verarbeitet sie, um eine Liste mit MediaItem-Objekten zu erstellen.

Ein MediaItem-Objekt stellt ein Video und die zugehörigen Metadaten wie Titel, Beschreibung, URL für ein Bild und URL für den Stream dar.

MediaTableViewController erstellt eine MediaListModel-Instanz und registriert sich dann als MediaListModelDelegate, um informiert zu werden, wenn die Medienmetadaten heruntergeladen wurden, damit die Tabellenansicht geladen werden kann.

Dem Nutzer wird eine Liste von Video-Thumbnails mit einer kurzen Beschreibung für jedes Video angezeigt. Wenn ein Element ausgewählt wird, wird die entsprechende MediaItem an MediaViewController übergeben.

MediaViewController

Dieser Ansichts-Controller zeigt die Metadaten zu einem bestimmten Video an und ermöglicht dem Nutzer, das Video lokal auf dem Mobilgerät abzuspielen.

Der Ansichts-Controller hostet ein LocalPlayerView, einige Mediensteuerelemente und einen Textbereich für die Beschreibung des ausgewählten Videos. Der Player deckt den oberen Teil des Bildschirms ab und bietet darunter Platz für eine detaillierte Beschreibung des Videos. Der Nutzer kann das Video abspielen/pausieren oder zur lokalen Videowiedergabe springen.

Häufig gestellte Fragen

5. Cast-Symbol hinzufügen

Abbildung des oberen Drittels eines iPhones, auf dem die CastVideos App ausgeführt wird, mit dem Cast-Symbol oben rechts

Auf einer für Google Cast optimierten App wird das Cast-Symbol in jedem Ansicht-Controller angezeigt. Wenn der Nutzer auf das Cast-Symbol klickt, wird eine Liste der Übertragungsgeräte angezeigt, die der Nutzer auswählen kann. Wenn der Nutzer Inhalte lokal auf dem Sendergerät wiedergegeben hat, wird die Wiedergabe auf diesem Übertragungsgerät gestartet oder fortgesetzt, wenn ein Übertragungsgerät ausgewählt wird. Der Nutzer kann während der Übertragung jederzeit auf das Cast-Symbol klicken und die Übertragung Ihrer App auf das Übertragungsgerät beenden. Der Nutzer muss auf jedem Bildschirm Ihrer App eine Verbindung zum Übertragungsgerät herstellen oder trennen können. Weitere Informationen hierzu finden Sie in der Checkliste für das Google Cast-Design.

Konfiguration

Für das Startprojekt sind dieselben Abhängigkeiten und Xcode-Einrichtungen wie für die fertige Beispiel-App erforderlich. Kehren Sie zu diesem Abschnitt zurück und führen Sie dieselben Schritte aus, um das GoogleCast.framework zum Start-App-Projekt hinzuzufügen.

Initialisierung

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. Dies geschieht in der Regel in der Methode application(_:didFinishLaunchingWithOptions:) des App-Delegaten, damit die automatische Sitzungswiederaufnahme beim Neustart der Absenderanwendung korrekt ausgelöst und die Suche nach Geräten gestartet werden kann.

Bei der Initialisierung von GCKCastContext muss ein GCKCastOptions-Objekt angegeben werden. Diese Klasse enthält Optionen, die das Verhalten des Frameworks beeinflussen. Das Wichtigste ist die ID der Empfänger-App, die verwendet wird, um die Ergebnisse der Übertragungsgeräteerkennung zu filtern und die Empfänger-App zu starten, wenn eine Übertragung gestartet wird.

Über die Methode application(_:didFinishLaunchingWithOptions:) können Sie auch einen Logging-Delegaten einrichten, der die Logging-Nachrichten vom Cast-Framework empfängt. Diese können bei der Fehlersuche und Fehlerbehebung hilfreich sein.

Wenn Sie eine für Google Cast optimierte App entwickeln, müssen Sie sich als Cast-Entwickler registrieren und eine App-ID für Ihre App erhalten. In diesem Codelab verwenden wir eine Beispiel-App-ID.

Füge in AppDelegate.swift den folgenden Code ein, um GCKCastContext mit der App-ID aus den Standardeinstellungen des Nutzers zu initialisieren, und füge einen Listener für das Google Cast-Framework hinzu:

import GoogleCast

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  fileprivate var enableSDKLogging = true

  ...

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

    ...
    let options = GCKCastOptions(discoveryCriteria: GCKDiscoveryCriteria(applicationID: kReceiverAppID))
    options.physicalVolumeButtonsWillControlDeviceVolume = true
    GCKCastContext.setSharedInstanceWith(options)

    window?.clipsToBounds = true
    setupCastLogging()
    ...
  }
  ...
  func setupCastLogging() {
    let logFilter = GCKLoggerFilter()
    let classesToLog = ["GCKDeviceScanner", "GCKDeviceProvider", "GCKDiscoveryManager", "GCKCastChannel",
                        "GCKMediaControlChannel", "GCKUICastButton", "GCKUIMediaController", "NSMutableDictionary"]
    logFilter.setLoggingLevel(.verbose, forClasses: classesToLog)
    GCKLogger.sharedInstance().filter = logFilter
    GCKLogger.sharedInstance().delegate = self
  }
}

...

// MARK: - GCKLoggerDelegate

extension AppDelegate: GCKLoggerDelegate {
  func logMessage(_ message: String,
                  at _: GCKLoggerLevel,
                  fromFunction function: String,
                  location: String) {
    if enableSDKLogging {
      // Send SDK's log messages directly to the console.
      print("\(location): \(function) - \(message)")
    }
  }
}

Cast-Symbol

Nachdem GCKCastContext initialisiert wurde, muss das Cast-Symbol hinzugefügt werden, damit der Nutzer ein Übertragungsgerät auswählen kann. Das Cast SDK bietet eine Komponente für das Cast-Symbol namens GCKUICastButton als abgeleitete UIButton-Klasse. Sie können sie der Titelleiste der Anwendung hinzufügen, indem Sie sie in UIBarButtonItem einschließen. Wir müssen das Cast-Symbol sowohl zu MediaTableViewController als auch zu MediaViewController hinzufügen.

Fügen Sie MediaTableViewController.swift und MediaViewController.swift den folgenden Code hinzu:

import GoogleCast

@objc(MediaTableViewController)
class MediaTableViewController: UITableViewController, GCKSessionManagerListener,
  MediaListModelDelegate, GCKRequestDelegate {
  private var castButton: GCKUICastButton!
  ...
  override func viewDidLoad() {
    print("MediaTableViewController - viewDidLoad")
    super.viewDidLoad()

    ...
    castButton = GCKUICastButton(frame: CGRect(x: CGFloat(0), y: CGFloat(0),
                                               width: CGFloat(24), height: CGFloat(24)))
    // Overwrite the UIAppearance theme in the AppDelegate.
    castButton.tintColor = UIColor.white
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)

    ...
  }
  ...
}

Fügen Sie MediaViewController.swift dann den folgenden Code hinzu:

import GoogleCast

@objc(MediaViewController)
class MediaViewController: UIViewController, GCKSessionManagerListener, GCKRemoteMediaClientListener,
  LocalPlayerViewDelegate, GCKRequestDelegate {
  private var castButton: GCKUICastButton!
  ...
  override func viewDidLoad() {
    super.viewDidLoad()
    print("in MediaViewController viewDidLoad")
    ...
    castButton = GCKUICastButton(frame: CGRect(x: CGFloat(0), y: CGFloat(0),
                                               width: CGFloat(24), height: CGFloat(24)))
    // Overwrite the UIAppearance theme in the AppDelegate.
    castButton.tintColor = UIColor.white
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)

    ...
  }
  ...
}

Führen Sie nun die App aus. Sie sollten ein Cast-Symbol in der Navigationsleiste der App sehen. Wenn Sie darauf klicken, werden die Übertragungsgeräte in Ihrem lokalen Netzwerk aufgelistet. Die Geräteerkennung wird automatisch vom GCKCastContext verwaltet. Wähle dein Übertragungsgerät aus. Die Beispiel-Empfänger-App wird auf dem Übertragungsgerät geladen. Sie können zwischen der Suchaktivität und der Aktivität im lokalen Player wechseln. Der Status des Cast-Symbols wird synchronisiert.

Wir haben noch keine Unterstützung für die Medienwiedergabe, daher kannst du noch keine Videos auf dem Übertragungsgerät wiedergeben. Klicken Sie auf das Cast-Symbol, um das Streamen zu beenden.

6. Streamen von Videoinhalten

Abbildung eines iPhones, auf der die CastVideos-App läuft, die Details zu einem bestimmten Video zeigt („Tears of Steel“). Unten ist der Miniplayer zu sehen.

Wir erweitern die Beispiel-App so, dass Videos auch per Fernzugriff auf Übertragungsgeräten abgespielt werden können. Dazu müssen wir auf die verschiedenen Ereignisse warten, die vom Cast-Framework generiert werden.

Medien werden gestreamt

Wenn Sie Medien auf einem Übertragungsgerät wiedergeben möchten, müssen folgende Voraussetzungen erfüllt sein:

  1. Erstelle ein GCKMediaInformation-Objekt aus dem Cast SDK, das ein Medienelement modelliert.
  2. Der Nutzer stellt eine Verbindung zum Übertragungsgerät her, um die Receiver-App zu starten.
  3. Laden Sie das GCKMediaInformation-Objekt in den Receiver und spielen Sie den Inhalt ab.
  4. Beobachten Sie den Medienstatus.
  5. Wiedergabebefehle basierend auf Nutzerinteraktionen an den Empfänger senden

Schritt 1 ist so, als würden Sie ein Objekt einem anderen zuordnen. GCKMediaInformation versteht das Cast SDK und MediaItem steht für die Kapselung eines Medienelements durch unsere App. können wir MediaItem ganz einfach GCKMediaInformation zuordnen. Schritt 2 im vorherigen Abschnitt wurde bereits ausgeführt. Schritt 3 lässt sich mit dem Cast SDK ganz einfach durchführen.

Die Beispiel-App MediaViewController unterscheidet bereits zwischen lokaler und Remote-Wiedergabe, indem sie diese Aufzählung verwendet:

enum PlaybackMode: Int {
  case none = 0
  case local
  case remote
}

private var playbackMode = PlaybackMode.none

In diesem Codelab ist es nicht wichtig, dass Sie genau verstehen, wie die gesamte Logik des Beispiel-Players funktioniert. Der Mediaplayer Ihrer App muss so angepasst werden, dass er die beiden Wiedergabeorte auf ähnliche Weise erkennt.

Derzeit ist der lokale Player immer im lokalen Wiedergabestatus, da er noch nichts über den Streamingstatus weiß. Wir müssen die Benutzeroberfläche basierend auf Statusübergängen im Cast-Framework aktualisieren. Wenn wir beispielsweise mit dem Streamen beginnen, müssen wir die lokale Wiedergabe beenden und einige Steuerelemente deaktivieren. Wenn wir das Streaming beenden, während wir uns in diesem Ansicht-Controller befinden, müssen wir ebenfalls zur lokalen Wiedergabe wechseln. Dazu müssen wir auf die verschiedenen Ereignisse warten, die vom Cast-Framework generiert werden.

Streamingsitzung verwalten

Beim Cast-Framework umfasst eine Cast-Sitzung die Schritte: Verbinden mit einem Gerät, Starten oder Teilnehmen, Herstellen einer Verbindung zu einer Empfänger-App und Initialisieren eines Mediensteuerungskanals, falls erforderlich. Über den Mediensteuerungskanal sendet und empfängt das Cast-Framework Nachrichten vom Empfänger-Mediaplayer.

Das Streamen wird automatisch gestartet, wenn der Nutzer ein Gerät über das Cast-Symbol auswählt, und automatisch beendet, wenn der Nutzer die Verbindung trennt. Auch die Wiederherstellung einer Verbindung zu einer Empfängersitzung aufgrund von Netzwerkproblemen erfolgt automatisch durch das Cast-Framework.

Streamingsitzungen werden vom GCKSessionManager verwaltet, auf den über GCKCastContext.sharedInstance().sessionManager zugegriffen werden kann. Mit den GCKSessionManagerListener-Callbacks können Sitzungsereignisse wie Erstellung, Sperrung, Wiederaufnahme und Beendigung überwacht werden.

Zunächst müssen wir unseren Sitzungs-Listener registrieren und einige Variablen initialisieren:

class MediaViewController: UIViewController, GCKSessionManagerListener,
  GCKRemoteMediaClientListener, LocalPlayerViewDelegate, GCKRequestDelegate {

  ...
  private var sessionManager: GCKSessionManager!
  ...

  required init?(coder: NSCoder) {
    super.init(coder: coder)

    sessionManager = GCKCastContext.sharedInstance().sessionManager

    ...
  }

  override func viewWillAppear(_ animated: Bool) {
    ...

    let hasConnectedSession: Bool = (sessionManager.hasConnectedSession())
    if hasConnectedSession, (playbackMode != .remote) {
      populateMediaInfo(false, playPosition: 0)
      switchToRemotePlayback()
    } else if sessionManager.currentSession == nil, (playbackMode != .local) {
      switchToLocalPlayback()
    }

    sessionManager.add(self)

    ...
  }

  override func viewWillDisappear(_ animated: Bool) {
    ...

    sessionManager.remove(self)
    sessionManager.currentCastSession?.remoteMediaClient?.remove(self)
    ...
    super.viewWillDisappear(animated)
  }

  func switchToLocalPlayback() {
    ...

    sessionManager.currentCastSession?.remoteMediaClient?.remove(self)

    ...
  }

  func switchToRemotePlayback() {
    ...

    sessionManager.currentCastSession?.remoteMediaClient?.add(self)

    ...
  }


  // MARK: - GCKSessionManagerListener

  func sessionManager(_: GCKSessionManager, didStart session: GCKSession) {
    print("MediaViewController: sessionManager didStartSession \(session)")
    setQueueButtonVisible(true)
    switchToRemotePlayback()
  }

  func sessionManager(_: GCKSessionManager, didResumeSession session: GCKSession) {
    print("MediaViewController: sessionManager didResumeSession \(session)")
    setQueueButtonVisible(true)
    switchToRemotePlayback()
  }

  func sessionManager(_: GCKSessionManager, didEnd _: GCKSession, withError error: Error?) {
    print("session ended with error: \(String(describing: error))")
    let message = "The Casting session has ended.\n\(String(describing: error))"
    if let window = appDelegate?.window {
      Toast.displayMessage(message, for: 3, in: window)
    }
    setQueueButtonVisible(false)
    switchToLocalPlayback()
  }

  func sessionManager(_: GCKSessionManager, didFailToStartSessionWithError error: Error?) {
    if let error = error {
      showAlert(withTitle: "Failed to start a session", message: error.localizedDescription)
    }
    setQueueButtonVisible(false)
  }

  func sessionManager(_: GCKSessionManager,
                      didFailToResumeSession _: GCKSession, withError _: Error?) {
    if let window = UIApplication.shared.delegate?.window {
      Toast.displayMessage("The Casting session could not be resumed.",
                           for: 3, in: window)
    }
    setQueueButtonVisible(false)
    switchToLocalPlayback()
  }

  ...
}

Wir möchten im MediaViewController informiert werden, wenn eine Verbindung mit dem Übertragungsgerät hergestellt oder getrennt wird, damit wir zum oder vom lokalen Player wechseln können. Die Verbindung kann nicht nur durch die Instanz Ihrer Anwendung unterbrochen werden, die auf Ihrem Mobilgerät ausgeführt wird, sondern auch durch eine andere Instanz Ihrer (oder einer anderen) Anwendung, die auf einem anderen Mobilgerät ausgeführt wird.

Die derzeit aktive Sitzung kann als GCKCastContext.sharedInstance().sessionManager.currentCastSession aufgerufen werden. Sitzungen werden als Reaktion auf Nutzergesten im Cast-Dialogfeld automatisch erstellt und gelöscht.

Medien werden geladen

Die GCKRemoteMediaClient im Cast SDK bietet eine Reihe praktischer APIs für die Verwaltung der Remote-Medienwiedergabe auf dem Empfänger. Bei einem GCKCastSession, das die Medienwiedergabe unterstützt, wird vom SDK automatisch eine GCKRemoteMediaClient-Instanz erstellt. Sie können als Attribut remoteMediaClient der Instanz GCKCastSession darauf zugreifen.

Füge den folgenden Code in MediaViewController.swift ein, um das aktuell ausgewählte Video auf dem Empfänger zu laden:

@objc(MediaViewController)
class MediaViewController: UIViewController, GCKSessionManagerListener,
  GCKRemoteMediaClientListener, LocalPlayerViewDelegate, GCKRequestDelegate {
  ...

  @objc func playSelectedItemRemotely() {
    loadSelectedItem(byAppending: false)
  }

  /**
   * Loads the currently selected item in the current cast media session.
   * @param appending If YES, the item is appended to the current queue if there
   * is one. If NO, or if
   * there is no queue, a new queue containing only the selected item is created.
   */
  func loadSelectedItem(byAppending appending: Bool) {
    print("enqueue item \(String(describing: mediaInfo))")
    if let remoteMediaClient = sessionManager.currentCastSession?.remoteMediaClient {
      let mediaQueueItemBuilder = GCKMediaQueueItemBuilder()
      mediaQueueItemBuilder.mediaInformation = mediaInfo
      mediaQueueItemBuilder.autoplay = true
      mediaQueueItemBuilder.preloadTime = TimeInterval(UserDefaults.standard.integer(forKey: kPrefPreloadTime))
      let mediaQueueItem = mediaQueueItemBuilder.build()
      if appending {
        let request = remoteMediaClient.queueInsert(mediaQueueItem, beforeItemWithID: kGCKMediaQueueInvalidItemID)
        request.delegate = self
      } else {
        let queueDataBuilder = GCKMediaQueueDataBuilder(queueType: .generic)
        queueDataBuilder.items = [mediaQueueItem]
        queueDataBuilder.repeatMode = remoteMediaClient.mediaStatus?.queueRepeatMode ?? .off

        let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
        mediaLoadRequestDataBuilder.mediaInformation = mediaInfo
        mediaLoadRequestDataBuilder.queueData = queueDataBuilder.build()

        let request = remoteMediaClient.loadMedia(with: mediaLoadRequestDataBuilder.build())
        request.delegate = self
      }
    }
  }
  ...
}

Aktualisiere nun verschiedene vorhandene Methoden, um die Cast Session-Logik zu verwenden, um die Remote-Wiedergabe zu unterstützen:

required init?(coder: NSCoder) {
  super.init(coder: coder)
  ...
  castMediaController = GCKUIMediaController()
  ...
}

func switchToLocalPlayback() {
  print("switchToLocalPlayback")
  if playbackMode == .local {
    return
  }
  setQueueButtonVisible(false)
  var playPosition: TimeInterval = 0
  var paused: Bool = false
  var ended: Bool = false
  if playbackMode == .remote {
    playPosition = castMediaController.lastKnownStreamPosition
    paused = (castMediaController.lastKnownPlayerState == .paused)
    ended = (castMediaController.lastKnownPlayerState == .idle)
    print("last player state: \(castMediaController.lastKnownPlayerState), ended: \(ended)")
  }
  populateMediaInfo((!paused && !ended), playPosition: playPosition)
  sessionManager.currentCastSession?.remoteMediaClient?.remove(self)
  playbackMode = .local
}

func switchToRemotePlayback() {
  print("switchToRemotePlayback; mediaInfo is \(String(describing: mediaInfo))")
  if playbackMode == .remote {
    return
  }
  // If we were playing locally, load the local media on the remote player
  if playbackMode == .local, (_localPlayerView.playerState != .stopped), (mediaInfo != nil) {
    print("loading media: \(String(describing: mediaInfo))")
    let paused: Bool = (_localPlayerView.playerState == .paused)
    let mediaQueueItemBuilder = GCKMediaQueueItemBuilder()
    mediaQueueItemBuilder.mediaInformation = mediaInfo
    mediaQueueItemBuilder.autoplay = !paused
    mediaQueueItemBuilder.preloadTime = TimeInterval(UserDefaults.standard.integer(forKey: kPrefPreloadTime))
    mediaQueueItemBuilder.startTime = _localPlayerView.streamPosition ?? 0
    let mediaQueueItem = mediaQueueItemBuilder.build()

    let queueDataBuilder = GCKMediaQueueDataBuilder(queueType: .generic)
    queueDataBuilder.items = [mediaQueueItem]
    queueDataBuilder.repeatMode = .off

    let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
    mediaLoadRequestDataBuilder.queueData = queueDataBuilder.build()

    let request = sessionManager.currentCastSession?.remoteMediaClient?.loadMedia(with: mediaLoadRequestDataBuilder.build())
    request?.delegate = self
  }
  _localPlayerView.stop()
  _localPlayerView.showSplashScreen()
  setQueueButtonVisible(true)
  sessionManager.currentCastSession?.remoteMediaClient?.add(self)
  playbackMode = .remote
}

/* Play has been pressed in the LocalPlayerView. */
func continueAfterPlayButtonClicked() -> Bool {
  let hasConnectedCastSession = sessionManager.hasConnectedCastSession
  if mediaInfo != nil, hasConnectedCastSession() {
    // Display an alert box to allow the user to add to queue or play
    // immediately.
    if actionSheet == nil {
      actionSheet = ActionSheet(title: "Play Item", message: "Select an action", cancelButtonText: "Cancel")
      actionSheet?.addAction(withTitle: "Play Now", target: self,
                             selector: #selector(playSelectedItemRemotely))
    }
    actionSheet?.present(in: self, sourceView: _localPlayerView)
    return false
  }
  return true
}

Führen Sie nun die App auf Ihrem Mobilgerät aus. Stellen Sie eine Verbindung zu Ihrem Übertragungsgerät her und starten Sie die Wiedergabe eines Videos. Das Video sollte auf dem Receiver wiedergegeben werden.

7. Mini-Controller

Gemäß der Checkliste für das Streaming-Design müssen alle Cast-Apps einen Mini-Controller haben, der angezeigt wird, wenn der Nutzer die aktuelle Inhaltsseite verlässt. Der Mini-Controller bietet sofortigen Zugriff und eine sichtbare Erinnerung für die aktuelle Übertragungssitzung.

Darstellung des unteren Teils eines iPhones, auf dem die CastVideos App ausgeführt wird, mit Fokus auf dem Mini-Controller

Das Cast SDK bietet eine Steuerleiste (GCKUIMiniMediaControlsViewController), die den Szenen hinzugefügt werden kann, in denen die permanenten Steuerelemente eingeblendet werden sollen.

Für die Beispiel-App verwenden wir GCKUICastContainerViewController, das einen anderen View Controller umschließt und unten ein GCKUIMiniMediaControlsViewController hinzufügt.

Ändern Sie die Datei AppDelegate.swift und fügen Sie in der folgenden Methode den folgenden Code für die Bedingung if useCastContainerViewController hinzu:

func application(_: UIApplication,
                 didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  ...
  let appStoryboard = UIStoryboard(name: "Main", bundle: nil)
  guard let navigationController = appStoryboard.instantiateViewController(withIdentifier: "MainNavigation")
    as? UINavigationController else { return false }
  let castContainerVC = GCKCastContext.sharedInstance().createCastContainerController(for: navigationController)
    as GCKUICastContainerViewController
  castContainerVC.miniMediaControlsItemEnabled = true
  window = UIWindow(frame: UIScreen.main.bounds)
  window?.rootViewController = castContainerVC
  window?.makeKeyAndVisible()
  ...
}

Fügen Sie diese Eigenschaft und einen Setter/Getter hinzu, um die Sichtbarkeit des Mini-Controllers zu steuern (diese werden in einem späteren Abschnitt verwendet):

var isCastControlBarsEnabled: Bool {
    get {
      if useCastContainerViewController {
        let castContainerVC = (window?.rootViewController as? GCKUICastContainerViewController)
        return castContainerVC!.miniMediaControlsItemEnabled
      } else {
        let rootContainerVC = (window?.rootViewController as? RootContainerViewController)
        return rootContainerVC!.miniMediaControlsViewEnabled
      }
    }
    set(notificationsEnabled) {
      if useCastContainerViewController {
        var castContainerVC: GCKUICastContainerViewController?
        castContainerVC = (window?.rootViewController as? GCKUICastContainerViewController)
        castContainerVC?.miniMediaControlsItemEnabled = notificationsEnabled
      } else {
        var rootContainerVC: RootContainerViewController?
        rootContainerVC = (window?.rootViewController as? RootContainerViewController)
        rootContainerVC?.miniMediaControlsViewEnabled = notificationsEnabled
      }
    }
  }

Starte die App und streame ein Video. Wenn die Wiedergabe auf dem Receiver beginnt, sollte der Mini-Controller unten in jeder Szene angezeigt werden. Sie können die Remote-Wiedergabe mit dem Mini-Controller steuern. Wenn Sie zwischen der Suchaktivität und der Aktivität des lokalen Players wechseln, sollte der Mini-Controller-Status mit dem Medienwiedergabestatus des Empfängers synchron bleiben.

8. Einleitendes Overlay

Gemäß der Checkliste für das Google Cast-Design muss eine Absender-App bestehenden Nutzern das Cast-Symbol vorstellen, um sie darüber zu informieren, dass die Sender-App jetzt das Streamen unterstützt und auch Neulingen von Google Cast hilft.

Abbildung eines iPhones, auf der die CastVideos App mit dem Cast-Symbol-Overlay ausgeführt wird, das das Cast-Symbol markiert und die Meldung „Durch Tippen auf den Fernseher und die Lautsprecher streamen“ erscheint

Die Klasse GCKCastContext hat die Methode presentCastInstructionsViewControllerOnce, mit der das Cast-Symbol hervorgehoben werden kann, wenn es Nutzern zum ersten Mal angezeigt wird. Fügen Sie MediaViewController.swift und MediaTableViewController.swift den folgenden Code hinzu:

override func viewDidLoad() {
  ...

  NotificationCenter.default.addObserver(self, selector: #selector(castDeviceDidChange),
                                         name: NSNotification.Name.gckCastStateDidChange,
                                         object: GCKCastContext.sharedInstance())
}

@objc func castDeviceDidChange(_: Notification) {
  if GCKCastContext.sharedInstance().castState != .noDevicesAvailable {
    // You can present the instructions on how to use Google Cast on
    // the first time the user uses you app
    GCKCastContext.sharedInstance().presentCastInstructionsViewControllerOnce(with: castButton)
  }
}

Wenn Sie die App auf Ihrem Mobilgerät ausführen, sollte das Einleitungs-Overlay zu sehen sein.

9. Maximierter Controller

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

Abbildung eines iPhones, auf denen die CastVideos App ausgeführt wird und ein Video abgespielt wird, wobei der maximierte Controller unten angezeigt wird

Der erweiterte Controller bietet eine Vollbildansicht, mit der Sie die Remote-Medienwiedergabe steuern können. In dieser Ansicht sollte eine Streaming-App alle überschaubaren Aspekte einer Streamingsitzung verwalten können, mit Ausnahme der Lautstärkeregelung des Empfängers und des Sitzungslebenszyklus (Streaming verbinden/beenden). Sie liefert außerdem alle Statusinformationen zur Mediensitzung (Artwork, Titel, Untertitel usw.).

Die Funktionen dieser Ansicht werden von der Klasse GCKUIExpandedMediaControlsViewController implementiert.

Zuerst musst du den standardmäßigen erweiterten Controller im Streaming-Kontext aktivieren. Ändern Sie AppDelegate.swift, um den standardmäßigen maximierten Controller zu aktivieren:

import GoogleCast

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  ...

  func application(_: UIApplication,
                   didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    ...
    // Add after the setShareInstanceWith(options) is set.
    GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true
    ...
  }
  ...
}

Fügen Sie MediaViewController.swift den folgenden Code hinzu, um den erweiterten Controller zu laden, wenn der Nutzer mit dem Streamen eines Videos beginnt:

@objc func playSelectedItemRemotely() {
  ...
  appDelegate?.isCastControlBarsEnabled = false
  GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()
}

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

Starten Sie die App und streamen Sie ein Video. Der maximierte Controller sollte angezeigt werden. Gehe zurück zur Liste der Videos. Wenn du auf den Mini-Controller klickst, wird der maximierte Controller wieder geladen.

10. Cast Connect-Unterstützung hinzufügen

Über die Cast Connect-Bibliothek können bestehende Absender-Apps über das Cast-Protokoll mit Android TV-Apps kommunizieren. Cast Connect baut auf der Cast-Infrastruktur auf, wobei deine Android TV-App als Receiver fungiert.

Abhängigkeiten

Achte in deinem Podfile darauf, dass google-cast-sdk auf 4.4.8 oder höher zeigt, wie unten aufgeführt. Wenn Sie eine Änderung an der Datei vorgenommen haben, führen Sie pod update über die Console aus, um die Änderung mit Ihrem Projekt zu synchronisieren.

pod 'google-cast-sdk', '>=4.4.8'

GCKLaunchOptions

Zum Starten der Android TV App, die auch als Android-Receiver bezeichnet wird, müssen wir das androidReceiverCompatible-Flag im GCKLaunchOptions-Objekt auf „true“ setzen. Dieses GCKLaunchOptions-Objekt gibt vor, wie der Empfänger gestartet wird, und wird an den GCKCastOptions übergeben, der in der gemeinsam genutzten Instanz mit GCKCastContext.setSharedInstanceWith festgelegt wird.

Fügen Sie Ihrem AppDelegate.swift die folgenden Zeilen hinzu:

let options = GCKCastOptions(discoveryCriteria:
                          GCKDiscoveryCriteria(applicationID: kReceiverAppID))
...
/** Following code enables CastConnect */
let launchOptions = GCKLaunchOptions()
launchOptions.androidReceiverCompatible = true
options.launchOptions = launchOptions

GCKCastContext.setSharedInstanceWith(options)

Anmeldedaten für den Start festlegen

Auf der Absenderseite können Sie GCKCredentialsData angeben, um anzugeben, wer an der Sitzung teilnimmt. credentials ist ein String, der vom Nutzer definiert werden kann, solange er von deiner ATV-App verstanden wird. Die GCKCredentialsData wird nur während des Starts oder der Teilnahme an deiner Android TV App übergeben. Wenn Sie die Einstellung neu festlegen, während eine Verbindung besteht, wird sie nicht an Ihre Android TV App übergeben.

Zum Festlegen von Startanmeldedaten muss GCKCredentialsData nach dem Festlegen von GCKLaunchOptions jederzeit definiert werden. Zur Veranschaulichung fügen wir der Schaltfläche Creds eine Logik hinzu, mit der die Anmeldedaten festgelegt werden, die beim Aufbau der Sitzung übergeben werden. Fügen Sie Ihrem MediaTableViewController.swift den folgenden Code hinzu:

class MediaTableViewController: UITableViewController, GCKSessionManagerListener, MediaListModelDelegate, GCKRequestDelegate {
  ...
  private var credentials: String? = nil
  ...
  override func viewDidLoad() {
    ...
    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Creds", style: .plain,
                                                       target: self, action: #selector(toggleLaunchCreds))
    ...
    setLaunchCreds()
  }
  ...
  @objc func toggleLaunchCreds(_: Any){
    if (credentials == nil) {
        credentials = "{\"userId\":\"id123\"}"
    } else {
        credentials = nil
    }
    Toast.displayMessage("Launch Credentials: "+(credentials ?? "Null"), for: 3, in: appDelegate?.window)
    print("Credentials set: "+(credentials ?? "Null"))
    setLaunchCreds()
  }
  ...
  func setLaunchCreds() {
    GCKCastContext.sharedInstance()
        .setLaunch(GCKCredentialsData(credentials: credentials))
  }
}

Anmeldedaten bei Ladeanfrage festlegen

Um credentials sowohl in deiner Web-App als auch in deiner Android TV-Receiver-App zu verarbeiten, füge in der Klasse MediaTableViewController.swift unter der Funktion loadSelectedItem den folgenden Code hinzu:

let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
...
mediaLoadRequestDataBuilder.credentials = credentials
...

Abhängig von der Empfänger-App, an die Ihr Absender etwas überträgt, wendet das SDK die oben genannten Anmeldedaten automatisch auf die laufende Sitzung an.

Cast Connect testen

Schritte zur Installation des Android TV APK auf Chromecast mit Google TV

  1. Ermitteln Sie die IP-Adresse Ihres Android TV-Geräts. Normalerweise befindet sich die Option unter Einstellungen > Netzwerk und Internet > (Netzwerkname, mit dem Ihr Gerät verbunden ist). Auf der rechten Seite werden die Details und die IP-Adresse Ihres Geräts im Netzwerk angezeigt.
  2. Verwenden Sie die IP-Adresse des Geräts, um über ADB mit dem Terminal eine Verbindung zu ihm herzustellen:
$ adb connect <device_ip_address>:5555
  1. Gehen Sie im Terminalfenster zum Ordner der obersten Ebene für die Codelab-Beispiele, die Sie zu Beginn dieses Codelabs heruntergeladen haben. Beispiel:
$ cd Desktop/ios_codelab_src
  1. Installiere die APK-Datei aus diesem Ordner auf deinem Android TV mit folgendem Befehl:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Sie sollten jetzt auf Ihrem Android TV-Gerät im Menü Meine Apps eine App mit dem Namen Videos streamen finden.
  2. Anschließend können Sie die App in einem Emulator oder auf einem Mobilgerät erstellen und ausführen. Wenn du eine Übertragungssitzung mit deinem Android TV-Gerät startest, sollte jetzt die Android-Empfängeranwendung auf deinem Android TV-Gerät gestartet werden. Wenn du ein Video von deinem mobilen iOS-Sender abspielst, sollte das Video im Android Receiver gestartet werden. Außerdem kannst du die Wiedergabe über die Fernbedienung deines Android TV-Geräts steuern.

11. Cast-Widgets anpassen

Initialisierung

Beginnen Sie mit dem Ordner „App-Done“. Fügen Sie der Methode applicationDidFinishLaunchingWithOptions in der Datei AppDelegate.swift Folgendes hinzu:

func application(_: UIApplication,
                 didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  ...
  let styler = GCKUIStyle.sharedInstance()
  ...
}

Nachdem Sie wie im Rest dieses Codelabs eine oder mehrere Anpassungen angewendet haben, führen Sie den Commit der Stile durch Aufrufen des folgenden Codes durch.

styler.apply()

Cast-Ansichten anpassen

Sie können alle Ansichten anpassen, die vom Cast Application Framework verwaltet werden, indem Sie für alle Ansichten Standardstilrichtlinien verwenden. Ändern wir als Beispiel die Farbe der Symbolfarbe.

styler.castViews.iconTintColor = .lightGray

Bei Bedarf können Sie die Standardeinstellungen für jeden Bildschirm überschreiben. Sie können beispielsweise die Farbe lightGrayColor für die Symbolfärbung nur für den maximierten Medien-Controller überschreiben.

styler.castViews.mediaControl.expandedController.iconTintColor = .green

Farben ändern

Sie können die Hintergrundfarbe für alle Ansichten oder für jede Ansicht einzeln anpassen. Mit dem folgenden Code wird die Hintergrundfarbe für alle von Cast Application Framework bereitgestellten Ansichten auf Blau gesetzt.

styler.castViews.backgroundColor = .blue
styler.castViews.mediaControl.miniController.backgroundColor = .yellow

Schriftarten ändern

Sie können Schriftarten für verschiedene Labels anpassen, die in Cast-Ansichten zu sehen sind. Wir wählen für alle Schriftarten „Courier-Oblique“ aus. zu Illustrationszwecken.

styler.castViews.headingTextFont = UIFont.init(name: "Courier-Oblique", size: 16) ?? UIFont.systemFont(ofSize: 16)
styler.castViews.mediaControl.headingTextFont = UIFont.init(name: "Courier-Oblique", size: 6) ?? UIFont.systemFont(ofSize: 6)

Ändern der Standardschaltflächenbilder

Füge dem Projekt deine eigenen benutzerdefinierten Bilder hinzu und weise sie deinen Schaltflächen zu, um sie zu gestalten.

let muteOnImage = UIImage.init(named: "yourImage.png")
if let muteOnImage = muteOnImage {
  styler.castViews.muteOnImage = muteOnImage
}

Design der Schaltfläche „Streamen“ ändern

Sie können Cast-Widgets auch mit dem UIAppearance-Protokoll einrichten. Mit dem folgenden Code wird das GCKUICastButton in allen angezeigten Ansichten gestaltet:

GCKUICastButton.appearance().tintColor = UIColor.gray

12. Glückwunsch

Jetzt wissen Sie, wie Sie unter iOS mithilfe der Cast SDK-Widgets eine Video-App für Google Cast aktivieren.

Weitere Informationen finden Sie im Entwicklerleitfaden für iOS-Absender.