From 07c0d78c71a60c000cf2aad9577bf0d321861cd0 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 16 Sep 2015 22:22:08 +0300 Subject: [PATCH 1/6] Add ASViewController and update Multiplex sample to use it. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 8 + AsyncDisplayKit/ASViewController.h | 18 ++ AsyncDisplayKit/ASViewController.m | 47 ++++++ AsyncDisplayKit/AsyncDisplayKit.h | 2 + .../Sample.xcodeproj/project.pbxproj | 6 + examples/Multiplex/Sample/ScreenNode.h | 19 +++ examples/Multiplex/Sample/ScreenNode.m | 156 ++++++++++++++++++ examples/Multiplex/Sample/ViewController.h | 4 +- examples/Multiplex/Sample/ViewController.m | 153 ++--------------- 9 files changed, 270 insertions(+), 143 deletions(-) create mode 100644 AsyncDisplayKit/ASViewController.h create mode 100644 AsyncDisplayKit/ASViewController.m create mode 100644 examples/Multiplex/Sample/ScreenNode.h create mode 100644 examples/Multiplex/Sample/ScreenNode.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index fec206ad..e6a84904 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -230,6 +230,8 @@ AC47D9451B3BB41900AAEE9D /* ASRelativeSize.h in Headers */ = {isa = PBXBuildFile; fileRef = AC47D9431B3BB41900AAEE9D /* ASRelativeSize.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC47D9461B3BB41900AAEE9D /* ASRelativeSize.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */; }; AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = AC6456071B0A335000CF11B8 /* ASCellNode.m */; }; + ACC945A91BA9E7A0005E1FB8 /* ASViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */; }; + ACC945AB1BA9E7C1005E1FB8 /* ASViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */; }; ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; ACF6ED1B1B17843500DA7C62 /* ASBackgroundLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */; }; ACF6ED1C1B17843500DA7C62 /* ASCenterLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -574,6 +576,8 @@ AC47D9431B3BB41900AAEE9D /* ASRelativeSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRelativeSize.h; path = AsyncDisplayKit/Layout/ASRelativeSize.h; sourceTree = ""; }; AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRelativeSize.mm; path = AsyncDisplayKit/Layout/ASRelativeSize.mm; sourceTree = ""; }; AC6456071B0A335000CF11B8 /* ASCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCellNode.m; sourceTree = ""; }; + ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASViewController.h; sourceTree = ""; }; + ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASViewController.m; sourceTree = ""; }; ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBackgroundLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h; sourceTree = ""; }; ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASBackgroundLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm; sourceTree = ""; }; ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASCenterLayoutSpec.h; path = AsyncDisplayKit/Layout/ASCenterLayoutSpec.h; sourceTree = ""; }; @@ -758,6 +762,8 @@ 0574D5E119C110610097DC25 /* ASTableViewProtocols.h */, 058D09DF195D050800B7D73C /* ASTextNode.h */, 058D09E0195D050800B7D73C /* ASTextNode.mm */, + ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */, + ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */, 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */, 058D09E1195D050800B7D73C /* Details */, 058D0A01195D050800B7D73C /* Private */, @@ -1114,6 +1120,7 @@ 058D0A63195D05DC00B7D73C /* ASTextNodeTypes.h in Headers */, 058D0A64195D05DC00B7D73C /* ASTextNodeWordKerner.h in Headers */, 058D0A81195D05F900B7D73C /* ASThread.h in Headers */, + ACC945A91BA9E7A0005E1FB8 /* ASViewController.h in Headers */, 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */, 205F0E211B376416007741D0 /* CGRect+ASConvenience.h in Headers */, 058D0A66195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.h in Headers */, @@ -1478,6 +1485,7 @@ 058D0A1E195D050800B7D73C /* ASTextNodeShadower.m in Sources */, 058D0A1F195D050800B7D73C /* ASTextNodeTextKitHelpers.mm in Sources */, 058D0A20195D050800B7D73C /* ASTextNodeWordKerner.m in Sources */, + ACC945AB1BA9E7C1005E1FB8 /* ASViewController.m in Sources */, 205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */, 058D0A21195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Sources */, 205F0E101B371875007741D0 /* UICollectionViewLayout+ASConvenience.m in Sources */, diff --git a/AsyncDisplayKit/ASViewController.h b/AsyncDisplayKit/ASViewController.h new file mode 100644 index 00000000..7be26d59 --- /dev/null +++ b/AsyncDisplayKit/ASViewController.h @@ -0,0 +1,18 @@ +// +// ASViewController.h +// AsyncDisplayKit +// +// Created by Huy Nguyen on 16/09/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import +#import + +@interface ASViewController : UIViewController + +@property (nonatomic, strong, readonly) ASDisplayNode *node; + +- (instancetype)initWithNode:(ASDisplayNode *)node; + +@end \ No newline at end of file diff --git a/AsyncDisplayKit/ASViewController.m b/AsyncDisplayKit/ASViewController.m new file mode 100644 index 00000000..66451983 --- /dev/null +++ b/AsyncDisplayKit/ASViewController.m @@ -0,0 +1,47 @@ +// +// 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 +{ + ASSizeRange constrainedSize = ASSizeRangeMake(self.view.bounds.size, self.view.bounds.size); + [_node measureWithSizeRange:constrainedSize]; + [super viewWillLayoutSubviews]; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + [_node recursivelyFetchData]; +} + +@end diff --git a/AsyncDisplayKit/AsyncDisplayKit.h b/AsyncDisplayKit/AsyncDisplayKit.h index fef5761c..6b2aa9ea 100644 --- a/AsyncDisplayKit/AsyncDisplayKit.h +++ b/AsyncDisplayKit/AsyncDisplayKit.h @@ -26,6 +26,8 @@ #import +#import + #import #import #import diff --git a/examples/Multiplex/Sample.xcodeproj/project.pbxproj b/examples/Multiplex/Sample.xcodeproj/project.pbxproj index a1a1bd3d..1383d385 100644 --- a/examples/Multiplex/Sample.xcodeproj/project.pbxproj +++ b/examples/Multiplex/Sample.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 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 */; }; /* End PBXBuildFile 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; }; 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 = ""; }; + ACC945AD1BA9EFBA005E1FB8 /* ScreenNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScreenNode.m; sourceTree = ""; }; 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 = ""; }; /* End PBXFileReference section */ @@ -72,6 +75,8 @@ 05E2128919D4DB510098F589 /* AppDelegate.m */, 05E2128B19D4DB510098F589 /* ViewController.h */, 05E2128C19D4DB510098F589 /* ViewController.m */, + ACC945AC1BA9EFB3005E1FB8 /* ScreenNode.h */, + ACC945AD1BA9EFBA005E1FB8 /* ScreenNode.m */, 05E2128419D4DB510098F589 /* Supporting Files */, ); path = Sample; @@ -214,6 +219,7 @@ 05E2128D19D4DB510098F589 /* ViewController.m in Sources */, 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */, 05E2128719D4DB510098F589 /* main.m in Sources */, + ACC945AE1BA9EFBA005E1FB8 /* ScreenNode.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/examples/Multiplex/Sample/ScreenNode.h b/examples/Multiplex/Sample/ScreenNode.h new file mode 100644 index 00000000..38ea2e63 --- /dev/null +++ b/examples/Multiplex/Sample/ScreenNode.h @@ -0,0 +1,19 @@ +// +// ScreenNode.h +// Sample +// +// Created by Huy Nguyen on 16/09/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import + +@interface ScreenNode : ASDisplayNode + +@property (nonatomic, strong) ASMultiplexImageNode *imageNode; +@property (nonatomic, strong) ASTextNode *textNode; + +- (void)start; +- (void)reload; + +@end \ No newline at end of file diff --git a/examples/Multiplex/Sample/ScreenNode.m b/examples/Multiplex/Sample/ScreenNode.m new file mode 100644 index 00000000..1032e059 --- /dev/null +++ b/examples/Multiplex/Sample/ScreenNode.m @@ -0,0 +1,156 @@ +// +// ScreenNode.m +// Sample +// +// Created by Huy Nguyen on 16/09/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ScreenNode.h" +#import "AsyncDisplayKit/AsyncDisplayKit.h" + +@interface ScreenNode() +@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 +{ + return + [ASInsetLayoutSpec + insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10) + child: + [ASStackLayoutSpec + stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical + spacing:10 + justifyContent:ASStackLayoutJustifyContentCenter + alignItems:ASStackLayoutAlignItemsCenter + children:@[[ASRatioLayoutSpec ratioLayoutSpecWithRatio:1 child:_imageNode], _textNode]]]; +} + +#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 diff --git a/examples/Multiplex/Sample/ViewController.h b/examples/Multiplex/Sample/ViewController.h index d0e9200d..b73c2908 100644 --- a/examples/Multiplex/Sample/ViewController.h +++ b/examples/Multiplex/Sample/ViewController.h @@ -9,8 +9,8 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#import +#import -@interface ViewController : UIViewController +@interface ViewController : ASViewController @end diff --git a/examples/Multiplex/Sample/ViewController.m b/examples/Multiplex/Sample/ViewController.m index 7af462d9..b3c57173 100644 --- a/examples/Multiplex/Sample/ViewController.m +++ b/examples/Multiplex/Sample/ViewController.m @@ -10,77 +10,39 @@ */ #import "ViewController.h" +#import "ScreenNode.h" -#import - - -@interface ViewController () -{ - ASMultiplexImageNode *_imageNode; - - UILabel *_textLabel; +@interface ViewController() { + ScreenNode *_screenNode; } @end - @implementation ViewController - (instancetype)init { - if (!(self = [super init])) + ScreenNode *node = [[ScreenNode alloc] init]; + if (!(self = [super initWithNode:node])) 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 - _textLabel = [[UILabel alloc] init]; - _textLabel.textAlignment = NSTextAlignmentCenter; - _textLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:22.0f]; + _screenNode = node; // tap to reload UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reload:)]; - [_textLabel addGestureRecognizer:gr]; - + [_screenNode.textNode.view addGestureRecognizer:gr]; return self; } -- (void)viewDidLoad +- (void)viewWillAppear:(BOOL)animated { - [super viewDidLoad]; - - [self.view addSubnode:_imageNode]; - [self.view addSubview:_textLabel]; - - [self start]; + [super viewWillAppear:animated]; + [_screenNode start]; } -- (void)viewWillLayoutSubviews -{ - 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); +- (void)reload:(id)sender { + [_screenNode reload]; } - (BOOL)prefersStatusBarHidden @@ -88,95 +50,4 @@ 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 From 72d108cd980a44c99c121fac6516d5fd74099bb3 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 16 Sep 2015 23:28:12 +0300 Subject: [PATCH 2/6] Minor fixes in ASViewController and Multiplex sample. --- AsyncDisplayKit/ASViewController.m | 3 ++- examples/Multiplex/Sample/ScreenNode.h | 2 +- examples/Multiplex/Sample/ScreenNode.m | 17 +++++++---------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/AsyncDisplayKit/ASViewController.m b/AsyncDisplayKit/ASViewController.m index 66451983..2a9f5551 100644 --- a/AsyncDisplayKit/ASViewController.m +++ b/AsyncDisplayKit/ASViewController.m @@ -33,7 +33,8 @@ - (void)viewWillLayoutSubviews { - ASSizeRange constrainedSize = ASSizeRangeMake(self.view.bounds.size, self.view.bounds.size); + CGSize viewSize = self.view.bounds.size; + ASSizeRange constrainedSize = ASSizeRangeMake(viewSize, viewSize); [_node measureWithSizeRange:constrainedSize]; [super viewWillLayoutSubviews]; } diff --git a/examples/Multiplex/Sample/ScreenNode.h b/examples/Multiplex/Sample/ScreenNode.h index 38ea2e63..b1b5024e 100644 --- a/examples/Multiplex/Sample/ScreenNode.h +++ b/examples/Multiplex/Sample/ScreenNode.h @@ -16,4 +16,4 @@ - (void)start; - (void)reload; -@end \ No newline at end of file +@end diff --git a/examples/Multiplex/Sample/ScreenNode.m b/examples/Multiplex/Sample/ScreenNode.m index 1032e059..109cc69e 100644 --- a/examples/Multiplex/Sample/ScreenNode.m +++ b/examples/Multiplex/Sample/ScreenNode.m @@ -64,16 +64,13 @@ - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { - return - [ASInsetLayoutSpec - insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10) - child: - [ASStackLayoutSpec - stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical - spacing:10 - justifyContent:ASStackLayoutJustifyContentCenter - alignItems:ASStackLayoutAlignItemsCenter - children:@[[ASRatioLayoutSpec ratioLayoutSpecWithRatio:1 child:_imageNode], _textNode]]]; + 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 - From fcd76dbd198adbc615f292fd5040b6c822cf5861 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 16 Sep 2015 23:36:19 +0300 Subject: [PATCH 3/6] Use nonnull annotation, instead of sanity check, for node property in ASViewController. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 2 +- AsyncDisplayKit/ASViewController.h | 8 ++++---- AsyncDisplayKit/ASViewController.m | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index e6a84904..6c4132aa 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -230,7 +230,7 @@ AC47D9451B3BB41900AAEE9D /* ASRelativeSize.h in Headers */ = {isa = PBXBuildFile; fileRef = AC47D9431B3BB41900AAEE9D /* ASRelativeSize.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC47D9461B3BB41900AAEE9D /* ASRelativeSize.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */; }; AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = AC6456071B0A335000CF11B8 /* ASCellNode.m */; }; - ACC945A91BA9E7A0005E1FB8 /* ASViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */; }; + ACC945A91BA9E7A0005E1FB8 /* ASViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; ACC945AB1BA9E7C1005E1FB8 /* ASViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */; }; ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; ACF6ED1B1B17843500DA7C62 /* ASBackgroundLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */; }; diff --git a/AsyncDisplayKit/ASViewController.h b/AsyncDisplayKit/ASViewController.h index 7be26d59..a00cebd9 100644 --- a/AsyncDisplayKit/ASViewController.h +++ b/AsyncDisplayKit/ASViewController.h @@ -7,12 +7,12 @@ // #import -#import +#import @interface ASViewController : UIViewController -@property (nonatomic, strong, readonly) ASDisplayNode *node; +@property (nonatomic, strong, readonly, nonnull) ASDisplayNode *node; -- (instancetype)initWithNode:(ASDisplayNode *)node; +- (nullable instancetype)initWithNode:(nonnull ASDisplayNode *)node; -@end \ No newline at end of file +@end diff --git a/AsyncDisplayKit/ASViewController.m b/AsyncDisplayKit/ASViewController.m index 2a9f5551..77395356 100644 --- a/AsyncDisplayKit/ASViewController.m +++ b/AsyncDisplayKit/ASViewController.m @@ -18,7 +18,6 @@ return nil; } - ASDisplayNodeAssertNotNil(node, @"Node must not be nil"); ASDisplayNodeAssertTrue(!node.layerBacked); _node = node; From 8ebc3bdf6713efe42578705e714d36591c1f5545 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 17 Sep 2015 00:03:39 +0300 Subject: [PATCH 4/6] Fix Multiplex sample does not load image: screenNode must be started before its -fetchData method is called. --- examples/Multiplex/Sample/ViewController.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/Multiplex/Sample/ViewController.m b/examples/Multiplex/Sample/ViewController.m index b3c57173..e694aa79 100644 --- a/examples/Multiplex/Sample/ViewController.m +++ b/examples/Multiplex/Sample/ViewController.m @@ -37,8 +37,9 @@ - (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; + // This should be done before calling super's viewWillAppear which triggers data fetching on the node. [_screenNode start]; + [super viewWillAppear:animated]; } - (void)reload:(id)sender { From 1ef1b647c9ffbd3204716d0cf7559b6377908cf9 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 17 Sep 2015 00:11:41 +0300 Subject: [PATCH 5/6] Remove unnecessary import in ScreenNode.m --- examples/Multiplex/Sample/ScreenNode.m | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/Multiplex/Sample/ScreenNode.m b/examples/Multiplex/Sample/ScreenNode.m index 109cc69e..632a50c4 100644 --- a/examples/Multiplex/Sample/ScreenNode.m +++ b/examples/Multiplex/Sample/ScreenNode.m @@ -7,7 +7,6 @@ // #import "ScreenNode.h" -#import "AsyncDisplayKit/AsyncDisplayKit.h" @interface ScreenNode() @end From a8643417100a86e0b75a1e55134d4ac7b1692890 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 17 Sep 2015 16:03:02 +0300 Subject: [PATCH 6/6] Remove nonnull and nuulable annotations in ASViewController. Travis don't recognize them (yet). --- AsyncDisplayKit/ASViewController.h | 5 +++-- AsyncDisplayKit/ASViewController.m | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASViewController.h b/AsyncDisplayKit/ASViewController.h index a00cebd9..e3b18661 100644 --- a/AsyncDisplayKit/ASViewController.h +++ b/AsyncDisplayKit/ASViewController.h @@ -11,8 +11,9 @@ @interface ASViewController : UIViewController -@property (nonatomic, strong, readonly, nonnull) ASDisplayNode *node; +@property (nonatomic, strong, readonly) ASDisplayNode *node; -- (nullable instancetype)initWithNode:(nonnull ASDisplayNode *)node; +//TODO Use nonnull annotation late on. Travis doesn't recognize it (yet). +- (instancetype)initWithNode:(ASDisplayNode *)node; @end diff --git a/AsyncDisplayKit/ASViewController.m b/AsyncDisplayKit/ASViewController.m index 77395356..2a9f5551 100644 --- a/AsyncDisplayKit/ASViewController.m +++ b/AsyncDisplayKit/ASViewController.m @@ -18,6 +18,7 @@ return nil; } + ASDisplayNodeAssertNotNil(node, @"Node must not be nil"); ASDisplayNodeAssertTrue(!node.layerBacked); _node = node;