1. סקירה כללית

ב-Codelab הזה תלמדו איך לשנות אפליקציית וידאו קיימת ל-iOS כדי להפעיל Cast של תוכן במכשיר תומך ב-Google Cast.
מה זה Google Cast?
עם Google Cast, משתמשים יכולים להפעיל Cast של תוכן מנייד לטלוויזיה. לאחר מכן, המשתמשים יכולים להשתמש בנייד כשלט רחוק להפעלת מדיה בטלוויזיה.
ערכת ה-SDK של Google Cast מאפשרת להרחיב את האפליקציה כדי לשלוט במכשירים שתומכים ב-Google Cast (כמו טלוויזיה או מערכת סאונד). Cast SDK מאפשר לכם להוסיף את רכיבי ממשק המשתמש הנדרשים על סמך רשימת המשימות לעיצוב של Google Cast.
הכנו רשימת משימות לעיצוב Google Cast כדי שחוויית המשתמש תהיה פשוטה וצפויה בכל הפלטפורמות הנתמכות.
מה אנחנו הולכים לבנות?
בסיום ה-codelab הזה, תהיה לכם אפליקציית וידאו ל-iOS שתאפשר לכם להפעיל Cast של סרטונים למכשיר Google Cast.
מה תלמדו
- איך מוסיפים את Google Cast SDK לאפליקציית וידאו לדוגמה.
- איך מוסיפים את הכפתור להפעלת Cast כדי לבחור מכשיר Cast.
- איך מתחברים למכשיר Cast ומפעילים מקלט מדיה
- איך מפעילים Cast של סרטון.
- איך מוסיפים לאפליקציה את המיני-בקר של Cast.
- איך מוסיפים בקר מורחב
- איך מספקים שכבת-על של מבצע היכרות.
- איך מתאימים אישית את הווידג'טים של Cast.
- איך משלבים את Cast Connect
הדרישות
- הגרסה העדכנית של Xcode.
- מכשיר נייד אחד עם iOS 9 ואילך (או Xcode Simulator).
- כבל USB להעברת נתונים כדי לחבר את המכשיר הנייד למחשב הפיתוח (אם משתמשים במכשיר).
- מכשיר Cast, כמו Chromecast או Android TV, שמחובר לאינטרנט.
- טלוויזיה או צג עם כניסת HDMI.
- כדי לבדוק את השילוב של Cast Connect, צריך Chromecast with Google TV, אבל הוא לא נדרש לשאר חלקי ה-Codelab. אם אין לכם כזה, אתם יכולים לדלג על השלב הוספת תמיכה ב-Cast Connect, לקראת סוף המדריך הזה.
חוויה
- צריך ידע קודם בפיתוח ל-iOS.
- תצטרכו גם ידע קודם בצפייה בטלוויזיה :)
איך תשתמשו במדריך הזה?
איך היית מדרג את חוויית השימוש שלך בפיתוח אפליקציות ל-iOS?
איך היית מדרג את חוויית הצפייה בטלוויזיה?
2. קבלת קוד לדוגמה
אפשר להוריד את כל הקוד לדוגמה למחשב...
ומחלצים את קובץ ה-ZIP שהורד.
3. הרצת האפליקציה לדוגמה

