1. סקירה כללית
בשיעור הזה תלמדו איך לשנות אפליקציית וידאו קיימת ב-iOS כדי להפעיל Cast של תוכן במכשיר שתומך ב-Google Cast.
מה זה Google Cast?
Google Cast מאפשר למשתמשים להעביר תוכן מהנייד לטלוויזיה. לאחר מכן, המשתמשים יכולים להשתמש בנייד שלהם כשלט רחוק להפעלת מדיה בטלוויזיה.
באמצעות Google Cast SDK תוכלו להרחיב את האפליקציה שלכם כך שתשלוט במכשירים שתומכים ב-Google Cast (כמו טלוויזיה או מערכת שמע). באמצעות Cast SDK אפשר להוסיף את רכיבי ממשק המשתמש הנדרשים על סמך רשימת המשימות לעיצוב של Google Cast.
רשימת המשימות לעיצוב של Google Cast נועדה להפוך את חוויית המשתמש ב-Cast לפשוטה ולצפויה בכל הפלטפורמות הנתמכות.
מה אנחנו הולכים ליצור?
בסיום ה-Codelab הזה, תהיה לך אפליקציית וידאו ל-iOS, שיוכל להפעיל Cast של סרטונים למכשיר Google Cast.
מה תלמדו
- איך מוסיפים את Google Cast SDK לאפליקציית וידאו לדוגמה.
- איך להוסיף את לחצן הפעלת Cast כדי לבחור מכשיר Google Cast.
- איך מתחברים למכשיר Cast ומפעילים מקלט מדיה.
- איך להפעיל Cast של סרטון.
- איך מוסיפים לאפליקציה שלכם שלט Cast mini.
- איך להוסיף בקר מורחב.
- איך להציג שכבת-על של מבוא.
- איך מתאימים אישית ווידג'טים של Cast.
- איך משלבים את Cast Connect
מה נדרש
- גרסה עדכנית של Xcode.
- מכשיר נייד אחד עם iOS מגרסה 9 ואילך (או סימולטור Xcode).
- כבל נתונים USB שמחברים את המכשיר הנייד למחשב הפיתוח (אם אתם משתמשים במכשיר).
- מכשיר Google 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 ובוחרים את מכשיר ה-Google Cast.
בוחרים סרטון ולוחצים על לחצן ההפעלה.
הסרטון יתחיל לפעול במכשיר Google Cast.
הבקר המורחב יוצג. אפשר להשתמש בלחצן ההפעלה וההשהיה כדי לשלוט בהפעלה.
חוזרים לרשימת הסרטונים.
מיני-בקר מוצג עכשיו בתחתית המסך.
אפשר ללחוץ על לחצן ההשהיה במיני-בקר כדי להשהות את הסרטון במקלט. לוחצים על לחצן ההפעלה בנגן המיני כדי להמשיך את הפעלת הסרטון.
לוחצים על הלחצן להפעלת Cast כדי להפסיק את ההעברה למכשיר Google Cast.
4. הכנת פרויקט ההתחלה
אנחנו צריכים להוסיף תמיכה ב-Google Cast לאפליקציה שהורדת. הנה כמה מונחים של Google Cast שנשתמש בהם ב-Codelab הזה:
- אפליקציית שליחה שפועלת במכשיר נייד או במחשב נייד,
- אפליקציית מקלט פועלת במכשיר Google 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
בקר התצוגה הזה מציג את המטא-נתונים של סרטון מסוים ומאפשר למשתמש להפעיל את הסרטון באופן מקומי במכשיר הנייד.
ב-View Controller מתארחים LocalPlayerView
, כמה פקדי מדיה ואזור טקסט שבו מוצג התיאור של הסרטון שנבחר. הנגן מכסה את החלק העליון של המסך, ומשאיר מקום לתיאור המפורט של הסרטון מתחתיו. המשתמש יכול להפעיל/להשהות או לדלג קדימה/לאחור בהפעלת הסרטון המקומי.
שאלות נפוצות
5. הוספת הלחצן להפעלת Cast
אפליקציה שתומכת ב-Cast מציגה את לחצן הפעלת Cast בכל בקרי תצוגה שלה. לחיצה על לחצן ההעברה מציגה רשימה של מכשירי Cast שהמשתמשים יכולים לבחור מתוכה. אם המשתמש הפעיל תוכן באופן מקומי במכשיר השולח, הבחירה במכשיר Cast מפעילה או ממשיכה את ההפעלה במכשיר ה-Cast הזה. בכל שלב במהלך הפעלת Cast, המשתמש יכול ללחוץ על הלחצן להפעלת Cast ולהפסיק את ההעברה של האפליקציה למכשיר Cast. למשתמש צריכה להיות אפשרות להתחבר למכשיר Cast או להתנתק ממנו בכל מסך באפליקציה, כפי שמתואר ברשימת המשימות לעיצוב של Google Cast.
תצורה
בפרויקט ההתחלה נדרשים אותם יחסי תלות והגדרת Xcode כמו שנדרשים עבור האפליקציה לדוגמה שהושלמה. חוזרים לקטע הזה ומבצעים את אותם שלבים כדי להוסיף את GoogleCast.framework
לפרויקט ההתחלתי של האפליקציה.
אתחול
למסגרת Cast יש אובייקט יחיד (singleton) גלובלי, GCKCastContext
, שמרכז את כל הפעילויות של המסגרת. חובה לאתחל את האובייקט הזה בשלב מוקדם במחזור החיים של האפליקציה, בדרך כלל בשיטה application(_:didFinishLaunchingWithOptions:)
של מקבל הגישה לאפליקציה, כך שהמשך אוטומטי של הסשן באפליקציה של השולח יוכל להתחיל לפעול בצורה תקינה וסריקה לאיתור מכשירים תוכל להתחיל.
כשמאתחלים את GCKCastContext
, צריך לספק אובייקט GCKCastOptions
. הסיווג הזה מכיל אפשרויות שמשפיעות על ההתנהגות של ה-framework. החשוב שבהם הוא מזהה האפליקציה 'המקלט', המשמש לסינון תוצאות הגילוי של מכשירי Cast ולהפעלת אפליקציית המקבל כאשר מתחילים סשן Cast.
השיטה application(_:didFinishLaunchingWithOptions:)
מתאימה גם להגדרת משתמש עם הרשאה לרישום ביומן לקבלת הודעות הרישום ביומן מ-Cast framework. המידע הזה יכול להועיל לניפוי באגים ולפתרון בעיות.
כשמפתחים אפליקציה משלכם עם תמיכה ב-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 frame
העברת מדיה
באופן כללי, כדי להפעיל מדיה במכשיר 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. לדוגמה, אם אנחנו מתחילים להפעיל Cast, צריך להפסיק את ההפעלה המקומית ולהשבית חלק מהפקדים. באופן דומה, אם מפסיקים את ההעברה (cast) כשנמצאים בבורר התצוגה הזה, צריך לעבור להפעלה מקומית. כדי לעשות זאת, אנחנו צריכים להאזין לאירועים השונים שנוצרים על ידי מסגרת Cast.
ניהול סשן של הפעלת Cast
במסגרת Cast, סשן העברה מורכב מהשלבים הבאים: התחברות למכשיר, הפעלה (או הצטרפות), התחברות לאפליקציית מקלט ואיפוס של ערוץ לניהול מדיה, אם רלוונטי. ערוץ בקרת המדיה הוא הדרך שבה מסגרת Cast שולחת ומקבלת הודעות מנגן המדיה המקבל.
סשן ההעברה יופעל באופן אוטומטי כשהמשתמש יבחר מכשיר מהלחצן 'העברה', וייפסק באופן אוטומטי כשהמשתמש יתנתק. גם החיבור מחדש לסשן של מקלט בגלל בעיות ברשת מנוהל באופן אוטומטי על ידי מסגרת ההעברה (cast).
סשנים של הפעלת Cast מנוהלים על ידי GCKSessionManager
, שאליו אפשר לגשת דרך GCKCastContext.sharedInstance().sessionManager
. אפשר להשתמש בקריאות החזרה (callbacks) של GCKSessionManagerListener
כדי לעקוב אחרי אירועי סשן, כמו יצירה, השעיה, המשך וסיום.
קודם צריך לרשום את ה-session שלנו (session Listener) ולאתחל כמה משתנים:
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
שתומך בהפעלת מדיה, ה-SDK ייצור באופן אוטומטי מופע של GCKRemoteMediaClient
. אפשר לגשת אליו כנכס 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 שתומכת בהפעלה מרחוק:
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 צריכות לספק שליטה מינימליסטית שתופיע כשהמשתמש מנווט אל מחוץ לדף התוכן הנוכחי. בנגן המיני יש גישה מיידית ותזכורת חזותית לסשן ההעברה הנוכחי.
ערכת ה-SDK של Cast מספקת סרגל בקרה, 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 מחייבת אפליקציית שולח שתציג את לחצן ההעברה למשתמשים קיימים, כדי להודיע להם שאפליקציית השולח תומכת עכשיו בהעברה וגם עוזרת למשתמשים חדשים ב-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). הדף מספק גם את כל פרטי הסטטוס של הסשן (הגרפיקה, כותרת, כותרת המשנה וכו').
הפונקציונליות של התצוגה הזו מיושמת על ידי המחלקה 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 Receiver, מוסיפים את הקוד הבא לכיתה MediaTableViewController.swift
, מתחת לפונקציה loadSelectedItem
:
let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
...
mediaLoadRequestDataBuilder.credentials = credentials
...
בהתאם לאפליקציית המקבל שאליה השולח מבצע העברה (cast), ה-SDK יחיל באופן אוטומטי את פרטי הכניסה שלמעלה על הסשן הנוכחי.
בדיקת Cast Connect
שלבים להתקנת ה-APK של Android TV ב-Chromecast with Google TV
- מאתרים את כתובת ה-IP של מכשיר Android TV. בדרך כלל, הוא מופיע בקטע הגדרות > רשת ואינטרנט > (שם הרשת שאליה המכשיר מחובר). בצד שמאל יוצגו הפרטים ואת כתובת ה-IP של המכשיר שלך ברשת.
- משתמשים בכתובת ה-IP של המכשיר כדי להתחבר אליו דרך ADB באמצעות הטרמינל:
$ adb connect <device_ip_address>:5555
- בחלון מסוף, עוברים לתיקייה ברמה העליונה של הדוגמאות ל-codelab שהורדתם בתחילת הקודלהב. לדוגמה:
$ cd Desktop/ios_codelab_src
- כדי להתקין את קובץ ה-APK בתיקייה הזו ב-Android TV, מריצים את הפקודה:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- עכשיו אמורה להופיע אפליקציה בשם העברת סרטונים בתפריט האפליקציות שלך במכשיר Android TV.
- בסיום, יש לבנות את האפליקציה ולהפעיל אותה באמולטור או במכשיר נייד. כשמתחילים סשן העברה (cast) עם מכשיר Android TV, אמורה להופיע אפליקציית Android Receiver ב-Android TV. הפעלת סרטון מהמכשיר הנייד עם iOS אמורה להפעיל את הסרטון במכשיר Android Receiver ולאפשר לכם לשלוט בהפעלה באמצעות השלט הרחוק של מכשיר Android TV.
11. התאמה אישית של ווידג'טים של הפעלת Cast
אתחול
מתחילים בתיקייה App-Done. מוסיפים את הטקסט הבא לשיטה applicationDidFinishLaunchingWithOptions
בקובץ AppDelegate.swift
.
func application(_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
let styler = GCKUIStyle.sharedInstance()
...
}
אחרי שתסיימו להחיל התאמה אישית אחת או יותר כפי שמתואר בהמשך הקוד, תוכלו לבצע את השמירה של הסגנונות באמצעות הקוד הבא:
styler.apply()
התאמה אישית של תצוגות Cast
אתם יכולים להתאים אישית את כל התצוגות שמנוהלות על ידי מסגרת האפליקציות של Cast, באמצעות הנחיות ברירת מחדל לסגנון בכל התצוגות. לדוגמה, נשנה את צבע הסמל.
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.