mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
[ASVideoNode] Change superclass to ASNetworkImageNode so that it can be its own placeholder (#1710)
* [ASVideoNode] Change superclass to ASNetworkImageNode so that ASVideoNode can be its own placeholder - remove _placeholderImageNode property of ASVideoNode (use self.image now instead) - move layoutSpecThatFits: code to calculateSizeThatFits: & layout: methods as ASImageNode uses calculateSizeThatFits: * [ASVideoNode] Tweaks to the definition of the delegate protocols to integrate with ASNetworkImageNode (superclass)
This commit is contained in:
@@ -45,7 +45,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
/**
|
/**
|
||||||
* The delegate, which must conform to the <ASNetworkImageNodeDelegate> protocol.
|
* The delegate, which must conform to the <ASNetworkImageNodeDelegate> protocol.
|
||||||
*/
|
*/
|
||||||
@property (atomic, weak, readwrite) id<ASNetworkImageNodeDelegate> delegate;
|
@property (nullable, atomic, weak, readwrite) id<ASNetworkImageNodeDelegate> delegate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A placeholder image to display while the URL is loading.
|
* A placeholder image to display while the URL is loading.
|
||||||
@@ -100,6 +100,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* notifications such as finished decoding and downloading an image.
|
* notifications such as finished decoding and downloading an image.
|
||||||
*/
|
*/
|
||||||
@protocol ASNetworkImageNodeDelegate <NSObject>
|
@protocol ASNetworkImageNodeDelegate <NSObject>
|
||||||
|
@optional
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification that the image node finished downloading an image.
|
* Notification that the image node finished downloading an image.
|
||||||
@@ -111,8 +112,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image;
|
- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image;
|
||||||
|
|
||||||
@optional
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification that the image node started to load
|
* Notification that the image node started to load
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#if TARGET_OS_IOS
|
#if TARGET_OS_IOS
|
||||||
#import <AsyncDisplayKit/ASButtonNode.h>
|
#import <AsyncDisplayKit/ASButtonNode.h>
|
||||||
|
#import <AsyncDisplayKit/ASNetworkImageNode.h>
|
||||||
|
|
||||||
@class AVAsset, AVPlayer, AVPlayerItem;
|
@class AVAsset, AVPlayer, AVPlayerItem;
|
||||||
@protocol ASVideoNodeDelegate;
|
@protocol ASVideoNodeDelegate;
|
||||||
@@ -31,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
// there is room for further expansion and optimization. Please report any issues or requests
|
// there is room for further expansion and optimization. Please report any issues or requests
|
||||||
// in an issue on GitHub: https://github.com/facebook/AsyncDisplayKit/issues
|
// in an issue on GitHub: https://github.com/facebook/AsyncDisplayKit/issues
|
||||||
|
|
||||||
@interface ASVideoNode : ASControlNode
|
@interface ASVideoNode : ASNetworkImageNode
|
||||||
|
|
||||||
- (void)play;
|
- (void)play;
|
||||||
- (void)pause;
|
- (void)pause;
|
||||||
@@ -60,11 +61,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
//! Defaults to AVLayerVideoGravityResizeAspect
|
//! Defaults to AVLayerVideoGravityResizeAspect
|
||||||
@property (atomic) NSString *gravity;
|
@property (atomic) NSString *gravity;
|
||||||
|
|
||||||
@property (nullable, atomic, weak, readwrite) id<ASVideoNodeDelegate> delegate;
|
@property (nullable, atomic, weak, readwrite) id<ASVideoNodeDelegate, ASNetworkImageNodeDelegate> delegate;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@protocol ASVideoNodeDelegate <NSObject>
|
@protocol ASVideoNodeDelegate <ASNetworkImageNodeDelegate>
|
||||||
@optional
|
@optional
|
||||||
/**
|
/**
|
||||||
* @abstract Delegate method invoked when the node's video has played to its end time.
|
* @abstract Delegate method invoked when the node's video has played to its end time.
|
||||||
|
|||||||
@@ -71,8 +71,6 @@ static NSString * const kStatus = @"status";
|
|||||||
int32_t _periodicTimeObserverTimescale;
|
int32_t _periodicTimeObserverTimescale;
|
||||||
CMTime _timeObserverInterval;
|
CMTime _timeObserverInterval;
|
||||||
|
|
||||||
ASImageNode *_placeholderImageNode; // TODO: Make ASVideoNode an ASImageNode subclass; remove this.
|
|
||||||
|
|
||||||
ASDisplayNode *_playerNode;
|
ASDisplayNode *_playerNode;
|
||||||
NSString *_gravity;
|
NSString *_gravity;
|
||||||
}
|
}
|
||||||
@@ -151,7 +149,7 @@ static NSString * const kStatus = @"status";
|
|||||||
self.player = [AVPlayer playerWithPlayerItem:playerItem];
|
self.player = [AVPlayer playerWithPlayerItem:playerItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_placeholderImageNode.image == nil) {
|
if (self.image == nil) {
|
||||||
[self generatePlaceholderImage];
|
[self generatePlaceholderImage];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,33 +191,34 @@ static NSString * const kStatus = @"status";
|
|||||||
[notificationCenter removeObserver:self name:AVPlayerItemNewErrorLogEntryNotification object:playerItem];
|
[notificationCenter removeObserver:self name:AVPlayerItemNewErrorLogEntryNotification object:playerItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
- (void)layout
|
||||||
{
|
{
|
||||||
// All subnodes should taking the whole node frame
|
[super layout];
|
||||||
CGSize maxSize = constrainedSize.max;
|
// The _playerNode wraps AVPlayerLayer, and therefore should extend across the entire bounds.
|
||||||
if (!CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero)) {
|
_playerNode.frame = self.bounds;
|
||||||
maxSize = self.preferredFrameSize;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent crashes through if infinite width or height
|
|
||||||
if (isinf(maxSize.width) || isinf(maxSize.height)) {
|
|
||||||
ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoNode");
|
|
||||||
maxSize = CGSizeZero;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stretch out play button, placeholder image player node to the max size
|
|
||||||
NSMutableArray *children = [NSMutableArray array];
|
|
||||||
|
|
||||||
if (_placeholderImageNode) {
|
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||||
_placeholderImageNode.preferredFrameSize = maxSize;
|
{
|
||||||
[children addObject:_placeholderImageNode];
|
ASDN::MutexLocker l(_videoLock);
|
||||||
}
|
CGSize calculatedSize = constrainedSize;
|
||||||
if (_playerNode) {
|
|
||||||
_playerNode.preferredFrameSize = maxSize;
|
// if a preferredFrameSize is set, call the superclass to return that instead of using the image size.
|
||||||
[children addObject:_playerNode];
|
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO)
|
||||||
}
|
calculatedSize = self.preferredFrameSize;
|
||||||
|
|
||||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:children];
|
// Prevent crashes through if infinite width or height
|
||||||
|
if (isinf(calculatedSize.width) || isinf(calculatedSize.height)) {
|
||||||
|
ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoNode");
|
||||||
|
calculatedSize = CGSizeZero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_playerNode) {
|
||||||
|
_playerNode.preferredFrameSize = calculatedSize;
|
||||||
|
[_playerNode measure:calculatedSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
return calculatedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)generatePlaceholderImage
|
- (void)generatePlaceholderImage
|
||||||
@@ -265,23 +264,10 @@ static NSString * const kStatus = @"status";
|
|||||||
- (void)setVideoPlaceholderImage:(UIImage *)image
|
- (void)setVideoPlaceholderImage:(UIImage *)image
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_videoLock);
|
ASDN::MutexLocker l(_videoLock);
|
||||||
|
if (image != nil) {
|
||||||
if (_placeholderImageNode == nil && image != nil) {
|
self.contentMode = ASContentModeFromVideoGravity(_gravity);
|
||||||
_placeholderImageNode = [[ASImageNode alloc] init];
|
|
||||||
_placeholderImageNode.layerBacked = YES;
|
|
||||||
_placeholderImageNode.contentMode = ASContentModeFromVideoGravity(_gravity);
|
|
||||||
}
|
}
|
||||||
|
self.image = image;
|
||||||
_placeholderImageNode.image = image;
|
|
||||||
|
|
||||||
ASPerformBlockOnMainThread(^{
|
|
||||||
ASDN::MutexLocker l(_videoLock);
|
|
||||||
|
|
||||||
if (_placeholderImageNode != nil) {
|
|
||||||
[self insertSubnode:_placeholderImageNode atIndex:0];
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||||
@@ -296,7 +282,7 @@ static NSString * const kStatus = @"status";
|
|||||||
if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) {
|
if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) {
|
||||||
self.playerState = ASVideoNodePlayerStateReadyToPlay;
|
self.playerState = ASVideoNodePlayerStateReadyToPlay;
|
||||||
// If we don't yet have a placeholder image update it now that we should have data available for it
|
// If we don't yet have a placeholder image update it now that we should have data available for it
|
||||||
if (_placeholderImageNode.image == nil) {
|
if (self.image == nil) {
|
||||||
[self generatePlaceholderImage];
|
[self generatePlaceholderImage];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,7 +379,6 @@ static NSString * const kStatus = @"status";
|
|||||||
|
|
||||||
self.player = nil;
|
self.player = nil;
|
||||||
self.currentItem = nil;
|
self.currentItem = nil;
|
||||||
_placeholderImageNode.image = nil;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,7 +481,7 @@ static NSString * const kStatus = @"status";
|
|||||||
if (_playerNode.isNodeLoaded) {
|
if (_playerNode.isNodeLoaded) {
|
||||||
((AVPlayerLayer *)_playerNode.layer).videoGravity = gravity;
|
((AVPlayerLayer *)_playerNode.layer).videoGravity = gravity;
|
||||||
}
|
}
|
||||||
_placeholderImageNode.contentMode = ASContentModeFromVideoGravity(gravity);
|
self.contentMode = ASContentModeFromVideoGravity(gravity);
|
||||||
_gravity = gravity;
|
_gravity = gravity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,11 +622,6 @@ static NSString * const kStatus = @"status";
|
|||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Internal Properties
|
#pragma mark - Internal Properties
|
||||||
- (ASImageNode *)placeholderImageNode
|
|
||||||
{
|
|
||||||
ASDN::MutexLocker l(_videoLock);
|
|
||||||
return _placeholderImageNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (AVPlayerItem *)currentItem
|
- (AVPlayerItem *)currentItem
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
}
|
}
|
||||||
@property (atomic, readwrite) ASInterfaceState interfaceState;
|
@property (atomic, readwrite) ASInterfaceState interfaceState;
|
||||||
@property (atomic, readonly) ASDisplayNode *spinner;
|
@property (atomic, readonly) ASDisplayNode *spinner;
|
||||||
@property (atomic, readonly) ASImageNode *placeholderImageNode;
|
|
||||||
@property (atomic, readwrite) ASDisplayNode *playerNode;
|
@property (atomic, readwrite) ASDisplayNode *playerNode;
|
||||||
@property (atomic, readwrite) AVPlayer *player;
|
@property (atomic, readwrite) AVPlayer *player;
|
||||||
@property (atomic, readwrite) BOOL shouldBePlaying;
|
@property (atomic, readwrite) BOOL shouldBePlaying;
|
||||||
@@ -355,16 +354,16 @@
|
|||||||
- (void)testSettingVideoGravityChangesPlaceholderContentMode
|
- (void)testSettingVideoGravityChangesPlaceholderContentMode
|
||||||
{
|
{
|
||||||
[_videoNode setVideoPlaceholderImage:[[UIImage alloc] init]];
|
[_videoNode setVideoPlaceholderImage:[[UIImage alloc] init]];
|
||||||
XCTAssertEqual(UIViewContentModeScaleAspectFit, _videoNode.placeholderImageNode.contentMode);
|
XCTAssertEqual(UIViewContentModeScaleAspectFit, _videoNode.contentMode);
|
||||||
|
|
||||||
_videoNode.gravity = AVLayerVideoGravityResize;
|
_videoNode.gravity = AVLayerVideoGravityResize;
|
||||||
XCTAssertEqual(UIViewContentModeScaleToFill, _videoNode.placeholderImageNode.contentMode);
|
XCTAssertEqual(UIViewContentModeScaleToFill, _videoNode.contentMode);
|
||||||
|
|
||||||
_videoNode.gravity = AVLayerVideoGravityResizeAspect;
|
_videoNode.gravity = AVLayerVideoGravityResizeAspect;
|
||||||
XCTAssertEqual(UIViewContentModeScaleAspectFit, _videoNode.placeholderImageNode.contentMode);
|
XCTAssertEqual(UIViewContentModeScaleAspectFit, _videoNode.contentMode);
|
||||||
|
|
||||||
_videoNode.gravity = AVLayerVideoGravityResizeAspectFill;
|
_videoNode.gravity = AVLayerVideoGravityResizeAspectFill;
|
||||||
XCTAssertEqual(UIViewContentModeScaleAspectFill, _videoNode.placeholderImageNode.contentMode);
|
XCTAssertEqual(UIViewContentModeScaleAspectFill, _videoNode.contentMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testChangingAssetsChangesPlaceholderImage
|
- (void)testChangingAssetsChangesPlaceholderImage
|
||||||
@@ -373,10 +372,10 @@
|
|||||||
|
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
[_videoNode setVideoPlaceholderImage:firstImage];
|
[_videoNode setVideoPlaceholderImage:firstImage];
|
||||||
XCTAssertEqual(firstImage, _videoNode.placeholderImageNode.image);
|
XCTAssertEqual(firstImage, _videoNode.image);
|
||||||
|
|
||||||
_videoNode.asset = _secondAsset;
|
_videoNode.asset = _secondAsset;
|
||||||
XCTAssertNotEqual(firstImage, _videoNode.placeholderImageNode.image);
|
XCTAssertNotEqual(firstImage, _videoNode.image);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testClearingFetchedContentShouldClearAssetData
|
- (void)testClearingFetchedContentShouldClearAssetData
|
||||||
@@ -397,12 +396,12 @@
|
|||||||
|
|
||||||
XCTAssertNotNil(_videoNode.player);
|
XCTAssertNotNil(_videoNode.player);
|
||||||
XCTAssertNotNil(_videoNode.currentItem);
|
XCTAssertNotNil(_videoNode.currentItem);
|
||||||
XCTAssertNotNil(_videoNode.placeholderImageNode.image);
|
XCTAssertNotNil(_videoNode.image);
|
||||||
|
|
||||||
[_videoNode clearFetchedData];
|
[_videoNode clearFetchedData];
|
||||||
XCTAssertNil(_videoNode.player);
|
XCTAssertNil(_videoNode.player);
|
||||||
XCTAssertNil(_videoNode.currentItem);
|
XCTAssertNil(_videoNode.currentItem);
|
||||||
XCTAssertNil(_videoNode.placeholderImageNode.image);
|
XCTAssertNil(_videoNode.image);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
Reference in New Issue
Block a user