קודם נראה איך נראית אפליקציית הדוגמה המלאה. האפליקציה היא נגן וידאו בסיסי. המשתמש יכול לבחור סרטון מתוך רשימה ואז להפעיל אותו באופן מקומי במכשיר או להפעיל אותו באמצעות Cast במכשיר Google Cast.
אחרי שמורידים את הקוד, ההוראות הבאות מתארות איך לפתוח ולהריץ את האפליקציה לדוגמה המלאה ב-Xcode:
שאלות נפוצות
הגדרת CocoaPods
כדי להגדיר את CocoaPods, עוברים למסוף ומתקינים באמצעות Ruby שזמין כברירת מחדל ב-macOS:
sudo gem install cocoapods
אם נתקלתם בבעיות, כדאי לעיין בתיעוד הרשמי כדי להוריד ולהתקין את הכלי לניהול תלות.
הגדרת הפרויקט
- עוברים למסוף ומנווטים אל ספריית ה-codelab.
- מתקינים את יחסי התלות מ-Podfile.
cd app-done pod update pod install
- פותחים את Xcode ובוחרים באפשרות Open another project... (פתיחת פרויקט אחר).
- בוחרים את הקובץ
CastVideos-ios.xcworkspaceמהספרייה
app-doneבתיקיית קוד הדוגמה.
הפעלת האפליקציה
בוחרים את היעד ואת הסימולטור, ואז מריצים את האפליקציה:

אחרי כמה שניות, אפליקציית הווידאו אמורה להופיע.
חשוב ללחוץ על 'אישור' כשמופיעה ההתראה לגבי אישור חיבורים נכנסים לרשת. אם לא תאשרו את האפשרות הזו, סמל ה-Cast לא יופיע.

לוחצים על הכפתור להפעלת Cast ובוחרים את מכשיר Cast.
בוחרים סרטון ולוחצים על לחצן ההפעלה.
הסרטון יתחיל לפעול במכשיר Cast.
השלט המורחב יוצג. אפשר להשתמש בלחצן ההפעלה או ההשהיה כדי לשלוט בהפעלה.
חוזרים לרשימת הסרטונים.
בשלב הזה יופיע בקר קטן בתחתית המסך.

לוחצים על לחצן ההשהיה במיני-בקר כדי להשהות את הסרטון במכשיר המקלט. כדי להמשיך להפעיל את הסרטון, לוחצים על לחצן ההפעלה במיני-בקר.
לוחצים על הכפתור להפעלת Cast כדי להפסיק את ההעברה למכשיר Cast של Google.
4. הכנת פרויקט ההתחלה

אנחנו צריכים להוסיף תמיכה ב-Google Cast לאפליקציית ההפעלה שהורדת. הנה כמה מונחים שקשורים ל-Google Cast שבהם נשתמש ב-codelab הזה:
- אפליקציית השולח פועלת במכשיר נייד או במחשב נייד,
- אפליקציית מקלט פועלת במכשיר Cast.
הגדרת הפרויקט
עכשיו אפשר להתחיל לבנות על בסיס פרויקט המתחילים באמצעות Xcode:
- עוברים למסוף ומנווטים אל ספריית ה-codelab.
- מתקינים את יחסי התלות מ-Podfile.
cd app-start pod update pod install
- פותחים את Xcode ובוחרים באפשרות Open another project... (פתיחת פרויקט אחר).
- בוחרים את הקובץ
CastVideos-ios.xcworkspaceמהספרייה
app-startבתיקיית קוד הדוגמה.
עיצוב אפליקציות
האפליקציה מאחזרת רשימה של סרטונים משרת אינטרנט מרוחק ומספקת רשימה למשתמש כדי לעיין בה. המשתמשים יכולים לבחור סרטון כדי לראות את הפרטים שלו או להפעיל אותו באופן מקומי במכשיר הנייד.
האפליקציה מורכבת משני בקרי תצוגה ראשיים: MediaTableViewController ו-MediaViewController.
MediaTableViewController
ב-UITableViewController הזה מוצגת רשימה של סרטונים ממופע MediaListModel. רשימת הסרטונים והמטא-נתונים המשויכים שלהם מאוחסנים בשרת מרוחק כקובץ JSON. MediaListModel מאחזר את ה-JSON הזה ומעבד אותו כדי ליצור רשימה של אובייקטים מסוג MediaItem.
MediaItem אובייקט מדגים סרטון ואת המטא-נתונים שמשויכים אליו, כמו השם, התיאור, כתובת ה-URL של התמונה וכתובת ה-URL של הסטרימינג.
MediaTableViewController יוצר מופע של MediaListModel ואז נרשם כ-MediaListModelDelegate כדי לקבל הודעה כשמורידים את המטא נתונים של המדיה, וכך הוא יכול לטעון את תצוגת הטבלה.
למשתמש מוצגת רשימה של תמונות ממוזערות של סרטונים עם תיאור קצר לכל סרטון. כשבוחרים פריט, הערך המתאים של MediaItem מועבר אל MediaViewController.
MediaViewController
בבקר התצוגה הזה מוצגים המטא-נתונים של סרטון מסוים, והמשתמש יכול להפעיל את הסרטון באופן מקומי במכשיר הנייד.
בבקר התצוגה יש LocalPlayerView, ממשק השליטה במדיה ואזור טקסט להצגת התיאור של הסרטון שנבחר. הנגן מכסה את החלק העליון של המסך, ומשאיר מקום לתיאור המפורט של הסרטון מתחתיו. המשתמש יכול להפעיל או להשהות את הסרטון המקומי או להעביר קדימה או אחורה.
שאלות נפוצות
5. הוספת הכפתור להפעלת Cast

