คู่มือนักพัฒนาซอฟต์แวร์นี้จะอธิบายวิธีเพิ่มการสนับสนุน Google Cast ลงใน iOS ของคุณ แอปผู้ส่งที่ใช้ iOS Sender SDK
อุปกรณ์เคลื่อนที่หรือแล็ปท็อปคือผู้ส่งซึ่งควบคุมการเล่น และ อุปกรณ์ Google Cast เป็นเครื่องรับซึ่งแสดงเนื้อหาในทีวี
เฟรมเวิร์กผู้ส่งหมายถึงไบนารีไลบรารีคลาสของ Cast และที่เกี่ยวข้อง ที่มีอยู่ขณะรันไทม์ผู้ส่ง แอปผู้ส่งหรือแอปแคสต์ หมายถึงแอปที่ทำงานกับผู้ส่งด้วย แอป Web Receiver หมายถึงแอปพลิเคชัน HTML ที่ทำงานบนเว็บรีซีฟเวอร์
เฟรมเวิร์กผู้ส่งใช้การออกแบบ Callback แบบไม่พร้อมกันเพื่อแจ้งผู้ส่ง แอปเหตุการณ์และการเปลี่ยนไปมาระหว่างสถานะต่างๆ ในชีวิตของแอป Cast
ขั้นตอนของแอป
ขั้นตอนต่อไปนี้จะอธิบายกระบวนการดำเนินการระดับสูงโดยทั่วไปสำหรับผู้ส่ง แอป iOS:
- เฟรมเวิร์กการแคสต์จะเริ่มต้น
GCKDiscoveryManager
อิงตามที่พักที่ระบุไว้ในGCKCastOptions
ถึง เริ่มสแกนหาอุปกรณ์ - เมื่อผู้ใช้คลิกปุ่ม "แคสต์" เฟรมเวิร์กจะแสดง "แคสต์" กล่องโต้ตอบที่มีรายการอุปกรณ์แคสต์ที่พบ
- เมื่อผู้ใช้เลือกอุปกรณ์แคสต์ เฟรมเวิร์กจะพยายามเปิดตัว แอป Web Receiver บนอุปกรณ์แคสต์
- เฟรมเวิร์กนี้จะเรียกใช้ Callback ในแอปผู้ส่งเพื่อยืนยันว่า เปิดตัวแอป Web Receiver
- เฟรมเวิร์กจะสร้างช่องทางการสื่อสารระหว่างผู้ส่งกับ แอป Web Receiver
- เฟรมเวิร์กนี้ใช้ช่องทางการสื่อสารเพื่อโหลดและควบคุมสื่อ การเล่นบนเว็บรีซีฟเวอร์
- เฟรมเวิร์กจะซิงค์สถานะการเล่นสื่อระหว่างผู้ส่งกับ ตัวรับเว็บ: เมื่อผู้ใช้ดำเนินการกับ UI ของผู้ส่ง เฟรมเวิร์กจะส่ง คำขอตัวควบคุมสื่อเหล่านั้นไปยังเว็บรีซีฟเวอร์ และเมื่อเว็บรีซีฟเวอร์ ส่งการอัปเดตสถานะสื่อ เฟรมเวิร์กจะอัปเดตสถานะของ UI ของผู้ส่ง
- เมื่อผู้ใช้คลิกปุ่ม "แคสต์" เพื่อยกเลิกการเชื่อมต่อจากอุปกรณ์ Cast เฟรมเวิร์กจะยกเลิกการเชื่อมต่อแอปผู้ส่งจาก Web Receiver
หากต้องการแก้ปัญหาผู้ส่ง คุณต้องเปิดใช้การบันทึก
สำหรับรายการชั้นเรียน วิธีการ และเหตุการณ์ทั้งหมดใน Google Cast เฟรมเวิร์ก iOS โปรดดู Google Cast iOS API ข้อมูลอ้างอิง ส่วนต่อไปนี้จะอธิบายขั้นตอน สำหรับการผสานรวม Cast กับแอป iOS
วิธีการโทรจากเทรดหลัก
เริ่มต้นบริบทการแคสต์
เฟรมเวิร์ก Cast มีออบเจ็กต์ Singleton แบบส่วนกลาง
GCKCastContext
ซึ่ง
ประสานงานกิจกรรมทั้งหมดของเฟรมเวิร์ก ต้องเริ่มต้นออบเจ็กต์นี้
ในช่วงต้นของวงจรชีวิตของแอปพลิเคชัน โดยทั่วไปใน
-[application:didFinishLaunchingWithOptions:]
วิธีในการมอบสิทธิ์แอป
การกลับมาทำงานอีกครั้งโดยอัตโนมัติเมื่อรีสตาร์ทแอปของผู้ส่งสามารถทริกเกอร์ได้อย่างถูกต้อง
GCKCastOptions
ต้องระบุออบเจ็กต์เมื่อเริ่มต้น GCKCastContext
คลาสนี้มีตัวเลือกที่ส่งผลต่อลักษณะการทำงานของเฟรมเวิร์ก มากที่สุด
ได้แก่ รหัสแอปพลิเคชัน Web Receiver ซึ่งใช้เพื่อกรอง
ผลการค้นหา และเปิดแอป Web Receiver เมื่อเซสชันการแคสต์
เลย
นอกจากนี้ เมธอด -[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
, ซึ่งสามารถใช้เพื่อให้ปุ่ม "แคสต์" โดดเด่นในครั้งแรกที่เว็บรีซีฟเวอร์ พร้อมใช้งาน แอปผู้ส่งสามารถปรับแต่งข้อความ ตำแหน่งของชื่อ และปุ่ม "ปิด"ปุ่ม "แคสต์": ตั้งแต่ SDK ผู้ส่ง iOS แคสต์ 4.6.0 เป็นต้นไป ปุ่ม "แคสต์" จะแสดงอยู่เสมอ เมื่ออุปกรณ์ของผู้ส่งเชื่อมต่อกับ Wi-Fi ครั้งแรกที่ผู้ใช้แตะ บนปุ่ม "แคสต์" หลังจากที่เริ่มเปิดแอปครั้งแรก กล่องโต้ตอบสิทธิ์ ปรากฏขึ้น เพื่อให้ผู้ใช้สามารถให้สิทธิ์แอปเข้าถึงเครือข่าย LAN ในอุปกรณ์ เครือข่าย ต่อมาเมื่อผู้ใช้แตะปุ่ม "แคสต์" จะปรากฏขึ้น ซึ่งจะแสดงรายการอุปกรณ์ที่พบ เมื่อผู้ใช้แตะ บนปุ่ม "แคสต์" ขณะที่อุปกรณ์เชื่อมต่ออยู่ ปุ่มจะแสดงหน้าจอปัจจุบัน ข้อมูลเมตาของสื่อ (เช่น ชื่อ ชื่อสตูดิโอบันทึกเสียง และภาพปก ) หรืออนุญาตให้ผู้ใช้ยกเลิกการเชื่อมต่อจากอุปกรณ์แคสต์ เมื่อผู้ใช้ แตะปุ่ม "แคสต์" ขณะที่ไม่มีอุปกรณ์ที่พร้อมใช้งาน หน้าจอ จะปรากฏขึ้นเพื่อให้ผู้ใช้ทราบถึงสาเหตุที่ไม่พบอุปกรณ์ และวิธีแก้ปัญหา
มินิคอนโทรลเลอร์: เมื่อผู้ใช้กำลังแคสต์เนื้อหาและได้ออกจากสตรีม หน้าเนื้อหาหรือตัวควบคุมที่ขยายไปยังหน้าจออื่นในแอปผู้ส่ง ตัวควบคุมขนาดเล็กแสดงที่ด้านล่างของหน้าจอเพื่อให้ผู้ใช้ ดูข้อมูลเมตาของสื่อที่กำลังแคสต์อยู่และเพื่อควบคุมการเล่น
ตัวควบคุมที่ขยาย: เมื่อผู้ใช้แคสต์เนื้อหา หากผู้ใช้คลิกการแจ้งเตือนสื่อหรือ ตัวควบคุมขนาดเล็ก ตัวควบคุมแบบขยายจะเปิดขึ้น เพื่อแสดง ข้อมูลเมตาสื่อที่เล่นอยู่และมีปุ่มต่างๆ สำหรับควบคุม การเล่นสื่อ
เพิ่มปุ่ม "แคสต์"
เฟรมเวิร์กนี้จะมีคอมโพเนนต์ปุ่ม "แคสต์" เป็นคลาสย่อย 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];
โดยค่าเริ่มต้น การแตะปุ่มนี้จะเปิดกล่องโต้ตอบ "แคสต์" ที่จัดเตรียมไว้ให้
GCKUICastButton
ลงในสตอรีบอร์ดโดยตรงได้ด้วย
กำหนดค่าการค้นพบอุปกรณ์
ในเฟรมเวิร์ก การค้นพบอุปกรณ์จะเกิดขึ้นโดยอัตโนมัติ คุณไม่จำเป็นต้อง เริ่มต้นหรือหยุดกระบวนการค้นหาอย่างชัดเจน เว้นแต่ว่าคุณจะใช้ UI ที่กำหนดเอง
การค้นพบในเฟรมเวิร์กจะได้รับการจัดการโดยชั้นเรียน
GCKDiscoveryManager
ซึ่งเป็นพร็อพเพอร์ตี้ของ
GCKCastContext
เฟรมเวิร์กจะมีคอมโพเนนต์กล่องโต้ตอบการแคสต์เริ่มต้นสำหรับการเลือกอุปกรณ์และ
รายการอุปกรณ์จะเรียงลำดับแบบพจนานุกรมตามชื่อที่เหมาะกับอุปกรณ์
วิธีการทำงานของการจัดการเซสชัน
Cast SDK นำเสนอแนวคิดของเซสชันการแคสต์ ซึ่งประกอบด้วยขั้นตอนการเชื่อมต่อกับอุปกรณ์ การเปิด (หรือการเข้าร่วม) เว็บ แอปตัวรับสัญญาณ การเชื่อมต่อกับแอปดังกล่าว และการเริ่มต้นใช้งานช่องทางควบคุมสื่อ ดูเว็บรีซีฟเวอร์ คำแนะนำเกี่ยวกับวงจรของแอปพลิเคชัน สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเซสชันการแคสต์และวงจรชีวิตของตัวรับเว็บ
ชั้นเรียนเป็นผู้จัดการเซสชัน
GCKSessionManager
ซึ่งเป็นพร็อพเพอร์ตี้ของ
GCKCastContext
แต่ละเซสชันจะแสดงโดยคลาสย่อยของชั้นเรียน
GCKSession
: ตัวอย่างเช่น
GCKCastSession
แสดงเซสชันที่มีอุปกรณ์แคสต์ คุณเข้าถึงการแคสต์ที่ใช้งานอยู่ในปัจจุบันได้
เซสชัน (หากมี) เป็นพร็อพเพอร์ตี้ currentCastSession
ของ GCKSessionManager
GCKSessionManagerListener
คุณสามารถใช้อินเทอร์เฟซสำหรับตรวจสอบเหตุการณ์
ของเซสชัน เช่น การสร้างเซสชัน
การระงับ การกลับมาทำงานอีกครั้ง และการสิ้นสุด เฟรมเวิร์กจะระงับ
เซสชันที่แอปผู้ส่งทำงานอยู่เบื้องหลังและพยายามกลับมาทำงานอีกครั้ง
เมื่อแอปกลับมาที่เบื้องหน้า (หรือเปิดเครื่องอีกครั้งหลังจาก
การสิ้นสุดแอปที่ผิดปกติ/ฉับพลันขณะมีเซสชันอยู่)
หากกำลังใช้กล่องโต้ตอบแคสต์อยู่ ระบบจะสร้างและแยกเซสชันออก
โดยอัตโนมัติตามท่าทางสัมผัสของผู้ใช้ ไม่เช่นนั้นแอปจะเริ่มต้นและสิ้นสุดได้
เซสชันอย่างชัดแจ้งผ่านเมธอดใน
GCKSessionManager
หากแอปจำเป็นต้องใช้การประมวลผลพิเศษตามวงจรการใช้งานเซสชัน
ก็จะลงทะเบียนอินสแตนซ์ GCKSessionManagerListener
ได้อย่างน้อย 1 รายการด้วย
GCKSessionManager
GCKSessionManagerListener
เป็นโปรโตคอลที่กำหนด
Callback สำหรับเหตุการณ์ดังกล่าว เช่น การเริ่มเซสชัน การสิ้นสุดเซสชัน เป็นต้น
การโอนสตรีม
การรักษาสถานะเซสชันเป็นพื้นฐานของการโอนสตรีมโดย ผู้ใช้สามารถย้ายสตรีมเสียงและวิดีโอที่มีอยู่ในอุปกรณ์ต่างๆ ได้โดยใช้คำสั่งเสียง, Google Home แอปหรือจออัจฉริยะ สื่อจะหยุดเล่นบนอุปกรณ์หนึ่ง (ต้นทาง) แต่เล่นต่อในอุปกรณ์อื่น ( ปลายทาง) อุปกรณ์แคสต์ทุกเครื่องที่มีเฟิร์มแวร์เวอร์ชันล่าสุดสามารถใช้เป็นแหล่งที่มาหรือปลายทางใน การโอนสตรีม
หากต้องการรับอุปกรณ์ปลายทางเครื่องใหม่ระหว่างการโอนสตรีม ให้ใช้
GCKCastSession#device
ในระหว่าง
[sessionManager:didResumeCastSession:]
Callback
โปรดดู การโอนสตรีมใน Web Receiver เพื่อดูข้อมูลเพิ่มเติม
การเชื่อมต่ออีกครั้งอัตโนมัติ
เฟรมเวิร์กการแคสต์เพิ่มตรรกะการเชื่อมต่อใหม่เพื่อจัดการการเชื่อมต่ออีกครั้งโดยอัตโนมัติ ในหลายกรณี เช่น
- กู้คืนจากสัญญาณ Wi-Fi ที่หายไปชั่วคราว
- กู้คืนจากโหมดสลีปของอุปกรณ์
- กู้คืนจากการเล่นขณะล็อกหน้าจอหรือขณะใช้แอปอื่น
- กู้คืนหากแอปขัดข้อง
วิธีการทำงานของการควบคุมสื่อ
หากเซสชันการแคสต์สร้างขึ้นด้วยแอปตัวรับเว็บที่รองรับสื่อ
อินสแตนซ์ของ
GCKRemoteMediaClient
จะสร้างขึ้นโดยอัตโนมัติโดยเฟรมเวิร์ก สามารถเข้าถึงได้ในฐานะ
พร็อพเพอร์ตี้ remoteMediaClient
ของ
GCKCastSession
อินสแตนซ์
วิธีการทั้งหมดใน GCKRemoteMediaClient
ซึ่งส่งคำขอไปยัง Web Receiver
จะแสดงผล
GCKRequest
ออบเจ็กต์ที่
สามารถใช้เพื่อติดตามคำขอนั้น ต
GCKRequestDelegate
สามารถกำหนดให้กับออบเจ็กต์นี้เพื่อรับการแจ้งเตือนเกี่ยวกับ
ของการดำเนินการนี้
คาดว่าอินสแตนซ์ของ GCKRemoteMediaClient
อาจมีการแชร์โดยหลายส่วนของแอป ซึ่งจริงๆ แล้วคอมโพเนนต์ภายในบางส่วน
ของเฟรมเวิร์ก เช่น กล่องโต้ตอบการแคสต์ และตัวควบคุมสื่อขนาดเล็ก
อินสแตนซ์ ด้วยเหตุนี้ GCKRemoteMediaClient
สนับสนุนการลงทะเบียน
GCKRemoteMediaClientListener
ตั้งค่าข้อมูลเมตาของสื่อ
GCKMediaMetadata
คลาสแสดงถึงข้อมูลเกี่ยวกับรายการสื่อที่คุณต้องการแคสต์ ดังต่อไปนี้
ตัวอย่างจะเป็นการสร้างอินสแตนซ์ GCKMediaMetadata
ใหม่สำหรับภาพยนตร์ และตั้งชื่อ
ชื่อรอง ชื่อสตูดิโอบันทึกเสียง และรูปภาพ 2 ภาพ
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
ด้วย enum
ค่า GCKVideoInfoHDRType
เพิ่มตัวควบคุมขนาดเล็ก
ตามการออกแบบการแคสต์ รายการตรวจสอบ แอปผู้ส่งควรมีการควบคุมแบบถาวรที่เรียกว่า มินิ ผู้ควบคุมข้อมูล ควรปรากฏเมื่อผู้ใช้ออกจากหน้าเนื้อหาปัจจุบัน ตัวควบคุมขนาดเล็กช่วยให้เข้าถึงได้ทันทีและแสดงการช่วยเตือน เซสชันการแคสต์ปัจจุบัน
เฟรมเวิร์ก 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 กำหนดให้แอปผู้ส่งต้องมีขยาย ผู้ควบคุมข้อมูล สำหรับสื่อที่กำลังแคสต์อยู่ ตัวควบคุมที่ขยายเป็นเวอร์ชันแบบเต็มหน้าจอของ มินิคอนโทรลเลอร์
ตัวควบคุมที่ขยายเป็นมุมมองแบบเต็มหน้าจอซึ่งให้การควบคุม การเล่นสื่อจากระยะไกล มุมมองนี้น่าจะอนุญาตให้แอปแคสต์จัดการ แง่มุมที่จัดการได้ของเซสชันการแคสต์ ยกเว้นปริมาณ Web Receiver การควบคุมและวงจรเซสชัน (เชื่อมต่อ/หยุดการแคสต์) และยังให้ประสบการณ์ ข้อมูลสถานะเกี่ยวกับเซสชันสื่อ (อาร์ตเวิร์ก ชื่อ คำบรรยาย และอื่นๆ เดียวกัน)
ฟังก์ชันการทำงานของมุมมองนี้ดำเนินการโดย
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 ของคุณ แอป เกี่ยวกับวิธีที่แอปผู้ส่งสามารถกำหนดค่าลักษณะที่ปรากฏของวิดเจ็ตแคสต์ได้
การควบคุมระดับเสียง
เฟรมเวิร์กการแคสต์จะจัดการระดับเสียงสำหรับแอปผู้ส่งโดยอัตโนมัติ
จะซิงค์ข้อมูลกับวอลุ่มของตัวรับเว็บสำหรับ
วิดเจ็ต 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];
จัดการข้อผิดพลาด
แอปของผู้ส่งต้องจัดการกับการเรียกกลับที่ผิดพลาดทั้งหมดและตัดสินใจ การตอบสนองที่ดีที่สุดสำหรับแต่ละขั้นตอน ในวงจรของการแคสต์ แอปสามารถแสดง ให้กับผู้ใช้ หรือผู้ใช้สามารถตัดสินใจสิ้นสุดเซสชันการแคสต์ได้
การบันทึก
GCKLogger
เป็น Singleton ที่ใช้สำหรับการบันทึกตามเฟรมเวิร์ก ใช้เมนู
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