diff --git a/AsyncDisplayKit/ASVideoPlayerNode.h b/AsyncDisplayKit/ASVideoPlayerNode.h index 2af9f4b9f0..983a9b1ec2 100644 --- a/AsyncDisplayKit/ASVideoPlayerNode.h +++ b/AsyncDisplayKit/ASVideoPlayerNode.h @@ -24,6 +24,7 @@ typedef enum { ASVideoPlayerNodeControlTypeElapsedText, ASVideoPlayerNodeControlTypeDurationText, ASVideoPlayerNodeControlTypeScrubber, + ASVideoPlayerNodeControlTypeFullScreenButton, ASVideoPlayerNodeControlTypeFlexGrowSpacer, } ASVideoPlayerNodeControlType; @@ -50,6 +51,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, readonly) ASVideoNodePlayerState playerState; @property (nonatomic, assign, readwrite) BOOL shouldAggressivelyRecoverFromStall; @property (nullable, nonatomic, strong, readwrite) NSURL *placeholderImageURL; +@property (nonatomic, strong, readonly) ASVideoNode *videoNode; //! Defaults to 100 @property (nonatomic, assign) int32_t periodicTimeObserverTimescale; @@ -125,6 +127,10 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Playback button delegate methods - (UIColor *)videoPlayerNodePlaybackButtonTint:(ASVideoPlayerNode *)videoPlayer; +#pragma mark - Fullscreen button delegate methods + +- (UIImage *)videoPlayerNodeFullScreenButtonImage:(ASVideoPlayerNode *)videoPlayer; + #pragma mark ASVideoNodeDelegate proxy methods /** @@ -132,6 +138,13 @@ NS_ASSUME_NONNULL_BEGIN * @param videoPlayerNode The ASVideoPlayerNode that was tapped. */ - (void)didTapVideoPlayerNode:(ASVideoPlayerNode *)videoPlayer; + +/** + * @abstract Delegate method invoked when fullcreen button is taped. + * @param buttonNode The fullscreen button node that was tapped. + */ +- (void)didTapFullScreenButtonNode:(ASButtonNode *)buttonNode; + /** * @abstract Delegate method invoked when ASVideoNode playback time is updated. * @param videoPlayerNode The video player node diff --git a/AsyncDisplayKit/ASVideoPlayerNode.mm b/AsyncDisplayKit/ASVideoPlayerNode.mm index bebf528ed0..de9353a0c8 100644 --- a/AsyncDisplayKit/ASVideoPlayerNode.mm +++ b/AsyncDisplayKit/ASVideoPlayerNode.mm @@ -26,6 +26,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; unsigned int delegateSpinnerTintColor:1; unsigned int delegateSpinnerStyle:1; unsigned int delegatePlaybackButtonTint:1; + unsigned int delegateFullScreenButtonImage:1; unsigned int delegateScrubberMaximumTrackTintColor:1; unsigned int delegateScrubberMinimumTrackTintColor:1; unsigned int delegateScrubberThumbTintColor:1; @@ -38,6 +39,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; unsigned int delegateVideoNodeShouldChangeState:1; unsigned int delegateVideoNodePlaybackDidFinish:1; unsigned int delegateDidTapVideoPlayerNode:1; + unsigned int delegateDidTapFullScreenButtonNode:1; unsigned int delegateVideoPlayerNodeDidSetCurrentItem:1; unsigned int delegateVideoPlayerNodeDidStallAtTimeInterval:1; unsigned int delegateVideoPlayerNodeDidStartInitialLoading:1; @@ -57,6 +59,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; NSMutableDictionary *_cachedControls; ASDefaultPlaybackButton *_playbackButtonNode; + ASButtonNode *_fullScreenButtonNode; ASTextNode *_elapsedTextNode; ASTextNode *_durationTextNode; ASDisplayNode *_scrubberNode; @@ -274,6 +277,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; case ASVideoPlayerNodeControlTypeScrubber: [self createScrubber]; break; + case ASVideoPlayerNodeControlTypeFullScreenButton: + [self createFullScreenButton]; + break; case ASVideoPlayerNodeControlTypeFlexGrowSpacer: [self createControlFlexGrowSpacer]; break; @@ -315,6 +321,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; [_cachedControls removeAllObjects]; _playbackButtonNode = nil; + _fullScreenButtonNode = nil; _elapsedTextNode = nil; _durationTextNode = nil; _scrubberNode = nil; @@ -343,6 +350,23 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; [self addSubnode:_playbackButtonNode]; } +- (void)createFullScreenButton +{ + if (_fullScreenButtonNode == nil) { + _fullScreenButtonNode = [[ASButtonNode alloc] init]; + _fullScreenButtonNode.style.preferredSize = CGSizeMake(16.0, 22.0); + + if (_delegateFlags.delegateFullScreenButtonImage) { + [_fullScreenButtonNode setImage:[_delegate videoPlayerNodeFullScreenButtonImage:self] forState:ASControlStateNormal]; + } + + [_fullScreenButtonNode addTarget:self action:@selector(didTapFullScreenButton:) forControlEvents:ASControlNodeEventTouchUpInside]; + [_cachedControls setObject:_fullScreenButtonNode forKey:@(ASVideoPlayerNodeControlTypeFullScreenButton)]; + } + + [self addSubnode:_fullScreenButtonNode]; +} + - (void)createElapsedTextField { if (_elapsedTextNode == nil) { @@ -366,6 +390,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; [_cachedControls setObject:_durationTextNode forKey:@(ASVideoPlayerNodeControlTypeDurationText)]; } + [self updateDurationTimeLabel]; [self addSubnode:_durationTextNode]; } @@ -624,6 +649,11 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; [self togglePlayPause]; } +- (void)didTapFullScreenButton:(ASButtonNode*)node +{ + [_delegate didTapFullScreenButtonNode:node]; +} + - (void)beginSeek { _isSeeking = YES; @@ -692,6 +722,10 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; if (_cachedControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]) { [controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]]; } + + if (_cachedControls[ @(ASVideoPlayerNodeControlTypeFullScreenButton) ]) { + [controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypeFullScreenButton) ]]; + } return controls; } @@ -795,7 +829,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; _delegateFlags.delegateVideoNodeShouldChangeState = [_delegate respondsToSelector:@selector(videoPlayerNode:shouldChangeVideoNodeStateTo:)]; _delegateFlags.delegateTimeLabelAttributedString = [_delegate respondsToSelector:@selector(videoPlayerNode:timeStringForTimeLabelType:forTime:)]; _delegateFlags.delegatePlaybackButtonTint = [_delegate respondsToSelector:@selector(videoPlayerNodePlaybackButtonTint:)]; + _delegateFlags.delegateFullScreenButtonImage = [_delegate respondsToSelector:@selector(videoPlayerNodeFullScreenButtonImage:)]; _delegateFlags.delegateDidTapVideoPlayerNode = [_delegate respondsToSelector:@selector(didTapVideoPlayerNode:)]; + _delegateFlags.delegateDidTapFullScreenButtonNode = [_delegate respondsToSelector:@selector(didTapFullScreenButtonNode:)]; _delegateFlags.delegateVideoPlayerNodeDidSetCurrentItem = [_delegate respondsToSelector:@selector(videoPlayerNode:didSetCurrentItem:)]; _delegateFlags.delegateVideoPlayerNodeDidStallAtTimeInterval = [_delegate respondsToSelector:@selector(videoPlayerNode:didStallAtTimeInterval:)]; _delegateFlags.delegateVideoPlayerNodeDidStartInitialLoading = [_delegate respondsToSelector:@selector(videoPlayerNodeDidStartInitialLoading:)]; @@ -892,6 +928,11 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; return _videoNode.URL; } +- (ASVideoNode*)videoNode +{ + return _videoNode; +} + - (void)setShouldAggressivelyRecoverFromStall:(BOOL)shouldAggressivelyRecoverFromStall { if (_shouldAggressivelyRecoverFromStall == shouldAggressivelyRecoverFromStall) { @@ -904,6 +945,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; #pragma mark - Helpers - (NSString *)timeStringForCMTime:(CMTime)time forTimeLabelType:(ASVideoPlayerNodeControlType)type { + if (!CMTIME_IS_VALID(time)) { + return @"00:00"; + } if (_delegateFlags.delegateTimeLabelAttributedString) { return [_delegate videoPlayerNode:self timeStringForTimeLabelType:type forTime:time]; }