iOS 앱에 Cast 통합

이 개발자 가이드에서는 iOS에 Google Cast 지원을 추가하는 방법을 설명합니다. iOS Sender SDK를 사용하여 발신기 앱에 전달됩니다.

휴대기기 또는 노트북은 재생을 제어하는 발신자이며, Google Cast 기기는 TV에 콘텐츠를 표시하는 수신기입니다.

발신자 프레임워크는 Cast 클래스 라이브러리 바이너리 및 연결된 리소스 수에 따라 다릅니다 발신자 앱 또는 Cast 앱 발신자에서도 실행 중인 앱을 나타냅니다. 웹 수신기 앱 웹 수신기에서 실행되는 HTML 애플리케이션을 나타냅니다.

발신자 프레임워크는 비동기 콜백 설계를 사용하여 발신자에게 알립니다. 이벤트 앱 및 Cast 앱 수명 주기의 다양한 상태 간 전환 있습니다.

앱 흐름

다음 단계에서는 발신자의 일반적인 개략적인 실행 흐름을 설명합니다. iOS 앱:

  • Cast 프레임워크는 GCKDiscoveryManager Google Cloud Platform에서 제공하는 GCKCastOptions 도착 기기 검색을 시작합니다
  • 사용자가 전송 버튼을 클릭하면 프레임워크에서 전송 버튼을 표시합니다. 검색된 Cast 기기 목록이 포함된 대화상자
  • 사용자가 Cast 기기를 선택하면 프레임워크는 Cast 기기의 웹 수신기 앱
  • 프레임워크는 발신기 앱에서 콜백을 호출하여 웹 수신기 앱이 실행되었습니다.
  • 프레임워크는 발신자와 이메일 주소 간에 통신 채널을 생성합니다. 웹 수신기 앱
  • 프레임워크는 통신 채널을 사용하여 미디어를 로드하고 제어합니다. 웹 수신기에서 재생됩니다.
  • 프레임워크는 미디어 재생 상태를 보내는 발신자와 Web Receiver: 사용자가 발신자 UI 작업을 하면 프레임워크는 해당 미디어 제어 요청을 웹 수신기에 보내고, 웹 수신기가 미디어 상태 업데이트를 전송하면 프레임워크는 발신자 UI의 상태를 업데이트합니다.
  • 사용자가 전송 버튼을 클릭하여 Cast 기기의 연결을 해제할 때 프레임워크는 웹 수신기에서 발신기 앱의 연결을 해제합니다.

발신자의 문제를 해결하려면 로깅을 사용 설정해야 합니다.

Google Cast의 모든 클래스, 메서드, 이벤트의 전체 목록은 Google Cast iOS API 참조 참조. 다음 섹션에서 단계를 다룹니다. iOS 앱에 Cast 통합하기에 대해 자세히 알아보세요.

기본 스레드에서 메서드 호출

Cast 컨텍스트 초기화

Cast 프레임워크에는 전역 싱글톤 객체인 GCKCastContext 는 프레임워크의 모든 활동을 조정합니다. 이 객체를 초기화해야 합니다. 애플리케이션 수명 주기 초기에, 일반적으로 -[application:didFinishLaunchingWithOptions:] 메서드를 사용해야 합니다. 발신기 앱 다시 시작 시 자동 세션 재개가 제대로 트리거될 수 있습니다.

GCKCastOptions GCKCastContext을 초기화할 때 객체를 제공해야 합니다. 이 클래스에는 프레임워크 동작에 영향을 미치는 옵션이 포함되어 있습니다. 가장 이 중에서 중요한 웹 수신기 애플리케이션 ID는 검색 결과를 확인하고 Cast 세션이 있을 때 웹 수신기 앱을 실행할 수 있습니다. 시작됩니다

