From eae76d26b79e26ab50b4ce9368b9946f99eec5d6 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Thu, 30 Jul 2015 16:50:11 -0700 Subject: [PATCH 1/3] Add expectedSize to ASNetworkImageNode ASNetworkImageNode defers to ASImageNode to return its calculatedSize. ASImageNode returns the size of its image. There is a good chance that ASNetworkImageNode hasn't downloaded its image yet when calculatedSize is called, so it returns a size of CGSizeZero. On top of that, it is possible that the size of the image is not actually the size that we wish to display in our node. I've added an "expectedImageSize" property that can be used to determine the calculatedSize of an ASNetworkImageNode. --- AsyncDisplayKit/ASNetworkImageNode.h | 5 +++++ AsyncDisplayKit/ASNetworkImageNode.mm | 23 +++++++++++++++++++++++ examples/Kittens/Sample/KittenNode.mm | 5 ++--- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/ASNetworkImageNode.h b/AsyncDisplayKit/ASNetworkImageNode.h index 5dc723521e..880202f485 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.h +++ b/AsyncDisplayKit/ASNetworkImageNode.h @@ -51,6 +51,11 @@ */ @property (atomic, strong, readwrite) UIImage *defaultImage; +/** + * The expected size of the image that will download + */ +@property (atomic, assign, readwrite) CGSize expectedImageSize; + /** * The URL of a new image to download and display. * diff --git a/AsyncDisplayKit/ASNetworkImageNode.mm b/AsyncDisplayKit/ASNetworkImageNode.mm index 658176eeee..c37a4953e1 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.mm +++ b/AsyncDisplayKit/ASNetworkImageNode.mm @@ -24,6 +24,7 @@ NSURL *_URL; UIImage *_defaultImage; + CGSize _expectedImageSize; NSUUID *_cacheUUID; id _imageDownload; @@ -44,6 +45,7 @@ _cache = cache; _downloader = downloader; _shouldCacheImage = YES; + _expectedImageSize = CGSizeZero; return self; } @@ -111,6 +113,18 @@ return _defaultImage; } +-(void)setExpectedImageSize:(CGSize)expectedImageSize +{ + ASDN::MutexLocker l(_lock); + _expectedImageSize = expectedImageSize; +} + +- (CGSize)expectedImageSize +{ + ASDN::MutexLocker l(_lock); + return _expectedImageSize; +} + - (void)setDelegate:(id)delegate { ASDN::MutexLocker l(_lock); @@ -153,6 +167,15 @@ } } +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + ASDN::MutexLocker l(_lock); + if (CGSizeEqualToSize(CGSizeZero, _expectedImageSize)) + return [super calculateSizeThatFits:constrainedSize]; + else + return _expectedImageSize; +} + #pragma mark - Private methods -- only call with lock. - (void)_cancelImageDownload diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 7e321712cc..678d3c4796 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -83,6 +83,7 @@ static const CGFloat kInnerPadding = 10.0f; _imageNode.URL = [NSURL URLWithString:[NSString stringWithFormat:@"http://placekitten.com/%zd/%zd", (NSInteger)roundl(_kittenSize.width), (NSInteger)roundl(_kittenSize.height)]]; + _imageNode.expectedImageSize = CGSizeMake(kImageSize, kImageSize); // _imageNode.contentMode = UIViewContentModeCenter; [self addSubnode:_imageNode]; @@ -131,8 +132,6 @@ static const CGFloat kInnerPadding = 10.0f; #if UseAutomaticLayout - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { - ASRatioLayoutSpec *imagePlaceholder = [ASRatioLayoutSpec newWithRatio:1.0 child:_imageNode]; - imagePlaceholder.flexBasis = ASRelativeDimensionMakeWithPoints(kImageSize); _textNode.flexShrink = YES; @@ -145,7 +144,7 @@ static const CGFloat kInnerPadding = 10.0f; .direction = ASStackLayoutDirectionHorizontal, .spacing = kInnerPadding } - children:@[imagePlaceholder, _textNode]]]; + children:@[_imageNode, _textNode]]]; } // With box model, you don't need to override this method, unless you want to add custom logic. From 496cc43337290c42d9f600a2bc8a62773f0f5910 Mon Sep 17 00:00:00 2001 From: rcancro Date: Mon, 3 Aug 2015 22:09:50 -0700 Subject: [PATCH 2/3] Updates to expectedImageSize * renamed property to requestedLayoutSize * moved property to ASDisplayNode --- AsyncDisplayKit/ASDisplayNode.h | 9 +++ AsyncDisplayKit/ASDisplayNode.mm | 14 +++++ AsyncDisplayKit/ASImageNode.mm | 4 +- AsyncDisplayKit/ASNetworkImageNode.h | 5 -- AsyncDisplayKit/ASNetworkImageNode.mm | 83 ++++++++++----------------- examples/Kittens/Sample/KittenNode.mm | 2 +- 6 files changed, 57 insertions(+), 60 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index fa052b3db9..2bd32d7850 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -185,6 +185,15 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); */ @property (nonatomic, readonly, assign) ASSizeRange constrainedSizeForCalculatedLayout; +/** + * @abstract Used by some implementations of measureWithSizeRange: to provide an intrisic content size + * to a node when one cannot be computed from its subnodes + * + * @return The requested size of this node + */ +@property (atomic, assign, readwrite) CGSize requestedLayoutSize; + + /** @name Managing the nodes hierarchy */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 43dbc037fa..7ad4f55bcc 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -47,6 +47,7 @@ @synthesize flexShrink = _flexShrink; @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; +@synthesize requestedLayoutSize = _requestedLayoutSize; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) { @@ -135,6 +136,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)()) _methodOverrides = overrides; _flexBasis = ASRelativeDimensionUnconstrained; + _requestedLayoutSize = CGSizeZero; } - (id)init @@ -1333,6 +1335,18 @@ static NSInteger incrementIfFound(NSInteger i) { return _constrainedSize; } +-(void)setRequestedLayoutSize:(CGSize)requestedLayoutSize +{ + ASDN::MutexLocker l(_propertyLock); + _requestedLayoutSize = requestedLayoutSize; +} + +- (CGSize)requestedLayoutSize +{ + ASDN::MutexLocker l(_propertyLock); + return _requestedLayoutSize; +} + - (UIImage *)placeholderImage { return nil; diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index d7cdebbd94..a9a3a33f78 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -111,7 +111,9 @@ - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { ASDN::MutexLocker l(_imageLock); - if (_image) + if (CGSizeEqualToSize(self.requestedLayoutSize, CGSizeZero) == NO) + return CGSizeMake(MIN(constrainedSize.width, self.requestedLayoutSize.width), MIN(constrainedSize.height, self.requestedLayoutSize.height)); + else if (_image) return _image.size; else return CGSizeZero; diff --git a/AsyncDisplayKit/ASNetworkImageNode.h b/AsyncDisplayKit/ASNetworkImageNode.h index 880202f485..5dc723521e 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.h +++ b/AsyncDisplayKit/ASNetworkImageNode.h @@ -51,11 +51,6 @@ */ @property (atomic, strong, readwrite) UIImage *defaultImage; -/** - * The expected size of the image that will download - */ -@property (atomic, assign, readwrite) CGSize expectedImageSize; - /** * The URL of a new image to download and display. * diff --git a/AsyncDisplayKit/ASNetworkImageNode.mm b/AsyncDisplayKit/ASNetworkImageNode.mm index c37a4953e1..416426b384 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.mm +++ b/AsyncDisplayKit/ASNetworkImageNode.mm @@ -18,17 +18,16 @@ ASDN::RecursiveMutex _lock; id _cache; id _downloader; - + // Only access any of these with _lock. __weak id _delegate; - + NSURL *_URL; UIImage *_defaultImage; - CGSize _expectedImageSize; - + NSUUID *_cacheUUID; id _imageDownload; - + BOOL _imageLoaded; } @@ -41,12 +40,11 @@ { if (!(self = [super init])) return nil; - + _cache = cache; _downloader = downloader; _shouldCacheImage = YES; - _expectedImageSize = CGSizeZero; - + return self; } @@ -70,19 +68,19 @@ - (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset { ASDN::MutexLocker l(_lock); - + if (URL == _URL || [URL isEqual:_URL]) { return; } - + [self _cancelImageDownload]; _imageLoaded = NO; - + _URL = URL; - + if (reset || _URL == nil) self.image = _defaultImage; - + if (self.nodeLoaded && self.layer.superlayer) [self _lazilyLoadImageIfNecessary]; } @@ -96,12 +94,12 @@ - (void)setDefaultImage:(UIImage *)defaultImage { ASDN::MutexLocker l(_lock); - + if (defaultImage == _defaultImage || [defaultImage isEqual:_defaultImage]) { return; } _defaultImage = defaultImage; - + if (!_imageLoaded) { self.image = _defaultImage; } @@ -113,18 +111,6 @@ return _defaultImage; } --(void)setExpectedImageSize:(CGSize)expectedImageSize -{ - ASDN::MutexLocker l(_lock); - _expectedImageSize = expectedImageSize; -} - -- (CGSize)expectedImageSize -{ - ASDN::MutexLocker l(_lock); - return _expectedImageSize; -} - - (void)setDelegate:(id)delegate { ASDN::MutexLocker l(_lock); @@ -140,17 +126,17 @@ - (void)displayWillStart { [super displayWillStart]; - + [self fetchData]; } - (void)clearFetchedData { [super clearFetchedData]; - + { ASDN::MutexLocker l(_lock); - + [self _cancelImageDownload]; self.image = _defaultImage; _imageLoaded = NO; @@ -167,15 +153,6 @@ } } -- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize -{ - ASDN::MutexLocker l(_lock); - if (CGSizeEqualToSize(CGSizeZero, _expectedImageSize)) - return [super calculateSizeThatFits:constrainedSize]; - else - return _expectedImageSize; -} - #pragma mark - Private methods -- only call with lock. - (void)_cancelImageDownload @@ -183,10 +160,10 @@ if (!_imageDownload) { return; } - + [_downloader cancelImageDownloadForIdentifier:_imageDownload]; _imageDownload = nil; - + _cacheUUID = nil; } @@ -208,16 +185,16 @@ if (_URL.isFileURL) { { ASDN::MutexLocker l(_lock); - + dispatch_async(dispatch_get_main_queue(), ^{ _imageLoaded = YES; - + if (self.shouldCacheImage) { self.image = [UIImage imageNamed:_URL.path]; } else { self.image = [UIImage imageWithContentsOfFile:_URL.path]; } - + [_delegate imageNode:self didLoadImage:self.image]; }); } @@ -228,42 +205,42 @@ if (strongSelf == nil) { return; } - + { ASDN::MutexLocker l(strongSelf->_lock); - + if (responseImage != NULL) { strongSelf->_imageLoaded = YES; strongSelf.image = [UIImage imageWithCGImage:responseImage]; } - + strongSelf->_imageDownload = nil; - + strongSelf->_cacheUUID = nil; } - + if (responseImage != NULL) { [strongSelf->_delegate imageNode:strongSelf didLoadImage:strongSelf.image]; } }; - + if (_cache != nil) { NSUUID *cacheUUID = [NSUUID UUID]; _cacheUUID = cacheUUID; - + void (^cacheCompletion)(CGImageRef) = ^(CGImageRef image) { // If the cache UUID changed, that means this request was cancelled. if (![_cacheUUID isEqual:cacheUUID]) { return; } - + if (image == NULL && _downloader != nil) { [self _downloadImageWithCompletion:finished]; } else { finished(image); } }; - + [_cache fetchCachedImageWithURL:_URL callbackQueue:dispatch_get_main_queue() completion:cacheCompletion]; diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 678d3c4796..b8f1734117 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -83,7 +83,7 @@ static const CGFloat kInnerPadding = 10.0f; _imageNode.URL = [NSURL URLWithString:[NSString stringWithFormat:@"http://placekitten.com/%zd/%zd", (NSInteger)roundl(_kittenSize.width), (NSInteger)roundl(_kittenSize.height)]]; - _imageNode.expectedImageSize = CGSizeMake(kImageSize, kImageSize); + _imageNode.requestedLayoutSize = CGSizeMake(kImageSize, kImageSize); // _imageNode.contentMode = UIViewContentModeCenter; [self addSubnode:_imageNode]; From c03ed9a8f4581175778667e22d00dea1bfb1f889 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Tue, 4 Aug 2015 08:12:32 -0700 Subject: [PATCH 3/3] ASDisplayNode returns requestedLayoutSize by default --- AsyncDisplayKit/ASDisplayNode.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 7ad4f55bcc..6cc75143fd 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1308,7 +1308,7 @@ static NSInteger incrementIfFound(NSInteger i) { - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { ASDisplayNodeAssertThreadAffinity(self); - return CGSizeZero; + return CGSizeMake(MIN(self.requestedLayoutSize.width, constrainedSize.width), MIN(self.requestedLayoutSize.height, constrainedSize.height)); } - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize