From ae1349eb16b8da224cd0b1a8b58fe4f041830381 Mon Sep 17 00:00:00 2001 From: Ryan Nystrom Date: Wed, 7 Jan 2015 16:18:48 -0800 Subject: [PATCH] Start downloading images when multiplex node is displayed fixes #178 --- AsyncDisplayKit/ASMultiplexImageNode.mm | 45 +++---------------- .../ASMultiplexImageNodeTests.m | 16 +++++-- examples/Multiplex/Sample/ViewController.m | 8 +++- 3 files changed, 25 insertions(+), 44 deletions(-) diff --git a/AsyncDisplayKit/ASMultiplexImageNode.mm b/AsyncDisplayKit/ASMultiplexImageNode.mm index 39c05cd645..d229f3225b 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.mm +++ b/AsyncDisplayKit/ASMultiplexImageNode.mm @@ -70,7 +70,6 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent // Networking. id _downloadIdentifier; - BOOL _canceledImageDownload; } //! @abstract Read-write redeclaration of property declared in ASMultiplexImageNode.h. @@ -79,16 +78,6 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent //! @abstract The image identifier that's being loaded by _loadNextImageWithCompletion:. @property (nonatomic, readwrite, copy) id loadingImageIdentifier; -/** - @abstract Indicates whether a change from one array of image identifiers to another should prompt the receiver to update its loaded image. - @param loadedIdentifier The image identifier that's currently loaded, or nil if no identifier is loaded yet. - @param loadingImageIdentifier The image identifier that's currently loading, or nil if no identifier is loading. - @param oldIdentifiers An array of image identifiers that were previously managed, or nil if no identifiers were managed before. - @param newIdentifiers The array of new image identifiers. - @result YES if the receiver should update its loaded image as a consequence of the newly assigned identifiers; NO otherwise. - */ -static inline BOOL _shouldUpdateAfterChangedImageIdentifiers(id loadedIdentifier, id loadingImageIdentifier, NSArray *oldIdentifiers, NSArray *newIdentifiers); - /** @abstract Returns the next image identifier that should be downloaded. @discussion This method obeys and reflects the value of `downloadsIntermediateImages`. @@ -180,7 +169,6 @@ static inline BOOL _shouldUpdateAfterChangedImageIdentifiers(id loadedIdentifier if (_downloadIdentifier) { [_downloader cancelImageDownloadForIdentifier:_downloadIdentifier]; _downloadIdentifier = nil; - _canceledImageDownload = YES; } } @@ -188,10 +176,7 @@ static inline BOOL _shouldUpdateAfterChangedImageIdentifiers(id loadedIdentifier { [super displayWillStart]; - if(_canceledImageDownload) { - [self _updatedImageIdentifiers]; - _canceledImageDownload = NO; - } + [self _loadImageIdentifiers]; } - (void)displayDidFinish @@ -268,24 +253,16 @@ static inline BOOL _shouldUpdateAfterChangedImageIdentifiers(id loadedIdentifier OSSpinLockUnlock(&_imageIdentifiersLock); return; } - - NSArray *oldImageIdentifiers = [[NSArray alloc] initWithArray:_imageIdentifiers]; - + _imageIdentifiers = [imageIdentifiers copy]; - - // Kick off image loading and display, if the identifiers have substantively changed. - BOOL shouldUpdateAfterChangedImageIdentifiers = _shouldUpdateAfterChangedImageIdentifiers(self.loadedImageIdentifier, self.loadingImageIdentifier, oldImageIdentifiers, _imageIdentifiers); - OSSpinLockUnlock(&_imageIdentifiersLock); - - if (shouldUpdateAfterChangedImageIdentifiers) { - [self _updatedImageIdentifiers]; - } } - (void)reloadImageIdentifierSources { - [self _updatedImageIdentifiers]; + // setting this to nil makes the node think it has not downloaded any images + _loadedImageIdentifier = nil; + [self _loadImageIdentifiers]; } #pragma mark - @@ -325,18 +302,10 @@ static inline BOOL _shouldUpdateAfterChangedImageIdentifiers(id loadedIdentifier _downloadIdentifier = downloadIdentifier; } + #pragma mark - Image Loading Machinery -static inline BOOL _shouldUpdateAfterChangedImageIdentifiers(id loadedIdentifier, id loadingImageIdentifier, NSArray *oldIdentifiers, NSArray *newIdentifiers) -{ - // If we're loading an identifier, we need to update if it isn't permitted to load anymore. - if (loadingImageIdentifier) - return ![newIdentifiers containsObject:loadingImageIdentifier]; - // We're not loading, so we need to update unless we've already loaded the best identifier. - return ![[newIdentifiers firstObject] isEqual:loadedIdentifier]; -} - -- (void)_updatedImageIdentifiers +- (void)_loadImageIdentifiers { // Kill any in-flight downloads. [self _setDownloadIdentifier:nil]; diff --git a/AsyncDisplayKitTests/ASMultiplexImageNodeTests.m b/AsyncDisplayKitTests/ASMultiplexImageNodeTests.m index 9f11f92d6f..a08173d549 100644 --- a/AsyncDisplayKitTests/ASMultiplexImageNodeTests.m +++ b/AsyncDisplayKitTests/ASMultiplexImageNodeTests.m @@ -108,6 +108,7 @@ static BOOL ASRunRunLoopUntilBlockIsTrue(BOOL (^block)()) [[[mockDataSource expect] andReturn:testImage] multiplexImageNode:imageNode imageForImageIdentifier:imageIdentifier]; imageNode.imageIdentifiers = @[imageIdentifier]; + [imageNode reloadImageIdentifierSources]; [mockDataSource verify]; @@ -153,8 +154,9 @@ static BOOL ASRunRunLoopUntilBlockIsTrue(BOOL (^block)()) completionBlock([self _testImage].CGImage); }] fetchCachedImageWithURL:[OCMArg any] callbackQueue:[OCMArg any] completion:[OCMArg any]]; - // Kick off loading. imageNode.imageIdentifiers = @[imageIdentifier]; + // Kick off loading. + [imageNode reloadImageIdentifierSources]; // Verify the data source. [mockDataSource verify]; @@ -189,6 +191,7 @@ static BOOL ASRunRunLoopUntilBlockIsTrue(BOOL (^block)()) }] multiplexImageNode:[OCMArg any] imageForImageIdentifier:[OCMArg any]]; imageNode.imageIdentifiers = @[highResIdentifier]; + [imageNode reloadImageIdentifierSources]; // At this point, we should have the high-res identifier loaded and the DS should have been hit once. XCTAssertEqualObjects(imageNode.loadedImageIdentifier, highResIdentifier, @"High res identifier should be loaded."); @@ -197,10 +200,11 @@ static BOOL ASRunRunLoopUntilBlockIsTrue(BOOL (^block)()) // Add the low res identifier. NSNumber *lowResIdentifier = @1; imageNode.imageIdentifiers = @[highResIdentifier, lowResIdentifier]; + [imageNode reloadImageIdentifierSources]; - // At this point the high-res should still be loaded, and the data source should not have been hit. + // At this point the high-res should still be loaded, and the data source should have been hit again XCTAssertEqualObjects(imageNode.loadedImageIdentifier, highResIdentifier, @"High res identifier should be loaded."); - XCTAssertTrue(dataSourceHits == 1, @"Unexpected DS hit count"); + XCTAssertTrue(dataSourceHits == 2, @"Unexpected DS hit count"); imageNode.delegate = nil; imageNode.dataSource = nil; @@ -226,6 +230,7 @@ static BOOL ASRunRunLoopUntilBlockIsTrue(BOOL (^block)()) }] multiplexImageNode:[OCMArg any] imageForImageIdentifier:[OCMArg any]]; imageNode.imageIdentifiers = @[lowResIdentifier]; + [imageNode reloadImageIdentifierSources]; // At this point, we should have the low-res identifier loaded and the DS should have been hit once. XCTAssertEqualObjects(imageNode.loadedImageIdentifier, lowResIdentifier, @"Low res identifier should be loaded."); @@ -234,6 +239,7 @@ static BOOL ASRunRunLoopUntilBlockIsTrue(BOOL (^block)()) // Add the low res identifier. NSNumber *highResIdentifier = @2; imageNode.imageIdentifiers = @[highResIdentifier, lowResIdentifier]; + [imageNode reloadImageIdentifierSources]; // At this point the high-res should be loaded, and the data source should been hit twice. XCTAssertEqualObjects(imageNode.loadedImageIdentifier, highResIdentifier, @"High res identifier should be loaded."); @@ -282,6 +288,7 @@ static BOOL ASRunRunLoopUntilBlockIsTrue(BOOL (^block)()) }] multiplexImageNode:[OCMArg any] imageForImageIdentifier:[OCMArg any]]; imageNode.imageIdentifiers = imageIdentifiers; + [imageNode reloadImageIdentifierSources]; XCTAssertTrue(loadedImageCount == identifierCount, @"Expected to load the same number of identifiers we supplied"); @@ -331,8 +338,9 @@ static BOOL ASRunRunLoopUntilBlockIsTrue(BOOL (^block)()) [[mockDelegate expect] multiplexImageNode:imageNode didUpdateImage:[OCMArg any] withIdentifier:imageIdentifier fromImage:nil withIdentifier:nil]; imageNode.delegate = mockDelegate; - // Kick off loading. imageNode.imageIdentifiers = @[imageIdentifier]; + // Kick off loading. + [imageNode reloadImageIdentifierSources]; // Wait until the image is loaded. ASRunRunLoopUntilBlockIsTrue(^BOOL{ diff --git a/examples/Multiplex/Sample/ViewController.m b/examples/Multiplex/Sample/ViewController.m index e96db45594..3eb3104827 100644 --- a/examples/Multiplex/Sample/ViewController.m +++ b/examples/Multiplex/Sample/ViewController.m @@ -50,7 +50,7 @@ _textLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:22.0f]; // tap to reload - UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(start)]; + UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reload:)]; [_textLabel addGestureRecognizer:gr]; @@ -92,10 +92,14 @@ _textLabel.text = @"loading…"; _textLabel.userInteractionEnabled = NO; - _imageNode.imageIdentifiers = nil; _imageNode.imageIdentifiers = @[ @"best", @"medium", @"worst" ]; // go! } +- (void)reload:(id)sender { + [self start]; + [_imageNode reloadImageIdentifierSources]; +} + #pragma mark - #pragma mark ASMultiplexImageNode data source & delegate.