-[application:didFinishLaunchingWithOptions:] 메서드를 사용하는 것도 좋은 방법입니다. 프레임워크에서 로깅 메시지를 수신하도록 로깅 대리자를 설정합니다. 이는 디버깅 및 문제 해결에 유용할 수 있습니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
@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)
    }
  }
}
Objective-C

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 iOS 발신기 SDK 4.6.0부터 전송 버튼이 항상 표시됨 발신기 기기가 Wi-Fi에 연결되었을 때 사용자가 처음 탭할 때 '전송' 버튼에 표시될 수도 있습니다. 사용자가 네트워크에 연결된 장치에 대한 로컬 네트워크 액세스 권한을 앱에 부여할 수 있도록 않습니다. 이후 사용자가 전송 버튼을 탭하면 전송 대화상자가 표시되고 검색된 기기가 나열됩니다. 사용자가 탭할 때 전송 버튼을 누르면 현재 실행 중인 기기가 미디어 메타데이터 (예: 제목, 녹음 스튜디오의 이름, 썸네일) 이미지)로 전송하거나 사용자가 전송 기기에서 연결을 해제할 수 있도록 허용합니다. 사용자가 사용 가능한 기기가 없을 때 전송 버튼 탭 가 표시되어 기기를 찾을 수 없는 이유에 대한 정보를 사용자에게 제공합니다. 문제 해결 방법을 알아봅니다.

  • 미니 컨트롤러: 사용자가 콘텐츠를 전송하고 현재 페이지에서 벗어났을 때 확장 컨트롤러가 발신기 앱의 다른 화면으로 전송되면 미니 컨트롤러가 화면 하단에 표시되어 사용자가 현재 전송 중인 미디어 메타데이터를 확인하고 재생을 제어합니다.

  • 확장 컨트롤러: 사용자가 콘텐츠를 전송할 때 미디어 알림을 클릭하거나 미니 컨트롤러가 실행되면 확장 컨트롤러가 실행되어 현재 재생 중인 미디어 메타데이터를 제어하고 있습니다.

전송 버튼 추가

프레임워크는 전송 버튼 구성요소를 UIButton 서브클래스로 제공합니다. 가능 UIBarButtonItem에 래핑하여 앱의 제목 표시줄에 추가됩니다. 일반적인 UIViewController 서브클래스는 다음과 같이 전송 버튼을 설치할 수 있습니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
castButton.tintColor = UIColor.gray
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: castButton)
Objective-C
GCKUICastButton *castButton = [[GCKUICastButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
castButton.tintColor = [UIColor grayColor];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:castButton];

기본적으로 버튼을 탭하면 프레임워크입니다

GCKUICastButton 스토리보드에 직접 추가할 수도 있습니다.

기기 검색 구성

프레임워크에서는 기기 검색이 자동으로 이루어집니다. 따라서 탐색 프로세스를 명시적으로 시작하거나 중지할 수 있습니다.

프레임워크에서의 검색은 클래스에 의해 관리됨 GCKDiscoveryManager님, 이는 GCKCastContext 이 프레임워크는 기기 선택을 위한 기본 Cast 대화상자 구성요소를 제공하고 제어 기기 목록은 기기에 적합한 이름에 따라 사전순으로 정렬됩니다.

세션 관리 작동 방식

Cast SDK는 Cast 세션이라는 개념을 도입하여 기기 연결, 웹 실행 (또는 참여) 단계를 결합한 설정입니다. 수신기 앱, 해당 앱에 연결, 미디어 제어 채널 초기화. Web Receiver 보기 애플리케이션 수명 주기 가이드 를 참조하세요.

세션은 수업에서 관리합니다. GCKSessionManager님, 이는 GCKCastContext 개별 세션은 클래스의 서브클래스로 표현됨 GCKSession: 예를 들면 다음과 같습니다. GCKCastSession Cast 기기의 세션을 나타냅니다. 현재 활성 상태인 전송에 액세스할 수 있습니다. 세션 (있는 경우)을 GCKSessionManagercurrentCastSession 속성으로 설정합니다.

GCKSessionManagerListener 인터페이스는 세션 생성, 세션 생성, 정지, 재개, 해지가 포함됩니다. 프레임워크는 자동으로 발신기 앱이 백그라운드로 전환되어 재개를 시도하는 경우 앱이 포그라운드로 돌아올 때 (또는 사용자가 세션이 활성 상태일 때 비정상적/갑작스러운 앱 종료)

전송 대화상자를 사용 중인 경우 세션이 생성되고 해제됩니다. 자동적으로 작동합니다. 그렇지 않으면 앱이 시작되고 종료될 수 있습니다. 에서 메서드를 통해 명시적으로 세션을 GCKSessionManager

앱이 세션 수명 주기에 대한 응답으로 특수 처리를 해야 하는 경우 GCKSessionManagerListener 인스턴스를 하나 이상 등록할 수 있습니다. GCKSessionManager GCKSessionManagerListener는 다음을 정의하는 프로토콜입니다. 세션 시작, 세션 종료 등과 같은 이벤트에 대한 콜백입니다.

스트림 이전

스트림 전송의 기본은 세션 상태 보존입니다. 사용자는 음성 명령, Google Home을 사용하여 기존 오디오 및 동영상 스트림을 여러 기기로 옮길 수 있습니다. 앱 또는 스마트 디스플레이입니다. 미디어는 한 기기 (소스)에서 재생이 중지되고 다른 기기 (소스)에서 계속 재생됩니다. 대상)입니다. 최신 펌웨어가 설치된 모든 Cast 기기는 스트림 이전

