คู่มือนักพัฒนาซอฟต์แวร์นี้อธิบายวิธีเพิ่มการรองรับ Google Cast ลงในแอปผู้ส่ง iOS โดยใช้ SDK Sender iOS
อุปกรณ์เคลื่อนที่หรือแล็ปท็อปคือผู้ส่งที่ควบคุมการเล่นและอุปกรณ์ Google Cast เป็นตัวรับที่แสดงเนื้อหาบนทีวี
กรอบการทํางานของผู้ส่งจะอ้างถึงไบนารีไลบรารีคลาส Cast และทรัพยากรที่เกี่ยวข้องซึ่งมีอยู่ที่รันไทม์ของผู้ส่ง แอปผู้ส่งหรือแอปแคสต์ หมายถึงแอปที่ทํางานอยู่บนผู้ส่งด้วย แอปรีซีฟเวอร์บนเว็บ สําหรับแอปพลิเคชัน HTML ที่ทํางานบนเว็บรีซีฟเวอร์
กรอบการทํางานของผู้ส่งใช้การออกแบบการเรียกกลับแบบไม่พร้อมกันเพื่อแจ้งให้แอปของผู้ส่งทราบถึงเหตุการณ์ และเพื่อสลับไปมาระหว่างสถานะต่างๆ ของวงจรชีวิตของแอป Cast
กระแสของแอป
ขั้นตอนต่อไปนี้จะอธิบายขั้นตอนการดําเนินงานระดับสูงทั่วไปสําหรับแอป iOS ของผู้ส่ง
- เฟรมเวิร์ก Cast เริ่มต้น
GCKDiscoveryManager
ตามพร็อพเพอร์ตี้ที่ให้ไว้ในGCKCastOptions
เพื่อเริ่มการสแกนหาอุปกรณ์ - เมื่อผู้ใช้คลิกปุ่ม "แคสต์" เฟรมเวิร์กจะแสดงกล่องโต้ตอบ "แคสต์" พร้อมรายการอุปกรณ์แคสต์ที่ค้นพบ
- เมื่อผู้ใช้เลือกอุปกรณ์แคสต์ กรอบการทํางานดังกล่าวจะพยายามเปิดแอป Web Receiver บนอุปกรณ์ Cast
- เฟรมเวิร์กจะเรียกใช้โค้ดเรียกกลับในแอปผู้ส่งเพื่อยืนยันว่าได้เปิดตัวแอป Web Receiver แล้ว
- เฟรมเวิร์กนี้จะสร้างช่องทางการสื่อสารระหว่างแอปของผู้ส่งและผู้รับเว็บ
- เฟรมเวิร์กนี้ใช้ช่องทางการสื่อสารเพื่อโหลดและควบคุมการเล่นสื่อบน Web Receiver
- เฟรมเวิร์กจะซิงค์สถานะการเล่นสื่อระหว่างผู้ส่งและ Web Receiver กล่าวคือเมื่อผู้ใช้ดําเนินการ UI ของผู้ส่ง เฟรมเวิร์กจะส่งคําขอควบคุมสื่อเหล่านั้นไปยัง Web Receiver และเมื่อ Web Receiver ส่งการอัปเดตสถานะสื่อ เฟรมเวิร์กจะอัปเดตสถานะของ UI ของผู้ส่ง
- เมื่อผู้ใช้คลิกปุ่ม "แคสต์" เพื่อยกเลิกการเชื่อมต่อจากอุปกรณ์ Cast เฟรมเวิร์กดังกล่าวจะยกเลิกการเชื่อมต่อแอปผู้ส่งจากตัวรับสัญญาณเว็บ
หากต้องการแก้ปัญหาของผู้ส่ง คุณต้องเปิดใช้การบันทึก
สําหรับรายการที่ครอบคลุมทุกคลาส เมธอด และเหตุการณ์ในกรอบงาน Google Cast iOS โปรดดูข้อมูลอ้างอิง API ของ Google Cast สําหรับ iOS ส่วนต่อไปนี้จะอธิบายขั้นตอนการรวม "แคสต์" เข้ากับแอป iOS
วิธีการโทรจากชุดข้อความหลัก
เริ่มต้นบริบทของ Cast
เฟรมเวิร์ก Cast มีออบเจ็กต์ Singleton สากลคือ GCKCastContext
ซึ่งจะประสานงานกิจกรรมของเฟรมเวิร์กทั้งหมด ออบเจ็กต์นี้จําเป็นต้องเริ่มต้นในวงจรแอปพลิเคชัน โดยปกติจะอยู่ในเมธอด -[application:didFinishLaunchingWithOptions:]
ของการมอบสิทธิ์แอป ดังนั้นการรีสตาร์ทเซสชันอัตโนมัติเมื่อรีสตาร์ทแอปของผู้ส่งจึงจะเรียกใช้ได้อย่างถูกต้อง
คุณต้องระบุออบเจ็กต์ GCKCastOptions
เมื่อเริ่มต้น GCKCastContext
คลาสนี้มีตัวเลือกที่ส่งผลต่อลักษณะการทํางานของเฟรมเวิร์ก สิ่งสําคัญที่สุดคือรหัสแอปพลิเคชันตัวรับสัญญาณเว็บซึ่งใช้เพื่อกรองผลการค้นหาการค้นพบและเปิดแอป "ตัวรับสัญญาณเว็บ" เมื่อเริ่มเซสชันการแคสต์
นอกจากนี้ เมธอด -[application:didFinishLaunchingWithOptions:]
ยังเหมาะสําหรับ
การตั้งค่าการมอบสิทธิ์การบันทึกเพื่อรับข้อความการบันทึกจากเฟรมเวิร์ก
เครื่องมือเหล่านี้อาจมีประโยชน์สําหรับการแก้ไขข้อบกพร่องและการแก้ปัญหา
@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 UX
Cast iOS SDK มีวิดเจ็ตที่สอดคล้องกับรายการตรวจสอบการออกแบบ Cast ดังนี้
การวางซ้อนที่แนะนํา: คลาส
GCKCastContext
มีเมธอดคือpresentCastInstructionsViewControllerOnceWithCastButton
ซึ่งสามารถใช้เพื่อไฮไลต์ปุ่ม "แคสต์" ได้เมื่อ Web Receiver พร้อมใช้งาน แอปผู้ส่งสามารถปรับแต่งข้อความ ตําแหน่งของข้อความชื่อ และปุ่มปิดปุ่ม "แคสต์": SDK ของผู้ส่ง iOS 4.6.0 เริ่มทํางานแล้ว ปุ่ม "แคสต์" จะแสดงเสมอเมื่ออุปกรณ์ของผู้ส่งเชื่อมต่อกับ Wi-Fi ครั้งแรกที่ผู้ใช้แตะปุ่ม "แคสต์" หลังจากเริ่มแอปเป็นครั้งแรก กล่องโต้ตอบสิทธิ์จะปรากฏขึ้นเพื่อให้ผู้ใช้สามารถให้สิทธิ์เครือข่ายภายในในการเข้าถึงอุปกรณ์ต่างๆ ในเครือข่ายได้ จากนั้น เมื่อผู้ใช้แตะปุ่ม "แคสต์" กล่องโต้ตอบแคสต์จะปรากฏขึ้นเพื่อแสดงรายชื่ออุปกรณ์ที่ค้นพบ เมื่อผู้ใช้แตะปุ่ม "แคสต์" ในขณะที่อุปกรณ์เชื่อมต่ออยู่ อุปกรณ์จะแสดงข้อมูลเมตาสื่อปัจจุบัน (เช่น ชื่อ สตูดิโอบันทึกเสียง และภาพขนาดย่อ) หรืออนุญาตให้ผู้ใช้ยกเลิกการเชื่อมต่อจากอุปกรณ์แคสต์ เมื่อผู้ใช้แตะปุ่ม "แคสต์" ในขณะที่ไม่มีอุปกรณ์พร้อมใช้งาน หน้าจอจะแสดงให้ข้อมูลเกี่ยวกับสาเหตุที่ไม่พบอุปกรณ์และวิธีแก้ปัญหา
ตัวควบคุมขนาดเล็ก: เมื่อผู้ใช้แคสต์เนื้อหาและนําทางออกไปจากหน้าเนื้อหาปัจจุบันหรือตัวควบคุมที่ขยายแล้วไปยังหน้าจออื่นในแอปผู้ส่ง ตัวควบคุมขนาดเล็กจะแสดงที่ด้านล่างของหน้าจอเพื่อให้ผู้ใช้เห็นข้อมูลเมตาของสื่อที่กําลังแคสต์อยู่และเพื่อควบคุมการเล่น
ตัวควบคุมที่ขยายแล้ว: เมื่อผู้ใช้กําลังแคสต์เนื้อหา หากผู้ใช้คลิกที่การแจ้งเตือนสื่อหรือตัวควบคุมขนาดเล็ก ตัวควบคุมที่ขยายจะทํางานเพื่อแสดงข้อมูลเมตาของสื่อที่เล่นอยู่ในขณะนั้นและมีปุ่มมากมายสําหรับควบคุมการเล่นสื่อ
เพิ่มปุ่ม "แคสต์"
เฟรมเวิร์กจะมีคอมโพเนนต์ปุ่ม "แคสต์" เป็นคลาสย่อย UIButton
แอปสามารถเพิ่ม
ลงในแถบชื่อของแอปโดยรวมไว้ใน UIBarButtonItem
คลาสย่อย UIViewController
ทั่วไปอาจติดตั้งปุ่ม "แคสต์" ได้ดังนี้
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
สามารถเพิ่มลงในบอร์ดโครงเรื่องได้โดยตรง
กําหนดค่าการค้นหาอุปกรณ์
ในเฟรมเวิร์ก การค้นพบอุปกรณ์จะเกิดขึ้นโดยอัตโนมัติ คุณไม่จําเป็นต้องเริ่มหรือหยุดกระบวนการค้นหาอย่างชัดแจ้ง เว้นแต่คุณใช้ UI ที่กําหนดเอง
การสํารวจในกรอบงานได้รับการจัดการโดยชั้นเรียน GCKDiscoveryManager
ซึ่งเป็นพร็อพเพอร์ตี้ของ GCKCastContext
เฟรมเวิร์กมีคอมโพเนนต์กล่องโต้ตอบ "แคสต์" เริ่มต้นสําหรับการเลือกและควบคุมอุปกรณ์ รายการอุปกรณ์จะเรียงลําดับตามพจนานุกรมตามชื่ออุปกรณ์
วิธีทํางานของการจัดการเซสชัน
Cast SDK จะแนะนําแนวคิดของเซสชันแคสต์ ซึ่งเป็นการรวมขั้นตอนการเชื่อมต่อกับอุปกรณ์ การเปิดตัว (หรือการเข้าร่วม) แอปตัวรับสัญญาณเว็บ การเชื่อมต่อกับแอปนั้น และการเริ่มต้นช่องทางการควบคุมสื่อ ดูข้อมูลเพิ่มเติมเกี่ยวกับเซสชันแคสต์และวงจรชีวิตของผู้รับเว็บได้ที่คู่มือวงจรการใช้งานแอปพลิเคชัน
เซสชันจะถูกจัดการโดยชั้นเรียน
GCKSessionManager
ซึ่งเป็นพร็อพเพอร์ตี้ของ GCKCastContext
เซสชันแต่ละรายการจะแสดงโดยคลาสย่อยของคลาส
GCKSession
เช่น
GCKCastSession
แสดงถึงเซสชันที่มีอุปกรณ์แคสต์ คุณสามารถเข้าถึงเซสชัน Cast ที่ใช้งานอยู่ในปัจจุบัน (หากมี) เป็นพร็อพเพอร์ตี้ currentCastSession
ของ GCKSessionManager
ได้
อินเทอร์เฟซ
GCKSessionManagerListener
จะใช้เพื่อตรวจสอบเหตุการณ์ของเซสชันได้ เช่น การสร้างเซสชัน การระงับ การกลับมาเล่นต่อ และการสิ้นสุดเซสชัน เฟรมเวิร์กจะระงับเซสชันโดยอัตโนมัติเมื่อแอปของผู้ส่งทํางานอยู่เบื้องหลังและพยายามกลับมาทํางานอีกครั้งเมื่อแอปกลับมาทํางานอยู่เบื้องหน้า (หรือถูกเปิดอีกครั้งหลังจากการสิ้นสุดของแอปที่ผิดปกติ/อย่างฉับพลันในระหว่างเซสชันทํางาน)
หากมีการใช้กล่องโต้ตอบ Cast ระบบจะสร้างเซสชันขึ้นและแยกออกจากกันโดยอัตโนมัติเพื่อตอบสนองต่อท่าทางสัมผัสของผู้ใช้ มิฉะนั้น แอปจะเริ่มและสิ้นสุดเซสชันอย่างชัดแจ้งผ่านเมธอดใน GCKSessionManager
หากแอปจําเป็นต้องทําการประมวลผลพิเศษเพื่อตอบสนองต่อเหตุการณ์ในวงจรเซสชัน แอปอาจบันทึกอินสแตนซ์ GCKSessionManagerListener
อย่างน้อย 1 รายการกับ GCKSessionManager
ได้ GCKSessionManagerListener
คือโปรโตคอลที่กําหนดการเรียกกลับสําหรับเหตุการณ์ดังกล่าว เช่น การเริ่มต้นเซสชัน การสิ้นสุดเซสชัน และอื่นๆ
การโอนสตรีม
การเก็บสถานะเซสชันไว้เป็นพื้นฐานในการโอนสตรีม ซึ่งผู้ใช้สามารถย้ายสตรีมเสียงและวิดีโอที่มีอยู่ในอุปกรณ์ต่างๆ โดยใช้คําสั่งเสียง, แอป Google Home หรือ Smart Display สื่อจะหยุดเล่นในอุปกรณ์หนึ่ง (แหล่งที่มา) และเล่นต่อไปในอุปกรณ์อื่น (ปลายทาง) อุปกรณ์แคสต์ที่มีเฟิร์มแวร์ล่าสุดจะใช้เป็นต้นทางหรือปลายทางในการโอนสตรีมได้
หากต้องการรับอุปกรณ์ปลายทางใหม่ในระหว่างการโอนสตรีม ให้ใช้พร็อพเพอร์ตี้ GCKCastSession#device
ระหว่างโค้ดเรียกกลับ [sessionManager:didResumeCastSession:]
ดูการโอนสตรีมในเครื่องรับเว็บสําหรับข้อมูลเพิ่มเติม
เชื่อมต่อใหม่อัตโนมัติ
เฟรมเวิร์ก Cast จะเพิ่มตรรกะการเชื่อมต่ออีกครั้งเพื่อจัดการการเชื่อมต่อใหม่โดยอัตโนมัติในกรณีมุมเล็กๆ น้อยๆ จํานวนมาก เช่น
- กู้คืนจากการสูญเสีย Wi-Fi ชั่วคราว
- กู้คืนจากโหมดสลีปของอุปกรณ์
- กู้คืนจากการเล่นอยู่เบื้องหลังของแอป
- กู้คืนหากแอปขัดข้อง
วิธีการทํางานของการควบคุมสื่อ
หากเซสชัน Cast สร้างขึ้นด้วยแอป Web Receiver ที่รองรับเนมสเปซสื่อ ระบบจะสร้างอินสแตนซ์ของ GCKRemoteMediaClient
โดยอัตโนมัติจากเฟรมเวิร์ก โดยจะเข้าถึงได้ในฐานะพร็อพเพอร์ตี้ remoteMediaClient
ของอินสแตนซ์ GCKCastSession
เมธอดทั้งหมดใน GCKRemoteMediaClient
ที่ออกคําขอไปยัง Web Receiver จะส่งออบเจ็กต์ GCKRequest
ซึ่งใช้เพื่อติดตามคําขอนั้นได้ คุณกําหนด GCKRequestDelegate
ให้กับออบเจ็กต์นี้เพื่อรับการแจ้งเตือนเกี่ยวกับผลลัพธ์ในขั้นสุดท้ายของการดําเนินการได้
เราคาดว่าอาจมีการแชร์อินสแตนซ์ของ GCKRemoteMediaClient
กับหลายส่วนของแอป และคอมโพเนนต์ภายในของเฟรมเวิร์กบางอย่าง เช่น กล่องโต้ตอบ Cast และการควบคุมสื่อขนาดเล็กมีการแชร์อินสแตนซ์ ด้วยเหตุนี้ GCKRemoteMediaClient
จึงรองรับการจดทะเบียนหลายรายการของ GCKRemoteMediaClientListener
ตั้งค่าข้อมูลเมตาของสื่อ
คลาส GCKMediaMetadata
แสดงข้อมูลเกี่ยวกับรายการสื่อที่คุณต้องการแคสต์ ตัวอย่างต่อไปนี้จะสร้างอินสแตนซ์ 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 รวมถึงความสูงและความกว้างเป็นพิกเซล ผลิตภัณฑ์ย่อยของรูปแบบ 4K มีการระบุอยู่ในพร็อพเพอร์ตี้ hdrType
ด้วยค่าแจกแจง GCKVideoInfoHDRType
เพิ่มตัวควบคุมขนาดเล็ก
ตามรายการตรวจสอบการออกแบบ Cast แอปผู้ส่งควรมีตัวควบคุมอย่างต่อเนื่องที่เรียกว่าตัวควบคุมขนาดเล็กที่ควรปรากฏขึ้นเมื่อผู้ใช้ออกจากหน้าเนื้อหาปัจจุบัน ตัวควบคุมขนาดเล็กช่วยให้คุณเข้าถึงได้ทันทีและการช่วยเตือนที่มองเห็นได้สําหรับเซสชันแคสต์ปัจจุบัน
เฟรมเวิร์ก Cast มีแถบควบคุม GCKUIMiniMediaControlsViewController
ซึ่งสามารถเพิ่มลงในฉากที่คุณต้องการแสดงตัวควบคุมขนาดเล็กได้
เมื่อแอปของผู้ส่งกําลังเล่นสตรีมแบบสดหรือวิดีโอหรือเสียง SDK จะแสดงปุ่มเล่น/หยุดโดยอัตโนมัติแทนปุ่มเล่น/หยุดชั่วคราวในตัวควบคุมขนาดเล็ก
โปรดดูปรับแต่ง UI ของผู้ส่ง iOS สําหรับวิธีที่ แอปผู้ส่งสามารถกําหนดค่าลักษณะที่ปรากฏของวิดเจ็ตแคสต์
การเพิ่มตัวควบคุมขนาดเล็กไปยังแอปของผู้ส่งทําได้ 2 วิธีดังนี้
- ให้เฟรมเวิร์ก 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
ฝังในตัวควบคุมมุมมองที่มีอยู่
วิธีที่ 2 คือเพิ่มตัวควบคุมขนาดเล็กในตัวควบคุมมุมมองที่มีอยู่โดยตรง โดยใช้ 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; }
ในตัวควบคุมมุมมองรูท ให้สร้างอินสแตนซ์ 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 กําหนดให้แอปของผู้ส่งส่งตัวควบคุมแบบขยายสําหรับสื่อที่กําลังแคสต์ ตัวควบคุมที่ขยายเป็นเวอร์ชันเต็มหน้าจอ ของตัวควบคุมขนาดเล็ก
ตัวควบคุมที่ขยายแล้วเป็นมุมมองแบบเต็มหน้าจอที่ให้คุณควบคุมการเล่นสื่อระยะไกลได้อย่างเต็มรูปแบบ มุมมองนี้ควรอนุญาตให้แอปแคสต์จัดการทุกๆ แง่มุมของเซสชันการแคสต์ได้ ยกเว้นการควบคุมระดับเสียงของตัวรับสัญญาณของเว็บ และวงจรเซสชัน (เชื่อมต่อ/หยุดแคสต์) นอกจากนี้ยังมีข้อมูลสถานะทั้งหมดเกี่ยวกับเซสชันสื่อ (อาร์ตเวิร์ก ชื่อเรื่อง ชื่อรอง และอื่นๆ)
ฟังก์ชันของมุมมองนี้จะใช้โดยคลาส GCKUIExpandedMediaControlsViewController
สิ่งแรกที่คุณต้องทําคือเปิดใช้ตัวควบคุมที่ขยายตามค่าเริ่มต้นในบริบทการแคสต์ แก้ไขผู้ได้รับมอบสิทธิ์ของแอปเพื่อเปิดใช้ตัวควบคุมแบบขยายตามค่าเริ่มต้น ดังนี้
func applicationDidFinishLaunching(_ application: UIApplication) { .. GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true ... }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES; .. }
เพิ่มโค้ดต่อไปนี้ลงในตัวควบคุมมุมมองเพื่อโหลดตัวควบคุมที่ขยาย เมื่อผู้ใช้เริ่มแคสต์วิดีโอ:
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 จะจัดการระดับเสียงสําหรับแอปของผู้ส่งโดยอัตโนมัติ เฟรมเวิร์กจะซิงค์ข้อมูลกับวอลุ่มตัวรับสัญญาณเว็บโดยอัตโนมัติสําหรับวิดเจ็ต UI ที่ให้มา หากต้องการซิงค์แถบเลื่อนที่แอปมีให้ ให้ใช้ GCKUIDeviceVolumeController
ปุ่มควบคุมระดับเสียง
ใช้ปุ่มปรับระดับเสียงบนอุปกรณ์ผู้ส่งเพื่อเปลี่ยนระดับเสียงของเซสชันแคสต์ในเครื่องรับสัญญาณอินเทอร์เน็ตได้โดยใช้แฟล็ก 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];
จัดการข้อผิดพลาด
เป็นสิ่งสําคัญมากที่แอปผู้ส่งจะต้องจัดการกับการตอบกลับการเรียกกลับที่ผิดพลาดทั้งหมดและทําการตัดสินใจได้ดีที่สุดสําหรับแต่ละขั้นตอนของวงจรการส่ง แอปสามารถแสดงกล่องโต้ตอบข้อผิดพลาดต่อผู้ใช้หรืออาจตัดสินใจสิ้นสุดเซสชันการแคสต์
Logging
GCKLogger
เป็นเฟรมเวิร์กเดียวที่ใช้สําหรับการบันทึกโดยเฟรมเวิร์ก ใช้ GCKLoggerDelegate
เพื่อปรับแต่งวิธีจัดการข้อความบันทึก
SDK นี้ใช้ GCKLogger
เพื่อสร้างเอาต์พุตการบันทึกในรูปแบบข้อความการแก้ไขข้อบกพร่อง ข้อผิดพลาด และคําเตือน บันทึกเหล่านี้ช่วยแก้ไขข้อบกพร่องและเป็นประโยชน์ในการแก้ปัญหาและระบุปัญหาต่างๆ โดยค่าเริ่มต้น เอาต์พุตบันทึกจะระงับไว้ แต่การกําหนด 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;
ชื่อคลาสอาจเป็นชื่อตามตัวอักษรหรือรูปแบบ glob เช่น GCKUI\*
และ GCK\*Session