Merge branch 'master' into update-objc

This commit is contained in:
Adlai Holler
2015-09-17 11:27:27 -07:00
29 changed files with 688 additions and 560 deletions

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
DS_Store .DS_Store
*.pbxuser *.pbxuser
*.perspective *.perspective

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
//
// ASViewController.h
// AsyncDisplayKit
//
// Created by Huy Nguyen on 16/09/15.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
@interface ASViewController : UIViewController
@property (nonatomic, strong, readonly) ASDisplayNode *node;
//TODO Use nonnull annotation late on. Travis doesn't recognize it (yet).
- (instancetype)initWithNode:(ASDisplayNode *)node;
@end

View File

@@ -0,0 +1,48 @@
//
// ASViewController.m
// AsyncDisplayKit
//
// Created by Huy Nguyen on 16/09/15.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import "ASViewController.h"
#import "ASAssert.h"
#import "ASDimension.h"
@implementation ASViewController
- (instancetype)initWithNode:(ASDisplayNode *)node
{
if (!(self = [super init])) {
return nil;
}
ASDisplayNodeAssertNotNil(node, @"Node must not be nil");
ASDisplayNodeAssertTrue(!node.layerBacked);
_node = node;
return self;
}
- (void)loadView
{
ASDisplayNodeAssertTrue(!_node.layerBacked);
self.view = _node.view;
}
- (void)viewWillLayoutSubviews
{
CGSize viewSize = self.view.bounds.size;
ASSizeRange constrainedSize = ASSizeRangeMake(viewSize, viewSize);
[_node measureWithSizeRange:constrainedSize];
[super viewWillLayoutSubviews];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[_node recursivelyFetchData];
}
@end

View File

@@ -26,6 +26,8 @@
#import <AsyncDisplayKit/ASScrollNode.h> #import <AsyncDisplayKit/ASScrollNode.h>
#import <AsyncDisplayKit/ASViewController.h>
#import <AsyncDisplayKit/ASLayout.h> #import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASDimension.h> #import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASLayoutable.h> #import <AsyncDisplayKit/ASLayoutable.h>
@@ -49,6 +51,7 @@
#import <AsyncDisplayKit/ASControlNode+Subclasses.h> #import <AsyncDisplayKit/ASControlNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h> #import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNodeExtraIvars.h> #import <AsyncDisplayKit/ASDisplayNodeExtraIvars.h>
#import <AsyncDisplayKit/ASEqualityHelpers.h>
#import <AsyncDisplayKit/ASHighlightOverlayLayer.h> #import <AsyncDisplayKit/ASHighlightOverlayLayer.h>
#import <AsyncDisplayKit/ASIndexPath.h> #import <AsyncDisplayKit/ASIndexPath.h>
#import <AsyncDisplayKit/ASLayoutOptions.h> #import <AsyncDisplayKit/ASLayoutOptions.h>
@@ -57,7 +60,6 @@
#import <AsyncDisplayKit/ASRangeHandler.h> #import <AsyncDisplayKit/ASRangeHandler.h>
#import <AsyncDisplayKit/ASRangeHandlerPreload.h> #import <AsyncDisplayKit/ASRangeHandlerPreload.h>
#import <AsyncDisplayKit/ASRangeHandlerRender.h> #import <AsyncDisplayKit/ASRangeHandlerRender.h>
//#import <AsyncDisplayKit/ASStackUnpositionedLayout.h>
#import <AsyncDisplayKit/ASTextNodeCoreTextAdditions.h> #import <AsyncDisplayKit/ASTextNodeCoreTextAdditions.h>
#import <AsyncDisplayKit/ASTextNodeRenderer.h> #import <AsyncDisplayKit/ASTextNodeRenderer.h>
#import <AsyncDisplayKit/ASTextNodeShadower.h> #import <AsyncDisplayKit/ASTextNodeShadower.h>
@@ -65,5 +67,7 @@
#import <AsyncDisplayKit/ASTextNodeTypes.h> #import <AsyncDisplayKit/ASTextNodeTypes.h>
#import <AsyncDisplayKit/ASTextNodeWordKerner.h> #import <AsyncDisplayKit/ASTextNodeWordKerner.h>
#import <AsyncDisplayKit/ASThread.h> #import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/CGRect+ASConvenience.h>
#import <AsyncDisplayKit/NSMutableAttributedString+TextKitAdditions.h> #import <AsyncDisplayKit/NSMutableAttributedString+TextKitAdditions.h>
#import <AsyncDisplayKit/UICollectionViewLayout+ASConvenience.h>
#import <AsyncDisplayKit/UIView+ASConvenience.h> #import <AsyncDisplayKit/UIView+ASConvenience.h>