스트림을 전송하는 동안 새 대상 기기를 가져오려면 GCKCastSession#device 속성을 [sessionManager:didResumeCastSession:] 있습니다.

자세한 내용은 웹 수신기에서 스트림 전송 를 참조하세요.

자동 재연결

Cast 프레임워크는 재연결 로직을 추가하여 재연결을 자동으로 처리합니다. 다음과 같은 여러 미묘한 예외의 경우를 예로 들겠습니다.

  • 일시적인 Wi-Fi 중단 복구
  • 기기 절전 모드 복구
  • 앱 백그라운드 상태에서 복구
  • 앱이 다운된 경우 복구

미디어 컨트롤 작동 방식

미디어를 지원하는 웹 수신기 앱으로 Cast 세션이 설정된 경우 네임스페이스, 즉 GCKRemoteMediaClient 프레임워크에 의해 자동으로 생성됩니다. GCP 콘솔로 액세스할 수 있습니다 remoteMediaClient 속성 GCKCastSession 인스턴스를 만들 수 있습니다

웹 수신기에 요청을 실행하는 GCKRemoteMediaClient의 모든 메서드 은 GCKRequest 객체 해당 요청을 추적하는 데 사용할 수 있습니다. 가 GCKRequestDelegate 이 객체에 할당되어 최종 결과에 대한 알림을 받을 수 있습니다. 표시됩니다.

GCKRemoteMediaClient의 인스턴스가 앱의 여러 부분과 일부 내부 구성요소에 의해 공유될 수 있습니다. '캐스팅' 대화상자나 미니 미디어 컨트롤 같은 프레임워크가 인스턴스를 만들 수 있습니다 이를 위해 GCKRemoteMediaClient 여러 개의 GCKRemoteMediaClientListener

미디어 메타데이터 설정

GCKMediaMetadata 클래스는 전송하려는 미디어 항목에 관한 정보를 나타냅니다. 다음 영화의 새 GCKMediaMetadata 인스턴스를 만들고 제목을 설정합니다. 자막, 레코딩 스튜디오 이름, 이미지 2개가 있어야 합니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
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))
Objective-C
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를 사용할 수 있습니다. 수신기에서 실행되는 미디어 플레이어 앱 제어(예: 재생) 있습니다

<ph type="x-smartling-placeholder">
</ph>
스위프트
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
}
Objective-C
GCKMediaInformationBuilder *mediaInfoBuilder =
  [[GCKMediaInformationBuilder alloc] initWithContentURL:
   [NSURL URLWithString:@"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"]];
mediaInfoBuilder.streamType = GCKMediaStreamTypeNone;
mediaInfoBuilder.contentType = @"video/mp4";
mediaInfoBuilder.metadata = metadata;
self.mediaInformation = [mediaInfoBuilder build];

GCKRequest *request = [self.sessionManager.currentSession.remoteMediaClient loadMedia:self.mediaInformation];
if (request != nil) {
  request.delegate = self;
}

다음 섹션도 참조하세요. 미디어 트랙 사용

4K 동영상 형식

미디어의 동영상 형식을 확인하려면 videoInfo 속성 GCKMediaStatus 포드의 현재 인스턴스를 GCKVideoInfo입니다. 이 인스턴스에는 HDR TV 형식의 유형과 높이 및 너비가 포함됩니다. 픽셀 4K 형식의 변형은 hdrType 속성에 enum으로 표시됩니다. 값이 GCKVideoInfoHDRType입니다.

미니 컨트롤러 추가

전송 디자인에 따르면 체크리스트 발신기 앱은 무선 전송 장치라는 영구 제어 기능을 컨트롤러 사용자가 현재 콘텐츠 페이지에서 벗어날 때 표시되어야 하는 광고 미니 컨트롤러는 즉시 액세스할 수 있으며 확인할 수 있습니다.

