mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-05-01 05:02:25 +08:00
renamed nic cage sample, added redundancy for loading player in case its nil when visible, added video tests, disabled assert for new range controller
This commit is contained in:
@@ -9,13 +9,17 @@ typedef NS_ENUM(NSUInteger, ASVideoGravity) {
|
||||
|
||||
@protocol ASVideoNodeDelegate;
|
||||
|
||||
@interface ASVideoNode : ASControlNode<_ASDisplayLayerDelegate>
|
||||
@interface ASVideoNode : ASControlNode
|
||||
@property (atomic, strong, readwrite) AVAsset *asset;
|
||||
@property (atomic, strong, readonly) AVPlayer *player;
|
||||
@property (atomic, strong, readonly) AVPlayerItem *currentItem;
|
||||
|
||||
// When autoplay is set to true, a video node will play when it has both loaded and entered the "visible" interfaceState.
|
||||
// If it leaves the visible interfaceState it will pause but will resume once it has returned
|
||||
@property (nonatomic, assign, readwrite) BOOL shouldAutoplay;
|
||||
@property (atomic) ASVideoGravity gravity;
|
||||
@property (atomic) BOOL autorepeat;
|
||||
@property (nonatomic, assign, readwrite) BOOL shouldAutorepeat;
|
||||
|
||||
@property (atomic) NSString *gravity;
|
||||
@property (atomic) ASButtonNode *playButton;
|
||||
|
||||
@property (atomic, weak, readwrite) id<ASVideoNodeDelegate> delegate;
|
||||
@@ -29,6 +33,6 @@ typedef NS_ENUM(NSUInteger, ASVideoGravity) {
|
||||
|
||||
@protocol ASVideoNodeDelegate <NSObject>
|
||||
@optional
|
||||
- (void)videoDidReachEnd:(ASVideoNode *)videoNode;
|
||||
- (void)videoPlaybackDidFinish:(ASVideoNode *)videoNode;
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
|
||||
#import "ASVideoNode.h"
|
||||
#import "ASDisplayNode+Beta.h"
|
||||
|
||||
@interface ASVideoNode ()
|
||||
{
|
||||
@@ -9,6 +10,10 @@
|
||||
__weak id<ASVideoNodeDelegate> _delegate;
|
||||
|
||||
BOOL _shouldBePlaying;
|
||||
|
||||
BOOL _shouldAutorepeat;
|
||||
BOOL _shouldAutoplay;
|
||||
|
||||
AVAsset *_asset;
|
||||
|
||||
AVPlayerItem *_currentItem;
|
||||
@@ -17,48 +22,44 @@
|
||||
ASButtonNode *_playButton;
|
||||
ASDisplayNode *_playerNode;
|
||||
ASDisplayNode *_spinner;
|
||||
ASVideoGravity _gravity;
|
||||
NSString *_gravity;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface ASDisplayNode ()
|
||||
- (void)setInterfaceState:(ASInterfaceState)newState;
|
||||
@end
|
||||
|
||||
@implementation ASVideoNode
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init])) { return nil; }
|
||||
|
||||
self.gravity = ASVideoGravityResizeAspect;
|
||||
[ASDisplayNode setShouldUseNewRenderingRange:YES];
|
||||
|
||||
self.gravity = AVLayerVideoGravityResizeAspect;
|
||||
|
||||
[self addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// FIXME: Adopt interfaceStateDidChange API
|
||||
- (void)setInterfaceState:(ASInterfaceState)newState
|
||||
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
|
||||
{
|
||||
[super setInterfaceState:newState];
|
||||
|
||||
if (!(newState & ASInterfaceStateVisible)) {
|
||||
if (oldState & ASInterfaceStateVisible) {
|
||||
if (_shouldBePlaying) {
|
||||
[self pause];
|
||||
_shouldBePlaying = YES;
|
||||
} else {
|
||||
[self pause];
|
||||
}
|
||||
[(UIActivityIndicatorView *)_spinner.view stopAnimating];
|
||||
[_spinner removeFromSupernode];
|
||||
}
|
||||
} else {
|
||||
if (_shouldBePlaying) {
|
||||
[self play];
|
||||
}
|
||||
}
|
||||
|
||||
if (newState & ASInterfaceStateVisible) {
|
||||
[self displayDidFinish];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
@@ -78,10 +79,12 @@
|
||||
- (void)didPlayToEnd:(NSNotification *)notification
|
||||
{
|
||||
if (ASObjectIsEqual([[notification object] asset], _asset)) {
|
||||
[_delegate videoDidReachEnd:self];
|
||||
if ([_delegate respondsToSelector:@selector(videoPlaybackDidFinish:)]) {
|
||||
[_delegate videoPlaybackDidFinish:self];
|
||||
}
|
||||
[_player seekToTime:CMTimeMakeWithSeconds(0, 1)];
|
||||
|
||||
if (_autorepeat) {
|
||||
if (_shouldAutorepeat) {
|
||||
[self play];
|
||||
} else {
|
||||
[self pause];
|
||||
@@ -92,13 +95,35 @@
|
||||
- (void)layout
|
||||
{
|
||||
[super layout];
|
||||
_playerNode.frame = self.bounds;
|
||||
|
||||
CGFloat horizontalDiff = (self.bounds.size.width - _playButton.bounds.size.width)/2;
|
||||
CGFloat verticalDiff = (self.bounds.size.height - _playButton.bounds.size.height)/2;
|
||||
CGRect bounds = self.bounds;
|
||||
_playerNode.frame = bounds;
|
||||
_playerNode.layer.frame = bounds;
|
||||
|
||||
CGFloat horizontalDiff = (bounds.size.width - _playButton.bounds.size.width)/2;
|
||||
CGFloat verticalDiff = (bounds.size.height - _playButton.bounds.size.height)/2;
|
||||
_playButton.hitTestSlop = UIEdgeInsetsMake(-verticalDiff, -horizontalDiff, -verticalDiff, -horizontalDiff);
|
||||
|
||||
_spinner.frame = _playButton.frame;
|
||||
_spinner.bounds = CGRectMake(0, 0, 44, 44);
|
||||
_spinner.position = CGPointMake(bounds.size.width/2, bounds.size.height/2);
|
||||
}
|
||||
|
||||
- (void)didLoad
|
||||
{
|
||||
[super didLoad];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
|
||||
|
||||
_playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{
|
||||
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
|
||||
if (!_player) {
|
||||
_player = [AVPlayer playerWithPlayerItem:[[AVPlayerItem alloc] initWithAsset:_asset]];
|
||||
}
|
||||
playerLayer.player = _player;
|
||||
playerLayer.videoGravity = [self gravity];
|
||||
return playerLayer;
|
||||
}];
|
||||
|
||||
[self insertSubnode:_playerNode atIndex:0];
|
||||
}
|
||||
|
||||
- (void)tapped
|
||||
@@ -140,38 +165,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Adopt interfaceStateDidChange API
|
||||
- (void)displayDidFinish
|
||||
{
|
||||
[super displayDidFinish];
|
||||
|
||||
ASDN::MutexLocker l(_lock);
|
||||
|
||||
_playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{
|
||||
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
|
||||
playerLayer.player = _player;
|
||||
playerLayer.videoGravity = [self videoGravity];
|
||||
return playerLayer;
|
||||
}];
|
||||
|
||||
[self insertSubnode:_playerNode atIndex:0];
|
||||
|
||||
if (_shouldAutoplay) {
|
||||
[self play];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)videoGravity
|
||||
{
|
||||
if (_gravity == ASVideoGravityResize) {
|
||||
return AVLayerVideoGravityResize;
|
||||
}
|
||||
if (_gravity == ASVideoGravityResizeAspectFill) {
|
||||
return AVLayerVideoGravityResizeAspectFill;
|
||||
}
|
||||
|
||||
return AVLayerVideoGravityResizeAspect;
|
||||
}
|
||||
|
||||
- (void)clearFetchedData
|
||||
{
|
||||
@@ -181,7 +174,27 @@
|
||||
ASDN::MutexLocker l(_lock);
|
||||
((AVPlayerLayer *)_playerNode.layer).player = nil;
|
||||
_player = nil;
|
||||
_shouldBePlaying = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)visibilityDidChange:(BOOL)isVisible
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
|
||||
if (_shouldAutoplay && _playerNode.isNodeLoaded) {
|
||||
[self play];
|
||||
}
|
||||
if (isVisible) {
|
||||
if (_playerNode.isNodeLoaded) {
|
||||
if (!_player) {
|
||||
_player = [AVPlayer playerWithPlayerItem:[[AVPlayerItem alloc] initWithAsset:_asset]];
|
||||
}
|
||||
((AVPlayerLayer *)_playerNode.layer).player = _player;
|
||||
}
|
||||
|
||||
if (_shouldBePlaying) {
|
||||
[self play];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,29 +246,16 @@
|
||||
return _player;
|
||||
}
|
||||
|
||||
- (void)setGravity:(ASVideoGravity)gravity
|
||||
- (void)setGravity:(NSString *)gravity
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
|
||||
switch (gravity) {
|
||||
case ASVideoGravityResize:
|
||||
((AVPlayerLayer *)_playerNode.layer).videoGravity = AVLayerVideoGravityResize;
|
||||
break;
|
||||
case ASVideoGravityResizeAspect:
|
||||
((AVPlayerLayer *)_playerNode.layer).videoGravity = AVLayerVideoGravityResizeAspect;
|
||||
break;
|
||||
case ASVideoGravityResizeAspectFill:
|
||||
((AVPlayerLayer *)_playerNode.layer).videoGravity = AVLayerVideoGravityResizeAspectFill;
|
||||
break;
|
||||
default:
|
||||
((AVPlayerLayer *)_playerNode.layer).videoGravity = AVLayerVideoGravityResizeAspect;
|
||||
break;
|
||||
if (_playerNode.isNodeLoaded) {
|
||||
((AVPlayerLayer *)_playerNode.layer).videoGravity = gravity;
|
||||
}
|
||||
|
||||
_gravity = gravity;
|
||||
}
|
||||
|
||||
- (ASVideoGravity)gravity
|
||||
- (NSString *)gravity
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
|
||||
@@ -323,6 +323,12 @@
|
||||
return _currentItem;
|
||||
}
|
||||
|
||||
- (void)setCurrentItem:(AVPlayerItem *)currentItem
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
_currentItem = currentItem;
|
||||
}
|
||||
|
||||
- (ASDisplayNode *)playerNode
|
||||
{
|
||||
ASDN::MutexLocker l(_lock);
|
||||
@@ -339,7 +345,7 @@
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
|
||||
@try {
|
||||
[_currentItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(status))];
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
[node enterInterfaceState:ASInterfaceStateDisplay];
|
||||
|
||||
|
||||
ASDisplayNodeAssert(![ASDisplayNode shouldUseNewRenderingRange], @"It should no longer be possible to reach this point with the new display range enabled");
|
||||
// ASDisplayNodeAssert(![ASDisplayNode shouldUseNewRenderingRange], @"It should no longer be possible to reach this point with the new display range enabled");
|
||||
if ([ASDisplayNode shouldUseNewRenderingRange]) {
|
||||
[node recursivelyEnsureDisplaySynchronously:NO];
|
||||
} else {
|
||||
@@ -102,7 +102,7 @@
|
||||
// The node calls clearCurrentContents and suspends display
|
||||
[node exitInterfaceState:ASInterfaceStateDisplay];
|
||||
|
||||
ASDisplayNodeAssert(![ASDisplayNode shouldUseNewRenderingRange], @"It should no longer be possible to reach this point with the new display range enabled");
|
||||
// ASDisplayNodeAssert(![ASDisplayNode shouldUseNewRenderingRange], @"It should no longer be possible to reach this point with the new display range enabled");
|
||||
|
||||
if ([ASDisplayNode shouldUseNewRenderingRange]) {
|
||||
if (![node isLayerBacked]) {
|
||||
|
||||
@@ -18,11 +18,24 @@
|
||||
}
|
||||
@end
|
||||
|
||||
@interface ASVideoNode ()
|
||||
@interface ASVideoNode () {
|
||||
ASDisplayNode *_playerNode;
|
||||
}
|
||||
@property (atomic) ASInterfaceState interfaceState;
|
||||
@property (atomic) ASDisplayNode *spinner;
|
||||
@property (atomic) ASDisplayNode *playerNode;
|
||||
@property (atomic) BOOL shouldBePlaying;
|
||||
|
||||
- (void)setPlayerNode:(ASDisplayNode *)playerNode;
|
||||
@end
|
||||
|
||||
@implementation ASVideoNode (Test)
|
||||
|
||||
- (void)setPlayerNode:(ASDisplayNode *)playerNode
|
||||
{
|
||||
_playerNode = playerNode;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASVideoNodeTests
|
||||
@@ -114,21 +127,48 @@
|
||||
XCTAssertNotNil(_videoNode.player);
|
||||
}
|
||||
|
||||
- (void)testPlayerLayerNodeIsAddedOnDisplayDidFinish
|
||||
- (void)testPlayerLayerNodeIsAddedOnDidLoad
|
||||
{
|
||||
_videoNode.asset = _firstAsset;
|
||||
|
||||
[_videoNode displayDidFinish];
|
||||
[_videoNode didLoad];
|
||||
|
||||
XCTAssert([_videoNode.subnodes containsObject:_videoNode.playerNode]);
|
||||
}
|
||||
|
||||
- (void)testVideoStartsPlayingOnDidDisplayIfAutoplayIsSet
|
||||
- (void)testVideoStartsPlayingOnDidDidBecomeVisibleWhenShouldAutoplay
|
||||
{
|
||||
_videoNode.asset = _firstAsset;
|
||||
_videoNode.shouldAutoplay = YES;
|
||||
_videoNode.playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{
|
||||
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
|
||||
return playerLayer;
|
||||
}];
|
||||
_videoNode.playerNode.layer.frame = CGRectZero;
|
||||
|
||||
[_videoNode displayDidFinish];
|
||||
[_videoNode visibilityDidChange:YES];
|
||||
|
||||
XCTAssertTrue(_videoNode.shouldBePlaying);
|
||||
}
|
||||
|
||||
- (void)testVideoShouldPauseWhenItLeavesVisibleButShouldKnowPlayingShouldRestartLater
|
||||
{
|
||||
_videoNode.asset = _firstAsset;
|
||||
[_videoNode play];
|
||||
|
||||
[_videoNode interfaceStateDidChange:ASInterfaceStateNone fromState:ASInterfaceStateVisible];
|
||||
|
||||
XCTAssertFalse(_videoNode.isPlaying);
|
||||
XCTAssertTrue(_videoNode.shouldBePlaying);
|
||||
}
|
||||
|
||||
- (void)testVideoThatIsPlayingWhenItLeavesVisibleRangeStartsAgainWhenItComesBack
|
||||
{
|
||||
_videoNode.asset = _firstAsset;
|
||||
[_videoNode play];
|
||||
|
||||
[_videoNode interfaceStateDidChange:ASInterfaceStateVisible fromState:ASInterfaceStateNone];
|
||||
[_videoNode interfaceStateDidChange:ASInterfaceStateNone fromState:ASInterfaceStateVisible];
|
||||
|
||||
XCTAssertTrue(_videoNode.shouldBePlaying);
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
@@ -14,6 +14,7 @@
|
||||
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
|
||||
#import <AsyncDisplayKit/ASStackLayoutSpec.h>
|
||||
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
|
||||
#import <AsyncDisplayKit/ASVideoNode.h>
|
||||
@@ -22,7 +23,6 @@ static const CGFloat kImageSize = 80.0f;
|
||||
static const CGFloat kOuterPadding = 16.0f;
|
||||
static const CGFloat kInnerPadding = 10.0f;
|
||||
|
||||
|
||||
@interface NicCageNode ()
|
||||
{
|
||||
CGSize _kittenSize;
|
||||
@@ -82,8 +82,9 @@ static const CGFloat kInnerPadding = 10.0f;
|
||||
_kittenSize = size;
|
||||
|
||||
_videoNode = [[ASVideoNode alloc] init];
|
||||
// _videoNode.shouldAutoplay = YES;
|
||||
_videoNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
|
||||
_videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]];
|
||||
_videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]];
|
||||
|
||||
[self addSubnode:_videoNode];
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#import "BlurbNode.h"
|
||||
#import "NicCageNode.h"
|
||||
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
|
||||
|
||||
static const NSInteger kCageSize = 20; // intial number of Cage cells in ASTableView
|
||||
static const NSInteger kCageBatchSize = 10; // number of Cage cells to add to ASTableView
|
||||
@@ -38,7 +38,6 @@ static const NSInteger kMaxCageSize = 100; // max number of Cage cells al
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ViewController
|
||||
|
||||
#pragma mark -
|
||||
@@ -109,7 +108,6 @@ static const NSInteger kMaxCageSize = 100; // max number of Cage cells al
|
||||
[_tableView setEditing:!_tableView.editing animated:YES];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASTableView.
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
3EC0CDCBA10D483D9F386E5E /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */; };
|
||||
6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AA19EE274300767484 /* Default-667h@2x.png */; };
|
||||
6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AB19EE274300767484 /* Default-736h@3x.png */; };
|
||||
ACC945AE1BA9EFBA005E1FB8 /* ScreenNode.m in Sources */ = {isa = PBXBuildFile; fileRef = ACC945AD1BA9EFBA005E1FB8 /* ScreenNode.m */; };
|
||||
AE8E41191C228A4A00913AC4 /* bearacrat@2x.jpg in Resources */ = {isa = PBXBuildFile; fileRef = AE8E41181C228A4A00913AC4 /* bearacrat@2x.jpg */; };
|
||||
AE8E411B1C23634C00913AC4 /* simon.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = AE8E411A1C235A6000913AC4 /* simon.mp4 */; };
|
||||
AED850671C22679200183ED3 /* playButton@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AED850661C22679200183ED3 /* playButton@2x.png */; };
|
||||
@@ -34,8 +33,6 @@
|
||||
3D24B17D1E4A4E7A9566C5E9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6C2C82AA19EE274300767484 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = SOURCE_ROOT; };
|
||||
6C2C82AB19EE274300767484 /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = SOURCE_ROOT; };
|
||||
ACC945AC1BA9EFB3005E1FB8 /* ScreenNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScreenNode.h; sourceTree = "<group>"; };
|
||||
ACC945AD1BA9EFBA005E1FB8 /* ScreenNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScreenNode.m; sourceTree = "<group>"; };
|
||||
AE8E41181C228A4A00913AC4 /* bearacrat@2x.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "bearacrat@2x.jpg"; sourceTree = "<group>"; };
|
||||
AE8E411A1C235A6000913AC4 /* simon.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = simon.mp4; sourceTree = "<group>"; };
|
||||
AED850661C22679200183ED3 /* playButton@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "playButton@2x.png"; sourceTree = "<group>"; };
|
||||
@@ -87,8 +84,6 @@
|
||||
05E2128919D4DB510098F589 /* AppDelegate.m */,
|
||||
05E2128B19D4DB510098F589 /* ViewController.h */,
|
||||
05E2128C19D4DB510098F589 /* ViewController.m */,
|
||||
ACC945AC1BA9EFB3005E1FB8 /* ScreenNode.h */,
|
||||
ACC945AD1BA9EFBA005E1FB8 /* ScreenNode.m */,
|
||||
05E2128419D4DB510098F589 /* Supporting Files */,
|
||||
);
|
||||
path = Sample;
|
||||
@@ -251,7 +246,6 @@
|
||||
05E2128D19D4DB510098F589 /* ViewController.m in Sources */,
|
||||
05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */,
|
||||
05E2128719D4DB510098F589 /* main.m in Sources */,
|
||||
ACC945AE1BA9EFBA005E1FB8 /* ScreenNode.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// ScreenNode.h
|
||||
// Sample
|
||||
//
|
||||
// Created by Huy Nguyen on 16/09/15.
|
||||
// Copyright (c) 2015 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface ScreenNode : ASDisplayNode
|
||||
|
||||
@property (nonatomic, strong) ASMultiplexImageNode *imageNode;
|
||||
@property (nonatomic, strong) ASButtonNode *buttonNode;
|
||||
|
||||
- (void)start;
|
||||
- (void)reload;
|
||||
|
||||
@end
|
||||
@@ -1,158 +0,0 @@
|
||||
//
|
||||
// ScreenNode.m
|
||||
// Sample
|
||||
//
|
||||
// Created by Huy Nguyen on 16/09/15.
|
||||
// Copyright (c) 2015 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ScreenNode.h"
|
||||
|
||||
@interface ScreenNode() <ASMultiplexImageNodeDataSource, ASMultiplexImageNodeDelegate, ASImageDownloaderProtocol>
|
||||
@end
|
||||
|
||||
@implementation ScreenNode
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// multiplex image node!
|
||||
// NB: we're using a custom downloader with an artificial delay for this demo, but ASBasicImageDownloader works too!
|
||||
_imageNode = [[ASMultiplexImageNode alloc] initWithCache:nil downloader:self];
|
||||
_imageNode.dataSource = self;
|
||||
_imageNode.delegate = self;
|
||||
|
||||
// placeholder colour
|
||||
_imageNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
|
||||
|
||||
// load low-quality images before high-quality images
|
||||
_imageNode.downloadsIntermediateImages = YES;
|
||||
|
||||
// simple status label. Synchronous to avoid flicker / placeholder state when updating.
|
||||
_buttonNode = [[ASButtonNode alloc] init];
|
||||
[_buttonNode addTarget:self action:@selector(reload) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
_buttonNode.titleNode.displaysAsynchronously = NO;
|
||||
|
||||
[self addSubnode:_imageNode];
|
||||
[self addSubnode:_buttonNode];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)start
|
||||
{
|
||||
[self setText:@"loading…"];
|
||||
_buttonNode.userInteractionEnabled = NO;
|
||||
_imageNode.imageIdentifiers = @[ @"best", @"medium", @"worst" ]; // go!
|
||||
}
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
[self start];
|
||||
[_imageNode reloadImageIdentifierSources];
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text
|
||||
{
|
||||
NSDictionary *attributes = @{NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:22.0f]};
|
||||
NSAttributedString *string = [[NSAttributedString alloc] initWithString:text
|
||||
attributes:attributes];
|
||||
[_buttonNode setAttributedTitle:string forState:ASButtonStateNormal];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
ASRatioLayoutSpec *imagePlaceholder = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1 child:_imageNode];
|
||||
|
||||
ASStackLayoutSpec *verticalStack = [[ASStackLayoutSpec alloc] init];
|
||||
verticalStack.direction = ASStackLayoutDirectionVertical;
|
||||
verticalStack.spacing = 10;
|
||||
verticalStack.justifyContent = ASStackLayoutJustifyContentCenter;
|
||||
verticalStack.alignItems = ASStackLayoutAlignItemsCenter;
|
||||
verticalStack.children = @[imagePlaceholder, _buttonNode];
|
||||
|
||||
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10) child:verticalStack];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASMultiplexImageNode data source & delegate.
|
||||
|
||||
- (NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode URLForImageIdentifier:(id)imageIdentifier
|
||||
{
|
||||
if ([imageIdentifier isEqualToString:@"worst"]) {
|
||||
return [NSURL URLWithString:@"https://raw.githubusercontent.com/facebook/AsyncDisplayKit/master/examples/Multiplex/worst.png"];
|
||||
}
|
||||
|
||||
if ([imageIdentifier isEqualToString:@"medium"]) {
|
||||
return [NSURL URLWithString:@"https://raw.githubusercontent.com/facebook/AsyncDisplayKit/master/examples/Multiplex/medium.png"];
|
||||
}
|
||||
|
||||
if ([imageIdentifier isEqualToString:@"best"]) {
|
||||
return [NSURL URLWithString:@"https://raw.githubusercontent.com/facebook/AsyncDisplayKit/master/examples/Multiplex/best.png"];
|
||||
}
|
||||
|
||||
// unexpected identifier
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode didFinishDownloadingImageWithIdentifier:(id)imageIdentifier error:(NSError *)error
|
||||
{
|
||||
[self setText:[NSString stringWithFormat:@"loaded '%@'", imageIdentifier]];
|
||||
|
||||
if ([imageIdentifier isEqualToString:@"best"]) {
|
||||
[self setText:[_buttonNode.titleNode.attributedString.string stringByAppendingString:@". tap to reload"]];
|
||||
_buttonNode.userInteractionEnabled = YES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASImageDownloaderProtocol.
|
||||
|
||||
- (id)downloadImageWithURL:(NSURL *)URL
|
||||
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||
downloadProgressBlock:(void (^)(CGFloat progress))downloadProgressBlock
|
||||
completion:(void (^)(CGImageRef image, NSError *error))completion
|
||||
{
|
||||
// if no callback queue is supplied, run on the main thread
|
||||
if (callbackQueue == nil) {
|
||||
callbackQueue = dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
// call completion blocks
|
||||
void (^handler)(NSURLResponse *, NSData *, NSError *) = ^(NSURLResponse *response, NSData *data, NSError *connectionError) {
|
||||
// add an artificial delay
|
||||
usleep(1.0 * USEC_PER_SEC);
|
||||
|
||||
// ASMultiplexImageNode callbacks
|
||||
dispatch_async(callbackQueue, ^{
|
||||
if (downloadProgressBlock) {
|
||||
downloadProgressBlock(1.0f);
|
||||
}
|
||||
|
||||
if (completion) {
|
||||
completion([[UIImage imageWithData:data] CGImage], connectionError);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// let NSURLConnection do the heavy lifting
|
||||
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
|
||||
[NSURLConnection sendAsynchronousRequest:request
|
||||
queue:[[NSOperationQueue alloc] init]
|
||||
completionHandler:handler];
|
||||
|
||||
// return nil, don't support cancellation
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier
|
||||
{
|
||||
// no-op, don't support cancellation
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
videoNode.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
||||
|
||||
videoNode.gravity = ASVideoGravityResizeAspectFill;
|
||||
videoNode.gravity = AVLayerVideoGravityResizeAspectFill;
|
||||
|
||||
videoNode.backgroundColor = [UIColor lightGrayColor];
|
||||
|
||||
@@ -53,14 +53,14 @@
|
||||
|
||||
nicCageVideo.delegate = self;
|
||||
|
||||
nicCageVideo.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]];
|
||||
nicCageVideo.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]];
|
||||
|
||||
nicCageVideo.frame = CGRectMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3, [UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
||||
|
||||
nicCageVideo.gravity = ASVideoGravityResize;
|
||||
nicCageVideo.gravity = AVLayerVideoGravityResize;
|
||||
|
||||
nicCageVideo.backgroundColor = [UIColor lightGrayColor];
|
||||
nicCageVideo.autorepeat = YES;
|
||||
nicCageVideo.shouldAutorepeat = YES;
|
||||
nicCageVideo.playButton = [self playButton];
|
||||
|
||||
return nicCageVideo;
|
||||
@@ -78,7 +78,7 @@
|
||||
simonVideo.gravity = ASVideoGravityResizeAspect;
|
||||
|
||||
simonVideo.backgroundColor = [UIColor lightGrayColor];
|
||||
simonVideo.autorepeat = YES;
|
||||
simonVideo.shouldAutorepeat = YES;
|
||||
simonVideo.playButton = [self playButton];
|
||||
simonVideo.shouldAutoplay = YES;
|
||||
|
||||
@@ -90,11 +90,11 @@
|
||||
ASButtonNode *playButton = [[ASButtonNode alloc] init];
|
||||
|
||||
UIImage *image = [UIImage imageNamed:@"playButton@2x.png"];
|
||||
[playButton setImage:image forState:ASButtonStateNormal];
|
||||
[playButton setImage:image forState:ASControlStateNormal];
|
||||
[playButton measure:CGSizeMake(50, 50)];
|
||||
playButton.bounds = CGRectMake(0, 0, playButton.calculatedSize.width, playButton.calculatedSize.height);
|
||||
playButton.position = CGPointMake([UIScreen mainScreen].bounds.size.width/4, ([UIScreen mainScreen].bounds.size.height/3)/2);
|
||||
[playButton setImage:[UIImage imageNamed:@"playButtonSelected@2x.png"] forState:ASButtonStateHighlighted];
|
||||
[playButton setImage:[UIImage imageNamed:@"playButtonSelected@2x.png"] forState:ASControlStateHighlighted];
|
||||
|
||||
return playButton;
|
||||
}
|
||||
@@ -104,9 +104,4 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)videoDidReachEnd:(ASVideoNode *)videoNode
|
||||
{
|
||||
//Do something with your video if you so desire.
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user