Set nodes dirty when sizeThatFits changes

Summary:
In YogaKit, nodes are not dirtied when `sizeThatFits` of leaf nodes change. This makes it difficult to reuse views like `UILabel` when their content (text) changes. I wrote a failing test to prove it and wrote a fix by caching the latest value of `sizeThatFits` in `YGNodeBridge` and checking for changes in `YGAttachNodesFromViewHierachy` (renamed to `YGUpdateNodesFromViewHierachy` to make it clear it's not simply reattaching nodes anymore). When a new size is detected, I call `YGNodeMarkDirty` on the leaf node.

**Unfortunately**, this makes another test fail for no logical reason (`testThatViewNotIncludedInFirstLayoutPassAreIncludedInSecond`). Either there's something I don't understand or I've un-advertedly unearthed a bug in the C implementation, but in what case would setting a node dirty make the layout return a different size?
Closes https://github.com/facebook/yoga/pull/308

Reviewed By: emilsjolander

Differential Revision: D4378826

Pulled By: dshahidehpour

fbshipit-source-id: cd0640bc863debb9e434370739416e3e33e57a08
This commit is contained in:
David Hart
2017-01-05 08:25:01 -08:00
committed by Facebook Github Bot
parent 1877a97898
commit 29c44e281c
3 changed files with 40 additions and 3 deletions

View File

@@ -96,6 +96,29 @@
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(514,21), containerSize), @"Size is actually %@", NSStringFromCGSize(containerSize));
}
- (void)testThatMarkingLeafsAsDirtyWillTriggerASizeRecalculation
{
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 50)];
[container yg_setUsesYoga:YES];
[container yg_setFlexDirection:YGFlexDirectionRow];
[container yg_setAlignItems:YGAlignFlexStart];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.text = @"This is a short text.";
label.numberOfLines = 1;
[label yg_setUsesYoga:YES];
[container addSubview:label];
[container yg_applyLayout];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(146,21), label.bounds.size), @"Size is actually %@", NSStringFromCGSize(label.bounds.size));
label.text = @"This is a slightly longer text.";
[label yg_markDirty];
[container yg_applyLayout];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(213,21), label.bounds.size), @"Size is actually %@", NSStringFromCGSize(label.bounds.size));
}
- (void)testFrameAndOriginPlacement
{
const CGSize containerSize = CGSizeMake(320, 50);

View File

@@ -75,4 +75,9 @@
*/
- (BOOL)yg_isLeaf;
/**
Mark that a view's layout needs to be recalculated. Only works for leaf views.
*/
- (void)yg_markDirty;
@end

View File

@@ -11,6 +11,8 @@
#import <objc/runtime.h>
static const void *kYGNodeBridgeAssociatedKey = &kYGNodeBridgeAssociatedKey;
@interface YGNodeBridge : NSObject
@property (nonatomic, assign, readonly) YGNodeRef cnode;
@end
@@ -39,6 +41,14 @@
@implementation UIView (Yoga)
- (void)yg_markDirty
{
YGNodeBridge *const bridge = objc_getAssociatedObject(self, kYGNodeBridgeAssociatedKey);
if (bridge != nil && [self yg_isLeaf]) {
YGNodeMarkDirty(bridge.cnode);
}
}
- (BOOL)yg_usesYoga
{
NSNumber *usesYoga = objc_getAssociatedObject(self, @selector(yg_usesYoga));
@@ -226,13 +236,12 @@
- (YGNodeRef)ygNode
{
YGNodeBridge *node = objc_getAssociatedObject(self, @selector(ygNode));
YGNodeBridge *node = objc_getAssociatedObject(self, kYGNodeBridgeAssociatedKey);
if (!node) {
node = [YGNodeBridge new];
YGNodeSetContext(node.cnode, (__bridge void *) self);
objc_setAssociatedObject(self, @selector(ygNode), node, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, kYGNodeBridgeAssociatedKey, node, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return node.cnode;
}