В этом руководстве для разработчиков описывается, как добавить поддержку Google Cast в приложение iOS Sender с помощью iOS Sender SDK.
Мобильное устройство или ноутбук является отправителем , который управляет воспроизведением, а устройство Google Cast — приемником , которое отображает контент на телевизоре.
Фреймворк отправителя относится к двоичному файлу библиотеки классов Cast и связанным ресурсам, присутствующим во время выполнения на отправителе. Приложение отправителя или приложение Cast относится к приложению, также работающему на отправителе. Приложение Web Receiver относится к приложению HTML, работающему на Web Receiver.
Фреймворк отправителя использует асинхронную конструкцию обратного вызова для информирования приложения-отправителя о событиях и для перехода между различными состояниями жизненного цикла приложения Cast.
Поток приложений
Следующие шаги описывают типичный высокоуровневый поток выполнения для приложения-отправителя iOS:
- Фреймворк Cast запускает
GCKDiscoveryManager
на основе свойств, предоставленных вGCKCastOptions
, чтобы начать сканирование устройств. - Когда пользователь нажимает кнопку Cast, фреймворк отображает диалоговое окно Cast со списком обнаруженных устройств Cast.
- Когда пользователь выбирает устройство Cast, фреймворк пытается запустить приложение Web Receiver на устройстве Cast.
- Фреймворк вызывает обратные вызовы в приложении-отправителе, чтобы подтвердить запуск приложения Web Receiver.
- Фреймворк создает канал связи между отправителем и приложениями Web Receiver.
- Фреймворк использует канал связи для загрузки и управления воспроизведением мультимедиа на веб-приемнике.
- Фреймворк синхронизирует состояние воспроизведения мультимедиа между отправителем и веб-получателем: когда пользователь выполняет действия пользовательского интерфейса отправителя, фреймворк передает эти запросы на управление мультимедиа веб-получателю, а когда веб-получатель отправляет обновления статуса мультимедиа, фреймворк обновляет состояние пользовательского интерфейса отправителя.
- Когда пользователь нажимает кнопку Cast, чтобы отключиться от устройства Cast, фреймворк отключит приложение-отправитель от веб-приемника.
Для устранения неполадок отправителя вам необходимо включить ведение журнала .
Полный список всех классов, методов и событий в фреймворке Google Cast iOS см. в Справочнике Google Cast iOS API . В следующих разделах описываются шаги по интеграции Cast в ваше приложение iOS.
Вызов методов из основного потока
Инициализируйте контекст Cast
Фреймворк Cast имеет глобальный одноэлементный объект GCKCastContext
, который координирует все действия фреймворка. Этот объект должен быть инициализирован на ранней стадии жизненного цикла приложения, обычно в методе -[application:didFinishLaunchingWithOptions:]
делегата приложения, чтобы автоматическое возобновление сеанса при перезапуске приложения-отправителя могло сработать должным образом.
Объект GCKCastOptions
должен быть предоставлен при инициализации GCKCastContext
. Этот класс содержит параметры, которые влияют на поведение фреймворка. Наиболее важным из них является идентификатор приложения Web Receiver, который используется для фильтрации результатов обнаружения и запуска приложения Web Receiver при запуске сеанса Cast.
Метод -[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
, который можно использовать для подсветки кнопки Cast в первый раз, когда доступен Web Receiver. Приложение-отправитель может настроить текст, положение текста заголовка и кнопку Dismiss.Кнопка Cast : Начиная с версии Cast iOS sender SDK 4.6.0, кнопка cast всегда видна, когда устройство-отправитель подключено к Wi-Fi. Когда пользователь впервые нажимает кнопку Cast после первоначального запуска приложения, появляется диалоговое окно разрешений, чтобы пользователь мог предоставить приложению доступ к локальной сети к устройствам в сети. Впоследствии, когда пользователь нажимает кнопку cast, отображается диалоговое окно cast, в котором перечислены обнаруженные устройства. Когда пользователь нажимает кнопку cast, когда устройство подключено, оно отображает текущие метаданные мультимедиа (такие как название, название студии звукозаписи и миниатюрное изображение) или позволяет пользователю отключиться от устройства cast. Когда пользователь нажимает кнопку cast, когда нет доступных устройств, отображается экран, предоставляющий пользователю информацию о том, почему устройства не найдены, и как устранить неполадки.
Мини-контроллер : когда пользователь транслирует контент и переходит со страницы текущего контента или расширенного контроллера на другой экран в приложении-отправителе, в нижней части экрана отображается мини-контроллер, позволяющий пользователю видеть метаданные текущего транслируемого медиаконтента и управлять воспроизведением.
Расширенный контроллер : если пользователь транслирует контент, нажав на уведомление о мультимедиа или мини-контроллер, запустится расширенный контроллер, который отображает метаданные воспроизводимого в данный момент мультимедиа и предоставляет несколько кнопок для управления воспроизведением мультимедиа.
Добавить кнопку трансляции
Фреймворк предоставляет компонент кнопки 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
также можно добавить непосредственно в раскадровку.
Настроить обнаружение устройств
В рамках платформы обнаружение устройств происходит автоматически. Нет необходимости явно запускать или останавливать процесс обнаружения, если только вы не реализуете пользовательский интерфейс.
Discovery в фреймворке управляется классом GCKDiscoveryManager
, который является свойством GCKCastContext
. Фреймворк предоставляет компонент диалога Cast по умолчанию для выбора и управления устройством. Список устройств упорядочен лексикографически по понятному имени устройства.
Как работает управление сеансом
Cast SDK представляет концепцию сеанса Cast, установление которого объединяет шаги подключения к устройству, запуска (или присоединения) приложения Web Receiver, подключения к этому приложению и инициализации канала управления мультимедиа. См. руководство по жизненному циклу приложения Web Receiver для получения дополнительной информации о сеансах Cast и жизненном цикле Web Receiver.
Сеансы управляются классом GCKSessionManager
, который является свойством GCKCastContext
. Отдельные сеансы представлены подклассами класса GCKSession
: например, GCKCastSession
представляет сеансы с устройствами Cast. Вы можете получить доступ к текущему активному сеансу Cast (если таковой имеется) как к свойству currentCastSession
GCKSessionManager
.
Интерфейс GCKSessionManagerListener
может использоваться для мониторинга событий сеанса, таких как создание сеанса, приостановка, возобновление и завершение. Фреймворк автоматически приостанавливает сеансы, когда приложение-отправитель переходит в фоновый режим, и пытается возобновить их, когда приложение возвращается на передний план (или перезапускается после ненормального/резкого завершения работы приложения, пока сеанс был активен).
Если используется диалог Cast, то сеансы создаются и автоматически завершаются в ответ на жесты пользователя. В противном случае приложение может запускать и завершать сеансы явно с помощью методов GCKSessionManager
.
Если приложению необходимо выполнить специальную обработку в ответ на события жизненного цикла сеанса, оно может зарегистрировать один или несколько экземпляров GCKSessionManagerListener
с помощью GCKSessionManager
. GCKSessionManagerListener
— это протокол, который определяет обратные вызовы для таких событий, как начало сеанса, конец сеанса и т. д.
Потоковая передача
Сохранение состояния сеанса является основой потоковой передачи, где пользователи могут перемещать существующие аудио- и видеопотоки между устройствами с помощью голосовых команд, приложения Google Home или интеллектуальных дисплеев. Медиа останавливается на одном устройстве (источнике) и продолжается на другом (адресате). Любое устройство Cast с последней версией прошивки может служить источником или адресатом потоковой передачи.
Чтобы получить новое целевое устройство во время передачи потока, используйте свойство GCKCastSession#device
во время обратного вызова [sessionManager:didResumeCastSession:]
.
Более подробную информацию см. в разделе Передача потока на веб-приемнике .
Автоматическое переподключение
Фреймворк Cast добавляет логику повторного подключения для автоматической обработки повторного подключения во многих сложных случаях, таких как:
- Восстановление после временной потери WiFi
- Выход из спящего режима устройства
- Восстановление после перевода приложения в фоновый режим
- Восстановление в случае сбоя приложения
Как работает контроль над СМИ
Если сеанс Cast установлен с приложением Web Receiver, которое поддерживает пространство имен мультимедиа, экземпляр GCKRemoteMediaClient
будет создан фреймворком автоматически; к нему можно получить доступ как к свойству remoteMediaClient
экземпляра GCKCastSession
.
Все методы GCKRemoteMediaClient
, которые отправляют запросы в Web Receiver, возвращают объект GCKRequest
, который можно использовать для отслеживания этого запроса. GCKRequestDelegate
можно назначить этому объекту для получения уведомлений о конечном результате операции.
Ожидается, что экземпляр GCKRemoteMediaClient
может совместно использоваться несколькими частями приложения, и, действительно, некоторые внутренние компоненты фреймворка, такие как диалог Cast и мини-элементы управления мультимедиа, совместно используют экземпляр. С этой целью GCKRemoteMediaClient
поддерживает регистрацию нескольких GCKRemoteMediaClientListener
s.
Установить метаданные мультимедиа
Класс 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 TV, а также высоту и ширину в пикселях. Варианты формата 4K указаны в свойстве hdrType
с помощью значений перечисления GCKVideoInfoHDRType
.
Добавить мини-контроллеры
Согласно контрольному списку дизайна Cast , приложение-отправитель должно предоставлять постоянный элемент управления, известный как мини-контроллер , который должен появляться, когда пользователь покидает текущую страницу контента. Мини-контроллер обеспечивает мгновенный доступ и видимое напоминание для текущего сеанса Cast.
Фреймворк Cast предоставляет панель управления GCKUIMiniMediaControlsViewController
, которую можно добавлять в сцены, в которых вы хотите отображать мини-контроллер.
Когда приложение-отправитель воспроизводит видео- или аудиопоток в прямом эфире, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы на мини-контроллере.
Информацию о том, как приложение отправителя может настраивать внешний вид виджетов Cast, см. в разделе Настройка пользовательского интерфейса iOS Sender .
Добавить мини-контроллер в приложение-отправитель можно двумя способами:
- Позвольте платформе 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; }
В корневом контроллере представления создайте экземпляр 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 автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы в расширенном контроллере.
Подробнее о том, как приложение-отправитель может настроить внешний вид виджетов Cast, см. в разделе Применение пользовательских стилей к приложению iOS.
Регулировка громкости
Фреймворк Cast автоматически управляет громкостью для приложения-отправителя. Фреймворк автоматически синхронизируется с громкостью Web Receiver для предоставленных виджетов пользовательского интерфейса. Чтобы синхронизировать слайдер, предоставленный приложением, используйте 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];
Обработка ошибок
Для приложений-отправителей очень важно обрабатывать все обратные вызовы ошибок и выбирать наилучший ответ для каждого этапа жизненного цикла Cast. Приложение может отображать пользователю диалоговые окна с ошибками или может решить завершить сеанс Cast.
Ведение журнала
GCKLogger
— это синглтон, используемый фреймворком для ведения журнала. Используйте 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
.
В этом руководстве для разработчиков описывается, как добавить поддержку Google Cast в приложение iOS Sender с помощью iOS Sender SDK.
Мобильное устройство или ноутбук является отправителем , который управляет воспроизведением, а устройство Google Cast — приемником , которое отображает контент на телевизоре.
Фреймворк отправителя относится к двоичному файлу библиотеки классов Cast и связанным ресурсам, присутствующим во время выполнения на отправителе. Приложение отправителя или приложение Cast относится к приложению, также работающему на отправителе. Приложение Web Receiver относится к приложению HTML, работающему на Web Receiver.
Фреймворк отправителя использует асинхронную конструкцию обратного вызова для информирования приложения-отправителя о событиях и для перехода между различными состояниями жизненного цикла приложения Cast.
Поток приложений
Следующие шаги описывают типичный высокоуровневый поток выполнения для приложения-отправителя iOS:
- Фреймворк Cast запускает
GCKDiscoveryManager
на основе свойств, предоставленных вGCKCastOptions
, чтобы начать сканирование устройств. - Когда пользователь нажимает кнопку Cast, фреймворк отображает диалоговое окно Cast со списком обнаруженных устройств Cast.
- Когда пользователь выбирает устройство Cast, фреймворк пытается запустить приложение Web Receiver на устройстве Cast.
- Фреймворк вызывает обратные вызовы в приложении-отправителе, чтобы подтвердить запуск приложения Web Receiver.
- Фреймворк создает канал связи между отправителем и приложениями Web Receiver.
- Фреймворк использует канал связи для загрузки и управления воспроизведением мультимедиа на веб-приемнике.
- Фреймворк синхронизирует состояние воспроизведения мультимедиа между отправителем и веб-получателем: когда пользователь выполняет действия пользовательского интерфейса отправителя, фреймворк передает эти запросы на управление мультимедиа веб-получателю, а когда веб-получатель отправляет обновления статуса мультимедиа, фреймворк обновляет состояние пользовательского интерфейса отправителя.
- Когда пользователь нажимает кнопку Cast, чтобы отключиться от устройства Cast, фреймворк отключит приложение-отправитель от веб-приемника.
Для устранения неполадок отправителя вам необходимо включить ведение журнала .
Полный список всех классов, методов и событий в фреймворке Google Cast iOS см. в Справочнике Google Cast iOS API . В следующих разделах описываются шаги по интеграции Cast в ваше приложение iOS.
Вызов методов из основного потока
Инициализируйте контекст Cast
Фреймворк Cast имеет глобальный одноэлементный объект GCKCastContext
, который координирует все действия фреймворка. Этот объект должен быть инициализирован на ранней стадии жизненного цикла приложения, обычно в методе -[application:didFinishLaunchingWithOptions:]
делегата приложения, чтобы автоматическое возобновление сеанса при перезапуске приложения-отправителя могло сработать должным образом.
Объект GCKCastOptions
должен быть предоставлен при инициализации GCKCastContext
. Этот класс содержит параметры, которые влияют на поведение фреймворка. Наиболее важным из них является идентификатор приложения Web Receiver, который используется для фильтрации результатов обнаружения и запуска приложения Web Receiver при запуске сеанса Cast.
Метод -[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
, который можно использовать для подсветки кнопки Cast в первый раз, когда доступен Web Receiver. Приложение-отправитель может настроить текст, положение текста заголовка и кнопку Dismiss.Кнопка Cast : Начиная с версии Cast iOS sender SDK 4.6.0, кнопка cast всегда видна, когда устройство-отправитель подключено к Wi-Fi. Когда пользователь впервые нажимает кнопку Cast после первоначального запуска приложения, появляется диалоговое окно разрешений, чтобы пользователь мог предоставить приложению доступ к локальной сети к устройствам в сети. Впоследствии, когда пользователь нажимает кнопку cast, отображается диалоговое окно cast, в котором перечислены обнаруженные устройства. Когда пользователь нажимает кнопку cast, когда устройство подключено, оно отображает текущие метаданные мультимедиа (такие как название, название студии звукозаписи и миниатюрное изображение) или позволяет пользователю отключиться от устройства cast. Когда пользователь нажимает кнопку cast, когда нет доступных устройств, отображается экран, предоставляющий пользователю информацию о том, почему устройства не найдены, и как устранить неполадки.
Мини-контроллер : когда пользователь транслирует контент и переходит со страницы текущего контента или расширенного контроллера на другой экран в приложении-отправителе, в нижней части экрана отображается мини-контроллер, позволяющий пользователю видеть метаданные текущего транслируемого медиаконтента и управлять воспроизведением.
Расширенный контроллер : если пользователь транслирует контент, нажав на уведомление о мультимедиа или мини-контроллер, запустится расширенный контроллер, который отображает метаданные воспроизводимого в данный момент мультимедиа и предоставляет несколько кнопок для управления воспроизведением мультимедиа.
Добавить кнопку трансляции
Фреймворк предоставляет компонент кнопки 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
также можно добавить непосредственно в раскадровку.
Настроить обнаружение устройств
В рамках платформы обнаружение устройств происходит автоматически. Нет необходимости явно запускать или останавливать процесс обнаружения, если только вы не реализуете пользовательский интерфейс.
Discovery в фреймворке управляется классом GCKDiscoveryManager
, который является свойством GCKCastContext
. Фреймворк предоставляет компонент диалога Cast по умолчанию для выбора и управления устройством. Список устройств упорядочен лексикографически по понятному имени устройства.
Как работает управление сеансом
Cast SDK представляет концепцию сеанса Cast, установление которого объединяет шаги подключения к устройству, запуска (или присоединения) приложения Web Receiver, подключения к этому приложению и инициализации канала управления мультимедиа. См. руководство по жизненному циклу приложения Web Receiver для получения дополнительной информации о сеансах Cast и жизненном цикле Web Receiver.
Сеансы управляются классом GCKSessionManager
, который является свойством GCKCastContext
. Отдельные сеансы представлены подклассами класса GCKSession
: например, GCKCastSession
представляет сеансы с устройствами Cast. Вы можете получить доступ к текущему активному сеансу Cast (если таковой имеется) как к свойству currentCastSession
GCKSessionManager
.
Интерфейс GCKSessionManagerListener
может использоваться для мониторинга событий сеанса, таких как создание сеанса, приостановка, возобновление и завершение. Фреймворк автоматически приостанавливает сеансы, когда приложение-отправитель переходит в фоновый режим, и пытается возобновить их, когда приложение возвращается на передний план (или перезапускается после ненормального/резкого завершения работы приложения, пока сеанс был активен).
Если используется диалог Cast, то сеансы создаются и автоматически завершаются в ответ на жесты пользователя. В противном случае приложение может запускать и завершать сеансы явно с помощью методов GCKSessionManager
.
Если приложению необходимо выполнить специальную обработку в ответ на события жизненного цикла сеанса, оно может зарегистрировать один или несколько экземпляров GCKSessionManagerListener
с помощью GCKSessionManager
. GCKSessionManagerListener
— это протокол, который определяет обратные вызовы для таких событий, как начало сеанса, конец сеанса и т. д.
Потоковая передача
Сохранение состояния сеанса является основой потоковой передачи, где пользователи могут перемещать существующие аудио- и видеопотоки между устройствами с помощью голосовых команд, приложения Google Home или интеллектуальных дисплеев. Медиа останавливается на одном устройстве (источнике) и продолжается на другом (адресате). Любое устройство Cast с последней версией прошивки может служить источником или адресатом потоковой передачи.
Чтобы получить новое целевое устройство во время передачи потока, используйте свойство GCKCastSession#device
во время обратного вызова [sessionManager:didResumeCastSession:]
.
Более подробную информацию см. в разделе Передача потока на веб-приемнике .
Автоматическое переподключение
Фреймворк Cast добавляет логику повторного подключения для автоматической обработки повторного подключения во многих сложных случаях, таких как:
- Восстановление после временной потери WiFi
- Выход из спящего режима устройства
- Восстановление после перевода приложения в фоновый режим
- Восстановление в случае сбоя приложения
Как работает контроль над СМИ
Если сеанс Cast установлен с приложением Web Receiver, которое поддерживает пространство имен мультимедиа, экземпляр GCKRemoteMediaClient
будет создан фреймворком автоматически; к нему можно получить доступ как к свойству remoteMediaClient
экземпляра GCKCastSession
.
Все методы GCKRemoteMediaClient
, которые отправляют запросы в Web Receiver, возвращают объект GCKRequest
, который можно использовать для отслеживания этого запроса. GCKRequestDelegate
можно назначить этому объекту для получения уведомлений о конечном результате операции.
Ожидается, что экземпляр GCKRemoteMediaClient
может совместно использоваться несколькими частями приложения, и, действительно, некоторые внутренние компоненты фреймворка, такие как диалог Cast и мини-элементы управления мультимедиа, совместно используют экземпляр. С этой целью GCKRemoteMediaClient
поддерживает регистрацию нескольких GCKRemoteMediaClientListener
s.
Установить метаданные мультимедиа
Класс 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 TV, а также высоту и ширину в пикселях. Варианты формата 4K указаны в свойстве hdrType
с помощью значений перечисления GCKVideoInfoHDRType
.
Добавить мини-контроллеры
Согласно контрольному списку дизайна Cast , приложение-отправитель должно предоставлять постоянный элемент управления, известный как мини-контроллер , который должен появляться, когда пользователь покидает текущую страницу контента. Мини-контроллер обеспечивает мгновенный доступ и видимое напоминание для текущего сеанса Cast.
Фреймворк Cast предоставляет панель управления GCKUIMiniMediaControlsViewController
, которую можно добавлять в сцены, в которых вы хотите отображать мини-контроллер.
Когда приложение-отправитель воспроизводит видео- или аудиопоток в прямом эфире, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы на мини-контроллере.
Информацию о том, как приложение отправителя может настраивать внешний вид виджетов Cast, см. в разделе Настройка пользовательского интерфейса iOS Sender .
Добавить мини-контроллер в приложение-отправитель можно двумя способами:
- Позвольте платформе 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; }
В корневом контроллере представления создайте экземпляр 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 автоматически отображает кнопку воспроизведения/остановки вместо кнопки воспроизведения/паузы в расширенном контроллере.
Подробнее о том, как приложение-отправитель может настроить внешний вид виджетов Cast, см. в разделе Применение пользовательских стилей к приложению iOS.
Регулировка громкости
Фреймворк Cast автоматически управляет громкостью для приложения-отправителя. Фреймворк автоматически синхронизируется с громкостью Web Receiver для предоставленных виджетов пользовательского интерфейса. Чтобы синхронизировать слайдер, предоставленный приложением, используйте 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];
Обработка ошибок
Для приложений-отправителей очень важно обрабатывать все обратные вызовы ошибок и выбирать наилучший ответ для каждого этапа жизненного цикла Cast. Приложение может отображать пользователю диалоговые окна с ошибками или может решить завершить сеанс Cast.
Ведение журнала
GCKLogger
— это синглтон, используемый фреймворком для ведения журнала. Используйте 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
.
В этом руководстве для разработчиков описывается, как добавить поддержку Google Cast в приложение iOS Sender с помощью iOS Sender SDK.
Мобильное устройство или ноутбук является отправителем , который управляет воспроизведением, а устройство Google Cast — приемником , которое отображает контент на телевизоре.
Структура отправителя относится к библиотеке актеров библиотеки и связанных с ними ресурсов, присутствующих во время выполнения отправителя. Приложение для отправителя или приложение CAST относится к приложению, также работающему на отправителе. Приложение Web Receiver относится к приложению HTML, работающему на веб -приемнике.
Фреймворк отправителя использует асинхронную конструкцию обратного вызова для информирования приложения-отправителя о событиях и для перехода между различными состояниями жизненного цикла приложения Cast.
Поток приложений
В следующих шагах описывается типичный поток выполнения высокого уровня для приложения для отправителя iOS:
- Актерская структура начинает
GCKDiscoveryManager
на основе свойств, представленных вGCKCastOptions
, чтобы начать сканирование на устройства. - Когда пользователь нажимает кнопку Cast, фреймворк отображает диалоговое окно Cast со списком обнаруженных устройств Cast.
- Когда пользователь выбирает устройство Cast, фреймворк пытается запустить приложение Web Receiver на устройстве Cast.
- Фреймворк вызывает обратные вызовы в приложении-отправителе, чтобы подтвердить запуск приложения Web Receiver.
- Фреймворк создает канал связи между отправителем и приложениями Web Receiver.
- Фреймворк использует канал связи для загрузки и управления воспроизведением мультимедиа на веб-приемнике.
- Фреймворк синхронизирует состояние воспроизведения мультимедиа между отправителем и веб-получателем: когда пользователь выполняет действия пользовательского интерфейса отправителя, фреймворк передает эти запросы на управление мультимедиа веб-получателю, а когда веб-получатель отправляет обновления статуса мультимедиа, фреймворк обновляет состояние пользовательского интерфейса отправителя.
- Когда пользователь нажимает кнопку Cast, чтобы отключиться от устройства Cast, фреймворк отключит приложение-отправитель от веб-приемника.
Чтобы устранение неполадок вашего отправителя, вам нужно включить журнал .
Для получения комплексного списка всех классов, методов и событий в рамках Google Cast IOS см. Справочник Google Cast IOS API . Следующие разделы охватывают шаги для интеграции актерского состава в приложение для iOS.
Методы вызова основного потока
Инициализировать контекст актерского состава
У Cast Framework есть глобальный объект 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
Актерские виджеты UX
Актерский iOS SDK предоставляет эти виджеты, которые соответствуют контрольному списку дизайна актеров:
Вводное наложение : класс
GCKCastContext
имеет метод,presentCastInstructionsViewControllerOnceWithCastButton
, который можно использовать для освещения кнопки CAST при первом доступном веб -приемнике. Приложение Sender может настроить текст, положение текста заголовка и кнопку увольнения.Кнопка отливки : начиная с литой iOS Sender SDK 4.6.0, кнопка отливки всегда видно, когда устройство отправителя подключено к Wi-Fi. В первый раз, когда пользователь нажимает на кнопку CAST после первоначального запуска приложения, появляется диалоговое окно разрешений, чтобы пользователь мог предоставить приложению локальную сеть доступ к устройствам в сети. Впоследствии, когда пользователь нажимает на кнопку отливки, отображается диалог отлив, в котором перечислены обнаруженные устройства. Когда пользователь нажимает на кнопку CAST во время подключения устройства, он отображает текущие метаданные носителя (например, заголовок, имя студии звукозаписи и миниатюрное изображение) или позволяет пользователю отключиться от устройства CAST. Когда пользователь нажимает на кнопку CAST, в то время как нет доступных устройств, будет отображаться экран, предоставляя пользователь информацию о том, почему устройства не найдены и как устранять устранение неполадок.
Mini Controller : Когда пользователь отбирает контент и отошел от текущей страницы содержимого или расширенного контроллера к другому экрану в приложении Sender, Mini Controller отображается в нижней части экрана, чтобы пользователь позволил пользователю видеть в настоящее время метаданные Castling Media и управлять воспроизведением.
Расширенный контроллер : Когда пользователь разыгрывает контент, если он нажимает на уведомление о носителях или мини -контроллер, расширенный контроллер запускает, который отображает в настоящее время игровые метаданные медиа и предоставляет несколько кнопок для управления воспроизведением носителя.
Добавить кнопку литой
Framework обеспечивает компонент литой кнопки в качестве подкласса 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];
По умолчанию, нажатие кнопки откроет диалог отлив, который обеспечивается Framework.
GCKUICastButton
также можно добавить непосредственно в раскадровку.
Настроить обнаружение устройств
В структуре обнаружение устройства происходит автоматически. Нет необходимости явно начинать или останавливать процесс обнаружения, если вы не реализуете пользовательский пользовательский интерфейс.
Discovery in The Framework управляется классом GCKDiscoveryManager
, который является свойством GCKCastContext
. Framework обеспечивает диалоговый компонент по умолчанию для выбора устройства и управления. Список устройств упорядочен лексикографически по имени.
Как работает управление сеансом
Cast SDK представляет концепцию актеров, создание которой объединяет шаги подключения к устройству, запуск (или соединения) приложения для веб -получателя, подключения к этому приложению и инициализации канала управления носителем. См. Руководство по жизненному циклу приложения для веб -получателя для получения дополнительной информации о сессиях CAST и жизненном цикле веб -получателя.
Сессии управляются классом GCKSessionManager
, который является свойством GCKCastContext
. Отдельные сеансы представлены подклассами класса GCKSession
: например, GCKCastSession
представляет сеансы GCKSessionManager
currentCastSession
Интерфейс GCKSessionManagerListener
может использоваться для мониторинга событий сеанса, таких как создание сеанса, подвеска, возобновление и прекращение. Фреймворк автоматически приостанавливает сеансы, когда приложение отправителя входит в фон, и пытается возобновить их, когда приложение возвращается на передний план (или перезаписывается после аномального/резкого завершения приложения, когда сеанс был активным).
Если используется диалоговое окно CAST, то автоматически создаются сеансы и разбиты в ответ на жесты пользователей. В противном случае приложение может запускать и заканчивать сеансы явно с помощью методов на GCKSessionManager
.
Если приложение необходимо выполнить специальную обработку в ответ на события жизненного цикла сеанса, оно может зарегистрировать один или несколько экземпляров GCKSessionManagerListener
с GCKSessionManager
. GCKSessionManagerListener
- это протокол, который определяет обратные вызовы для таких событий, как запуск сеанса, конец сеанса и так далее.
Потоковая передача
Сохранение состояния сеанса является основой передачи потока, где пользователи могут перемещать существующие аудио и видеопотоки на разных устройствах, используя голосовые команды, приложение Google Home или Smart Displays. СМИ перестает играть на одном устройстве (источник) и продолжается на другом (пункт назначения). Любое отличное устройство с новейшей прошивкой может служить источниками или направлениями при передаче потока.
Чтобы получить новое назначение устройства во время передачи потока, используйте свойство GCKCastSession#device
во время [sessionManager:didResumeCastSession:]
обратный вызов.
См. Перевод потока на веб -приемнике для получения дополнительной информации.
Автоматическое переподключение
Структура литой добавляет логику переподключения, чтобы автоматически обрабатывать переподключение во многих тонких угловых случаях, таких как:
- Восстановиться после временной потери Wi -Fi
- Восстановиться после сна
- Восстановиться после фонового приложения
- Восстановиться, если приложение разбилось
Как работает управление медиа
Если сессия создана с помощью приложения для веб -приемника, которое поддерживает пространство имен медиа, экземпляр GCKRemoteMediaClient
будет создан автоматически в рамках; Доступ к нему можно получить как свойство remoteMediaClient
экземпляра GCKCastSession
.
Все методы на GCKRemoteMediaClient
, какие запросы на вопрос в веб -приемник возвращают объект GCKRequest
, который можно использовать для отслеживания этого запроса. GCKRequestDelegate
может быть назначен этому объекту для получения уведомлений о возможном результате операции.
Ожидается, что экземпляр GCKRemoteMediaClient
может быть использован несколькими частями приложения, и, действительно, некоторые внутренние компоненты структуры, таких как диалог CAST и Mini Media Controls, делятся экземпляром. С этой целью GCKRemoteMediaClient
поддерживает регистрацию множественных GCKRemoteMediaClientListener
S.
Установить метаданные мультимедиа
Класс 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 TV и высота и ширину в пикселях. Варианты формата 4K указаны в свойстве hdrType
с помощью значений enum GCKVideoInfoHDRType
.
Добавить мини -контроллеры
Согласно контрольному списку дизайна актеров , приложение для отправителя должно предоставить постоянный контроль, известный как мини -контроллер , который должен появляться, когда пользователь отступает от текущей страницы содержимого. Mini Controller обеспечивает мгновенный доступ и видимое напоминание для текущего сессии.
Cast Framework предоставляет панель управления, GCKUIMiniMediaControlsViewController
, которая может быть добавлена в сцены, в которых вы хотите показать мини -контроллер.
Когда ваше приложение для отправителя воспроизводит видео или аудио в прямом эфире, SDK автоматически отображает кнопку воспроизведения/остановки вместо кнопки Play/Pause в мини -контроллере.
См. Настройте пользовательский интерфейс отправителя iOS для того, как ваше приложение для отправителя может настроить внешний вид виджетов.
Есть два способа добавить мини -контроллер в приложение для отправителя:
- Пусть кастрюля управляет макетом мини -контроллера, обернув ваш существующий контроллер представления своим собственным контроллером представления.
- Управляйте макетом виджета Mini Controller, добавив его к существующему контроллеру представления, предоставив подвесок в раскадровке.
Обернуть с помощью 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; }
В вашем контроллере с корнем создайте экземпляр 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]; }
Добавить расширенный контроллер
Контрольный список Design Design Google требует приложения для отправителя для предоставления расширенного контроллера для сети. Расширенный контроллер - это полная версия мини -контроллера.
Расширенный контроллер - это полноэкранный вид, который предлагает полное управление воспроизведением удаленного носителя. Эта точка зрения должна позволить приложению Casting управлять каждым управляемым аспектом актеров, за исключением управления объемом и жизненным циклом сеанса веб -приемника (Connect/Stop Casting). Он также предоставляет всю информацию о статусе о сессии СМИ (произведения искусства, название, подзаголовок и т. Д.).
Функциональность этой точки зрения реализована классом 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 автоматически отображает кнопку воспроизведения/остановки вместо кнопки Play/Pause в расширенном контроллере.
См. Применить пользовательские стили к приложению для iOS для того, как ваше приложение для отправителя может настроить внешний вид виджетов актеров.
Регулировка громкости
Актерная структура автоматически управляет томом для приложения отправителя. Фтруктура автоматически синхронизируется с объемом веб -приемника для предоставленных виджетов пользовательского интерфейса. Для синхронизации слайдера, предоставленного приложением, используйте 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
- это синглтон, используемый для регистрации за рамками. Используйте 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
.