View File

@@ -64,7 +64,7 @@
* *
* @param layoutOptions The layoutOptions to copy from * @param layoutOptions The layoutOptions to copy from
*/ */
- (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions; - (void)copyFromOptions:(ASLayoutOptions *)layoutOptions;
#pragma mark - ASStackLayoutable #pragma mark - ASStackLayoutable

View File

@@ -86,11 +86,11 @@ static Class gDefaultLayoutOptionsClass = nil;
- (id)copyWithZone:(NSZone *)zone - (id)copyWithZone:(NSZone *)zone
{ {
ASLayoutOptions *copy = [[[self class] alloc] init]; ASLayoutOptions *copy = [[[self class] alloc] init];
[copy copyIntoOptions:self]; [copy copyFromOptions:self];
return copy; return copy;
} }
- (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions - (void)copyFromOptions:(ASLayoutOptions *)layoutOptions
{ {
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
self.flexBasis = layoutOptions.flexBasis; self.flexBasis = layoutOptions.flexBasis;

View File

@@ -82,7 +82,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
id<ASLayoutable> finalLayoutable = [child finalLayoutable]; id<ASLayoutable> finalLayoutable = [child finalLayoutable];
if (finalLayoutable != child) { if (finalLayoutable != child) {
[finalLayoutable.layoutOptions copyIntoOptions:child.layoutOptions]; [finalLayoutable.layoutOptions copyFromOptions:child.layoutOptions];
return finalLayoutable; return finalLayoutable;
} }
} }

View File

@@ -0,0 +1,19 @@
framework module AsyncDisplayKit {
umbrella header "AsyncDisplayKit.h"
export *
module * {
export *
}
explicit module ASControlNode_Subclasses {
header "ASControlNode+Subclasses.h"
export *
}
explicit module ASDisplayNode_Subclasses {
header "ASDisplayNode+Subclasses.h"
export *
}
}

View File

@@ -6,8 +6,8 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*/ */
#import <AsyncDisplayKit/ASBasicImageDownloaderInternal.h>
#import <AsyncDisplayKit/ASBasicImageDownloader.h> #import <AsyncDisplayKit/ASBasicImageDownloader.h>
#import "ASBasicImageDownloaderInternal.h"
#import <OCMock/OCMock.h> #import <OCMock/OCMock.h>

View File

@@ -7,8 +7,7 @@
*/ */
#import "ASSnapshotTestCase.h" #import "ASSnapshotTestCase.h"
#import "ASDisplayNodeInternal.h"
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
@implementation ASSnapshotTestCase @implementation ASSnapshotTestCase

View File

@@ -379,7 +379,7 @@
}]; }];
} }
- (void)testRelayoutRowsAfterEditingModeIsChangedAndTheyBecomeVisible - (void)DISABLED_testRelayoutRowsAfterEditingModeIsChangedAndTheyBecomeVisible
{ {
CGSize tableViewSize = CGSizeMake(100, 500); CGSize tableViewSize = CGSizeMake(100, 500);
ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, tableViewSize.width, tableViewSize.height) ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, tableViewSize.width, tableViewSize.height)

View File

