Merge pull request #181 from sjchmiela/feature/support_fullscreen_mode
Support fullscreen mode
This commit is contained in:
commit
3c6da9782f
@ -1,10 +1,13 @@
|
||||
#import "RCTView.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "AVKit/AVKit.h"
|
||||
#import "UIView+FindUIViewController.h"
|
||||
#import "RCTVideoPlayerViewController.h"
|
||||
#import "RCTVideoPlayerViewControllerDelegate.h"
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
|
||||
@interface RCTVideo : UIView
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate>
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
75
RCTVideo.m
75
RCTVideo.m
@ -37,6 +37,8 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
|
||||
BOOL _paused;
|
||||
BOOL _repeat;
|
||||
NSString * _resizeMode;
|
||||
BOOL _fullscreenPlayerPresented;
|
||||
UIViewController * _presentingViewController;
|
||||
}
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
@ -69,7 +71,9 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
|
||||
}
|
||||
|
||||
- (AVPlayerViewController*)createPlayerViewController:(AVPlayer*)player withPlayerItem:(AVPlayerItem*)playerItem {
|
||||
AVPlayerViewController* playerLayer= [[AVPlayerViewController alloc] init];
|
||||
RCTVideoPlayerViewController* playerLayer= [[RCTVideoPlayerViewController alloc] init];
|
||||
playerLayer.showsPlaybackControls = NO;
|
||||
playerLayer.rctDelegate = self;
|
||||
playerLayer.view.frame = self.bounds;
|
||||
playerLayer.player = _player;
|
||||
playerLayer.view.frame = self.bounds;
|
||||
@ -436,6 +440,54 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
|
||||
_repeat = repeat;
|
||||
}
|
||||
|
||||
- (BOOL)getFullscreen
|
||||
{
|
||||
return _fullscreenPlayerPresented;
|
||||
}
|
||||
|
||||
- (void)setFullscreen:(BOOL)fullscreen
|
||||
{
|
||||
if( fullscreen && !_fullscreenPlayerPresented )
|
||||
{
|
||||
// Ensure player view controller is not null
|
||||
if( !_playerViewController )
|
||||
{
|
||||
[self usePlayerViewController];
|
||||
}
|
||||
// Set presentation style to fullscreen
|
||||
[_playerViewController setModalPresentationStyle:UIModalPresentationFullScreen];
|
||||
|
||||
// Find the nearest view controller
|
||||
UIViewController *viewController = [self firstAvailableUIViewController];
|
||||
if( !viewController )
|
||||
{
|
||||
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
|
||||
viewController = keyWindow.rootViewController;
|
||||
if( viewController.childViewControllers.count > 0 )
|
||||
{
|
||||
viewController = viewController.childViewControllers.lastObject;
|
||||
}
|
||||
}
|
||||
if( viewController )
|
||||
{
|
||||
_presentingViewController = viewController;
|
||||
[_eventDispatcher sendInputEventWithName:@"onVideoFullscreenPlayerWillPresent" body:@{@"target": self.reactTag}];
|
||||
[viewController presentViewController:_playerViewController animated:true completion:^{
|
||||
_playerViewController.showsPlaybackControls = YES;
|
||||
_fullscreenPlayerPresented = fullscreen;
|
||||
[_eventDispatcher sendInputEventWithName:@"onVideoFullscreenPlayerDidPresent" body:@{@"target": self.reactTag}];
|
||||
}];
|
||||
}
|
||||
}
|
||||
else if ( !fullscreen && _fullscreenPlayerPresented )
|
||||
{
|
||||
[self videoPlayerViewControllerWillDismiss:_playerViewController];
|
||||
[_presentingViewController dismissViewControllerAnimated:true completion:^{
|
||||
[self videoPlayerViewControllerDidDismiss:_playerViewController];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)usePlayerViewController
|
||||
{
|
||||
if( _player )
|
||||
@ -478,6 +530,27 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - RCTVideoPlayerViewControllerDelegate
|
||||
|
||||
- (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController
|
||||
{
|
||||
if (_playerViewController == playerViewController && _fullscreenPlayerPresented)
|
||||
{
|
||||
[_eventDispatcher sendInputEventWithName:@"onVideoFullscreenPlayerWillDismiss" body:@{@"target": self.reactTag}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)videoPlayerViewControllerDidDismiss:(AVPlayerViewController *)playerViewController
|
||||
{
|
||||
if (_playerViewController == playerViewController && _fullscreenPlayerPresented)
|
||||
{
|
||||
_fullscreenPlayerPresented = false;
|
||||
_presentingViewController = nil;
|
||||
[self applyModifiers];
|
||||
[_eventDispatcher sendInputEventWithName:@"onVideoFullscreenPlayerDidDismiss" body:@{@"target": self.reactTag}];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - React View Management
|
||||
|
||||
- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex
|
||||
|
@ -7,6 +7,8 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
31CAFB211CADA8CD009BCF6F /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 31CAFB201CADA8CD009BCF6F /* UIView+FindUIViewController.m */; };
|
||||
31CAFB2F1CADC77F009BCF6F /* RCTVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 31CAFB2E1CADC77F009BCF6F /* RCTVideoPlayerViewController.m */; };
|
||||
BBD49E3F1AC8DEF000610F8E /* RCTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = BBD49E3A1AC8DEF000610F8E /* RCTVideo.m */; };
|
||||
BBD49E401AC8DEF000610F8E /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BBD49E3C1AC8DEF000610F8E /* RCTVideoManager.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
@ -25,6 +27,11 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
134814201AA4EA6300B7C361 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
31CAFB1F1CADA8CD009BCF6F /* UIView+FindUIViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+FindUIViewController.h"; sourceTree = "<group>"; };
|
||||
31CAFB201CADA8CD009BCF6F /* UIView+FindUIViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+FindUIViewController.m"; sourceTree = "<group>"; };
|
||||
31CAFB2D1CADC77F009BCF6F /* RCTVideoPlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVideoPlayerViewController.h; sourceTree = "<group>"; };
|
||||
31CAFB2E1CADC77F009BCF6F /* RCTVideoPlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVideoPlayerViewController.m; sourceTree = "<group>"; };
|
||||
31CAFB301CAE6B5F009BCF6F /* RCTVideoPlayerViewControllerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVideoPlayerViewControllerDelegate.h; sourceTree = "<group>"; };
|
||||
BBD49E391AC8DEF000610F8E /* RCTVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVideo.h; sourceTree = "<group>"; };
|
||||
BBD49E3A1AC8DEF000610F8E /* RCTVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVideo.m; sourceTree = "<group>"; };
|
||||
BBD49E3B1AC8DEF000610F8E /* RCTVideoManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVideoManager.h; sourceTree = "<group>"; };
|
||||
@ -55,8 +62,13 @@
|
||||
children = (
|
||||
BBD49E391AC8DEF000610F8E /* RCTVideo.h */,
|
||||
BBD49E3A1AC8DEF000610F8E /* RCTVideo.m */,
|
||||
31CAFB301CAE6B5F009BCF6F /* RCTVideoPlayerViewControllerDelegate.h */,
|
||||
31CAFB2D1CADC77F009BCF6F /* RCTVideoPlayerViewController.h */,
|
||||
31CAFB2E1CADC77F009BCF6F /* RCTVideoPlayerViewController.m */,
|
||||
BBD49E3B1AC8DEF000610F8E /* RCTVideoManager.h */,
|
||||
BBD49E3C1AC8DEF000610F8E /* RCTVideoManager.m */,
|
||||
31CAFB1F1CADA8CD009BCF6F /* UIView+FindUIViewController.h */,
|
||||
31CAFB201CADA8CD009BCF6F /* UIView+FindUIViewController.m */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
@ -117,6 +129,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
31CAFB211CADA8CD009BCF6F /* UIView+FindUIViewController.m in Sources */,
|
||||
31CAFB2F1CADC77F009BCF6F /* RCTVideoPlayerViewController.m in Sources */,
|
||||
BBD49E3F1AC8DEF000610F8E /* RCTVideo.m in Sources */,
|
||||
BBD49E401AC8DEF000610F8E /* RCTVideoManager.m in Sources */,
|
||||
);
|
||||
|
@ -24,7 +24,11 @@ RCT_EXPORT_MODULE();
|
||||
@"onVideoError",
|
||||
@"onVideoProgress",
|
||||
@"onVideoSeek",
|
||||
@"onVideoEnd"
|
||||
@"onVideoEnd",
|
||||
@"onVideoFullscreenPlayerWillPresent",
|
||||
@"onVideoFullscreenPlayerDidPresent",
|
||||
@"onVideoFullscreenPlayerWillDismiss",
|
||||
@"onVideoFullscreenPlayerDidDismiss"
|
||||
];
|
||||
}
|
||||
|
||||
@ -43,6 +47,7 @@ RCT_EXPORT_VIEW_PROPERTY(volume, float);
|
||||
RCT_EXPORT_VIEW_PROPERTY(rate, float);
|
||||
RCT_EXPORT_VIEW_PROPERTY(seek, float);
|
||||
RCT_EXPORT_VIEW_PROPERTY(currentTime, float);
|
||||
RCT_EXPORT_VIEW_PROPERTY(fullscreen, BOOL);
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
|
15
RCTVideoPlayerViewController.h
Normal file
15
RCTVideoPlayerViewController.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// RCTVideoPlayerViewController.h
|
||||
// RCTVideo
|
||||
//
|
||||
// Created by Stanisław Chmiela on 31.03.2016.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AVKit/AVKit.h>
|
||||
#import "RCTVideo.h"
|
||||
#import "RCTVideoPlayerViewControllerDelegate.h"
|
||||
|
||||
@interface RCTVideoPlayerViewController : AVPlayerViewController
|
||||
@property (nonatomic, weak) id<RCTVideoPlayerViewControllerDelegate> rctDelegate;
|
||||
@end
|
28
RCTVideoPlayerViewController.m
Normal file
28
RCTVideoPlayerViewController.m
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// RCTVideoPlayerViewController.m
|
||||
// RCTVideo
|
||||
//
|
||||
// Created by Stanisław Chmiela on 31.03.2016.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RCTVideoPlayerViewController.h"
|
||||
|
||||
@interface RCTVideoPlayerViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTVideoPlayerViewController
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
[_rctDelegate videoPlayerViewControllerDidDismiss:self];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[_rctDelegate videoPlayerViewControllerWillDismiss:self];
|
||||
[super viewWillDisappear:animated];
|
||||
}
|
||||
|
||||
@end
|
15
RCTVideoPlayerViewControllerDelegate.h
Normal file
15
RCTVideoPlayerViewControllerDelegate.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// RCTVideoPlayerViewControllerDelegate.h
|
||||
// RCTVideo
|
||||
//
|
||||
// Created by Stanisław Chmiela on 01.04.2016.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AVKit/AVKit.h"
|
||||
|
||||
@protocol RCTVideoPlayerViewControllerDelegate <NSObject>
|
||||
- (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController;
|
||||
- (void)videoPlayerViewControllerDidDismiss:(AVPlayerViewController *)playerViewController;
|
||||
@end
|
15
UIView+FindUIViewController.h
Normal file
15
UIView+FindUIViewController.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// UIView+FindUIViewController.h
|
||||
// RCTVideo
|
||||
//
|
||||
// Created by Stanisław Chmiela on 31.03.2016.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
// Source: http://stackoverflow.com/a/3732812/1123156
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIView (FindUIViewController)
|
||||
- (UIViewController *) firstAvailableUIViewController;
|
||||
- (id) traverseResponderChainForUIViewController;
|
||||
@end
|
28
UIView+FindUIViewController.m
Normal file
28
UIView+FindUIViewController.m
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// UIView+FindUIViewController.m
|
||||
// RCTVideo
|
||||
//
|
||||
// Created by Stanisław Chmiela on 31.03.2016.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
// Source: http://stackoverflow.com/a/3732812/1123156
|
||||
|
||||
#import "UIView+FindUIViewController.h"
|
||||
|
||||
@implementation UIView (FindUIViewController)
|
||||
- (UIViewController *) firstAvailableUIViewController {
|
||||
// convenience function for casting and to "mask" the recursive function
|
||||
return (UIViewController *)[self traverseResponderChainForUIViewController];
|
||||
}
|
||||
|
||||
- (id) traverseResponderChainForUIViewController {
|
||||
id nextResponder = [self nextResponder];
|
||||
if ([nextResponder isKindOfClass:[UIViewController class]]) {
|
||||
return nextResponder;
|
||||
} else if ([nextResponder isKindOfClass:[UIView class]]) {
|
||||
return [nextResponder traverseResponderChainForUIViewController];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
@end
|
48
Video.js
48
Video.js
@ -21,6 +21,8 @@ export default class Video extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.seek = this.seek.bind(this);
|
||||
this.presentFullscreenPlayer = this.presentFullscreenPlayer.bind(this);
|
||||
this.dismissFullscreenPlayer = this.dismissFullscreenPlayer.bind(this);
|
||||
this._assignRoot = this._assignRoot.bind(this);
|
||||
this._onLoadStart = this._onLoadStart.bind(this);
|
||||
this._onLoad = this._onLoad.bind(this);
|
||||
@ -28,6 +30,10 @@ export default class Video extends Component {
|
||||
this._onProgress = this._onProgress.bind(this);
|
||||
this._onSeek = this._onSeek.bind(this);
|
||||
this._onEnd = this._onEnd.bind(this);
|
||||
this._onFullscreenPlayerWillPresent = this._onFullscreenPlayerWillPresent.bind(this);
|
||||
this._onFullscreenPlayerDidPresent = this._onFullscreenPlayerDidPresent.bind(this);
|
||||
this._onFullscreenPlayerWillDismiss = this._onFullscreenPlayerWillDismiss.bind(this);
|
||||
this._onFullscreenPlayerDidDismiss = this._onFullscreenPlayerDidDismiss.bind(this);
|
||||
}
|
||||
|
||||
setNativeProps(nativeProps) {
|
||||
@ -38,6 +44,14 @@ export default class Video extends Component {
|
||||
this.setNativeProps({ seek: time });
|
||||
}
|
||||
|
||||
presentFullscreenPlayer() {
|
||||
this.setNativeProps({ fullscreen: true });
|
||||
}
|
||||
|
||||
dismissFullscreenPlayer() {
|
||||
this.setNativeProps({ fullscreen: false });
|
||||
}
|
||||
|
||||
_assignRoot(component) {
|
||||
this._root = component;
|
||||
}
|
||||
@ -78,6 +92,30 @@ export default class Video extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
_onFullscreenPlayerWillPresent(event) {
|
||||
if (this.props.onFullscreenPlayerWillPresent) {
|
||||
this.props.onFullscreenPlayerWillPresent(event.nativeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
_onFullscreenPlayerDidPresent(event) {
|
||||
if (this.props.onFullscreenPlayerDidPresent) {
|
||||
this.props.onFullscreenPlayerDidPresent(event.nativeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
_onFullscreenPlayerWillDismiss(event) {
|
||||
if (this.props.onFullscreenPlayerWillDismiss) {
|
||||
this.props.onFullscreenPlayerWillDismiss(event.nativeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
_onFullscreenPlayerDidDismiss(event) {
|
||||
if (this.props.onFullscreenPlayerDidDismiss) {
|
||||
this.props.onFullscreenPlayerDidDismiss(event.nativeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
source,
|
||||
@ -119,6 +157,10 @@ export default class Video extends Component {
|
||||
onVideoProgress: this._onProgress,
|
||||
onVideoSeek: this._onSeek,
|
||||
onVideoEnd: this._onEnd,
|
||||
onVideoFullscreenPlayerWillPresent: this._onFullscreenPlayerWillPresent,
|
||||
onVideoFullscreenPlayerDidPresent: this._onFullscreenPlayerDidPresent,
|
||||
onVideoFullscreenPlayerWillDismiss: this._onFullscreenPlayerWillDismiss,
|
||||
onVideoFullscreenPlayerDidDismiss: this._onFullscreenPlayerDidDismiss,
|
||||
});
|
||||
|
||||
return (
|
||||
@ -134,6 +176,7 @@ Video.propTypes = {
|
||||
/* Native only */
|
||||
src: PropTypes.object,
|
||||
seek: PropTypes.number,
|
||||
fullscreen: PropTypes.bool,
|
||||
|
||||
/* Wrapper component */
|
||||
source: PropTypes.object,
|
||||
@ -151,6 +194,10 @@ Video.propTypes = {
|
||||
onProgress: PropTypes.func,
|
||||
onSeek: PropTypes.func,
|
||||
onEnd: PropTypes.func,
|
||||
onFullscreenPlayerWillPresent: PropTypes.func,
|
||||
onFullscreenPlayerDidPresent: PropTypes.func,
|
||||
onFullscreenPlayerWillDismiss: PropTypes.func,
|
||||
onFullscreenPlayerDidDismiss: PropTypes.func,
|
||||
|
||||
/* Required by react-native */
|
||||
scaleX: React.PropTypes.number,
|
||||
@ -165,5 +212,6 @@ const RCTVideo = requireNativeComponent('RCTVideo', Video, {
|
||||
nativeOnly: {
|
||||
src: true,
|
||||
seek: true,
|
||||
fullscreen: true,
|
||||
},
|
||||
});
|
||||
|
@ -18,6 +18,11 @@
|
||||
"RCTVideoManager.h",
|
||||
"RCTVideoManager.m",
|
||||
"README.md",
|
||||
"UIView+FindUIViewController.h",
|
||||
"UIView+FindUIViewController.m",
|
||||
"RCTVideoPlayerViewController.h",
|
||||
"RCTVideoPlayerViewController.m",
|
||||
"RCTVideoPlayerViewControllerDelegate.h",
|
||||
"Video.js",
|
||||
"VideoResizeMode.js",
|
||||
"react-native-video.podspec"
|
||||
|
Loading…
Reference in New Issue
Block a user