Cast 프레임워크는 컨트롤 바를 제공합니다. GCKUIMiniMediaControlsViewController님, 미니 컨트롤러를 표시하려는 장면에 추가할 수 있습니다.

발신기 앱이 동영상이나 오디오 라이브 스트림을 재생할 때 SDK는 재생/일시중지 버튼 대신 재생/중지 버튼이 자동으로 표시됩니다. 실행할 수 있습니다.

iOS 발신자 UI 맞춤설정에서 발신기 앱에서 Cast 위젯의 모양을 구성할 수 있습니다.

미니 컨트롤러를 발신기 앱에 추가하는 방법에는 두 가지가 있습니다.

  • Cast 프레임워크가 래핑을 통해 미니 컨트롤러의 레이아웃을 관리하도록 허용합니다. 자체 뷰 컨트롤러가 있는 기존 뷰 컨트롤러에 전달할 수 있습니다.
  • 미니 컨트롤러 위젯을 하위 뷰를 제공하여 기존 뷰 컨트롤러를 만듭니다.

GCKUICastContainerViewController를 사용하여 래핑

첫 번째 방법은 GCKUICastContainerViewController 이는 또 다른 뷰 컨트롤러를 래핑하고 GCKUIMiniMediaControlsViewController 을 클릭합니다. 이 접근 방식은 맞춤 측정기준을 맞춤설정할 수 없다는 점에서 컨테이너 뷰 컨트롤러의 동작을 구성할 수 없습니다.

이 첫 번째 방법은 일반적으로 -[application:didFinishLaunchingWithOptions:] 메서드를 호출하면 됩니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
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()

  ...
}
Objective-C
- (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];
  ...

}
<ph type="x-smartling-placeholder">
</ph>
스위프트
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
    }
  }
}
Objective-C

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

기존 뷰 컨트롤러에 삽입

두 번째 방법은 미니 컨트롤러를 기존 뷰에 직접 추가하는 것입니다. kube-apiserver와 createMiniMediaControlsViewController kubectl 명령어 GCKUIMiniMediaControlsViewController 하위 뷰로 컨테이너 뷰 컨트롤러에 추가합니다.

앱 대리자에서 뷰 컨트롤러를 설정합니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
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
}
Objective-C
- (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를 만듭니다. 인스턴스를 생성하고 컨테이너 뷰 컨트롤러에 하위 뷰로 추가합니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
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)
    }
  }

...
Objective-C

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 는 미니 컨트롤러가 표시되어야 하는 시점을 호스트 뷰 컨트롤러에 알려줍니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
  func miniMediaControlsViewController(_: GCKUIMiniMediaControlsViewController,
                                       shouldAppear _: Bool) {
    updateControlBarsVisibility()
  }
Objective-C
- (void)miniMediaControlsViewController:
            (GCKUIMiniMediaControlsViewController *)miniMediaControlsViewController
                           shouldAppear:(BOOL)shouldAppear {
  [self updateControlBarsVisibility];
}

확장된 컨트롤러 추가

Google Cast 디자인 체크리스트에 따르면 발신기 앱은 확장된 컨트롤러 설정할 수 있습니다. 확장된 컨트롤러는 전체 화면 버전입니다. 만드는 것입니다.

확장된 컨트롤러는 전체 화면 보기로, 원격 미디어 재생 이 뷰를 통해 전송 앱에서 전송 세션의 관리 가능한 측면(웹 수신기 볼륨은 예외) 세션 수명 주기 (전송 연결/중지)에 따라 달라집니다. 또한 이 도구를 사용하면 미디어 세션에 대한 상태 정보 (아트워크, 제목, 부제목 등) 참조).

이 뷰의 기능은 GCKUIExpandedMediaControlsViewController 클래스에 대해 자세히 알아보세요.

가장 먼저 해야 할 일은 기본 확장 컨트롤러를 사용 설정하는 것입니다. 전송합니다. 기본 확장 컨트롤러를 사용 설정하도록 앱 대리자를 수정합니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
func applicationDidFinishLaunching(_ application: UIApplication) {
  ..

  GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true

  ...
}
Objective-C
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...

  [GCKCastContext sharedInstance].useDefaultExpandedMediaControls = YES;

  ..
}

다음 코드를 뷰 컨트롤러에 추가하여 확장된 컨트롤러를 로드합니다. 사용자가 동영상 전송을 시작할 때:

