במדריך הזה למפתחים מוסבר איך להוסיף תמיכה ב-Google Cast ל-iOS. אפליקציית שולח באמצעות iOS Sender SDK.
המכשיר הנייד או המחשב הנייד הוא השולח ששולט בהפעלה. מכשיר Google Cast הוא המקלט שמציג את התוכן בטלוויזיה.
מסגרת השולח מתייחסת לקובץ הבינארי של הספרייה של מחלקה של Cast, משאבים שקיימים בזמן הריצה אצל השולח. אפליקציית השולח או האפליקציה להפעלת Cast מתייחס לאפליקציה שפועלת גם על השולח. האפליקציה Web Gettingr מתייחס לאפליקציית ה-HTML שפועלת במקלט האינטרנט.
מסגרת השולח משתמשת בעיצוב אסינכרוני של קריאה חוזרת כדי ליידע את השולח אפליקציית אירועים ולעבור בין מצבים שונים של חיי אפליקציית Cast במחזוריות.
זרימת אפליקציה
השלבים הבאים מתארים את תהליך הביצוע ברמה העליונה הטיפוסי של שולח אפליקציה ל-iOS:
- המערכת של Cast מתחילה
GCKDiscoveryManager
על סמך הנכסים שצוינוGCKCastOptions
עד להתחיל לסרוק אחר מכשירים. - כשהמשתמש לוחץ על הלחצן להפעלת Cast, ה-framework מציג את ההעברה תיבת דו-שיח עם רשימת מכשירי Cast שהתגלו.
- כשהמשתמש בוחר מכשיר Cast, ה-framework מנסה להפעיל את אפליקציית מכשיר ה-Cast.
- ה-framework מפעיל קריאות חוזרות (callbacks) באפליקציית השולח כדי לאשר האפליקציה של מכשיר ה-Web קבלה הופעלה.
- ה-framework יוצר ערוץ תקשורת בין השולח לבין אפליקציות של המקבל.
- ה-framework משתמש בערוץ התקשורת כדי לטעון מדיה ולשלוט בה הפעלה במקלט האינטרנט.
- ה-framework מסתנכרן את מצב הפעלת המדיה בין השולח לבין מקבל אינטרנט: כשהמשתמש מבצע פעולות בממשק המשתמש של השולח, ה-framework עובר הבקשות האלו לבקרת מדיה אל מקלט האינטרנט, ומתי מקלט האינטרנט שולחת עדכונים לגבי סטטוס המדיה, ה-framework מעדכנת את המצב של ממשק המשתמש של השולח.
- כשהמשתמש לוחץ על לחצן הפעלת Cast כדי להתנתק ממכשיר ה-Cast, ה-framework ינתק את האפליקציה של השולח ממקלט האינטרנט.
כדי לפתור בעיות בשולח, צריך להפעיל את הרישום ביומן.
לרשימה מקיפה של כל הכיתות, השיטות והאירועים ב-Google Cast במסגרת iOS, ראו Google Cast iOS API הפניה. הקטעים הבאים מתארים את השלבים לשילוב של Cast באפליקציה שלכם ל-iOS.
שיטות שיחה מה-thread הראשי
אתחול ההקשר של הפעלת Cast
ל-Cast יש אובייקט גלובלי מסוג Singleton,
GCKCastContext
,
שמרכז את כל הפעילויות של המסגרת. צריך לאתחל את האובייקט הזה
בתחילת מחזור החיים של האפליקציה,
השיטה -[application:didFinishLaunchingWithOptions:]
של בעל הגישה לאפליקציה, כך
שהמשך אוטומטי של סשן לאחר הפעלה מחדש של אפליקציית השולח יכול לפעול בצורה תקינה.
GCKCastOptions
כאשר מאתחלים את GCKCastContext
, יש לספק אובייקט.
הסיווג הזה מכיל אפשרויות שמשפיעות על ההתנהגות של ה-framework. במידה הרבה ביותר
חשוב באחד מהם הוא מזהה האפליקציה של מקלט האינטרנט, שמשמש לסינון
תוצאות גילוי ולהפעיל את האפליקציה 'מקלט אינטרנט' כאשר הפעלת Cast
בתהליך.
גם שיטת -[application:didFinishLaunchingWithOptions:]
היא מקום טוב
כדי להגדיר נציג מורשה לרישום ביומן שיקבל את הודעות הרישום ביומן מה-framework.
המידע הזה יכול להועיל לניפוי באגים ולפתרון בעיות.
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate { let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID let kDebugLoggingEnabled = true var window: UIWindow? func applicationDidFinishLaunching(_ application: UIApplication) { let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) let options = GCKCastOptions(discoveryCriteria: criteria) GCKCastContext.setSharedInstanceWith(options) // Enable logger. GCKLogger.sharedInstance().delegate = self ... } // MARK: - GCKLoggerDelegate func logMessage(_ message: String, at level: GCKLoggerLevel, fromFunction function: String, location: String) { if (kDebugLoggingEnabled) { print(function + " - " + message) } } }
AppDelegate.h
@interface AppDelegate () <GCKLoggerDelegate> @end
AppDelegate.m
@implementation AppDelegate static NSString *const kReceiverAppID = @"AABBCCDD"; static const BOOL kDebugLoggingEnabled = YES; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc] initWithApplicationID:kReceiverAppID]; GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria:criteria]; [GCKCastContext setSharedInstanceWithOptions:options]; // Enable logger. [GCKLogger sharedInstance].delegate = self; ... return YES; } ... #pragma mark - GCKLoggerDelegate - (void)logMessage:(NSString *)message atLevel:(GCKLoggerLevel)level fromFunction:(NSString *)function location:(NSString *)location { if (kDebugLoggingEnabled) { NSLog(@"%@ - %@, %@", function, message, location); } } @end
ווידג'טים של חוויית המשתמש ב-Cast
ערכת ה-SDK של Cast ל-iOS מספקת את הווידג'טים האלה שתואמים ל-Cast Design רשימת משימות:
שכבת-על של סרטון מבוא: למחלקה
GCKCastContext
יש method,presentCastInstructionsViewControllerOnceWithCastButton
, כך שניתן להשתמש בו כדי להדגיש את לחצן הפעלת Cast בפעם הראשונה במקלט אינטרנט זמין. אפליקציית השולח יכולה להתאים אישית את הטקסט והמיקום של הכותרת טקסט ועל הלחצן 'סגירה'.הלחצן להפעלת Cast: החל מגרסה 4.6.0 של Cast ב-Assistant השולח ב-iOS, לחצן ההעברה תמיד גלוי כשהמכשיר השולח מחובר ל-Wi-Fi. בפעם הראשונה שהמשתמש מקיש בלחצן הפעלת Cast אחרי שמפעילים את האפליקציה, מופיעה תיבת דו-שיח עם הרשאות מוצגת כדי שהמשתמש יוכל להעניק לאפליקציה גישה לרשת המקומית למכשירים הרשת. לאחר מכן, כשהמשתמש מקיש על הלחצן להפעלת Cast, מופעל Cast תוצג תיבת דו-שיח עם רשימת המכשירים שזוהו. כשהמשתמש מקיש בלחיצה על הלחצן להפעלת Cast כשהמכשיר מחובר, הוא מציג את מטא-נתונים של מדיה (כמו שם, שם אולפן ההקלטות ותמונה ממוזערת תמונה) או מאפשרת למשתמש להתנתק ממכשיר ה-Cast. כשהמשתמש הוא מקיש על הלחצן להפעלת Cast כשאין מכשירים זמינים, יוצגו למשתמש מידע על הסיבה לכך שמכשירים לא נמצאו ואיך לפתור בעיות.
בקר מיני: כשהמשתמש מבצע Cast של תוכן ומנווטים אל מחוץ לאזור הנוכחי את דף התוכן או את בקר המורחב למסך אחר באפליקציית השולח, המיני-בקר מוצג בתחתית המסך כדי לאפשר למשתמש לראות את המטא-נתונים של המדיה שמתבצעת בהם העברה ולשלוט בהפעלה.
בקר מורחב: כשהמשתמש מעביר תוכן, אם הוא לוחץ על התראת המדיה או מיני-בקר, ההשקה של הבקר המורחב, שמציג את שמפעילה מטא-נתונים של מדיה כרגע, ומספקת כמה לחצנים לשליטה הפעלת מדיה.
הוספת לחצן להפעלת Cast
ה-framework מספק רכיב של לחצן הפעלת Cast כמחלקה משנית ב-UIButton
. אפשר
יתווסף לשורת הכותרת של האפליקציה על ידי גלישה בתוך UIBarButtonItem
. טיפוסית
תת-קבוצה אחת (UIViewController
) יכולה להתקין לחצן להפעלת Cast באופן הבא:
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24)) castButton.tintColor = UIColor.gray navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)]; castButton.tintColor = [UIColor grayColor]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];
כברירת מחדל, הקשה על הלחצן תפתח את תיבת הדו-שיח של הפעלת Cast שמסופקת על ידי .
GCKUICastButton
ניתן גם להוסיף ישירות ללוח הסיפור.
הגדרת גילוי מכשירים
ב-framework, גילוי המכשיר מתבצע באופן אוטומטי. אין צורך להתחיל או להפסיק באופן מפורש את תהליך הגילוי, אלא אם מטמיעים ממשק משתמש בהתאמה אישית.
החשיפה במסגרת מנוהלת על ידי הכיתה
GCKDiscoveryManager
שהיא תכונה של
GCKCastContext
framework מספק רכיב ברירת מחדל של תיבת דו-שיח Cast לבחירת מכשירים
בקרה. רשימת המכשירים מסודרת לקסיקוגרפיה לפי השם הידידותי למכשיר.
איך פועל ניהול הסשנים
ב-Cast SDK מוצג הקונספט של סשן הפעלת Cast, שמשלבת את השלבים של התחברות למכשיר, השקה (או הצטרפות) של אתר אפליקציית המקבל, התחברות לאפליקציה והפעלה של ערוץ בקרת מדיה. צפייה במקלט האינטרנט מדריך למחזור החיים של אפליקציה לקבלת מידע נוסף על סשנים של הפעלת Cast ועל מחזור החיים של מקלט האינטרנט.
הסשנים מנוהלים על ידי הכיתה
GCKSessionManager
שהיא תכונה של
GCKCastContext
ביקורים בודדים מיוצגים על ידי מחלקות משנה של הכיתה
GCKSession
: לדוגמה,
GCKCastSession
מייצג סשנים עם מכשירי Cast. אפשר לגשת להפעלת Cast שפעילה עכשיו
סשן (אם בכלל), כמאפיין currentCastSession
של GCKSessionManager
.
GCKSessionManagerListener
יכול לשמש למעקב אחרי אירועי סשן, כמו יצירת סשן,
השעיה, חידוש וסיום. ה-framework מושעה באופן אוטומטי
ביקורים כשאפליקציית השולח עוברת לרקע ומנסה להמשיך
אותם כשהאפליקציה חוזרת לחזית (או מופעלת מחדש לאחר
סיום חריגה או פתאומי של האפליקציה בזמן שהסשן פעיל).
אם משתמשים בתיבת הדו-שיח של הפעלת Cast, נוצרים סשנים ומפסיקים
באופן אוטומטי בתגובה לתנועות של המשתמש. אחרת, האפליקציה יכולה להתחיל ולהסתיים
סשנים באופן מפורש באמצעות methods ב-
GCKSessionManager
אם האפליקציה צריכה לבצע עיבוד מיוחד בתגובה למחזור החיים של הסשן
אירועים, היא יכולה לרשום מופע אחד או יותר של GCKSessionManagerListener
עם
GCKSessionManager
. GCKSessionManagerListener
הוא פרוטוקול שמגדיר
קריאות חוזרות (callback) לאירועים כמו התחלת סשן, סיום סשן וכו'.
העברה בסטרימינג
שימור מצב הסשן הוא הבסיס להעברת השידור, המשתמשים יכולים להעביר שידורי אודיו ווידאו קיימים בין מכשירים באמצעות פקודות קוליות, Google Home אפליקציה או מסכים חכמים. המדיה מפסיקה לפעול במכשיר אחד (המקור) וממשיכה במכשיר אחר (המקור היעד). כל מכשיר Cast עם הקושחה האחרונה יכול לשמש כמקורות או יעדים העברה בסטרימינג.
כדי לקבל את מכשיר היעד החדש במהלך ההעברה בסטרימינג, צריך להשתמש ב
GCKCastSession#device
במהלך
[sessionManager:didResumeCastSession:]
קריאה חוזרת.
צפייה העברת סטרימינג במקלט אינטרנטי אפשר לקבל מידע נוסף.
חיבור מחדש אוטומטי
מסגרת Cast מוסיפה לוגיקת חיבור מחדש כדי לטפל בחיבור מחדש באופן אוטומטי בהרבה מקרים עדינים, כמו:
- התאוששות מאובדן זמני של רשת ה-Wi-Fi
- התאוששות ממצב שינה במכשיר
- שחזור מהפעלה ברקע של האפליקציה
- שחזור אם האפליקציה קרסה
איך פועל ממשק השליטה במדיה
אם סשן של הפעלת Cast נוצר באמצעות אפליקציית אינטרנט של מקלט שתומכת במדיה
מרחב שמות, מופע של
GCKRemoteMediaClient
ייווצרו אוטומטית על ידי ה-framework. אפשר לגשת אליו בתור
המאפיין remoteMediaClient
של
GCKCastSession
מכונה.
כל השיטות ב-GCKRemoteMediaClient
ששולחות בקשות למקלט האינטרנט
יחזיר
אובייקט GCKRequest
יכול לשמש למעקב אחרי הבקשה הזו. א'
GCKRequestDelegate
אפשר להקצות אותו לאובייקט הזה כדי לקבל התראות על
כתוצאה של הפעולה.
צפוי שהמופע של GCKRemoteMediaClient
יכול להיות משותף לכמה חלקים של האפליקציה, ואכן כמה רכיבים פנימיים
של המסגרת, כמו תיבת הדו-שיח של הפעלת Cast ופקדי מיני מדיה משתפים
מכונה. לשם כך, GCKRemoteMediaClient
תומך ברישום של כמה
GCKRemoteMediaClientListener
.
הגדרת מטא-נתונים של מדיה
GCKMediaMetadata
class מייצג מידע על פריט מדיה שרוצים להפעיל Cast. הבאים
בדוגמה יוצרת מופע GCKMediaMetadata
חדש של סרט ומגדירה את שם הסרט,
כתובית, שם אולפן ההקלטות ושתי תמונות.
let metadata = GCKMediaMetadata() metadata.setString("Big Buck Bunny (2008)", forKey: kGCKMetadataKeyTitle) metadata.setString("Big Buck Bunny tells the story of a giant rabbit with a heart bigger than " + "himself. When one sunny day three rodents rudely harass him, something " + "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon " + "tradition he prepares the nasty rodents a comical revenge.", forKey: kGCKMetadataKeySubtitle) metadata.addImage(GCKImage(url: URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg")!, width: 480, height: 360))
GCKMediaMetadata *metadata = [[GCKMediaMetadata alloc] initWithMetadataType:GCKMediaMetadataTypeMovie]; [metadata setString:@"Big Buck Bunny (2008)" forKey:kGCKMetadataKeyTitle]; [metadata setString:@"Big Buck Bunny tells the story of a giant rabbit with a heart bigger than " "himself. When one sunny day three rodents rudely harass him, something " "snaps... and the rabbit ain't no bunny anymore! In the typical cartoon " "tradition he prepares the nasty rodents a comical revenge." forKey:kGCKMetadataKeySubtitle]; [metadata addImage:[[GCKImage alloc] initWithURL:[[NSURL alloc] initWithString:@"https://commondatastorage.googleapis.com/" "gtv-videos-bucket/sample/images/BigBuckBunny.jpg"] width:480 height:360]];
אפשר לעיין בבחירת תמונות שמירה במטמון הקטע על השימוש בתמונות עם מטא-נתונים של מדיה.
טעינת מדיה
כדי לטעון פריט מדיה, צריך ליצור קובץ
GCKMediaInformation
באמצעות המטא-נתונים של המדיה. ואז מקבלים את
GCKCastSession
ו-
להשתמש ב
GCKRemoteMediaClient
כדי לטעון את המדיה באפליקציה של המקבל. אחר כך תוכלו להשתמש ב-GCKRemoteMediaClient
לשליטה באפליקציה של נגן מדיה שפועלת במקלט, למשל לצורך הפעלה,
לעצור ולעצור.
let url = URL.init(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4") guard let mediaURL = url else { print("invalid mediaURL") return } let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL) mediaInfoBuilder.streamType = GCKMediaStreamType.none; mediaInfoBuilder.contentType = "video/mp4" mediaInfoBuilder.metadata = metadata; mediaInformation = mediaInfoBuilder.build() guard let mediaInfo = mediaInformation else { print("invalid mediaInformation") return } if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInfo) { request.delegate = self }
GCKMediaInformationBuilder *mediaInfoBuilder = [[GCKMediaInformationBuilder alloc] initWithContentURL: [NSURL URLWithString:@"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"]]; mediaInfoBuilder.streamType = GCKMediaStreamTypeNone; mediaInfoBuilder.contentType = @"video/mp4"; mediaInfoBuilder.metadata = metadata; self.mediaInformation = [mediaInfoBuilder build]; GCKRequest *request = [self.sessionManager.currentSession.remoteMediaClient loadMedia:self.mediaInformation]; if (request != nil) { request.delegate = self; }
ראו גם את הקטע בנושא שימוש בטראקים של מדיה.
פורמט וידאו 4K
כדי לקבוע מהו פורמט הווידאו של המדיה, צריך להשתמש במאפיין videoInfo
של
GCKMediaStatus
כדי לקבל את המופע הנוכחי של
GCKVideoInfo
.
המופע הזה כולל את הסוג של פורמט HDR TV ואת הגובה והרוחב של
פיקסלים. וריאציות של פורמט 4K מסומנות בנכס hdrType
בציון טיפוסים בני מנייה (enum)
ערכים GCKVideoInfoHDRType
.
הוספת מיני-בקרים
בהתאם לעיצוב ההעברה (cast) רשימת המשימות, אפליקציית שולח צריכה לספק שליטה מתמשכת, שנקראת mini בקר משחקים צריכות להופיע כשהמשתמש מנווט אל מחוץ לדף התוכן הנוכחי. המיני-בקר מספק גישה מיידית ותזכורת גלויה סשן הפעלת ה-Cast הנוכחי.
מסגרת Cast מספקת סרגל בקרה
GCKUIMiniMediaControlsViewController
שאפשר להוסיף לסצנות שבהן רוצים להציג את המיני-בקר.
כשאפליקציית השולח מפעילה שידור חי של וידאו או אודיו, ה-SDK מציג באופן אוטומטי לחצן הפעלה/עצירה במקום לחצן ההפעלה/ההשהיה במיני-בקר.
במאמר התאמה אישית של ממשק המשתמש של השולח ב-iOS מוסבר איך אפליקציית השולח יכולה להגדיר את המראה של הווידג'טים של Cast.
יש שתי דרכים להוסיף את המיני-בקר לאפליקציית שולח:
- אפשר למסגרת של Cast לנהל את הפריסה של המיני-בקר על ידי האריזה את נאמן המידע הקיים של התצוגה המפורטת שלו.
- לנהל בעצמך את הפריסה של הווידג'ט של המיני-בקר על ידי הוספתו בקר תצוגה קיימת על ידי מתן תצוגת משנה בסטוריבורד.
גלישה באמצעות ה-GCKUICastContainerViewController
הדרך הראשונה היא להשתמש
GCKUICastContainerViewController
שעוטף את עוד בקר התצוגה ומוסיף
GCKUIMiniMediaControlsViewController
שלמטה. הגישה הזו מוגבלת כי אי אפשר להתאים אישית
אנימציה ולא ניתן להגדיר את ההתנהגות של בקר תצוגת הקונטיינר.
הדרך הראשונה הזו מתבצעת בדרך כלל
השיטה -[application:didFinishLaunchingWithOptions:]
של מקבל הגישה לאפליקציה:
func applicationDidFinishLaunching(_ application: UIApplication) { ... // Wrap main view in the GCKUICastContainerViewController and display the mini controller. let appStoryboard = UIStoryboard(name: "Main", bundle: nil) let navigationController = appStoryboard.instantiateViewController(withIdentifier: "MainNavigation") let castContainerVC = GCKCastContext.sharedInstance().createCastContainerController(for: navigationController) castContainerVC.miniMediaControlsItemEnabled = true window = UIWindow(frame: UIScreen.main.bounds) window!.rootViewController = castContainerVC window!.makeKeyAndVisible() ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... // Wrap main view in the GCKUICastContainerViewController and display the mini controller. UIStoryboard *appStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; UINavigationController *navigationController = [appStoryboard instantiateViewControllerWithIdentifier:@"MainNavigation"]; GCKUICastContainerViewController *castContainerVC = [[GCKCastContext sharedInstance] createCastContainerControllerForViewController:navigationController]; castContainerVC.miniMediaControlsItemEnabled = YES; self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; self.window.rootViewController = castContainerVC; [self.window makeKeyAndVisible]; ... }
var castControlBarsEnabled: Bool { set(enabled) { if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController { castContainerVC.miniMediaControlsItemEnabled = enabled } else { print("GCKUICastContainerViewController is not correctly configured") } } get { if let castContainerVC = self.window?.rootViewController as? GCKUICastContainerViewController { return castContainerVC.miniMediaControlsItemEnabled } else { print("GCKUICastContainerViewController is not correctly configured") return false } } }
AppDelegate.h
@interface AppDelegate : UIResponder <UIApplicationDelegate> @property (nonatomic, strong) UIWindow *window; @property (nonatomic, assign) BOOL castControlBarsEnabled; @end
AppDelegate.m
@implementation AppDelegate ... - (void)setCastControlBarsEnabled:(BOOL)notificationsEnabled { GCKUICastContainerViewController *castContainerVC; castContainerVC = (GCKUICastContainerViewController *)self.window.rootViewController; castContainerVC.miniMediaControlsItemEnabled = notificationsEnabled; } - (BOOL)castControlBarsEnabled { GCKUICastContainerViewController *castContainerVC; castContainerVC = (GCKUICastContainerViewController *)self.window.rootViewController; return castContainerVC.miniMediaControlsItemEnabled; } ... @end
הטמעה בבקר תצוגה קיים
הדרך השנייה היא להוסיף את המיני-בקר ישירות לתצוגה הקיימת.
באמצעות
createMiniMediaControlsViewController
כדי ליצור
GCKUIMiniMediaControlsViewController
ולאחר מכן מוסיפים אותו לבקר התצוגה של הקונטיינר כתצוגת משנה.
מגדירים את בקר התצוגות באמצעות האפליקציה 'הענקת גישה':
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { ... GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true window?.clipsToBounds = true let rootContainerVC = (window?.rootViewController as? RootContainerViewController) rootContainerVC?.miniMediaControlsViewEnabled = true ... return true }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; self.window.clipsToBounds = YES; RootContainerViewController *rootContainerVC; rootContainerVC = (RootContainerViewController *)self.window.rootViewController; rootContainerVC.miniMediaControlsViewEnabled = YES; ... return YES; }
ב-root, צריך ליצור GCKUIMiniMediaControlsViewController
ולהוסיף אותו לבקר של תצוגת הקונטיינר כתצוגת משנה:
let kCastControlBarsAnimationDuration: TimeInterval = 0.20 @objc(RootContainerViewController) class RootContainerViewController: UIViewController, GCKUIMiniMediaControlsViewControllerDelegate { @IBOutlet weak private var _miniMediaControlsContainerView: UIView! @IBOutlet weak private var _miniMediaControlsHeightConstraint: NSLayoutConstraint! private var miniMediaControlsViewController: GCKUIMiniMediaControlsViewController! var miniMediaControlsViewEnabled = false { didSet { if self.isViewLoaded { self.updateControlBarsVisibility() } } } var overriddenNavigationController: UINavigationController? override var navigationController: UINavigationController? { get { return overriddenNavigationController } set { overriddenNavigationController = newValue } } var miniMediaControlsItemEnabled = false override func viewDidLoad() { super.viewDidLoad() let castContext = GCKCastContext.sharedInstance() self.miniMediaControlsViewController = castContext.createMiniMediaControlsViewController() self.miniMediaControlsViewController.delegate = self self.updateControlBarsVisibility() self.installViewController(self.miniMediaControlsViewController, inContainerView: self._miniMediaControlsContainerView) } func updateControlBarsVisibility() { if self.miniMediaControlsViewEnabled && self.miniMediaControlsViewController.active { self._miniMediaControlsHeightConstraint.constant = self.miniMediaControlsViewController.minHeight self.view.bringSubview(toFront: self._miniMediaControlsContainerView) } else { self._miniMediaControlsHeightConstraint.constant = 0 } UIView.animate(withDuration: kCastControlBarsAnimationDuration, animations: {() -> Void in self.view.layoutIfNeeded() }) self.view.setNeedsLayout() } func installViewController(_ viewController: UIViewController?, inContainerView containerView: UIView) { if let viewController = viewController { self.addChildViewController(viewController) viewController.view.frame = containerView.bounds containerView.addSubview(viewController.view) viewController.didMove(toParentViewController: self) } } func uninstallViewController(_ viewController: UIViewController) { viewController.willMove(toParentViewController: nil) viewController.view.removeFromSuperview() viewController.removeFromParentViewController() } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "NavigationVCEmbedSegue" { self.navigationController = (segue.destination as? UINavigationController) } } ...
RootContainerViewController.h
static const NSTimeInterval kCastControlBarsAnimationDuration = 0.20; @interface RootContainerViewController () <GCKUIMiniMediaControlsViewControllerDelegate> { __weak IBOutlet UIView *_miniMediaControlsContainerView; __weak IBOutlet NSLayoutConstraint *_miniMediaControlsHeightConstraint; GCKUIMiniMediaControlsViewController *_miniMediaControlsViewController; } @property(nonatomic, weak, readwrite) UINavigationController *navigationController; @property(nonatomic, assign, readwrite) BOOL miniMediaControlsViewEnabled; @property(nonatomic, assign, readwrite) BOOL miniMediaControlsItemEnabled; @end
RootContainerViewController.m
@implementation RootContainerViewController - (void)viewDidLoad { [super viewDidLoad]; GCKCastContext *castContext = [GCKCastContext sharedInstance]; _miniMediaControlsViewController = [castContext createMiniMediaControlsViewController]; _miniMediaControlsViewController.delegate = self; [self updateControlBarsVisibility]; [self installViewController:_miniMediaControlsViewController inContainerView:_miniMediaControlsContainerView]; } - (void)setMiniMediaControlsViewEnabled:(BOOL)miniMediaControlsViewEnabled { _miniMediaControlsViewEnabled = miniMediaControlsViewEnabled; if (self.isViewLoaded) { [self updateControlBarsVisibility]; } } - (void)updateControlBarsVisibility { if (self.miniMediaControlsViewEnabled && _miniMediaControlsViewController.active) { _miniMediaControlsHeightConstraint.constant = _miniMediaControlsViewController.minHeight; [self.view bringSubviewToFront:_miniMediaControlsContainerView]; } else { _miniMediaControlsHeightConstraint.constant = 0; } [UIView animateWithDuration:kCastControlBarsAnimationDuration animations:^{ [self.view layoutIfNeeded]; }]; [self.view setNeedsLayout]; } - (void)installViewController:(UIViewController *)viewController inContainerView:(UIView *)containerView { if (viewController) { [self addChildViewController:viewController]; viewController.view.frame = containerView.bounds; [containerView addSubview:viewController.view]; [viewController didMoveToParentViewController:self]; } } - (void)uninstallViewController:(UIViewController *)viewController { [viewController willMoveToParentViewController:nil]; [viewController.view removeFromSuperview]; [viewController removeFromParentViewController]; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"NavigationVCEmbedSegue"]) { self.navigationController = (UINavigationController *)segue.destinationViewController; } } ... @end
GCKUIMiniMediaControlsViewControllerDelegate
מנחה את הבקר בתצוגת המארח מתי המיני-בקר צריך להיות גלוי:
func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController, shouldAppear _: Bool) { updateControlBarsVisibility() }
- (void)miniMediaControlsViewController: (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController shouldAppear:(BOOL)shouldAppear { [self updateControlBarsVisibility]; }
הוספה של בקר מורחב
כדי להשתמש ברשימת המשימות לעיצוב של Google Cast נדרשת אפליקציית שולח שתספק בקר משחקים עבור המדיה שמועברת. הבקר המורחב הוא גרסת מסך מלא של את המיני-בקר.
הבקר המורחב כולל תצוגת מסך מלא שמציעה שליטה מלאה הפעלה של מדיה מרחוק. התצוגה הזו צריכה לאפשר לאפליקציית העברה לנהל את כל היבט שניתן לנהל בסשן של הפעלת Cast, מלבד עוצמת הקול של מקלט האינטרנט שליטה ומחזור החיים של סשן (חיבור/עצירה של הפעלת ה-Cast). הוא גם מספק את כל פרטי מצב של סשן המדיה (גרפיקה, כותרת, כותרת משנה וכו') ).
הפונקציונליות של תצוגה זו מיושמת על ידי
GCKUIExpandedMediaControlsViewController
בכיתה.
הדבר הראשון שצריך לעשות הוא להפעיל את הבקר המורחב שמוגדר כברירת מחדל ב את ההקשר של הפעלת ה-Cast. משנים את הגורם האחראי לאפליקציה כדי להפעיל את השלט הרחוק המורחב שמוגדר כברירת מחדל:
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
כדי לטעון את הבקר המורחב, צריך להוסיף את הקוד הבא לבקר התצוגה כשהמשתמש מתחיל להעביר (cast) סרטון:
func playSelectedItemRemotely() { GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls() ... // Load your media sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation) }
- (void)playSelectedItemRemotely { [[GCKCastContext sharedInstance] presentDefaultExpandedMediaControls]; ... // Load your media [self.sessionManager.currentSession.remoteMediaClient loadMedia:mediaInformation]; }
גם הבקר המורחב יופעל באופן אוטומטי כשהמשתמש מקישים על המיני-בקר.
כשאפליקציית השולח מפעילה שידור חי של וידאו או אודיו, ה-SDK מציג באופן אוטומטי לחצן הפעלה/עצירה במקום לחצן ההפעלה/ההשהיה בבקר המורחב.
איך מחילים סגנונות מותאמים אישית במכשירי iOS אפליקציה לאופן שבו אפליקציית השולח יכולה להגדיר את המראה של הווידג'טים של Cast.
בקרת עוצמת הקול
מסגרת ההעברה (cast) מנהלת באופן אוטומטי את עוצמת הקול באפליקציית השולח.
של framework מסתנכרן אוטומטית עם נפח Web Acceptr
הווידג'טים שסופקו של ממשק המשתמש. כדי לסנכרן פס הזזה שסופק על ידי האפליקציה, צריך להשתמש באפשרות
GCKUIDeviceVolumeController
בקרת עוצמת הקול של הלחצן הפיזי
אפשר להשתמש בלחצני עוצמת הקול במכשיר השולח כדי לשנות
עוצמת הקול של הפעלת Cast במקלט האינטרנט באמצעות
הדגל physicalVolumeButtonsWillControlDeviceVolume
ב
GCKCastOptions
,
שמוגדר
GCKCastContext
.
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID) let options = GCKCastOptions(discoveryCriteria: criteria) options.physicalVolumeButtonsWillControlDeviceVolume = true GCKCastContext.setSharedInstanceWith(options)
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc] initWithApplicationID:kReceiverAppID]; GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria :criteria]; options.physicalVolumeButtonsWillControlDeviceVolume = YES; [GCKCastContext setSharedInstanceWithOptions:options];
טיפול בשגיאות
חשוב מאוד שאפליקציות שולחות יטפלו בכל הקריאות החוזרות של השגיאות ויחליטו את התשובה הטובה ביותר לכל שלב במחזור החיים של הפעלת Cast. האפליקציה יכולה להציג תיבות דו-שיח עם שגיאות למשתמש, או שהוא יכול להחליט לסיים את הפעלת ה-Cast.
רישום ביומן
GCKLogger
הוא מקטע סינגל שמשמש לרישום ביומן על ידי ה-framework. משתמשים ב
GCKLoggerDelegate
כדי להתאים אישית את אופן הטיפול בהודעות ביומן.
באמצעות GCKLogger
, ה-SDK יוצר פלט של רישום ביומן בצורה של ניפוי באגים
הודעות, שגיאות ואזהרות. ההודעות האלה ביומן עוזרות לניפוי באגים ומועילות
לפתרון בעיות ולזיהוי בעיות. כברירת מחדל, פלט היומן הוא
לא פעיל, אבל אפליקציית השולח יכולה לקבל GCKLoggerDelegate
את ההודעות האלה מה-SDK ולרשום אותן במסוף המערכת.
@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate { let kReceiverAppID = kGCKDefaultMediaReceiverApplicationID let kDebugLoggingEnabled = true var window: UIWindow? func applicationDidFinishLaunching(_ application: UIApplication) { ... // Enable logger. GCKLogger.sharedInstance().delegate = self ... } // MARK: - GCKLoggerDelegate func logMessage(_ message: String, at level: GCKLoggerLevel, fromFunction function: String, location: String) { if (kDebugLoggingEnabled) { print(function + " - " + message) } } }
AppDelegate.h
@interface AppDelegate () <GCKLoggerDelegate> @end
AppDelegate.m
@implementation AppDelegate static NSString *const kReceiverAppID = @"AABBCCDD"; static const BOOL kDebugLoggingEnabled = YES; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... // Enable logger. [GCKLogger sharedInstance].delegate = self; ... return YES; } ... #pragma mark - GCKLoggerDelegate - (void)logMessage:(NSString *)message atLevel:(GCKLoggerLevel)level fromFunction:(NSString *)function location:(NSString *)location { if (kDebugLoggingEnabled) { NSLog(@"%@ - %@, %@", function, message, location); } } @end
כדי להפעיל גם הודעות ניפוי באגים והודעות מפורטות, יש להוסיף את השורה הזו לקוד אחרי הגדרת המשתמש האחראי (מוצג קודם):
let filter = GCKLoggerFilter.init() filter.minimumLevel = GCKLoggerLevel.verbose GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setMinimumLevel:GCKLoggerLevelVerbose]; [GCKLogger sharedInstance].filter = filter;
אפשר גם לסנן את ההודעות ביומן שהופקו על ידי
GCKLogger
מגדירים את רמת הרישום המינימלית ביומן לכל כיתה, לדוגמה:
let filter = GCKLoggerFilter.init() filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton", "GCKUIImageCache", "NSMutableDictionary"]) GCKLogger.sharedInstance().filter = filter
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init]; [filter setLoggingLevel:GCKLoggerLevelVerbose forClasses:@[@"GCKUICastButton", @"GCKUIImageCache", @"NSMutableDictionary" ]]; [GCKLogger sharedInstance].filter = filter;
שמות המחלקות יכולים להיות שמות מילוליים או תבניות גלובוס, לדוגמה,
GCKUI\*
וגם GCK\*Session