באפליקציה שתומכת ב-Cast, הכפתור להפעלת Cast מוצג בכל בקר התצוגה שלה. כשלוחצים על הכפתור להפעלת Cast, מוצגת רשימה של מכשירי Cast שהמשתמש יכול לבחור מתוכה. אם המשתמש הפעיל תוכן באופן מקומי במכשיר השולח, בחירה במכשיר Cast תתחיל או תמשיך את ההפעלה במכשיר ה-Cast. במהלך סשן Cast, המשתמש יכול ללחוץ על הכפתור להפעלת Cast ולהפסיק את ההעברה של האפליקציה למכשיר Cast. המשתמש צריך להיות מסוגל להתחבר למכשיר Cast או להתנתק ממנו מכל מסך באפליקציה, כמו שמתואר ברשימת המשימות לעיצוב של Google Cast.
הגדרות אישיות
פרויקט ההתחלה דורש את אותן תלות והגדרות של Xcode כמו באפליקציית הדוגמה המלאה. חוזרים לקטע הזה ופועלים לפי אותם השלבים כדי להוסיף את GoogleCast.framework לפרויקט של אפליקציית ההתחלה.
אתחול
ל-Cast Framework יש אובייקט singleton גלובלי, GCKCastContext, שמתאם את כל הפעילויות של ה-Framework. צריך לאתחל את האובייקט הזה בשלב מוקדם במחזור החיים של האפליקציה, בדרך כלל בשיטה application(_:didFinishLaunchingWithOptions:) של נציג האפליקציה, כדי שהפעלה מחדש אוטומטית של הסשן בהפעלה מחדש של אפליקציית השולח תופעל בצורה תקינה וסריקת המכשירים תוכל להתחיל.
כשמאתחלים את GCKCastContext, צריך לספק אובייקט GCKCastOptions. הכיתה הזו מכילה אפשרויות שמשפיעות על ההתנהגות של המסגרת. הכי חשוב מביניהם הוא מזהה אפליקציית המקלט, שמשמש לסינון תוצאות החיפוש של מכשירי Cast ולהפעלת אפליקציית המקלט כשמתחילים סשן Cast.
שיטת application(_:didFinishLaunchingWithOptions:) היא גם מקום טוב להגדיר נציג רישום ביומן כדי לקבל את הודעות הרישום ביומן ממסגרת Cast. הם יכולים להיות שימושיים לניפוי באגים ולפתרון בעיות.
כשמפתחים אפליקציה משלכם תומכת ב-Cast, צריך להירשם כמפתחי Cast ולקבל מזהה אפליקציה לאפליקציה. בסדנת התכנות הזו נשתמש במזהה אפליקציה לדוגמה.
מוסיפים את הקוד הבא ל-AppDelegate.swift כדי להפעיל את GCKCastContext עם מזהה האפליקציה מהגדרות ברירת המחדל של המשתמש, ומוסיפים כלי לרישום ביומן עבור Google Cast Framework:
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
עכשיו, אחרי שהרכיב GCKCastContext אותחל, צריך להוסיף את הכפתור להפעלת Cast כדי לאפשר למשתמש לבחור מכשיר Cast. Cast SDK מספק רכיב של הכפתור להפעלת Cast שנקרא GCKUICastButton בתור מחלקת משנה של UIButton. אפשר להוסיף אותו לסרגל הכותרת של האפליקציה על ידי הוספתו בין התגים UIBarButtonItem. צריך להוסיף את הכפתור להפעלת Cast גם ל-MediaTableViewController וגם ל-MediaViewController.
מוסיפים את הקוד הבא לקובץ MediaTableViewController.swift ולקובץ MediaViewController.swift:
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)
...
}
...
}
לאחר מכן, מוסיפים את הקוד הבא ל-MediaViewController.swift:
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)
...
}
...
}
עכשיו מפעילים את האפליקציה. אמור להופיע הכפתור להפעלת Cast בסרגל הניווט של האפליקציה. כשלוחצים עליו, מוצגת רשימה של מכשירי Cast ברשת המקומית. גילוי המכשירים מנוהל באופן אוטומטי על ידי GCKCastContext. בוחרים את מכשיר ה-Cast, ואפליקציית המקלט לדוגמה תיטען במכשיר ה-Cast. אתם יכולים לעבור בין פעילות הגלישה לבין פעילות הנגן המקומי, והמצב של הכפתור להפעלת Cast נשאר מסונכרן.
עדיין לא הוספנו תמיכה בהפעלת מדיה, כך שאי אפשר להפעיל סרטונים במכשיר Cast. לוחצים על הכפתור להפעלת Cast כדי להפסיק את ההעברה.
6. העברה (cast) של תוכן סרטון