<ph type="x-smartling-placeholder">
</ph>
스위프트
func playSelectedItemRemotely() {
  GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()

  ...

  // Load your media
  sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation)
}
Objective-C
- (void)playSelectedItemRemotely {
  [[GCKCastContext sharedInstance] presentDefaultExpandedMediaControls];

  ...

  // Load your media
  [self.sessionManager.currentSession.remoteMediaClient loadMedia:mediaInformation];
}

또한 사용자가 확장 컨트롤러가 실행될 때 미니 컨트롤러를 탭합니다.

발신기 앱이 동영상이나 오디오 라이브 스트림을 재생할 때 SDK는 재생/일시중지 버튼 대신 재생/중지 버튼이 자동으로 표시됩니다. 사용할 수 있습니다.

iOS에 맞춤 스타일 적용 앱 전송 위젯의 모양을 구성하는 방법을 설정할 수 있습니다.

볼륨 제어

Cast 프레임워크는 발신기 앱의 볼륨을 자동으로 관리합니다. 이 자동으로 웹 수신기 볼륨과 동기화하여 UI 위젯을 통해 제공할 수 있습니다. 앱에서 제공하는 슬라이더를 동기화하려면 GCKUIDeviceVolumeController

물리적 버튼 볼륨 컨트롤

발신기 기기의 실제 볼륨 버튼을 사용하여 웹 수신기의 Cast 세션 볼륨을 physicalVolumeButtonsWillControlDeviceVolume 플래그가 지정된 GCKCastOptions님, 이는 포드의 GCKCastContext입니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
let criteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
options.physicalVolumeButtonsWillControlDeviceVolume = true
GCKCastContext.setSharedInstanceWith(options)
Objective-C
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc]
                                          initWithApplicationID:kReceiverAppID];
GCKCastOptions *options = [[GCKCastOptions alloc]
                                          initWithDiscoveryCriteria :criteria];
options.physicalVolumeButtonsWillControlDeviceVolume = YES;
[GCKCastContext setSharedInstanceWithOptions:options];

오류 처리

발신기 앱이 모든 오류 콜백을 처리하고 최적의 응답을 찾습니다. 앱은 표시할 수 있는 사용자에게 전송하거나 사용자가 전송 세션을 종료할지 결정할 수 있습니다.

로깅

GCKLogger 는 프레임워크에서 로깅에 사용하는 싱글톤입니다. 사용 GCKLoggerDelegate 로그 메시지 처리 방법을 맞춤설정할 수 있습니다.

GCKLogger를 사용하여 SDK는 디버그 형식으로 로깅 출력을 생성합니다. 메시지, 오류 및 경고가 포함됩니다 이러한 로그 메시지는 디버깅에 도움이 되며 문제를 식별하고 해결하는 데 도움이 될 수 있습니다 기본적으로 로그 출력은 다음과 같습니다. 표시되지 않지만 GCKLoggerDelegate를 할당하면 발신기 앱은 시스템 콘솔에 로깅합니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
@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)
    }
  }
}
Objective-C

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

디버그 메시지 및 상세 메시지도 사용 설정하려면 다음과 같이 대리자를 설정합니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
let filter = GCKLoggerFilter.init()
filter.minimumLevel = GCKLoggerLevel.verbose
GCKLogger.sharedInstance().filter = filter
Objective-C
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setMinimumLevel:GCKLoggerLevelVerbose];
[GCKLogger sharedInstance].filter = filter;

또한 로그 메시지를 필터링하여 GCKLogger 클래스당 최소 로깅 수준을 설정합니다. 예를 들면 다음과 같습니다.

<ph type="x-smartling-placeholder">
</ph>
스위프트
let filter = GCKLoggerFilter.init()
filter.setLoggingLevel(GCKLoggerLevel.verbose, forClasses: ["GCKUICastButton",
                                                            "GCKUIImageCache",
                                                            "NSMutableDictionary"])
GCKLogger.sharedInstance().filter = filter
Objective-C
GCKLoggerFilter *filter = [[GCKLoggerFilter alloc] init];
[filter setLoggingLevel:GCKLoggerLevelVerbose
             forClasses:@[@"GCKUICastButton",
                          @"GCKUIImageCache",
                          @"NSMutableDictionary"
                          ]];
[GCKLogger sharedInstance].filter = filter;

클래스 이름은 리터럴 이름 또는 glob 패턴일 수 있습니다. 예를 들면 다음과 같습니다. GCKUI\*GCK\*Session