@@ -77,8 +77,11 @@
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */, AC3C4A8D1A11F80C00143C57 /* Images.xcassets */,
AC3C4A611A11F47200143C57 /* Supporting Files */, AC3C4A611A11F47200143C57 /* Supporting Files */,
); );
indentWidth = 2;
path = Sample; path = Sample;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
}; };
AC3C4A611A11F47200143C57 /* Supporting Files */ = { AC3C4A611A11F47200143C57 /* Supporting Files */ = {
isa = PBXGroup; isa = PBXGroup;

View File

@@ -52,7 +52,10 @@
1A943BF0259746F18D6E423F /* Frameworks */, 1A943BF0259746F18D6E423F /* Frameworks */,
1AE410B73DA5C3BD087ACDD7 /* Pods */, 1AE410B73DA5C3BD087ACDD7 /* Pods */,
); );
indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
}; };
05E2128219D4DB510098F589 /* Products */ = { 05E2128219D4DB510098F589 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;

View File

@@ -52,7 +52,10 @@
1A943BF0259746F18D6E423F /* Frameworks */, 1A943BF0259746F18D6E423F /* Frameworks */,
1AE410B73DA5C3BD087ACDD7 /* Pods */, 1AE410B73DA5C3BD087ACDD7 /* Pods */,
); );
indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
}; };
05E2128219D4DB510098F589 /* Products */ = { 05E2128219D4DB510098F589 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;

View File

@@ -58,7 +58,10 @@
1A943BF0259746F18D6E423F /* Frameworks */, 1A943BF0259746F18D6E423F /* Frameworks */,
1AE410B73DA5C3BD087ACDD7 /* Pods */, 1AE410B73DA5C3BD087ACDD7 /* Pods */,
); );
indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
}; };
05E2128219D4DB510098F589 /* Products */ = { 05E2128219D4DB510098F589 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;

View File

@@ -61,6 +61,7 @@
indentWidth = 2; indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2; tabWidth = 2;
usesTabs = 0;
}; };
05E2128219D4DB510098F589 /* Products */ = { 05E2128219D4DB510098F589 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;

View File

@@ -14,6 +14,7 @@
3EC0CDCBA10D483D9F386E5E /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */; }; 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 */; }; 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 */; }; 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 */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@@ -29,6 +30,8 @@
3D24B17D1E4A4E7A9566C5E9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 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; }; 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; }; 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>"; };
C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; }; C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@@ -52,7 +55,10 @@
1A943BF0259746F18D6E423F /* Frameworks */, 1A943BF0259746F18D6E423F /* Frameworks */,
1AE410B73DA5C3BD087ACDD7 /* Pods */, 1AE410B73DA5C3BD087ACDD7 /* Pods */,
); );
indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
}; };
05E2128219D4DB510098F589 /* Products */ = { 05E2128219D4DB510098F589 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
@@ -69,6 +75,8 @@
05E2128919D4DB510098F589 /* AppDelegate.m */, 05E2128919D4DB510098F589 /* AppDelegate.m */,
05E2128B19D4DB510098F589 /* ViewController.h */, 05E2128B19D4DB510098F589 /* ViewController.h */,
05E2128C19D4DB510098F589 /* ViewController.m */, 05E2128C19D4DB510098F589 /* ViewController.m */,
ACC945AC1BA9EFB3005E1FB8 /* ScreenNode.h */,
ACC945AD1BA9EFBA005E1FB8 /* ScreenNode.m */,
05E2128419D4DB510098F589 /* Supporting Files */, 05E2128419D4DB510098F589 /* Supporting Files */,
); );
path = Sample; path = Sample;
@@ -211,6 +219,7 @@
05E2128D19D4DB510098F589 /* ViewController.m in Sources */, 05E2128D19D4DB510098F589 /* ViewController.m in Sources */,
05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */, 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */,
05E2128719D4DB510098F589 /* main.m in Sources */, 05E2128719D4DB510098F589 /* main.m in Sources */,
ACC945AE1BA9EFBA005E1FB8 /* ScreenNode.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@@ -0,0 +1,19 @@
//
// 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) ASTextNode *textNode;
- (void)start;
- (void)reload;
@end

View File

@@ -0,0 +1,152 @@
//
// 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
_textNode = [[ASTextNode alloc] init];
[self addSubnode:_imageNode];
[self addSubnode:_textNode];
return self;
}
- (void)start
{
[self setText:@"loading…"];
_textNode.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];
_textNode.attributedString = string;
[self setNeedsLayout];
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASRatioLayoutSpec *imagePlaceholder = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1 child:_imageNode];
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:10
justifyContent:ASStackLayoutJustifyContentCenter
alignItems:ASStackLayoutAlignItemsCenter
children:@[imagePlaceholder, _textNode]];
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:[_textNode.attributedString.string stringByAppendingString:@". tap to reload"]];
_textNode.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

View File

@@ -9,8 +9,8 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#import <UIKit/UIKit.h> #import <AsyncDisplayKit/ASViewController.h>
@interface ViewController : UIViewController @interface ViewController : ASViewController
@end @end

View File

@@ -10,77 +10,40 @@
*/ */
#import "ViewController.h" #import "ViewController.h"
#import "ScreenNode.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h> @interface ViewController() {
ScreenNode *_screenNode;
@interface ViewController () <ASMultiplexImageNodeDataSource, ASMultiplexImageNodeDelegate, ASImageDownloaderProtocol>
{
ASMultiplexImageNode *_imageNode;
UILabel *_textLabel;
} }
@end @end
@implementation ViewController @implementation ViewController
- (instancetype)init - (instancetype)init
{ {
if (!(self = [super init])) ScreenNode *node = [[ScreenNode alloc] init];
if (!(self = [super initWithNode:node]))
return nil; return nil;
_screenNode = node;
// 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
_textLabel = [[UILabel alloc] init];
_textLabel.textAlignment = NSTextAlignmentCenter;
_textLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:22.0f];
// tap to reload // tap to reload
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reload:)]; UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reload:)];
[_textLabel addGestureRecognizer:gr]; [_screenNode.textNode.view addGestureRecognizer:gr];
return self; return self;
} }
- (void)viewDidLoad - (void)viewWillAppear:(BOOL)animated
{ {
[super viewDidLoad]; // This should be done before calling super's viewWillAppear which triggers data fetching on the node.
[_screenNode start];
[self.view addSubnode:_imageNode]; [super viewWillAppear:animated];
[self.view addSubview:_textLabel];
[self start];
} }
- (void)viewWillLayoutSubviews - (void)reload:(id)sender {
{ [_screenNode reload];
static CGFloat padding = 40.0f;
// lay out image
CGFloat imageWidth = self.view.bounds.size.width - padding;
CGPoint imageOrigin = CGPointMake(roundf((self.view.bounds.size.width - imageWidth) / 2.0f),
roundf((self.view.bounds.size.height - imageWidth) / 2.0f));
_imageNode.frame = (CGRect){ imageOrigin, CGSizeMake(imageWidth, imageWidth) };
// label
CGSize textSize = [_textLabel sizeThatFits:CGSizeMake(self.view.bounds.size.width, FLT_MAX)];
_textLabel.frame = CGRectMake(0.0f, imageOrigin.y + imageWidth + padding, self.view.bounds.size.width, textSize.height);
} }
- (BOOL)prefersStatusBarHidden - (BOOL)prefersStatusBarHidden
@@ -88,95 +51,4 @@
return YES; return YES;
} }
- (void)start
{
_textLabel.text = @"loading…";
_textLabel.userInteractionEnabled = NO;
_imageNode.imageIdentifiers = @[ @"best", @"medium", @"worst" ]; // go!
}
- (void)reload:(id)sender {
[self start];
[_imageNode reloadImageIdentifierSources];
}
#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
{
_textLabel.text = [NSString stringWithFormat:@"loaded '%@'", imageIdentifier];
if ([imageIdentifier isEqualToString:@"best"]) {
_textLabel.text = [_textLabel.text stringByAppendingString:@". tap to reload"];
_textLabel.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 @end

View File

@@ -66,7 +66,10 @@
1A943BF0259746F18D6E423F /* Frameworks */, 1A943BF0259746F18D6E423F /* Frameworks */,
1AE410B73DA5C3BD087ACDD7 /* Pods */, 1AE410B73DA5C3BD087ACDD7 /* Pods */,
); );
indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
}; };
05E2128219D4DB510098F589 /* Products */ = { 05E2128219D4DB510098F589 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;

View File

@@ -52,6 +52,7 @@
indentWidth = 2; indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2; tabWidth = 2;
usesTabs = 0;
}; };
050E7C6F19D22E19004363C2 /* Products */ = { 050E7C6F19D22E19004363C2 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;

View File

@@ -57,7 +57,10 @@
058968F11ABCE06E0059CE2A /* Life Without CocoaPods */, 058968F11ABCE06E0059CE2A /* Life Without CocoaPods */,
058968F01ABCE06E0059CE2A /* Products */, 058968F01ABCE06E0059CE2A /* Products */,
); );
indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
}; };
058968F01ABCE06E0059CE2A /* Products */ = { 058968F01ABCE06E0059CE2A /* Products */ = {
isa = PBXGroup; isa = PBXGroup;