נרחיב את אפליקציית הדוגמה כך שתכלול גם הפעלת סרטונים מרחוק במכשיר Cast. כדי לעשות את זה, אנחנו צריכים להאזין לאירועים השונים שנוצרים על ידי Cast Framework.
הפעלת Cast של מדיה
באופן כללי, כדי להפעיל מדיה במכשיר Cast, צריך לבצע את הפעולות הבאות:
- יוצרים אובייקט
GCKMediaInformationמ-Cast SDK שמדמה פריט מדיה. - המשתמש מתחבר למכשיר Cast כדי להפעיל את אפליקציית המקלט.
- טוענים את אובייקט
GCKMediaInformationבמכשיר המקבל ומפעילים את התוכן. - מעקב אחרי סטטוס המדיה.
- שליחת פקודות הפעלה למקבל על סמך אינטראקציות של משתמשים.
בשלב 1 ממפים אובייקט אחד לאובייקט אחר. GCKMediaInformation הוא אובייקט שמערכת Cast SDK מבינה, ו-MediaItem הוא האובייקט שבו האפליקציה שלנו מכילה פריט מדיה. אפשר למפות בקלות MediaItem ל-GCKMediaInformation. כבר ביצענו את שלב 2 בקטע הקודם. קל לבצע את שלב 3 באמצעות Cast SDK.
אפליקציית הדוגמה MediaViewController כבר מבחינה בין הפעלה מקומית להפעלה מרחוק באמצעות ה-enum הזה:
enum PlaybackMode: Int {
case none = 0
case local
case remote
}
private var playbackMode = PlaybackMode.none
ב-Codelab הזה לא חשוב שתבינו בדיוק איך פועלת כל הלוגיקה של השחקן לדוגמה. חשוב להבין שצריך לשנות את נגן המדיה באפליקציה כדי שיהיה מודע לשני מיקומי ההפעלה באופן דומה.
בשלב הזה, הנגן המקומי תמיד נמצא במצב הפעלה מקומי, כי הוא עדיין לא יודע דבר על מצבי ההעברה. אנחנו צריכים לעדכן את ממשק המשתמש על סמך מעברי מצב שמתרחשים ב-Cast Framework. לדוגמה, אם מתחילים להפעיל Cast, צריך להפסיק את ההפעלה המקומית ולהשבית חלק מהאמצעים לשליטה בהפעלה. באופן דומה, אם נפסיק את ההפעלה ל-Chromecast כשאנחנו בבקר התצוגה הזה, נצטרך לעבור להפעלה מקומית. כדי לטפל בזה, צריך להאזין לאירועים השונים שנוצרים על ידי Cast Framework.
ניהול סשנים של Cast
במסגרת Cast, סשן Cast משלב את השלבים של התחברות למכשיר, הפעלה (או הצטרפות), התחברות לאפליקציית מקלט ואתחול של ערוץ בקרת מדיה, אם רלוונטי. ערוץ בקרת המדיה הוא האופן שבו מסגרת Cast שולחת ומקבלת הודעות מנגן המדיה של המקלט.
הפעלת Cast תתחיל באופן אוטומטי כשהמשתמש יבחר מכשיר מהכפתור להפעלת Cast, ותיפסק באופן אוטומטי כשהמשתמש ינתק את החיבור. גם חיבור מחדש לסשן של מקלט בגלל בעיות ברשת מתבצע אוטומטית על ידי Cast Framework.
הפעילות של Cast מנוהלת על ידי GCKSessionManager, שאפשר לגשת אליה דרך GCKCastContext.sharedInstance().sessionManager. אפשר להשתמש בGCKSessionManagerListener callbacks כדי לעקוב אחרי אירועים בסשן, כמו יצירה, השעיה, חידוש וסיום.
קודם צריך לרשום את מאזין הסשנים שלנו ולאתחל כמה משתנים:
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()
}
...
}
ב-MediaViewController, אנחנו רוצים לקבל הודעה כשמתחברים למכשיר Cast או מתנתקים ממנו, כדי שנוכל לעבור לנגן המקומי או ממנו. חשוב לזכור שהקישוריות יכולה להיפסק לא רק בגלל המופע של האפליקציה שפועל במכשיר הנייד, אלא גם בגלל מופע אחר של האפליקציה שלכם (או של אפליקציה אחרת) שפועל במכשיר נייד אחר.
אפשר לגשת לסשן הפעיל הנוכחי בתור GCKCastContext.sharedInstance().sessionManager.currentCastSession. הסשנים נוצרים ומוסרים באופן אוטומטי בתגובה לתנועות של המשתמשים בתיבות הדו-שיח של Cast.
המדיה בטעינה
ב-Cast SDK, GCKRemoteMediaClient מספק קבוצה של ממשקי API נוחים לניהול הפעלת מדיה מרחוק במקבל. במקרה של GCKCastSession שתומך בהפעלת מדיה, המערכת תיצור באופן אוטומטי מופע של GCKRemoteMediaClient באמצעות ה-SDK. אפשר לגשת אליו כמאפיין remoteMediaClient של מופע GCKCastSession.
מוסיפים את הקוד הבא ל-MediaViewController.swift כדי לטעון את הסרטון שנבחר כרגע במכשיר המקבל:
@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
}
}
}
...
}
עכשיו צריך לעדכן שיטות קיימות שונות כדי להשתמש בלוגיקה של Cast Session לתמיכה בהפעלה מרחוק:
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
}
עכשיו מריצים את האפליקציה במכשיר הנייד. מתחברים למכשיר Cast ומתחילים להפעיל סרטון. הסרטון אמור להתחיל לפעול במכשיר המקבל.
7. בקר קטן
בצ'קליסט של עיצוב Cast נדרש שכל אפליקציות Cast יציגו בקר מיני כשהמשתמש עובר מדף התוכן הנוכחי. השלט הקטן מספק גישה מיידית ותזכורת גלויה להפעלה הנוכחית של Cast.

