diff --git a/CHANGELOG.md b/CHANGELOG.md index f7267c240c..efbc62d548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Add ASCollectionGalleryLayoutDelegate - an async collection layout that makes same-size collections (e.g photo galleries, pagers, etc) fast and lightweight! [Huy Nguyen](https://github.com/nguyenhuy/) [#76](https://github.com/TextureGroup/Texture/pull/76) [#451](https://github.com/TextureGroup/Texture/pull/451) - Fix an issue that causes infinite layout loop in ASDisplayNode after [#428](https://github.com/TextureGroup/Texture/pull/428) [Huy Nguyen](https://github.com/nguyenhuy) [#455](https://github.com/TextureGroup/Texture/pull/455) - Fix an issue in layout transition that causes it to unexpectedly use the old layout [Huy Nguyen](https://github.com/nguyenhuy) [#464](https://github.com/TextureGroup/Texture/pull/464) +- Add -[ASDisplayNode detailedLayoutDescription] property to aid debugging. [Adlai Holler](https://github.com/Adlai-Holler) [#476](https://github.com/TextureGroup/Texture/pull/476) ##2.3.5 - Fix an issue where inserting/deleting sections could lead to inconsistent supplementary element behavior. [Adlai Holler](https://github.com/Adlai-Holler) diff --git a/Source/ASDisplayNode+Layout.mm b/Source/ASDisplayNode+Layout.mm index 31f0739c73..66a5687685 100644 --- a/Source/ASDisplayNode+Layout.mm +++ b/Source/ASDisplayNode+Layout.mm @@ -319,7 +319,7 @@ ASPrimitiveTraitCollectionDeprecatedImplementation } as_activity_create_for_scope("Update node layout for current bounds"); - as_log_verbose(ASLayoutLog(), "Node %@, bounds size %@, calculatedSize %@, calculatedIsDirty %d", self, NSStringFromCGSize(boundsSizeForLayout), NSStringFromCGSize(_calculatedDisplayNodeLayout->layout.size), _calculatedDisplayNodeLayout->isDirty()); + as_log_verbose(ASLayoutLog(), "Node %@, bounds size %@, calculatedSize %@, calculatedIsDirty %d", self, NSStringFromCGSize(boundsSizeForLayout), NSStringFromCGSize(_calculatedDisplayNodeLayout->layout.size), _calculatedDisplayNodeLayout->version < _layoutVersion.load()); // _calculatedDisplayNodeLayout is not reusable we need to transition to a new one [self cancelLayoutTransition]; @@ -543,7 +543,6 @@ ASPrimitiveTraitCollectionDeprecatedImplementation return; } - BOOL shouldInvalidateLayout = NO; { ASDN::MutexLocker l(__instanceLock__); @@ -557,13 +556,14 @@ ASPrimitiveTraitCollectionDeprecatedImplementation ASDisplayNodeAssert(NO, @"Can't start a transition when one of the supernodes is performing one."); return; } - - shouldInvalidateLayout = ASSizeRangeEqualToSizeRange([self _locked_constrainedSizeForCalculatedLayout], constrainedSize); } - if (shouldInvalidateLayout) { - [self setNeedsLayout]; - } + // Invalidate calculated layout because this method acts as an animated "setNeedsLayout" for nodes. + // If the user has reconfigured the node and calls this, we should never return a stale layout + // for subsequent calls to layoutThatFits: regardless of size range. We choose this method rather than + // -setNeedsLayout because that method also triggers a CA layout invalidation, which isn't necessary at this time. + // See https://github.com/TextureGroup/Texture/issues/463 + [self invalidateCalculatedLayout]; // Every new layout transition has a transition id associated to check in subsequent transitions for cancelling int32_t transitionID = [self _startNewTransition]; diff --git a/Source/ASDisplayNode.h b/Source/ASDisplayNode.h index f97f4dfc94..24347d09b5 100644 --- a/Source/ASDisplayNode.h +++ b/Source/ASDisplayNode.h @@ -584,6 +584,11 @@ extern NSInteger const ASDefaultDrawingPriority; */ - (NSString *)displayNodeRecursiveDescription AS_WARN_UNUSED_RESULT; +/** + * A detailed description of this node's layout state. This is useful when debugging. + */ +@property (atomic, copy, readonly) NSString *detailedLayoutDescription; + @end /** diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 2855fa91a8..cf90d518b3 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -3377,6 +3377,38 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) { return subtree; } +- (NSString *)detailedLayoutDescription +{ + ASPushMainThreadAssertionsDisabled(); + ASDN::MutexLocker l(__instanceLock__); + auto props = [NSMutableArray array]; + + [props addObject:@{ @"layoutVersion": @(_layoutVersion.load()) }]; + [props addObject:@{ @"bounds": [NSValue valueWithCGRect:self.bounds] }]; + + if (_calculatedDisplayNodeLayout != nullptr) { + ASDisplayNodeLayout c = *_calculatedDisplayNodeLayout; + [props addObject:@{ @"calculatedLayout": c.layout }]; + [props addObject:@{ @"calculatedVersion": @(c.version) }]; + [props addObject:@{ @"calculatedConstrainedSize" : NSStringFromASSizeRange(c.constrainedSize) }]; + if (c.requestedLayoutFromAbove) { + [props addObject:@{ @"calculatedRequestedLayoutFromAbove": @"YES" }]; + } + } + if (_pendingDisplayNodeLayout != nullptr) { + ASDisplayNodeLayout p = *_pendingDisplayNodeLayout; + [props addObject:@{ @"pendingLayout": p.layout }]; + [props addObject:@{ @"pendingVersion": @(p.version) }]; + [props addObject:@{ @"pendingConstrainedSize" : NSStringFromASSizeRange(p.constrainedSize) }]; + if (p.requestedLayoutFromAbove) { + [props addObject:@{ @"pendingRequestedLayoutFromAbove": (id)kCFNull }]; + } + } + + ASPopMainThreadAssertionsDisabled(); + return ASObjectDescriptionMake(self, props); +} + @end #pragma mark - ASDisplayNode UIKit / CA Categories diff --git a/Source/ASNetworkImageNode.mm b/Source/ASNetworkImageNode.mm index d6bd4dfbe8..c68cb792e6 100755 --- a/Source/ASNetworkImageNode.mm +++ b/Source/ASNetworkImageNode.mm @@ -671,7 +671,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; return; } - as_log_verbose(ASImageLoadingLog(), "Downloaded image for %@ img: %@ url: %@", self, [imageContainer asdk_image], url); + as_log_verbose(ASImageLoadingLog(), "Downloaded image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL); // Grab the lock for the rest of the block ASDN::MutexLocker l(strongSelf->__instanceLock__);