From 100d2b4f2667cf1cf6f9f0d5d82c09276dbff8ba Mon Sep 17 00:00:00 2001 From: Erekle Date: Thu, 19 May 2016 15:07:39 +0400 Subject: [PATCH] moved spinner to ASVIdeoPlayerNode --- AsyncDisplayKit/ASVideoNode.mm | 51 +---------- AsyncDisplayKit/ASVideoPlayerNode.h | 5 ++ AsyncDisplayKit/ASVideoPlayerNode.mm | 86 +++++++++++++++++-- AsyncDisplayKitTests/ASVideoNodeTests.m | 14 +-- .../ASDKTube/Sample/Nodes/VideoContentCell.m | 13 +-- 5 files changed, 91 insertions(+), 78 deletions(-) diff --git a/AsyncDisplayKit/ASVideoNode.mm b/AsyncDisplayKit/ASVideoNode.mm index 39de422ba9..8e15cb9e14 100644 --- a/AsyncDisplayKit/ASVideoNode.mm +++ b/AsyncDisplayKit/ASVideoNode.mm @@ -74,7 +74,6 @@ static NSString * const kStatus = @"status"; ASImageNode *_placeholderImageNode; // TODO: Make ASVideoNode an ASImageNode subclass; remove this. ASDisplayNode *_playerNode; - ASDisplayNode *_spinnerNode; NSString *_gravity; } @@ -220,13 +219,6 @@ static NSString * const kStatus = @"status"; [children addObject:_playerNode]; } - // Center spinner node - if (_spinnerNode) { - ASCenterLayoutSpec *centerLayoutSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:_spinnerNode]; - centerLayoutSpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize); - [children addObject:centerLayoutSpec]; - } - return [ASStaticLayoutSpec staticLayoutSpecWithChildren:children]; } @@ -303,7 +295,6 @@ static NSString * const kStatus = @"status"; if ([keyPath isEqualToString:kStatus]) { if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) { self.playerState = ASVideoNodePlayerStateReadyToPlay; - [self removeSpinner]; // If we don't yet have a placeholder image update it now that we should have data available for it if (_placeholderImageNode.image == nil) { [self generatePlaceholderImage]; @@ -320,7 +311,6 @@ static NSString * const kStatus = @"status"; } else if ([keyPath isEqualToString:kplaybackBufferEmpty]) { if (_shouldBePlaying && [change[NSKeyValueChangeNewKey] boolValue] == true && ASInterfaceStateIncludesVisible(self.interfaceState)) { self.playerState = ASVideoNodePlayerStateLoading; - [self showSpinner]; } } } @@ -559,9 +549,8 @@ static NSString * const kStatus = @"status"; _shouldBePlaying = YES; if (![self ready]) { - [self showSpinner]; + self.playerState = ASVideoNodePlayerStateLoading; } else { - [self removeSpinner]; self.playerState = ASVideoNodePlayerStatePlaying; } } @@ -571,35 +560,6 @@ static NSString * const kStatus = @"status"; return _currentPlayerItem.status == AVPlayerItemStatusReadyToPlay; } -- (void)showSpinner -{ - ASDN::MutexLocker l(_videoLock); - - if (!_spinnerNode) { - _spinnerNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView *{ - UIActivityIndicatorView *spinnnerView = [[UIActivityIndicatorView alloc] init]; - spinnnerView.color = [UIColor whiteColor]; - return spinnnerView; - }]; - _spinnerNode.preferredFrameSize = CGSizeMake(44.0, 44.0); - - [self addSubnode:_spinnerNode]; - [self setNeedsLayout]; - } - [(UIActivityIndicatorView *)_spinnerNode.view startAnimating]; -} - -- (void)removeSpinner -{ - ASDN::MutexLocker l(_videoLock); - - if (!_spinnerNode) { - return; - } - [_spinnerNode removeFromSupernode]; - _spinnerNode = nil; -} - - (void)pause { ASDN::MutexLocker l(_videoLock); @@ -608,7 +568,6 @@ static NSString * const kStatus = @"status"; } self.playerState = ASVideoNodePlayerStatePaused; [_player pause]; - [self removeSpinner]; _shouldBePlaying = NO; } @@ -656,7 +615,6 @@ static NSString * const kStatus = @"status"; - (void)videoNodeDidStall:(NSNotification *)notification { self.playerState = ASVideoNodePlayerStateLoading; - [self showSpinner]; if (_delegateFlags.delegateVideoNodeDidStallAtTimeInterval) { [_delegate videoNode:self didStallAtTimeInterval:CMTimeGetSeconds(_player.currentItem.currentTime)]; } @@ -680,13 +638,6 @@ static NSString * const kStatus = @"status"; } #pragma mark - Internal Properties - -- (ASDisplayNode *)spinner -{ - ASDN::MutexLocker l(_videoLock); - return _spinnerNode; -} - - (ASImageNode *)placeholderImageNode { ASDN::MutexLocker l(_videoLock); diff --git a/AsyncDisplayKit/ASVideoPlayerNode.h b/AsyncDisplayKit/ASVideoPlayerNode.h index 4a7338e6f9..d5fb937d8a 100644 --- a/AsyncDisplayKit/ASVideoPlayerNode.h +++ b/AsyncDisplayKit/ASVideoPlayerNode.h @@ -42,6 +42,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, readwrite) BOOL shouldAutorepeat; @property (nonatomic, assign, readwrite) BOOL muted; @property (nonatomic, assign, readonly) ASVideoNodePlayerState playerState; +@property (nonatomic, assign, readwrite) BOOL shouldAggressivelyRecoverFromStall; + //! Defaults to 100 @property (nonatomic, assign) int32_t periodicTimeObserverTimescale; //! Defaults to AVLayerVideoGravityResizeAspect @@ -95,6 +97,9 @@ NS_ASSUME_NONNULL_BEGIN - (UIColor *)videoPlayerNodeScrubberThumbTint:(ASVideoPlayerNode *)videoPlayer; - (UIImage *)videoPlayerNodeScrubberThumbImage:(ASVideoPlayerNode *)videoPlayer; +#pragma mark - Spinner delegate methods +- (UIColor *)videoPlayerNodeSpinnerTint:(ASVideoPlayerNode *)videoPlayer; + #pragma mark - Playback button delegate methods - (UIColor *)videoPlayerNodePlaybackButtonTint:(ASVideoPlayerNode *)videoPlayer; diff --git a/AsyncDisplayKit/ASVideoPlayerNode.mm b/AsyncDisplayKit/ASVideoPlayerNode.mm index 021ce66cac..b62093c3c6 100644 --- a/AsyncDisplayKit/ASVideoPlayerNode.mm +++ b/AsyncDisplayKit/ASVideoPlayerNode.mm @@ -19,6 +19,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; struct { unsigned int delegateNeededControls:1; + unsigned int delegateSpinnerTintColor:1; unsigned int delegatePlaybackButtonTint:1; unsigned int delegateScrubberMaximumTrackTintColor:1; unsigned int delegateScrubberMinimumTrackTintColor:1; @@ -48,6 +49,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; ASTextNode *_durationTextNode; ASDisplayNode *_scrubberNode; ASStackLayoutSpec *_controlFlexGrowSpacerSpec; + ASDisplayNode *_spinnerNode; BOOL _isSeeking; CMTime _duration; @@ -60,6 +62,8 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; int32_t _periodicTimeObserverTimescale; NSString *_gravity; + BOOL _shouldAggressivelyRecoverFromStall; + UIColor *_defaultControlsColor; } @@ -355,9 +359,18 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; if (toState == ASVideoNodePlayerStatePlaying) { _playbackButtonNode.buttonType = ASDefaultPlaybackButtonTypePause; - } else { + [self removeSpinner]; + } else if (toState != ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying && toState != ASVideoNodePlayerStateReadyToPlay) { _playbackButtonNode.buttonType = ASDefaultPlaybackButtonTypePlay; } + + if (toState == ASVideoNodePlayerStateLoading || toState == ASVideoNodePlayerStateInitialLoading) { + [self showSpinner]; + } + + if (toState == ASVideoNodePlayerStateReadyToPlay || toState == ASVideoNodePlayerStatePaused || toState == ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying) { + [self removeSpinner]; + } } - (BOOL)videoNode:(ASVideoNode *)videoNode shouldChangePlayerStateTo:(ASVideoNodePlayerState)state @@ -402,16 +415,12 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; if (_delegateFlags.delegateDidTapVideoPlayerNode) { [_delegate didTapVideoPlayerNode:self]; } else { - if (videoNode.playerState == ASVideoNodePlayerStatePlaying) { - [videoNode pause]; - } else { - [videoNode play]; - } + [self manageVideoNodePlayback]; } } #pragma mark - Actions -- (void)didTapPlaybackButton:(ASControlNode*)node +- (void)manageVideoNodePlayback { if (_videoNode.playerState == ASVideoNodePlayerStatePlaying) { [_videoNode pause]; @@ -420,6 +429,43 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; } } +- (void)showSpinner +{ + ASDN::MutexLocker l(_videoPlayerLock); + + if (!_spinnerNode) { + _spinnerNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView *{ + UIActivityIndicatorView *spinnnerView = [[UIActivityIndicatorView alloc] init]; + spinnnerView.color = _defaultControlsColor; + if (_delegateFlags.delegateSpinnerTintColor) { + spinnnerView.color = [_delegate videoPlayerNodeSpinnerTint:self]; + } + return spinnnerView; + }]; + _spinnerNode.preferredFrameSize = CGSizeMake(44.0, 44.0); + + [self addSubnode:_spinnerNode]; + [self setNeedsLayout]; + } + [(UIActivityIndicatorView *)_spinnerNode.view startAnimating]; +} + +- (void)removeSpinner +{ + ASDN::MutexLocker l(_videoPlayerLock); + + if (!_spinnerNode) { + return; + } + [_spinnerNode removeFromSupernode]; + _spinnerNode = nil; +} + +- (void)didTapPlaybackButton:(ASControlNode*)node +{ + [self manageVideoNodePlayback]; +} + - (void)beginSeek { _isSeeking = YES; @@ -445,7 +491,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; [_videoNode.player seekToTime:CMTimeMakeWithSeconds(seconds, _videoNode.periodicTimeObserverTimescale)]; if (_videoNode.playerState != ASVideoNodePlayerStatePlaying) { - [_videoNode play]; + [self manageVideoNodePlayback]; } } @@ -510,10 +556,20 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; layoutSpec = [self defaultLayoutSpecThatFits:maxSize]; } + NSMutableArray *children = [[NSMutableArray alloc] init]; + + if (_spinnerNode) { + ASCenterLayoutSpec *centerLayoutSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:_spinnerNode]; + centerLayoutSpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize); + [children addObject:centerLayoutSpec]; + } + ASOverlayLayoutSpec *overlaySpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_videoNode overlay:layoutSpec]; overlaySpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize); - return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[overlaySpec]]; + [children addObject:overlaySpec]; + + return [ASStaticLayoutSpec staticLayoutSpecWithChildren:children]; } - (ASLayoutSpec*)defaultLayoutSpecThatFits:(CGSize)maxSize @@ -559,6 +615,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; memset(&_delegateFlags, 0, sizeof(_delegateFlags)); } else { _delegateFlags.delegateNeededControls = [_delegate respondsToSelector:@selector(videoPlayerNodeNeededControls:)]; + _delegateFlags.delegateSpinnerTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeSpinnerTint:)]; _delegateFlags.delegateScrubberMaximumTrackTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberMaximumTrackTint:)]; _delegateFlags.delegateScrubberMinimumTrackTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberMinimumTrackTint:)]; _delegateFlags.delegateScrubberThumbTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberThumbTint:)]; @@ -629,6 +686,17 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; return _videoNode.playerState; } +- (BOOL)shouldAggressivelyRecoverFromStall +{ + return _videoNode.shouldAggressivelyRecoverFromStall; +} + +- (void)setShouldAggressivelyRecoverFromStall:(BOOL)shouldAggressivelyRecoverFromStall +{ + _shouldAggressivelyRecoverFromStall = shouldAggressivelyRecoverFromStall; + _videoNode.shouldAggressivelyRecoverFromStall = _shouldAggressivelyRecoverFromStall; +} + #pragma mark - Helpers - (NSString *)timeStringForCMTime:(CMTime)time forTimeLabelType:(ASVideoPlayerNodeControlType)type { diff --git a/AsyncDisplayKitTests/ASVideoNodeTests.m b/AsyncDisplayKitTests/ASVideoNodeTests.m index eaa7e63a5b..72e78cbf27 100644 --- a/AsyncDisplayKitTests/ASVideoNodeTests.m +++ b/AsyncDisplayKitTests/ASVideoNodeTests.m @@ -50,13 +50,6 @@ _requestedKeys = @[ @"playable" ]; } - -- (void)testSpinnerDefaultsToNil -{ - XCTAssertNil(_videoNode.spinner); -} - - - (void)testOnPlayIfVideoIsNotReadyInitializeSpinnerAndAddAsSubnode { _videoNode.asset = _firstAsset; @@ -73,8 +66,6 @@ { _videoNode.interfaceState = ASInterfaceStateFetchData; [_videoNode play]; - - XCTAssertNotNil(_videoNode.spinner); } @@ -96,8 +87,7 @@ [_videoNode play]; [_videoNode pause]; - - XCTAssertFalse(((UIActivityIndicatorView *)_videoNode.spinner.view).isAnimating); + } @@ -119,8 +109,6 @@ [_videoNode play]; [_videoNode observeValueForKeyPath:@"status" ofObject:[_videoNode currentItem] change:@{NSKeyValueChangeNewKey : @(AVPlayerItemStatusReadyToPlay)} context:NULL]; - - XCTAssertFalse(((UIActivityIndicatorView *)_videoNode.spinner.view).isAnimating); } diff --git a/examples/ASDKTube/Sample/Nodes/VideoContentCell.m b/examples/ASDKTube/Sample/Nodes/VideoContentCell.m index 2a163f9c44..f5fdd4b5c5 100644 --- a/examples/ASDKTube/Sample/Nodes/VideoContentCell.m +++ b/examples/ASDKTube/Sample/Nodes/VideoContentCell.m @@ -31,7 +31,6 @@ { self = [super init]; if (self) { - _videoModel = video; _titleNode = [[ASTextNode alloc] init]; @@ -55,6 +54,7 @@ _videoPlayerNode = [[ASVideoPlayerNode alloc] initWithUrl:_videoModel.url]; _videoPlayerNode.delegate = self; + _videoPlayerNode.backgroundColor = [UIColor blackColor]; [self addSubnode:_videoPlayerNode]; } return self; @@ -101,19 +101,20 @@ #pragma mark - ASVideoPlayerNodeDelegate - (void)didTapVideoPlayerNode:(ASVideoPlayerNode *)videoPlayer { - if (_videoPlayerNode.playerState == ASVideoNodePlayerStatePlaying) { + if (_videoPlayerNode.isPlaying) { NSLog(@"TRANSITION"); + [_videoPlayerNode pause]; } else { [_videoPlayerNode play]; } } -- (NSArray *)videoPlayerNodeNeededControls:(ASVideoPlayerNode *)videoPlayer +/*- (NSArray *)videoPlayerNodeNeededControls:(ASVideoPlayerNode *)videoPlayer { return @[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]; -} +}*/ -- (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer forControls:(NSDictionary *)controls forMaximumSize:(CGSize)maxSize +/*- (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer forControls:(NSDictionary *)controls forMaximumSize:(CGSize)maxSize { NSMutableArray *bottomControls = [[NSMutableArray alloc] init]; @@ -147,5 +148,5 @@ return mainVerticalStack; -} +}*/ @end