Cast SDK מספק סרגל בקרה, GCKUIMiniMediaControlsViewController, שאפשר להוסיף לסצנות שבהן רוצים להציג את אמצעי הבקרה הקבועים.
באפליקציה לדוגמה, נשתמש ב-GCKUICastContainerViewController שעוטף בקר תצוגה אחר ומוסיף GCKUIMiniMediaControlsViewController בתחתית.
משנים את קובץ AppDelegate.swift ומוסיפים את הקוד הבא לתנאי if useCastContainerViewController בשיטה הבאה:
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()
...
}
מוסיפים את המאפיין הזה ואת הפונקציות setter/getter כדי לשלוט בנראות של אמצעי הבקרה המינימלי (נשתמש בהם בקטע מאוחר יותר):
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
}
}
}
מפעילים את האפליקציה ומפעילים Cast לסרטון. כשההפעלה מתחילה במקלט, אמור להופיע מיני-בקר בחלק התחתון של כל סצנה. אפשר לשלוט בהפעלה מרחוק באמצעות המיני-בקר. אם עוברים בין פעילות הגלישה לבין פעילות הנגן המקומי, הסטטוס של המיני-בקר צריך להישאר מסונכרן עם סטטוס הפעלת המדיה במכשיר המקבל.
8. שכבת-על של מבצע היכרות
בצ'קליסט של עיצוב Google Cast נדרש מאפליקציות ששולחות תוכן להעברה להציג את הכפתור להפעלת Cast למשתמשים קיימים כדי ליידע אותם שהאפליקציה תומכת עכשיו בהעברה, וגם כדי לעזור למשתמשים חדשים ב-Google Cast.

