Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit 52d5899

Browse files
hannahmbananaappleguy
authored andcommitted
[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)
1 parent 4804f42 commit 52d5899

4 files changed

Lines changed: 46 additions & 67 deletions

File tree

AsyncDisplayKit/ASNetworkImageNode.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ NS_ASSUME_NONNULL_BEGIN
4545
/**
4646
* The delegate, which must conform to the <ASNetworkImageNodeDelegate> protocol.
4747
*/
48-
@property (atomic, weak, readwrite) id<ASNetworkImageNodeDelegate> delegate;
48+
@property (nullable, atomic, weak, readwrite) id<ASNetworkImageNodeDelegate> delegate;
4949

5050
/**
5151
* A placeholder image to display while the URL is loading.
@@ -100,6 +100,7 @@ NS_ASSUME_NONNULL_BEGIN
100100
* notifications such as finished decoding and downloading an image.
101101
*/
102102
@protocol ASNetworkImageNodeDelegate <NSObject>
103+
@optional
103104

104105
/**
105106
* Notification that the image node finished downloading an image.
@@ -111,8 +112,6 @@ NS_ASSUME_NONNULL_BEGIN
111112
*/
112113
- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image;
113114

114-
@optional
115-
116115
/**
117116
* Notification that the image node started to load
118117
*

AsyncDisplayKit/ASVideoNode.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#if TARGET_OS_IOS
1010
#import <AsyncDisplayKit/ASButtonNode.h>
11+
#import <AsyncDisplayKit/ASNetworkImageNode.h>
1112

1213
@class AVAsset, AVPlayer, AVPlayerItem;
1314
@protocol ASVideoNodeDelegate;
@@ -31,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
3132
// there is room for further expansion and optimization. Please report any issues or requests
3233
// in an issue on GitHub: https://github.com/facebook/AsyncDisplayKit/issues
3334

34-
@interface ASVideoNode : ASControlNode
35+
@interface ASVideoNode : ASNetworkImageNode
3536

3637
- (void)play;
3738
- (void)pause;
@@ -60,11 +61,11 @@ NS_ASSUME_NONNULL_BEGIN
6061
//! Defaults to AVLayerVideoGravityResizeAspect
6162
@property (atomic) NSString *gravity;
6263

63-
@property (nullable, atomic, weak, readwrite) id<ASVideoNodeDelegate> delegate;
64+
@property (nullable, atomic, weak, readwrite) id<ASVideoNodeDelegate, ASNetworkImageNodeDelegate> delegate;
6465

6566
@end
6667

67-
@protocol ASVideoNodeDelegate <NSObject>
68+
@protocol ASVideoNodeDelegate <ASNetworkImageNodeDelegate>
6869
@optional
6970
/**
7071
* @abstract Delegate method invoked when the node's video has played to its end time.

AsyncDisplayKit/ASVideoNode.mm

Lines changed: 32 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ @interface ASVideoNode ()
7171
int32_t _periodicTimeObserverTimescale;
7272
CMTime _timeObserverInterval;
7373

74-
ASImageNode *_placeholderImageNode; // TODO: Make ASVideoNode an ASImageNode subclass; remove this.
75-
7674
ASDisplayNode *_playerNode;
7775
NSString *_gravity;
7876
}
@@ -151,7 +149,7 @@ - (void)prepareToPlayAsset:(AVAsset *)asset withKeys:(NSArray<NSString *> *)requ
151149
self.player = [AVPlayer playerWithPlayerItem:playerItem];
152150
}
153151

154-
if (_placeholderImageNode.image == nil) {
152+
if (self.image == nil) {
155153
[self generatePlaceholderImage];
156154
}
157155

@@ -193,33 +191,34 @@ - (void)removePlayerItemObservers:(AVPlayerItem *)playerItem
193191
[notificationCenter removeObserver:self name:AVPlayerItemNewErrorLogEntryNotification object:playerItem];
194192
}
195193

196-
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
194+
- (void)layout
197195
{
198-
// All subnodes should taking the whole node frame
199-
CGSize maxSize = constrainedSize.max;
200-
if (!CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero)) {
201-
maxSize = self.preferredFrameSize;
202-
}
203-
204-
// Prevent crashes through if infinite width or height
205-
if (isinf(maxSize.width) || isinf(maxSize.height)) {
206-
ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoNode");
207-
maxSize = CGSizeZero;
208-
}
209-
210-
// Stretch out play button, placeholder image player node to the max size
211-
NSMutableArray *children = [NSMutableArray array];
196+
[super layout];
197+
// The _playerNode wraps AVPlayerLayer, and therefore should extend across the entire bounds.
198+
_playerNode.frame = self.bounds;
199+
}
212200

213-
if (_placeholderImageNode) {
214-
_placeholderImageNode.preferredFrameSize = maxSize;
215-
[children addObject:_placeholderImageNode];
216-
}
217-
if (_playerNode) {
218-
_playerNode.preferredFrameSize = maxSize;
219-
[children addObject:_playerNode];
220-
}
221-
222-
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:children];
201+
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
202+
{
203+
ASDN::MutexLocker l(_videoLock);
204+
CGSize calculatedSize = constrainedSize;
205+
206+
// if a preferredFrameSize is set, call the superclass to return that instead of using the image size.
207+
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO)
208+
calculatedSize = self.preferredFrameSize;
209+
210+
// Prevent crashes through if infinite width or height
211+
if (isinf(calculatedSize.width) || isinf(calculatedSize.height)) {
212+
ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoNode");
213+
calculatedSize = CGSizeZero;
214+
}
215+
216+
if (_playerNode) {
217+
_playerNode.preferredFrameSize = calculatedSize;
218+
[_playerNode measure:calculatedSize];
219+
}
220+
221+
return calculatedSize;
223222
}
224223

225224
- (void)generatePlaceholderImage
@@ -265,23 +264,10 @@ - (void)imageAtTime:(CMTime)imageTime completionHandler:(void(^)(UIImage *image)
265264
- (void)setVideoPlaceholderImage:(UIImage *)image
266265
{
267266
ASDN::MutexLocker l(_videoLock);
268-
269-
if (_placeholderImageNode == nil && image != nil) {
270-
_placeholderImageNode = [[ASImageNode alloc] init];
271-
_placeholderImageNode.layerBacked = YES;
272-
_placeholderImageNode.contentMode = ASContentModeFromVideoGravity(_gravity);
267+
if (image != nil) {
268+
self.contentMode = ASContentModeFromVideoGravity(_gravity);
273269
}
274-
275-
_placeholderImageNode.image = image;
276-
277-
ASPerformBlockOnMainThread(^{
278-
ASDN::MutexLocker l(_videoLock);
279-
280-
if (_placeholderImageNode != nil) {
281-
[self insertSubnode:_placeholderImageNode atIndex:0];
282-
[self setNeedsLayout];
283-
}
284-
});
270+
self.image = image;
285271
}
286272

287273
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
@@ -296,7 +282,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
296282
if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) {
297283
self.playerState = ASVideoNodePlayerStateReadyToPlay;
298284
// If we don't yet have a placeholder image update it now that we should have data available for it
299-
if (_placeholderImageNode.image == nil) {
285+
if (self.image == nil) {
300286
[self generatePlaceholderImage];
301287
}
302288
}
@@ -393,7 +379,6 @@ - (void)clearFetchedData
393379

394380
self.player = nil;
395381
self.currentItem = nil;
396-
_placeholderImageNode.image = nil;
397382
}
398383
}
399384

@@ -496,7 +481,7 @@ - (void)setGravity:(NSString *)gravity
496481
if (_playerNode.isNodeLoaded) {
497482
((AVPlayerLayer *)_playerNode.layer).videoGravity = gravity;
498483
}
499-
_placeholderImageNode.contentMode = ASContentModeFromVideoGravity(gravity);
484+
self.contentMode = ASContentModeFromVideoGravity(gravity);
500485
_gravity = gravity;
501486
}
502487

@@ -637,11 +622,6 @@ - (void)errorWhilePlaying:(NSNotification *)notification
637622
}
638623

639624
#pragma mark - Internal Properties
640-
- (ASImageNode *)placeholderImageNode
641-
{
642-
ASDN::MutexLocker l(_videoLock);
643-
return _placeholderImageNode;
644-
}
645625

646626
- (AVPlayerItem *)currentItem
647627
{

AsyncDisplayKitTests/ASVideoNodeTests.m

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ @interface ASVideoNode () {
2929
}
3030
@property (atomic, readwrite) ASInterfaceState interfaceState;
3131
@property (atomic, readonly) ASDisplayNode *spinner;
32-
@property (atomic, readonly) ASImageNode *placeholderImageNode;
3332
@property (atomic, readwrite) ASDisplayNode *playerNode;
3433
@property (atomic, readwrite) AVPlayer *player;
3534
@property (atomic, readwrite) BOOL shouldBePlaying;
@@ -355,16 +354,16 @@ - (void)testVideoResumedWhenBufferIsLikelyToKeepUp
355354
- (void)testSettingVideoGravityChangesPlaceholderContentMode
356355
{
357356
[_videoNode setVideoPlaceholderImage:[[UIImage alloc] init]];
358-
XCTAssertEqual(UIViewContentModeScaleAspectFit, _videoNode.placeholderImageNode.contentMode);
357+
XCTAssertEqual(UIViewContentModeScaleAspectFit, _videoNode.contentMode);
359358

360359
_videoNode.gravity = AVLayerVideoGravityResize;
361-
XCTAssertEqual(UIViewContentModeScaleToFill, _videoNode.placeholderImageNode.contentMode);
360+
XCTAssertEqual(UIViewContentModeScaleToFill, _videoNode.contentMode);
362361

363362
_videoNode.gravity = AVLayerVideoGravityResizeAspect;
364-
XCTAssertEqual(UIViewContentModeScaleAspectFit, _videoNode.placeholderImageNode.contentMode);
363+
XCTAssertEqual(UIViewContentModeScaleAspectFit, _videoNode.contentMode);
365364

366365
_videoNode.gravity = AVLayerVideoGravityResizeAspectFill;
367-
XCTAssertEqual(UIViewContentModeScaleAspectFill, _videoNode.placeholderImageNode.contentMode);
366+
XCTAssertEqual(UIViewContentModeScaleAspectFill, _videoNode.contentMode);
368367
}
369368

370369
- (void)testChangingAssetsChangesPlaceholderImage
@@ -373,10 +372,10 @@ - (void)testChangingAssetsChangesPlaceholderImage
373372

374373
_videoNode.asset = _firstAsset;
375374
[_videoNode setVideoPlaceholderImage:firstImage];
376-
XCTAssertEqual(firstImage, _videoNode.placeholderImageNode.image);
375+
XCTAssertEqual(firstImage, _videoNode.image);
377376

378377
_videoNode.asset = _secondAsset;
379-
XCTAssertNotEqual(firstImage, _videoNode.placeholderImageNode.image);
378+
XCTAssertNotEqual(firstImage, _videoNode.image);
380379
}
381380

382381
- (void)testClearingFetchedContentShouldClearAssetData
@@ -397,12 +396,12 @@ - (void)testClearingFetchedContentShouldClearAssetData
397396

398397
XCTAssertNotNil(_videoNode.player);
399398
XCTAssertNotNil(_videoNode.currentItem);
400-
XCTAssertNotNil(_videoNode.placeholderImageNode.image);
399+
XCTAssertNotNil(_videoNode.image);
401400

402401
[_videoNode clearFetchedData];
403402
XCTAssertNil(_videoNode.player);
404403
XCTAssertNil(_videoNode.currentItem);
405-
XCTAssertNil(_videoNode.placeholderImageNode.image);
404+
XCTAssertNil(_videoNode.image);
406405
}
407406

408407
@end

0 commit comments

Comments
 (0)