למחלקת GCKCastContext יש שיטה, presentCastInstructionsViewControllerOnce, שאפשר להשתמש בה כדי להדגיש את הכפתור להפעלת Cast כשהוא מוצג למשתמשים בפעם הראשונה. מוסיפים את הקוד הבא לקובץ MediaViewController.swift ולקובץ MediaTableViewController.swift:
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)
}
}
מריצים את האפליקציה במכשיר הנייד, ואמור להופיע מסך הפתיחה.
9. שלט מורחב
במסגרת רשימת המשימות לעיצוב של Google Cast, אפליקציית השולח צריכה לספק בקר מורחב למדיה שמועברת ב-Cast. הבקר המורחב הוא גרסה של הבקר המוקטן במסך מלא.

השלט המורחב הוא תצוגה במסך מלא שמאפשרת שליטה מלאה בהפעלת המדיה מרחוק. בתצוגה הזו, אפליקציה להפעלת Cast אמורה לנהל כל היבט שניתן לניהול של סשן Cast, למעט בקרת עוצמת הקול של המכשיר המקבל ומחזור החיים של הסשן (חיבור/הפסקת הפעלת Cast). היא גם מספקת את כל פרטי הסטטוס של סשן המדיה (יצירת אומנות, שם, כתוביות וכו').
הפונקציונליות של התצוגה הזו מיושמת על ידי המחלקה GCKUIExpandedMediaControlsViewController.
הדבר הראשון שצריך לעשות הוא להפעיל את בקר ההפעלה המורחב שמוגדר כברירת מחדל בהקשר של Cast. משנים את AppDelegate.swift כדי להפעיל את בקר ברירת המחדל המורחב:
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
...
}
...
}
מוסיפים את הקוד הבא ל-MediaViewController.swift כדי לטעון את אמצעי הבקרה המורחב כשהמשתמש מתחיל להפעיל Cast לסרטון:
@objc func playSelectedItemRemotely() {
...
appDelegate?.isCastControlBarsEnabled = false
GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()
}
השלט המורחב יופעל אוטומטית גם כשהמשתמש יקיש על השלט המיני.
מפעילים את האפליקציה ומפעילים Cast לסרטון. אמור להופיע בקר מורחב. חוזרים לרשימת הסרטונים, וכשלוחצים על המיני-בקר, הבקר המורחב נטען שוב.
10. הוספת תמיכה ב-Cast Connect
ספריית Cast Connect מאפשרת לאפליקציות שולחות קיימות לתקשר עם אפליקציות Android TV באמצעות פרוטוקול Cast. Cast Connect מבוסס על תשתית Cast, ואפליקציה ל-Android TV פועלת כמקלט.
תלויות
ב-Podfile, מוודאים שהערך של google-cast-sdk הוא 4.4.8 ומעלה, כפי שמפורט בהמשך. אם ביצעתם שינוי בקובץ, מריצים את הפקודה pod update מהמסוף כדי לסנכרן את השינוי עם הפרויקט.
pod 'google-cast-sdk', '>=4.4.8'
GCKLaunchOptions
כדי להפעיל את אפליקציית Android TV, שנקראת גם Android Receiver, צריך להגדיר את הדגל androidReceiverCompatible כ-true באובייקט GCKLaunchOptions. אובייקט GCKLaunchOptions הזה קובע איך להפעיל את המקלט, והוא מועבר אל GCKCastOptions שמוגדרים במופע המשותף באמצעות GCKCastContext.setSharedInstanceWith.
מוסיפים את השורות הבאות לקובץ AppDelegate.swift:
let options = GCKCastOptions(discoveryCriteria:
GCKDiscoveryCriteria(applicationID: kReceiverAppID))
...
/** Following code enables CastConnect */
let launchOptions = GCKLaunchOptions()
launchOptions.androidReceiverCompatible = true
options.launchOptions = launchOptions
GCKCastContext.setSharedInstanceWith(options)
הגדרת פרטי כניסה להפעלה
בצד השולח, אפשר לציין GCKCredentialsData כדי לייצג את מי שמצטרף להפעלה. credentials היא מחרוזת שאפשר להגדיר אותה, כל עוד אפליקציית ATV יכולה להבין אותה. הפרמטר GCKCredentialsData מועבר לאפליקציה ל-Android TV רק בזמן ההפעלה או ההצטרפות. אם תגדירו אותו מחדש בזמן שאתם מחוברים, הוא לא יועבר לאפליקציה ל-Android TV.
כדי להגדיר את פרטי ההפעלה, צריך להגדיר את GCKCredentialsData בכל שלב אחרי הגדרת GCKLaunchOptions. כדי להמחיש את זה, נוסיף לוגיקה לכפתור Creds כדי להגדיר את פרטי הכניסה שיועברו כשיוקם הסשן. מוסיפים את הקוד הבא לקובץ MediaTableViewController.swift:
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))
}
}
הגדרת פרטי כניסה בבקשת טעינה
כדי לטפל ב-credentials באפליקציות של מקלט האינטרנט ושל Android TV, מוסיפים את הקוד הבא למחלקה MediaTableViewController.swift בפונקציה loadSelectedItem:
let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
...
mediaLoadRequestDataBuilder.credentials = credentials
...
בהתאם לאפליקציית המקלט שאליה השולח מעביר תוכן, ה-SDK יחיל באופן אוטומטי את פרטי הכניסה שלמעלה על ההפעלה המתמשכת.
בדיקת Cast Connect
שלבים להתקנת קובץ APK של Android TV ב-Chromecast with Google TV
- מאתרים את כתובת ה-IP של מכשיר Android TV. בדרך כלל, הוא מופיע בקטע הגדרות > רשת ואינטרנט > (שם הרשת שאליה המכשיר מחובר). בצד שמאל יופיעו הפרטים וכתובת ה-IP של המכשיר ברשת.
- משתמשים בכתובת ה-IP של המכשיר כדי להתחבר אליו באמצעות ADB דרך הטרמינל:
$ adb connect <device_ip_address>:5555
- בחלון הטרמינל, עוברים לתיקייה ברמה העליונה של הדוגמאות של ה-codelab שהורדתם בתחילת ה-codelab הזה. לדוגמה:
$ cd Desktop/ios_codelab_src
- כדי להתקין את קובץ ה-APK בתיקייה הזו ב-Android TV, מריצים את הפקודה:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- עכשיו אמורה להופיע אפליקציה בשם Cast Videos בתפריט Your Apps במכשיר Android TV.
- אחרי שמסיימים, יוצרים את האפליקציה ומריצים אותה באמולטור או במכשיר נייד. כשמקימים סשן Cast עם מכשיר Android TV, אפליקציית Android Receiver אמורה להיפתח ב-Android TV. הפעלת סרטון מהשולח בנייד עם iOS אמורה להפעיל את הסרטון במקלט Android ולאפשר לכם לשלוט בהפעלה באמצעות השלט של מכשיר Android TV.
11. התאמה אישית של ווידג'טים של Cast
אתחול
מתחילים עם התיקייה App-Done. מוסיפים את הקוד הבא לשיטה applicationDidFinishLaunchingWithOptions בקובץ AppDelegate.swift.
func application(_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
let styler = GCKUIStyle.sharedInstance()
...
}
אחרי שמסיימים להחיל התאמה אישית אחת או יותר כמו שמתואר בהמשך ה-codelab הזה, צריך לבצע commit לסגנונות על ידי הפעלת הקוד שלמטה
styler.apply()
התאמה אישית של תצוגות ב-Cast
אפשר להתאים אישית את כל התצוגות שמנוהלות על ידי Cast Application Framework באמצעות הנחיות סגנון שמוגדרות כברירת מחדל בכל התצוגות. לדוגמה, נשנה את גוון הצבע של הסמל.
styler.castViews.iconTintColor = .lightGray
אם צריך, אפשר לשנות את הגדרות ברירת המחדל לכל מסך בנפרד. לדוגמה, כדי לשנות את צבע הגוון של הסמל מ-lightGrayColor רק בממשק השליטה המורחב במדיה.
styler.castViews.mediaControl.expandedController.iconTintColor = .green
שינוי צבעים
אתם יכולים להתאים אישית את צבע הרקע לכל התצוגות (או לכל תצוגה בנפרד). הקוד הבא מגדיר את צבע הרקע לכחול לכל התצוגות שמוגדרות על ידי Cast Application Framework.
styler.castViews.backgroundColor = .blue
styler.castViews.mediaControl.miniController.backgroundColor = .yellow
שינוי גופנים
אתם יכולים להתאים אישית את הגופנים של תוויות שונות שמוצגות בתצוגות של Cast. לצורך המחשה, נגדיר את כל הגופנים ל-Courier-Oblique.
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)
שינוי תמונות ברירת המחדל של הלחצנים
אתם יכולים להוסיף לפרויקט תמונות בהתאמה אישית ולהקצות את התמונות ללחצנים כדי לעצב אותם.
let muteOnImage = UIImage.init(named: "yourImage.png")
if let muteOnImage = muteOnImage {
styler.castViews.muteOnImage = muteOnImage
}
שינוי העיצוב של הכפתור להפעלת Cast
אפשר גם להחיל עיצוב על הווידג'טים של Cast באמצעות פרוטוקול UIAppearance. הקוד הבא מגדיר את העיצוב של GCKUICastButton בכל התצוגות שבהן הוא מופיע:
GCKUICastButton.appearance().tintColor = UIColor.gray
12. מזל טוב
עכשיו אתם יודעים איך להוסיף תמיכה ב-Cast לאפליקציית וידאו באמצעות הווידג'טים של Cast SDK ב-iOS.
פרטים נוספים זמינים במדריך למפתחים בנושא שליחה ב-iOS.