Improved the objective-c and swift api

Summary:
Compared to what was planned, I added the `overflow` value which seemed missing. I had to modify the implementation a bit for all values which are backed by a `YGValue`, but we should probably enable the pixel dimensions in Objective-C and Swift somehow later.
Closes https://github.com/facebook/yoga/pull/322

Reviewed By: dshahidehpour

Differential Revision: D4386906

Pulled By: emilsjolander

fbshipit-source-id: 05ac0e571ef3a8ff0be31469e449a7b23f102218
This commit is contained in:
David Hart
2017-01-07 08:22:43 -08:00
committed by Facebook Github Bot
parent c169a98be6
commit 8d320ceac2
13 changed files with 765 additions and 607 deletions

View File

@@ -23,7 +23,7 @@
XCTAssertEqual(0, YGNodeGetInstanceCount()); XCTAssertEqual(0, YGNodeGetInstanceCount());
UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
[view yg_setFlexBasis:1]; view.yoga.flexBasis = 1;
XCTAssertEqual(1, YGNodeGetInstanceCount()); XCTAssertEqual(1, YGNodeGetInstanceCount());
view = nil; view = nil;
@@ -35,11 +35,11 @@
XCTAssertEqual(0, YGNodeGetInstanceCount()); XCTAssertEqual(0, YGNodeGetInstanceCount());
UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
[view yg_setFlexBasis:1]; view.yoga.flexBasis = 1;
for (int i=0; i<10; i++) { for (int i=0; i<10; i++) {
UIView *subview = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview = [[UIView alloc] initWithFrame:CGRectZero];
[subview yg_setFlexBasis:1]; subview.yoga.flexBasis = 1;
[view addSubview:subview]; [view addSubview:subview];
} }
XCTAssertEqual(11, YGNodeGetInstanceCount()); XCTAssertEqual(11, YGNodeGetInstanceCount());
@@ -53,69 +53,69 @@
- (void)testUsesYoga - (void)testUsesYoga
{ {
UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
XCTAssertFalse([view yg_usesYoga]); XCTAssertFalse(view.yoga.isEnabled);
[view yg_setUsesYoga:YES]; view.yoga.isEnabled = YES;
XCTAssertTrue([view yg_usesYoga]); XCTAssertTrue(view.yoga.isEnabled);
[view yg_setUsesYoga:NO]; view.yoga.isEnabled = NO;
XCTAssertFalse([view yg_usesYoga]); XCTAssertFalse(view.yoga.isEnabled);
} }
- (void)testSizeThatFitsAsserts - (void)testSizeThatFitsAsserts
{ {
UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
dispatch_sync(dispatch_queue_create("com.facebook.Yoga.testing", DISPATCH_QUEUE_SERIAL), ^(void){ dispatch_sync(dispatch_queue_create("com.facebook.Yoga.testing", DISPATCH_QUEUE_SERIAL), ^(void){
XCTAssertThrows([view yg_intrinsicSize]); XCTAssertThrows(view.yoga.intrinsicSize);
}); });
} }
- (void)testSizeThatFitsSmoke - (void)testSizeThatFitsSmoke
{ {
UIView *container = [[UIView alloc] initWithFrame:CGRectZero]; UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
[container yg_setUsesYoga:YES]; container.yoga.isEnabled = YES;
[container yg_setFlexDirection:YGFlexDirectionRow]; container.yoga.flexDirection = YGFlexDirectionRow;
[container yg_setAlignItems:YGAlignFlexStart]; container.yoga.alignItems = YGAlignFlexStart;
UILabel *longTextLabel = [[UILabel alloc] initWithFrame:CGRectZero]; UILabel *longTextLabel = [[UILabel alloc] initWithFrame:CGRectZero];
longTextLabel.text = @"This is a very very very very very very very very long piece of text."; longTextLabel.text = @"This is a very very very very very very very very long piece of text.";
longTextLabel.lineBreakMode = NSLineBreakByTruncatingTail; longTextLabel.lineBreakMode = NSLineBreakByTruncatingTail;
longTextLabel.numberOfLines = 1; longTextLabel.numberOfLines = 1;
[longTextLabel yg_setUsesYoga:YES]; longTextLabel.yoga.isEnabled = YES;
[longTextLabel yg_setFlexShrink:1]; longTextLabel.yoga.flexShrink = 1;
[container addSubview:longTextLabel]; [container addSubview:longTextLabel];
UIView *textBadgeView = [[UIView alloc] initWithFrame:CGRectZero]; UIView *textBadgeView = [[UIView alloc] initWithFrame:CGRectZero];
[textBadgeView yg_setUsesYoga:YES]; textBadgeView.yoga.isEnabled = YES;
[textBadgeView yg_setMargin:3.0 forEdge:YGEdgeLeft]; textBadgeView.yoga.marginLeft = 3.0;
[textBadgeView yg_setWidth:10]; textBadgeView.yoga.width = 10;
[textBadgeView yg_setHeight:10]; textBadgeView.yoga.height = 10;
[container addSubview:textBadgeView]; [container addSubview:textBadgeView];
const CGSize containerSize = [container yg_intrinsicSize]; const CGSize containerSize = container.yoga.intrinsicSize;
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(514,21), containerSize), @"Size is actually %@", NSStringFromCGSize(containerSize)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(514,21), containerSize), @"Size is actually %@", NSStringFromCGSize(containerSize));
} }
- (void)testThatMarkingLeafsAsDirtyWillTriggerASizeRecalculation - (void)testThatMarkingLeafsAsDirtyWillTriggerASizeRecalculation
{ {
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 50)]; UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 50)];
[container yg_setUsesYoga:YES]; container.yoga.isEnabled = YES;
[container yg_setFlexDirection:YGFlexDirectionRow]; container.yoga.flexDirection = YGFlexDirectionRow;
[container yg_setAlignItems:YGAlignFlexStart]; container.yoga.alignItems = YGAlignFlexStart;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero]; UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.text = @"This is a short text."; label.text = @"This is a short text.";
label.numberOfLines = 1; label.numberOfLines = 1;
[label yg_setUsesYoga:YES]; label.yoga.isEnabled = YES;
[container addSubview:label]; [container addSubview:label];
[container yg_applyLayout]; [container.yoga applyLayout];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(146,21), label.bounds.size), @"Size is actually %@", NSStringFromCGSize(label.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(146,21), label.bounds.size), @"Size is actually %@", NSStringFromCGSize(label.bounds.size));
label.text = @"This is a slightly longer text."; label.text = @"This is a slightly longer text.";
[label yg_markDirty]; [label.yoga markDirty];
[container yg_applyLayout]; [container.yoga applyLayout];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(213,21), label.bounds.size), @"Size is actually %@", NSStringFromCGSize(label.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(213,21), label.bounds.size), @"Size is actually %@", NSStringFromCGSize(label.bounds.size));
} }
@@ -124,17 +124,17 @@
const CGSize containerSize = CGSizeMake(320, 50); const CGSize containerSize = CGSizeMake(320, 50);
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)]; UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)];
[container yg_setUsesYoga:YES]; container.yoga.isEnabled = YES;
[container yg_setFlexDirection:YGFlexDirectionRow]; container.yoga.flexDirection = YGFlexDirectionRow;
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
UIView *subview = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview = [[UIView alloc] initWithFrame:CGRectZero];
[subview yg_setUsesYoga:YES]; subview.yoga.isEnabled = YES;
[subview yg_setFlexGrow:1]; subview.yoga.flexGrow = 1;
[container addSubview:subview]; [container addSubview:subview];
} }
[container yg_applyLayout]; [container.yoga applyLayout];
XCTAssertFalse(CGRectIntersectsRect([container.subviews objectAtIndex:0].frame, [container.subviews objectAtIndex:1].frame)); XCTAssertFalse(CGRectIntersectsRect([container.subviews objectAtIndex:0].frame, [container.subviews objectAtIndex:1].frame));
XCTAssertFalse(CGRectIntersectsRect([container.subviews objectAtIndex:1].frame, [container.subviews objectAtIndex:2].frame)); XCTAssertFalse(CGRectIntersectsRect([container.subviews objectAtIndex:1].frame, [container.subviews objectAtIndex:2].frame));
@@ -153,33 +153,33 @@
const CGSize containerSize = CGSizeMake(300, 50); const CGSize containerSize = CGSizeMake(300, 50);
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)]; UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)];
[container yg_setUsesYoga:YES]; container.yoga.isEnabled = YES;
[container yg_setFlexDirection:YGFlexDirectionRow]; container.yoga.flexDirection = YGFlexDirectionRow;
UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero];
[subview1 yg_setUsesYoga:YES]; subview1.yoga.isEnabled = YES;
[subview1 yg_setFlexGrow:1]; subview1.yoga.flexGrow = 1;
[container addSubview:subview1]; [container addSubview:subview1];
UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero];
[subview2 yg_setUsesYoga:YES]; subview2.yoga.isEnabled = YES;
[subview2 yg_setFlexGrow:1]; subview2.yoga.flexGrow = 1;
[container addSubview:subview2]; [container addSubview:subview2];
UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero];
[subview3 yg_setUsesYoga:YES]; subview3.yoga.isEnabled = YES;
[subview3 yg_setFlexGrow:1]; subview3.yoga.flexGrow = 1;
[container addSubview:subview3]; [container addSubview:subview3];
[container yg_applyLayout]; [container.yoga applyLayout];
XCTAssertTrue(CGRectEqualToRect(subview1.frame, CGRectMake(0, 0, 100, 50))); XCTAssertTrue(CGRectEqualToRect(subview1.frame, CGRectMake(0, 0, 100, 50)));
XCTAssertTrue(CGRectEqualToRect(subview2.frame, CGRectMake(100, 0, 100, 50)), @"It's actually %@", NSStringFromCGRect(subview2.frame)); XCTAssertTrue(CGRectEqualToRect(subview2.frame, CGRectMake(100, 0, 100, 50)), @"It's actually %@", NSStringFromCGRect(subview2.frame));
XCTAssertTrue(CGRectEqualToRect(subview3.frame, CGRectMake(200, 0, 100, 50))); XCTAssertTrue(CGRectEqualToRect(subview3.frame, CGRectMake(200, 0, 100, 50)));
[container exchangeSubviewAtIndex:2 withSubviewAtIndex:0]; [container exchangeSubviewAtIndex:2 withSubviewAtIndex:0];
[subview2 yg_setIncludeInLayout:NO]; subview2.yoga.isIncludedInLayout = NO;
[container yg_applyLayout]; [container.yoga applyLayout];
XCTAssertTrue(CGRectEqualToRect(subview3.frame, CGRectMake(0, 0, 150, 50))); XCTAssertTrue(CGRectEqualToRect(subview3.frame, CGRectMake(0, 0, 150, 50)));
XCTAssertTrue(CGRectEqualToRect(subview1.frame, CGRectMake(150, 0, 150, 50))); XCTAssertTrue(CGRectEqualToRect(subview1.frame, CGRectMake(150, 0, 150, 50)));
@@ -193,32 +193,32 @@
const CGSize containerSize = CGSizeMake(300, 50); const CGSize containerSize = CGSizeMake(300, 50);
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)]; UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)];
[container yg_setUsesYoga:YES]; container.yoga.isEnabled = YES;
[container yg_setFlexDirection:YGFlexDirectionRow]; container.yoga.flexDirection = YGFlexDirectionRow;
UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero];
[subview1 yg_setUsesYoga:YES]; subview1.yoga.isEnabled = YES;
[subview1 yg_setFlexGrow:1]; subview1.yoga.flexGrow = 1;
[container addSubview:subview1]; [container addSubview:subview1];
UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero];
[subview2 yg_setUsesYoga:YES]; subview2.yoga.isEnabled = YES;
[subview2 yg_setFlexGrow:1]; subview2.yoga.flexGrow = 1;
[container addSubview:subview2]; [container addSubview:subview2];
UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero];
[subview3 yg_setUsesYoga:YES]; subview3.yoga.isEnabled = YES;
[subview3 yg_setFlexGrow:1]; subview3.yoga.flexGrow = 1;
[container addSubview:subview3]; [container addSubview:subview3];
[container yg_applyLayout]; [container.yoga applyLayout];
for (UIView *view in container.subviews) { for (UIView *view in container.subviews) {
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(view.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(view.bounds.size));
} }
[subview3 yg_setIncludeInLayout:NO]; subview3.yoga.isIncludedInLayout = NO;
[container yg_applyLayout]; [container.yoga applyLayout];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview1.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview1.bounds.size));
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview2.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview2.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview2.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview2.bounds.size));
@@ -230,62 +230,62 @@
- (void)testThatNumberOfChildrenIsCorrectWhenWeIgnoreSubviews - (void)testThatNumberOfChildrenIsCorrectWhenWeIgnoreSubviews
{ {
UIView *container = [[UIView alloc] initWithFrame:CGRectZero]; UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
[container yg_setUsesYoga:YES]; container.yoga.isEnabled = YES;
[container yg_setFlexDirection:YGFlexDirectionRow]; container.yoga.flexDirection = YGFlexDirectionRow;
UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero];
[subview1 yg_setUsesYoga:YES]; subview1.yoga.isEnabled = YES;
[subview1 yg_setIncludeInLayout:NO]; subview1.yoga.isIncludedInLayout = NO;
[container addSubview:subview1]; [container addSubview:subview1];
UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero];
[subview2 yg_setUsesYoga:YES]; subview2.yoga.isEnabled = YES;
[subview2 yg_setIncludeInLayout:NO]; subview2.yoga.isIncludedInLayout = NO;
[container addSubview:subview2]; [container addSubview:subview2];
UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero];
[subview3 yg_setUsesYoga:YES]; subview3.yoga.isEnabled = YES;
[subview3 yg_setIncludeInLayout:YES]; subview3.yoga.isIncludedInLayout = YES;
[container addSubview:subview3]; [container addSubview:subview3];
[container yg_applyLayout]; [container.yoga applyLayout];
XCTAssertEqual(1, [container yg_numberOfChildren]); XCTAssertEqual(1, container.yoga.numberOfChildren);
[subview2 yg_setIncludeInLayout:YES]; subview2.yoga.isIncludedInLayout = YES;
[container yg_applyLayout]; [container.yoga applyLayout];
XCTAssertEqual(2, [container yg_numberOfChildren]); XCTAssertEqual(2, container.yoga.numberOfChildren);
} }
- (void)testThatViewNotIncludedInFirstLayoutPassAreIncludedInSecond - (void)testThatViewNotIncludedInFirstLayoutPassAreIncludedInSecond
{ {
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)]; UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)];
[container yg_setUsesYoga:YES]; container.yoga.isEnabled = YES;
[container yg_setFlexDirection:YGFlexDirectionRow]; container.yoga.flexDirection = YGFlexDirectionRow;
UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero];
[subview1 yg_setUsesYoga:YES]; subview1.yoga.isEnabled = YES;
[subview1 yg_setFlexGrow:1]; subview1.yoga.flexGrow = 1;
[container addSubview:subview1]; [container addSubview:subview1];
UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero];
[subview2 yg_setUsesYoga:YES]; subview2.yoga.isEnabled = YES;
[subview2 yg_setFlexGrow:1]; subview2.yoga.flexGrow = 1;
[container addSubview:subview2]; [container addSubview:subview2];
UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero];
[subview3 yg_setUsesYoga:YES]; subview3.yoga.isEnabled = YES;
[subview3 yg_setFlexGrow:1]; subview3.yoga.flexGrow = 1;
[subview3 yg_setIncludeInLayout:NO]; subview3.yoga.isIncludedInLayout = NO;
[container addSubview:subview3]; [container addSubview:subview3];
[container yg_applyLayout]; [container.yoga applyLayout];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview1.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview1.bounds.size));
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview2.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview2.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview2.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview2.bounds.size));
XCTAssertTrue(CGSizeEqualToSize(CGSizeZero, subview3.bounds.size), @"Actual size %@", NSStringFromCGSize(subview3.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeZero, subview3.bounds.size), @"Actual size %@", NSStringFromCGSize(subview3.bounds.size));
[subview3 yg_setIncludeInLayout:YES]; subview3.yoga.isIncludedInLayout = YES;
[container yg_applyLayout]; [container.yoga applyLayout];
for (UIView *view in container.subviews) { for (UIView *view in container.subviews) {
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(view.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(view.bounds.size));
} }
@@ -294,60 +294,60 @@
- (void)testyg_isLeafFlag - (void)testyg_isLeafFlag
{ {
UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
XCTAssertTrue(view.yg_isLeaf); XCTAssertTrue(view.yoga.isLeaf);
for (int i=0; i<10; i++) { for (int i=0; i<10; i++) {
UIView *subview = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview = [[UIView alloc] initWithFrame:CGRectZero];
[view addSubview:subview]; [view addSubview:subview];
} }
XCTAssertTrue(view.yg_isLeaf); XCTAssertTrue(view.yoga.isLeaf);
[view yg_setUsesYoga:YES]; view.yoga.isEnabled = YES;
[view yg_setWidth:50.0]; view.yoga.width = 50.0;
XCTAssertTrue(view.yg_isLeaf); XCTAssertTrue(view.yoga.isLeaf);
UIView *const subview = view.subviews[0]; UIView *const subview = view.subviews[0];
[subview yg_setUsesYoga:YES]; subview.yoga.isEnabled = YES;
[subview yg_setWidth:50.0]; subview.yoga.width = 50.0;
XCTAssertFalse(view.yg_isLeaf); XCTAssertFalse(view.yoga.isLeaf);
} }
- (void)testThatWeCorrectlyAttachNestedViews - (void)testThatWeCorrectlyAttachNestedViews
{ {
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)]; UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)];
[container yg_setUsesYoga:YES]; container.yoga.isEnabled = YES;
[container yg_setFlexDirection:YGFlexDirectionColumn]; container.yoga.flexDirection = YGFlexDirectionColumn;
UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero];
[subview1 yg_setUsesYoga:YES]; subview1.yoga.isEnabled = YES;
[subview1 yg_setWidth:100]; subview1.yoga.width = 100;
[subview1 yg_setFlexGrow:1]; subview1.yoga.flexGrow = 1;
[subview1 yg_setFlexDirection:YGFlexDirectionColumn]; subview1.yoga.flexDirection = YGFlexDirectionColumn;
[container addSubview:subview1]; [container addSubview:subview1];
UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero];
[subview2 yg_setUsesYoga:YES]; subview2.yoga.isEnabled = YES;
[subview2 yg_setWidth:150]; subview2.yoga.width = 150;
[subview2 yg_setFlexGrow:1]; subview2.yoga.flexGrow = 1;
[subview2 yg_setFlexDirection:YGFlexDirectionColumn]; subview2.yoga.flexDirection = YGFlexDirectionColumn;
[container addSubview:subview2]; [container addSubview:subview2];
for (UIView *view in @[subview1, subview2]) { for (UIView *view in @[subview1, subview2]) {
UIView *someView = [[UIView alloc] initWithFrame:CGRectZero]; UIView *someView = [[UIView alloc] initWithFrame:CGRectZero];
[someView yg_setUsesYoga:YES]; someView.yoga.isEnabled = YES;
[someView yg_setFlexGrow:1]; someView.yoga.flexGrow = 1;
[view addSubview:someView]; [view addSubview:someView];
} }
[container yg_applyLayout]; [container.yoga applyLayout];
// Add the same amount of new views, reapply layout. // Add the same amount of new views, reapply layout.
for (UIView *view in @[subview1, subview2]) { for (UIView *view in @[subview1, subview2]) {
UIView *someView = [[UIView alloc] initWithFrame:CGRectZero]; UIView *someView = [[UIView alloc] initWithFrame:CGRectZero];
[someView yg_setUsesYoga:YES]; someView.yoga.isEnabled = YES;
[someView yg_setFlexGrow:1]; someView.yoga.flexGrow = 1;
[view addSubview:someView]; [view addSubview:someView];
} }
[container yg_applyLayout]; [container.yoga applyLayout];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 25), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview1.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 25), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview1.bounds.size));
for (UIView *subview in subview1.subviews) { for (UIView *subview in subview1.subviews) {
@@ -369,19 +369,19 @@
- (void)testThatANonLeafNodeCanBecomeALeafNode - (void)testThatANonLeafNodeCanBecomeALeafNode
{ {
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)]; UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)];
[container yg_setUsesYoga:YES]; container.yoga.isEnabled = YES;
UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero];
[subview1 yg_setUsesYoga:YES]; subview1.yoga.isEnabled = YES;
[container addSubview:subview1]; [container addSubview:subview1];
UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero];
[subview2 yg_setUsesYoga:YES]; subview2.yoga.isEnabled = YES;
[subview1 addSubview:subview2]; [subview1 addSubview:subview2];
[container yg_applyLayout]; [container.yoga applyLayout];
[subview2 removeFromSuperview]; [subview2 removeFromSuperview];
[container yg_applyLayout]; [container.yoga applyLayout];
} }
@end @end

View File

@@ -8,76 +8,10 @@
*/ */
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <yoga/Yoga.h> #import "YGLayout.h"
@interface UIView (Yoga) @interface UIView (Yoga)
/** @property (nonatomic, readonly, strong) YGLayout *yoga;
The property that decides if we should include this view when calculating layout. Defaults to YES.
*/
@property (nonatomic, readwrite, assign, setter=yg_setIncludeInLayout:) BOOL yg_includeInLayout;
/**
The property that decides during layout/sizing whether or not yg_* properties should be applied. Defaults to NO.
*/
@property (nonatomic, readwrite, assign, setter=yg_setUsesYoga:) BOOL yg_usesYoga;
- (void)yg_setDirection:(YGDirection)direction;
- (void)yg_setFlexDirection:(YGFlexDirection)flexDirection;
- (void)yg_setJustifyContent:(YGJustify)justifyContent;
- (void)yg_setAlignContent:(YGAlign)alignContent;
- (void)yg_setAlignItems:(YGAlign)alignItems;
- (void)yg_setAlignSelf:(YGAlign)alignSelf;
- (void)yg_setPositionType:(YGPositionType)positionType;
- (void)yg_setFlexWrap:(YGWrap)flexWrap;
- (void)yg_setOverflow:(YGOverflow)overflow;
- (void)yg_setFlexGrow:(CGFloat)flexGrow;
- (void)yg_setFlexShrink:(CGFloat)flexShrink;
- (void)yg_setFlexBasis:(CGFloat)flexBasis;
- (void)yg_setPosition:(CGFloat)position forEdge:(YGEdge)edge;
- (void)yg_setMargin:(CGFloat)margin forEdge:(YGEdge)edge;
- (void)yg_setPadding:(CGFloat)padding forEdge:(YGEdge)edge;
- (void)yg_setWidth:(CGFloat)width;
- (void)yg_setHeight:(CGFloat)height;
- (void)yg_setMinWidth:(CGFloat)minWidth;
- (void)yg_setMinHeight:(CGFloat)minHeight;
- (void)yg_setMaxWidth:(CGFloat)maxWidth;
- (void)yg_setMaxHeight:(CGFloat)maxHeight;
// Yoga specific properties, not compatible with flexbox specification
- (void)yg_setAspectRatio:(CGFloat)aspectRatio;
/**
Get the resolved direction of this node. This won't be YGDirectionInherit
*/
- (YGDirection)yg_resolvedDirection;
/**
Perform a layout calculation and update the frames of the views in the hierarchy with the results
*/
- (void)yg_applyLayout;
/**
Returns the size of the view if no constraints were given. This could equivalent to calling [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)];
*/
- (CGSize)yg_intrinsicSize;
/**
Returns the number of children that are using Flexbox.
*/
- (NSUInteger)yg_numberOfChildren;
/**
Return a BOOL indiciating whether or not we this node contains any subviews that are included in Yoga's layout.
*/
- (BOOL)yg_isLeaf;
/**
Mark that a view's layout needs to be recalculated. Only works for leaf views.
*/
- (void)yg_markDirty;
@end @end

View File

@@ -8,404 +8,23 @@
*/ */
#import "UIView+Yoga.h" #import "UIView+Yoga.h"
#import "YGLayout+Private.h"
#import <objc/runtime.h> #import <objc/runtime.h>
static const void *kYGNodeBridgeAssociatedKey = &kYGNodeBridgeAssociatedKey; static const void *kYGYogaAssociatedKey = &kYGYogaAssociatedKey;
@interface YGNodeBridge : NSObject @implementation UIView (YogaKit)
@property (nonatomic, assign, readonly) YGNodeRef cnode;
@end
@implementation YGNodeBridge - (YGLayout *)yoga
+ (void)initialize
{ {
YGSetExperimentalFeatureEnabled(YGExperimentalFeatureWebFlexBasis, true); YGLayout *yoga = objc_getAssociatedObject(self, kYGYogaAssociatedKey);
} if (!yoga) {
yoga = [[YGLayout alloc] initWithView:self];
- (instancetype)init objc_setAssociatedObject(self, kYGYogaAssociatedKey, yoga, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
{
if ([super init]) {
_cnode = YGNodeNew();
} }
return self; return yoga;
} }
- (void)dealloc
{
YGNodeFree(_cnode);
}
@end
@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));
return [usesYoga boolValue];
}
- (BOOL)yg_includeInLayout
{
NSNumber *includeInLayout = objc_getAssociatedObject(self, @selector(yg_includeInLayout));
return (includeInLayout != nil) ? [includeInLayout boolValue] : YES;
}
- (NSUInteger)yg_numberOfChildren
{
return YGNodeGetChildCount([self ygNode]);
}
- (BOOL)yg_isLeaf
{
NSAssert([NSThread isMainThread], @"This method must be called on the main thread.");
if ([self yg_usesYoga]) {
for (UIView *subview in self.subviews) {
if ([subview yg_usesYoga] && [subview yg_includeInLayout]) {
return NO;
}
}
}
return YES;
}
#pragma mark - Setters
- (void)yg_setIncludeInLayout:(BOOL)includeInLayout
{
objc_setAssociatedObject(
self,
@selector(yg_includeInLayout),
@(includeInLayout),
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)yg_setUsesYoga:(BOOL)enabled
{
objc_setAssociatedObject(
self,
@selector(yg_usesYoga),
@(enabled),
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)yg_setDirection:(YGDirection)direction
{
YGNodeStyleSetDirection([self ygNode], direction);
}
- (void)yg_setFlexDirection:(YGFlexDirection)flexDirection
{
YGNodeStyleSetFlexDirection([self ygNode], flexDirection);
}
- (void)yg_setJustifyContent:(YGJustify)justifyContent
{
YGNodeStyleSetJustifyContent([self ygNode], justifyContent);
}
- (void)yg_setAlignContent:(YGAlign)alignContent
{
YGNodeStyleSetAlignContent([self ygNode], alignContent);
}
- (void)yg_setAlignItems:(YGAlign)alignItems
{
YGNodeStyleSetAlignItems([self ygNode], alignItems);
}
- (void)yg_setAlignSelf:(YGAlign)alignSelf
{
YGNodeStyleSetAlignSelf([self ygNode], alignSelf);
}
- (void)yg_setPositionType:(YGPositionType)positionType
{
YGNodeStyleSetPositionType([self ygNode], positionType);
}
- (void)yg_setFlexWrap:(YGWrap)flexWrap
{
YGNodeStyleSetFlexWrap([self ygNode], flexWrap);
}
- (void)yg_setOverflow:(YGOverflow)overflow
{
YGNodeStyleSetOverflow([self ygNode], overflow);
}
- (void)yg_setFlexGrow:(CGFloat)flexGrow
{
YGNodeStyleSetFlexGrow([self ygNode], flexGrow);
}
- (void)yg_setFlexShrink:(CGFloat)flexShrink
{
YGNodeStyleSetFlexShrink([self ygNode], flexShrink);
}
- (void)yg_setFlexBasis:(CGFloat)flexBasis
{
YGNodeStyleSetFlexBasis([self ygNode], flexBasis);
}
- (void)yg_setPosition:(CGFloat)position forEdge:(YGEdge)edge
{
YGNodeStyleSetPosition([self ygNode], edge, position);
}
- (void)yg_setMargin:(CGFloat)margin forEdge:(YGEdge)edge
{
YGNodeStyleSetMargin([self ygNode], edge, margin);
}
- (void)yg_setPadding:(CGFloat)padding forEdge:(YGEdge)edge
{
YGNodeStyleSetPadding([self ygNode], edge, padding);
}
- (void)yg_setWidth:(CGFloat)width
{
YGNodeStyleSetWidth([self ygNode], width);
}
- (void)yg_setHeight:(CGFloat)height
{
YGNodeStyleSetHeight([self ygNode], height);
}
- (void)yg_setMinWidth:(CGFloat)minWidth
{
YGNodeStyleSetMinWidth([self ygNode], minWidth);
}
- (void)yg_setMinHeight:(CGFloat)minHeight
{
YGNodeStyleSetMinHeight([self ygNode], minHeight);
}
- (void)yg_setMaxWidth:(CGFloat)maxWidth
{
YGNodeStyleSetMaxWidth([self ygNode], maxWidth);
}
- (void)yg_setMaxHeight:(CGFloat)maxHeight
{
YGNodeStyleSetMaxHeight([self ygNode], maxHeight);
}
- (void)yg_setAspectRatio:(CGFloat)aspectRatio
{
YGNodeStyleSetAspectRatio([self ygNode], aspectRatio);
}
#pragma mark - Layout and Sizing
- (YGDirection)yg_resolvedDirection
{
return YGNodeLayoutGetDirection([self ygNode]);
}
- (void)yg_applyLayout
{
[self calculateLayoutWithSize:self.bounds.size];
YGApplyLayoutToViewHierarchy(self);
}
- (CGSize)yg_intrinsicSize
{
const CGSize constrainedSize = {
.width = YGUndefined,
.height = YGUndefined,
};
return [self calculateLayoutWithSize:constrainedSize];
}
#pragma mark - Private
- (YGNodeRef)ygNode
{
YGNodeBridge *node = objc_getAssociatedObject(self, kYGNodeBridgeAssociatedKey);
if (!node) {
node = [YGNodeBridge new];
YGNodeSetContext(node.cnode, (__bridge void *) self);
objc_setAssociatedObject(self, kYGNodeBridgeAssociatedKey, node, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return node.cnode;
}
- (CGSize)calculateLayoutWithSize:(CGSize)size
{
NSAssert([NSThread isMainThread], @"YG Layout calculation must be done on main.");
NSAssert([self yg_usesYoga], @"YG Layout is not enabled for this view.");
YGAttachNodesFromViewHierachy(self);
const YGNodeRef node = [self ygNode];
YGNodeCalculateLayout(
node,
size.width,
size.height,
YGNodeStyleGetDirection(node));
return (CGSize) {
.width = YGNodeLayoutGetWidth(node),
.height = YGNodeLayoutGetHeight(node),
};
}
static YGSize YGMeasureView(
YGNodeRef node,
float width,
YGMeasureMode widthMode,
float height,
YGMeasureMode heightMode)
{
const CGFloat constrainedWidth = (widthMode == YGMeasureModeUndefined) ? CGFLOAT_MAX : width;
const CGFloat constrainedHeight = (heightMode == YGMeasureModeUndefined) ? CGFLOAT_MAX: height;
UIView *view = (__bridge UIView*) YGNodeGetContext(node);
const CGSize sizeThatFits = [view sizeThatFits:(CGSize) {
.width = constrainedWidth,
.height = constrainedHeight,
}];
return (YGSize) {
.width = YGSanitizeMeasurement(constrainedWidth, sizeThatFits.width, widthMode),
.height = YGSanitizeMeasurement(constrainedHeight, sizeThatFits.height, heightMode),
};
}
static CGFloat YGSanitizeMeasurement(
CGFloat constrainedSize,
CGFloat measuredSize,
YGMeasureMode measureMode)
{
CGFloat result;
if (measureMode == YGMeasureModeExactly) {
result = constrainedSize;
} else if (measureMode == YGMeasureModeAtMost) {
result = MIN(constrainedSize, measuredSize);
} else {
result = measuredSize;
}
return result;
}
static BOOL YGNodeHasExactSameChildren(const YGNodeRef node, NSArray<UIView *> *subviews)
{
if (YGNodeGetChildCount(node) != subviews.count) {
return NO;
}
for (int i=0; i<subviews.count; i++) {
if (YGNodeGetChild(node, i) != subviews[i].ygNode) {
return NO;
}
}
return YES;
}
static void YGAttachNodesFromViewHierachy(UIView *const view)
{
const YGNodeRef node = [view ygNode];
// Only leaf nodes should have a measure function
if (view.yg_isLeaf) {
YGRemoveAllChildren(node);
YGNodeSetMeasureFunc(node, YGMeasureView);
} else {
YGNodeSetMeasureFunc(node, NULL);
NSMutableArray<UIView *> *subviewsToInclude = [[NSMutableArray alloc] initWithCapacity:view.subviews.count];
for (UIView *subview in view.subviews) {
if ([subview yg_includeInLayout]) {
[subviewsToInclude addObject:subview];
}
}
if (!YGNodeHasExactSameChildren(node, subviewsToInclude)) {
YGRemoveAllChildren(node);
for (int i=0; i<subviewsToInclude.count; i++) {
YGNodeInsertChild(node, [subviewsToInclude[i] ygNode], i);
}
}
for (UIView *const subview in subviewsToInclude) {
YGAttachNodesFromViewHierachy(subview);
}
}
}
static void YGRemoveAllChildren(const YGNodeRef node)
{
if (node == NULL) {
return;
}
while (YGNodeGetChildCount(node) > 0) {
YGNodeRemoveChild(node, YGNodeGetChild(node, YGNodeGetChildCount(node) - 1));
}
}
static CGFloat YGRoundPixelValue(CGFloat value)
{
static CGFloat scale;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^(){
scale = [UIScreen mainScreen].scale;
});
return round(value * scale) / scale;
}
static void YGApplyLayoutToViewHierarchy(UIView *view)
{
NSCAssert([NSThread isMainThread], @"Framesetting should only be done on the main thread.");
if (![view yg_includeInLayout]) {
return;
}
YGNodeRef node = [view ygNode];
const CGPoint topLeft = {
YGNodeLayoutGetLeft(node),
YGNodeLayoutGetTop(node),
};
const CGPoint bottomRight = {
topLeft.x + YGNodeLayoutGetWidth(node),
topLeft.y + YGNodeLayoutGetHeight(node),
};
view.frame = (CGRect) {
.origin = {
.x = YGRoundPixelValue(topLeft.x),
.y = YGRoundPixelValue(topLeft.y),
},
.size = {
.width = YGRoundPixelValue(bottomRight.x) - YGRoundPixelValue(topLeft.x),
.height = YGRoundPixelValue(bottomRight.y) - YGRoundPixelValue(topLeft.y),
},
};
if (!view.yg_isLeaf) {
for (NSUInteger i=0; i<view.subviews.count; i++) {
YGApplyLayoutToViewHierarchy(view.subviews[i]);
}
}
}
@end @end

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "YGLayout.h"
@interface YGLayout (Private)
- (instancetype)initWithView:(UIView *)view;
@end

114
YogaKit/YGLayout.h Normal file
View File

@@ -0,0 +1,114 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
#import <yoga/YGEnums.h>
@interface YGLayout : NSObject
/**
The property that decides if we should include this view when calculating layout. Defaults to YES.
*/
@property (nonatomic, readwrite, assign, setter=setIncludedInLayout:) BOOL isIncludedInLayout;
/**
The property that decides during layout/sizing whether or not styling properties should be applied. Defaults to NO.
*/
@property (nonatomic, readwrite, assign, setter=setEnabled:) BOOL isEnabled;
@property (nonatomic, readwrite, assign) YGDirection direction;
@property (nonatomic, readwrite, assign) YGFlexDirection flexDirection;
@property (nonatomic, readwrite, assign) YGJustify justifyContent;
@property (nonatomic, readwrite, assign) YGAlign alignContent;
@property (nonatomic, readwrite, assign) YGAlign alignItems;
@property (nonatomic, readwrite, assign) YGAlign alignSelf;
@property (nonatomic, readwrite, assign) YGPositionType position;
@property (nonatomic, readwrite, assign) YGWrap flexWrap;
@property (nonatomic, readwrite, assign) YGOverflow overflow;
@property (nonatomic, readwrite, assign) CGFloat flexGrow;
@property (nonatomic, readwrite, assign) CGFloat flexShrink;
@property (nonatomic, readwrite, assign) CGFloat flexBasis;
@property (nonatomic, readwrite, assign) CGFloat left;
@property (nonatomic, readwrite, assign) CGFloat top;
@property (nonatomic, readwrite, assign) CGFloat right;
@property (nonatomic, readwrite, assign) CGFloat bottom;
@property (nonatomic, readwrite, assign) CGFloat start;
@property (nonatomic, readwrite, assign) CGFloat end;
@property (nonatomic, readwrite, assign) CGFloat marginLeft;
@property (nonatomic, readwrite, assign) CGFloat marginTop;
@property (nonatomic, readwrite, assign) CGFloat marginRight;
@property (nonatomic, readwrite, assign) CGFloat marginBottom;
@property (nonatomic, readwrite, assign) CGFloat marginStart;
@property (nonatomic, readwrite, assign) CGFloat marginEnd;
@property (nonatomic, readwrite, assign) CGFloat marginHorizontal;
@property (nonatomic, readwrite, assign) CGFloat marginVertical;
@property (nonatomic, readwrite, assign) CGFloat margin;
@property (nonatomic, readwrite, assign) CGFloat paddingLeft;
@property (nonatomic, readwrite, assign) CGFloat paddingTop;
@property (nonatomic, readwrite, assign) CGFloat paddingRight;
@property (nonatomic, readwrite, assign) CGFloat paddingBottom;
@property (nonatomic, readwrite, assign) CGFloat paddingStart;
@property (nonatomic, readwrite, assign) CGFloat paddingEnd;
@property (nonatomic, readwrite, assign) CGFloat paddingHorizontal;
@property (nonatomic, readwrite, assign) CGFloat paddingVertical;
@property (nonatomic, readwrite, assign) CGFloat padding;
@property (nonatomic, readwrite, assign) CGFloat borderLeftWidth;
@property (nonatomic, readwrite, assign) CGFloat borderTopWidth;
@property (nonatomic, readwrite, assign) CGFloat borderRightWidth;
@property (nonatomic, readwrite, assign) CGFloat borderBottomWidth;
@property (nonatomic, readwrite, assign) CGFloat borderStartWidth;
@property (nonatomic, readwrite, assign) CGFloat borderEndWidth;
@property (nonatomic, readwrite, assign) CGFloat borderWidth;
@property (nonatomic, readwrite, assign) CGFloat width;
@property (nonatomic, readwrite, assign) CGFloat height;
@property (nonatomic, readwrite, assign) CGFloat minWidth;
@property (nonatomic, readwrite, assign) CGFloat minHeight;
@property (nonatomic, readwrite, assign) CGFloat maxWidth;
@property (nonatomic, readwrite, assign) CGFloat maxHeight;
// Yoga specific properties, not compatible with flexbox specification
@property (nonatomic, readwrite, assign) CGFloat aspectRatio;
/**
Get the resolved direction of this node. This won't be YGDirectionInherit
*/
@property (nonatomic, readonly, assign) YGDirection resolvedDirection;
/**
Perform a layout calculation and update the frames of the views in the hierarchy with the results
*/
- (void)applyLayout NS_SWIFT_NAME(applyLayout());
/**
Returns the size of the view if no constraints were given. This could equivalent to calling [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)];
*/
@property (nonatomic, readonly, assign) CGSize intrinsicSize;
/**
Returns the number of children that are using Flexbox.
*/
@property (nonatomic, readonly, assign) NSUInteger numberOfChildren;
/**
Return a BOOL indiciating whether or not we this node contains any subviews that are included in Yoga's layout.
*/
@property (nonatomic, readonly, assign) BOOL isLeaf;
/**
Mark that a view's layout needs to be recalculated. Only works for leaf views.
*/
- (void)markDirty;
@end

380
YogaKit/YGLayout.m Normal file
View File

@@ -0,0 +1,380 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "YGLayout+Private.h"
#import "UIView+Yoga.h"
#import <yoga/Yoga.h>
#define YG_STYLE_PROPERTY_IMPL(type, lowercased_name, capitalized_name) \
- (type)lowercased_name \
{ \
return YGNodeStyleGet##capitalized_name(self.node); \
} \
\
- (void)set##capitalized_name:(type)lowercased_name \
{ \
YGNodeStyleSet##capitalized_name(self.node, lowercased_name); \
}
#define YG_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, property, edge) \
- (CGFloat)lowercased_name { \
return YGNodeStyleGet##property(self.node, edge); \
} \
\
- (void)set##capitalized_name:(CGFloat)lowercased_name { \
YGNodeStyleSet##property(self.node, edge, lowercased_name); \
}
#define YG_STYLE_VALUE_PROPERTY_IMPL(lowercased_name, capitalized_name) \
- (CGFloat)lowercased_name \
{ \
YGValue value = YGNodeStyleGet##capitalized_name(self.node); \
if (value.unit == YGUnitPixel) { \
return value.value; \
} else { \
return YGUndefined; \
} \
} \
\
- (void)set##capitalized_name:(CGFloat)lowercased_name \
{ \
YGNodeStyleSet##capitalized_name(self.node, lowercased_name); \
}
#define YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, edge, edge_suffix) \
- (CGFloat)lowercased_name##edge_suffix \
{ \
YGValue value = YGNodeStyleGet##capitalized_name(self.node, edge); \
if (value.unit == YGUnitPixel) { \
return value.value; \
} else { \
return YGUndefined; \
} \
} \
\
- (void)set##capitalized_name##edge_suffix:(CGFloat)lowercased_name \
{ \
YGNodeStyleSet##capitalized_name(self.node, edge, lowercased_name); \
}
#define YG_STYLE_ALL_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name) \
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeLeft, Left) \
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeTop, Top) \
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeRight, Right) \
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeBottom, Bottom) \
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeStart, Start) \
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeEnd, End) \
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeHorizontal, Horizontal) \
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeVertical, Vertical) \
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeAll, )
@interface YGLayout ()
@property (nonatomic, weak, readonly) UIView *view;
@property (nonatomic, assign, readonly) YGNodeRef node;
@end
@implementation YGLayout
@synthesize isEnabled=_isEnabled;
@synthesize isIncludedInLayout=_isIncludedInLayout;
+ (void)initialize
{
YGSetExperimentalFeatureEnabled(YGExperimentalFeatureWebFlexBasis, true);
}
- (instancetype)initWithView:(UIView*)view
{
if (self = [super init]) {
_view = view;
_node = YGNodeNew();
YGNodeSetContext(_node, (__bridge void *) view);
_isEnabled = NO;
_isIncludedInLayout = YES;
}
return self;
}
- (void)dealloc
{
YGNodeFree(self.node);
}
- (void)markDirty
{
if (self.isLeaf) {
YGNodeMarkDirty(self.node);
}
}
- (NSUInteger)numberOfChildren
{
return YGNodeGetChildCount(self.node);
}
- (BOOL)isLeaf
{
NSAssert([NSThread isMainThread], @"This method must be called on the main thread.");
if (self.isEnabled) {
for (UIView *subview in self.view.subviews) {
YGLayout *const yoga = subview.yoga;
if (yoga.isEnabled && yoga.isIncludedInLayout) {
return NO;
}
}
}
return YES;
}
#pragma mark - Style
- (YGPositionType)position
{
return YGNodeStyleGetPositionType(self.node);
}
- (void)setPosition:(YGPositionType)position
{
YGNodeStyleSetPositionType(self.node, position);
}
YG_STYLE_PROPERTY_IMPL(YGDirection, direction, Direction)
YG_STYLE_PROPERTY_IMPL(YGFlexDirection, flexDirection, FlexDirection)
YG_STYLE_PROPERTY_IMPL(YGJustify, justifyContent, JustifyContent)
YG_STYLE_PROPERTY_IMPL(YGAlign, alignContent, AlignContent)
YG_STYLE_PROPERTY_IMPL(YGAlign, alignItems, AlignItems)
YG_STYLE_PROPERTY_IMPL(YGAlign, alignSelf, AlignSelf)
YG_STYLE_PROPERTY_IMPL(YGWrap, flexWrap, FlexWrap)
YG_STYLE_PROPERTY_IMPL(YGOverflow, overflow, Overflow)
YG_STYLE_PROPERTY_IMPL(CGFloat, flexGrow, FlexGrow)
YG_STYLE_PROPERTY_IMPL(CGFloat, flexShrink, FlexShrink)
YG_STYLE_VALUE_PROPERTY_IMPL(flexBasis, FlexBasis)
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeLeft, Left)
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeTop, Top)
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeRight, Right)
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeBottom, Bottom)
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeStart, Start)
YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeEnd, End)
YG_STYLE_ALL_EDGE_PROPERTY_UNIT_IMPL(margin, Margin)
YG_STYLE_ALL_EDGE_PROPERTY_UNIT_IMPL(padding, Padding)
YG_STYLE_EDGE_PROPERTY_IMPL(borderLeftWidth, BorderLeftWidth, Border, YGEdgeLeft)
YG_STYLE_EDGE_PROPERTY_IMPL(borderTopWidth, BorderTopWidth, Border, YGEdgeTop)
YG_STYLE_EDGE_PROPERTY_IMPL(borderRightWidth, BorderRightWidth, Border, YGEdgeRight)
YG_STYLE_EDGE_PROPERTY_IMPL(borderBottomWidth, BorderBottomWidth, Border, YGEdgeBottom)
YG_STYLE_EDGE_PROPERTY_IMPL(borderStartWidth, BorderStartWidth, Border, YGEdgeStart)
YG_STYLE_EDGE_PROPERTY_IMPL(borderEndWidth, BorderEndWidth, Border, YGEdgeEnd)
YG_STYLE_EDGE_PROPERTY_IMPL(borderWidth, BorderWidth, Border, YGEdgeAll)
YG_STYLE_VALUE_PROPERTY_IMPL(width, Width)
YG_STYLE_VALUE_PROPERTY_IMPL(height, Height)
YG_STYLE_VALUE_PROPERTY_IMPL(minWidth, MinWidth)
YG_STYLE_VALUE_PROPERTY_IMPL(minHeight, MinHeight)
YG_STYLE_VALUE_PROPERTY_IMPL(maxWidth, MaxWidth)
YG_STYLE_VALUE_PROPERTY_IMPL(maxHeight, MaxHeight)
YG_STYLE_PROPERTY_IMPL(CGFloat, aspectRatio, AspectRatio)
#pragma mark - Layout and Sizing
- (YGDirection)resolvedDirection
{
return YGNodeLayoutGetDirection(self.node);
}
- (void)applyLayout
{
[self calculateLayoutWithSize:self.view.bounds.size];
YGApplyLayoutToViewHierarchy(self.view);
}
- (CGSize)intrinsicSize
{
const CGSize constrainedSize = {
.width = YGUndefined,
.height = YGUndefined,
};
return [self calculateLayoutWithSize:constrainedSize];
}
#pragma mark - Private
- (CGSize)calculateLayoutWithSize:(CGSize)size
{
NSAssert([NSThread isMainThread], @"Yoga calculation must be done on main.");
NSAssert(self.isEnabled, @"Yoga is not enabled for this view.");
YGAttachNodesFromViewHierachy(self.view);
const YGNodeRef node = self.node;
YGNodeCalculateLayout(
node,
size.width,
size.height,
YGNodeStyleGetDirection(node));
return (CGSize) {
.width = YGNodeLayoutGetWidth(node),
.height = YGNodeLayoutGetHeight(node),
};
}
static YGSize YGMeasureView(
YGNodeRef node,
float width,
YGMeasureMode widthMode,
float height,
YGMeasureMode heightMode)
{
const CGFloat constrainedWidth = (widthMode == YGMeasureModeUndefined) ? CGFLOAT_MAX : width;
const CGFloat constrainedHeight = (heightMode == YGMeasureModeUndefined) ? CGFLOAT_MAX: height;
UIView *view = (__bridge UIView*) YGNodeGetContext(node);
const CGSize sizeThatFits = [view sizeThatFits:(CGSize) {
.width = constrainedWidth,
.height = constrainedHeight,
}];
return (YGSize) {
.width = YGSanitizeMeasurement(constrainedWidth, sizeThatFits.width, widthMode),
.height = YGSanitizeMeasurement(constrainedHeight, sizeThatFits.height, heightMode),
};
}
static CGFloat YGSanitizeMeasurement(
CGFloat constrainedSize,
CGFloat measuredSize,
YGMeasureMode measureMode)
{
CGFloat result;
if (measureMode == YGMeasureModeExactly) {
result = constrainedSize;
} else if (measureMode == YGMeasureModeAtMost) {
result = MIN(constrainedSize, measuredSize);
} else {
result = measuredSize;
}
return result;
}
static BOOL YGNodeHasExactSameChildren(const YGNodeRef node, NSArray<UIView *> *subviews)
{
if (YGNodeGetChildCount(node) != subviews.count) {
return NO;
}
for (int i=0; i<subviews.count; i++) {
if (YGNodeGetChild(node, i) != subviews[i].yoga.node) {
return NO;
}
}
return YES;
}
static void YGAttachNodesFromViewHierachy(UIView *const view)
{
YGLayout *const yoga = view.yoga;
const YGNodeRef node = yoga.node;
// Only leaf nodes should have a measure function
if (yoga.isLeaf) {
YGRemoveAllChildren(node);
YGNodeSetMeasureFunc(node, YGMeasureView);
} else {
YGNodeSetMeasureFunc(node, NULL);
NSMutableArray<UIView *> *subviewsToInclude = [[NSMutableArray alloc] initWithCapacity:view.subviews.count];
for (UIView *subview in view.subviews) {
if (subview.yoga.isIncludedInLayout) {
[subviewsToInclude addObject:subview];
}
}
if (!YGNodeHasExactSameChildren(node, subviewsToInclude)) {
YGRemoveAllChildren(node);
for (int i=0; i<subviewsToInclude.count; i++) {
YGNodeInsertChild(node, subviewsToInclude[i].yoga.node, i);
}
}
for (UIView *const subview in subviewsToInclude) {
YGAttachNodesFromViewHierachy(subview);
}
}
}
static void YGRemoveAllChildren(const YGNodeRef node)
{
if (node == NULL) {
return;
}
while (YGNodeGetChildCount(node) > 0) {
YGNodeRemoveChild(node, YGNodeGetChild(node, YGNodeGetChildCount(node) - 1));
}
}
static CGFloat YGRoundPixelValue(CGFloat value)
{
static CGFloat scale;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^(){
scale = [UIScreen mainScreen].scale;
});
return round(value * scale) / scale;
}
static void YGApplyLayoutToViewHierarchy(UIView *view)
{
NSCAssert([NSThread isMainThread], @"Framesetting should only be done on the main thread.");
const YGLayout *yoga = view.yoga;
if (!yoga.isIncludedInLayout) {
return;
}
YGNodeRef node = yoga.node;
const CGPoint topLeft = {
YGNodeLayoutGetLeft(node),
YGNodeLayoutGetTop(node),
};
const CGPoint bottomRight = {
topLeft.x + YGNodeLayoutGetWidth(node),
topLeft.y + YGNodeLayoutGetHeight(node),
};
view.frame = (CGRect) {
.origin = {
.x = YGRoundPixelValue(topLeft.x),
.y = YGRoundPixelValue(topLeft.y),
},
.size = {
.width = YGRoundPixelValue(bottomRight.x) - YGRoundPixelValue(topLeft.x),
.height = YGRoundPixelValue(bottomRight.y) - YGRoundPixelValue(topLeft.y),
},
};
if (!yoga.isLeaf) {
for (NSUInteger i=0; i<view.subviews.count; i++) {
YGApplyLayoutToViewHierarchy(view.subviews[i]);
}
}
}
@end

View File

@@ -20,6 +20,9 @@
13687D821DF87CF200E7C260 /* Yoga.c in Sources */ = {isa = PBXBuildFile; fileRef = 13687D621DF8778F00E7C260 /* Yoga.c */; }; 13687D821DF87CF200E7C260 /* Yoga.c in Sources */ = {isa = PBXBuildFile; fileRef = 13687D621DF8778F00E7C260 /* Yoga.c */; };
13687D851DF87D1E00E7C260 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13687D841DF87D1E00E7C260 /* UIKit.framework */; }; 13687D851DF87D1E00E7C260 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13687D841DF87D1E00E7C260 /* UIKit.framework */; };
13687D871DF87D2400E7C260 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13687D861DF87D2400E7C260 /* Foundation.framework */; }; 13687D871DF87D2400E7C260 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13687D861DF87D2400E7C260 /* Foundation.framework */; };
638A94431E1EF5D000A726AD /* YGLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 638A94411E1EF5D000A726AD /* YGLayout.m */; };
638A94451E1EF8A900A726AD /* YGValue.h in yoga */ = {isa = PBXBuildFile; fileRef = 638A94441E1EF89C00A726AD /* YGValue.h */; };
638A94481E1F06D100A726AD /* SwiftViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 638A94471E1F06D100A726AD /* SwiftViewController.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
@@ -29,6 +32,7 @@
dstPath = include/yoga; dstPath = include/yoga;
dstSubfolderSpec = 16; dstSubfolderSpec = 16;
files = ( files = (
638A94451E1EF8A900A726AD /* YGValue.h in yoga */,
13687D781DF878C600E7C260 /* YGEnums.h in yoga */, 13687D781DF878C600E7C260 /* YGEnums.h in yoga */,
13687D791DF878C600E7C260 /* YGMacros.h in yoga */, 13687D791DF878C600E7C260 /* YGMacros.h in yoga */,
13687D7A1DF878C600E7C260 /* Yoga.h in yoga */, 13687D7A1DF878C600E7C260 /* Yoga.h in yoga */,
@@ -68,6 +72,12 @@
13687D6A1DF8778F00E7C260 /* UIView+Yoga.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+Yoga.m"; sourceTree = "<group>"; }; 13687D6A1DF8778F00E7C260 /* UIView+Yoga.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+Yoga.m"; sourceTree = "<group>"; };
13687D841DF87D1E00E7C260 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 13687D841DF87D1E00E7C260 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
13687D861DF87D2400E7C260 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 13687D861DF87D2400E7C260 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
638A94401E1EF5D000A726AD /* YGLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGLayout.h; sourceTree = "<group>"; };
638A94411E1EF5D000A726AD /* YGLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YGLayout.m; sourceTree = "<group>"; };
638A94421E1EF5D000A726AD /* YGLayout+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "YGLayout+Private.h"; sourceTree = "<group>"; };
638A94441E1EF89C00A726AD /* YGValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGValue.h; sourceTree = "<group>"; };
638A94461E1F06D100A726AD /* YogaKitSample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "YogaKitSample-Bridging-Header.h"; sourceTree = "<group>"; };
638A94471E1F06D100A726AD /* SwiftViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -109,6 +119,8 @@
13687D4A1DF8748400E7C260 /* AppDelegate.m */, 13687D4A1DF8748400E7C260 /* AppDelegate.m */,
13687D4C1DF8748400E7C260 /* ViewController.h */, 13687D4C1DF8748400E7C260 /* ViewController.h */,
13687D4D1DF8748400E7C260 /* ViewController.m */, 13687D4D1DF8748400E7C260 /* ViewController.m */,
638A94471E1F06D100A726AD /* SwiftViewController.swift */,
638A94461E1F06D100A726AD /* YogaKitSample-Bridging-Header.h */,
13687D521DF8748400E7C260 /* Assets.xcassets */, 13687D521DF8748400E7C260 /* Assets.xcassets */,
13687D571DF8748400E7C260 /* Info.plist */, 13687D571DF8748400E7C260 /* Info.plist */,
13687D461DF8748400E7C260 /* Supporting Files */, 13687D461DF8748400E7C260 /* Supporting Files */,
@@ -127,6 +139,7 @@
13687D5D1DF8778F00E7C260 /* yoga */ = { 13687D5D1DF8778F00E7C260 /* yoga */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
638A94441E1EF89C00A726AD /* YGValue.h */,
13687D5E1DF8778F00E7C260 /* YGEnums.h */, 13687D5E1DF8778F00E7C260 /* YGEnums.h */,
13687D5F1DF8778F00E7C260 /* YGMacros.h */, 13687D5F1DF8778F00E7C260 /* YGMacros.h */,
13687D601DF8778F00E7C260 /* YGNodeList.c */, 13687D601DF8778F00E7C260 /* YGNodeList.c */,
@@ -143,6 +156,9 @@
children = ( children = (
13687D691DF8778F00E7C260 /* UIView+Yoga.h */, 13687D691DF8778F00E7C260 /* UIView+Yoga.h */,
13687D6A1DF8778F00E7C260 /* UIView+Yoga.m */, 13687D6A1DF8778F00E7C260 /* UIView+Yoga.m */,
638A94401E1EF5D000A726AD /* YGLayout.h */,
638A94421E1EF5D000A726AD /* YGLayout+Private.h */,
638A94411E1EF5D000A726AD /* YGLayout.m */,
); );
name = YogaKit; name = YogaKit;
path = ..; path = ..;
@@ -185,11 +201,12 @@
13687D3B1DF8748300E7C260 /* Project object */ = { 13687D3B1DF8748300E7C260 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0810; LastUpgradeCheck = 0820;
ORGANIZATIONNAME = facebook; ORGANIZATIONNAME = facebook;
TargetAttributes = { TargetAttributes = {
13687D421DF8748300E7C260 = { 13687D421DF8748300E7C260 = {
CreatedOnToolsVersion = 8.1; CreatedOnToolsVersion = 8.1;
LastSwiftMigration = 0820;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
}; };
}; };
@@ -228,12 +245,14 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
638A94481E1F06D100A726AD /* SwiftViewController.swift in Sources */,
13687D801DF87CEC00E7C260 /* UIView+Yoga.m in Sources */, 13687D801DF87CEC00E7C260 /* UIView+Yoga.m in Sources */,
13687D4E1DF8748400E7C260 /* ViewController.m in Sources */, 13687D4E1DF8748400E7C260 /* ViewController.m in Sources */,
13687D4B1DF8748400E7C260 /* AppDelegate.m in Sources */, 13687D4B1DF8748400E7C260 /* AppDelegate.m in Sources */,
13687D821DF87CF200E7C260 /* Yoga.c in Sources */, 13687D821DF87CF200E7C260 /* Yoga.c in Sources */,
13687D811DF87CF200E7C260 /* YGNodeList.c in Sources */, 13687D811DF87CF200E7C260 /* YGNodeList.c in Sources */,
13687D481DF8748400E7C260 /* main.m in Sources */, 13687D481DF8748400E7C260 /* main.m in Sources */,
638A94431E1EF5D000A726AD /* YGLayout.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -258,6 +277,7 @@
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -305,6 +325,7 @@
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -332,10 +353,14 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
INFOPLIST_FILE = YogaKitSample/Info.plist; INFOPLIST_FILE = YogaKitSample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.YogaKitSample; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.YogaKitSample;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "YogaKitSample/YogaKitSample-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
}; };
name = Debug; name = Debug;
}; };
@@ -343,10 +368,13 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
INFOPLIST_FILE = YogaKitSample/Info.plist; INFOPLIST_FILE = YogaKitSample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.YogaKitSample; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.YogaKitSample;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "YogaKitSample/YogaKitSample-Bridging-Header.h";
SWIFT_VERSION = 3.0;
}; };
name = Release; name = Release;
}; };
@@ -369,6 +397,7 @@
13687D5C1DF8748400E7C260 /* Release */, 13687D5C1DF8748400E7C260 /* Release */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };

View File

@@ -0,0 +1,40 @@
/**
* Copyright 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE-examples file in the root directory of this source tree.
*/
import UIKit
class SwiftViewController: UIViewController {
override func viewDidLoad() {
let root = self.view!
root.backgroundColor = .red
root.yoga.isEnabled = true
root.yoga.width = self.view.bounds.size.width
root.yoga.height = self.view.bounds.size.height
root.yoga.alignItems = .center
root.yoga.justifyContent = .center
let child1 = UIView()
child1.backgroundColor = .blue
child1.yoga.isEnabled = true
child1.yoga.width = 100
child1.yoga.height = 10
let child2 = UIView()
child2.backgroundColor = .green
child2.frame = CGRect(x: 0, y: 0, width: 200, height: 100)
let child3 = UIView()
child3.backgroundColor = .yellow
child3.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
child2.addSubview(child3)
root.addSubview(child1)
root.addSubview(child2)
root.yoga.applyLayout()
}
}

View File

@@ -20,17 +20,17 @@
{ {
UIView *root = self.view; UIView *root = self.view;
root.backgroundColor = [UIColor redColor]; root.backgroundColor = [UIColor redColor];
[root yg_setUsesYoga:YES]; root.yoga.isEnabled = YES;
[root yg_setWidth:self.view.bounds.size.width]; root.yoga.width = self.view.bounds.size.width;
[root yg_setHeight:self.view.bounds.size.height]; root.yoga.height = self.view.bounds.size.height;
[root yg_setAlignItems:YGAlignCenter]; root.yoga.alignItems = YGAlignCenter;
[root yg_setJustifyContent:YGJustifyCenter]; root.yoga.justifyContent = YGJustifyCenter;
UIView *child1 = [UIView new]; UIView *child1 = [UIView new];
child1.backgroundColor = [UIColor blueColor]; child1.backgroundColor = [UIColor blueColor];
[child1 yg_setUsesYoga:YES]; child1.yoga.isEnabled = YES;
[child1 yg_setWidth:100]; child1.yoga.width = 100;
[child1 yg_setHeight:100]; child1.yoga.height = 100;
UIView *child2 = [UIView new]; UIView *child2 = [UIView new];
child2.backgroundColor = [UIColor greenColor]; child2.backgroundColor = [UIColor greenColor];
@@ -53,7 +53,7 @@
[child2 addSubview:child3]; [child2 addSubview:child3];
[root addSubview:child1]; [root addSubview:child1];
[root addSubview:child2]; [root addSubview:child2];
[root yg_applyLayout]; [root.yoga applyLayout];
} }

View File

@@ -0,0 +1,10 @@
/**
* Copyright 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE-examples file in the root directory of this source tree.
*/
#import "UIView+Yoga.h"
#import <yoga/Yoga.h>

View File

@@ -119,21 +119,21 @@ def to_java_upper(symbol):
root = os.path.dirname(os.path.abspath(__file__)) root = os.path.dirname(os.path.abspath(__file__))
# write out C headers # write out C & Objective-C headers
with open(root + '/yoga/YGEnums.h', 'w') as f: with open(root + '/yoga/YGEnums.h', 'w') as f:
f.write(LICENSE) f.write(LICENSE)
f.write('#pragma once\n\n') f.write('#pragma once\n\n')
f.write('#include "YGMacros.h"\n\n') f.write('#include "YGMacros.h"\n\n')
f.write('YG_EXTERN_C_BEGIN\n\n') f.write('YG_EXTERN_C_BEGIN\n\n')
for name, values in ENUMS.items(): for name, values in ENUMS.items():
f.write('#define YG%sCount %s\n' % (name, len(values))) f.write('#define YG%sCount %s\n' % (name, len(values)))
f.write('typedef enum YG%s {\n' % name) f.write('typedef YG_ENUM_BEGIN(YG%s) {\n' % name)
for value in values: for value in values:
if isinstance(value, tuple): if isinstance(value, tuple):
f.write(' YG%s%s = %d,\n' % (name, value[0], value[1])) f.write(' YG%s%s = %d,\n' % (name, value[0], value[1]))
else: else:
f.write(' YG%s%s,\n' % (name, value)) f.write(' YG%s%s,\n' % (name, value))
f.write('} YG%s;\n' % name) f.write('} YG_ENUM_END(YG%s);\n' % name)
f.write('\n') f.write('\n')
f.write('YG_EXTERN_C_END\n') f.write('YG_EXTERN_C_END\n')

View File

@@ -14,29 +14,29 @@
YG_EXTERN_C_BEGIN YG_EXTERN_C_BEGIN
#define YGFlexDirectionCount 4 #define YGFlexDirectionCount 4
typedef enum YGFlexDirection { typedef YG_ENUM_BEGIN(YGFlexDirection) {
YGFlexDirectionColumn, YGFlexDirectionColumn,
YGFlexDirectionColumnReverse, YGFlexDirectionColumnReverse,
YGFlexDirectionRow, YGFlexDirectionRow,
YGFlexDirectionRowReverse, YGFlexDirectionRowReverse,
} YGFlexDirection; } YG_ENUM_END(YGFlexDirection);
#define YGMeasureModeCount 3 #define YGMeasureModeCount 3
typedef enum YGMeasureMode { typedef YG_ENUM_BEGIN(YGMeasureMode) {
YGMeasureModeUndefined, YGMeasureModeUndefined,
YGMeasureModeExactly, YGMeasureModeExactly,
YGMeasureModeAtMost, YGMeasureModeAtMost,
} YGMeasureMode; } YG_ENUM_END(YGMeasureMode);
#define YGPrintOptionsCount 3 #define YGPrintOptionsCount 3
typedef enum YGPrintOptions { typedef YG_ENUM_BEGIN(YGPrintOptions) {
YGPrintOptionsLayout = 1, YGPrintOptionsLayout = 1,
YGPrintOptionsStyle = 2, YGPrintOptionsStyle = 2,
YGPrintOptionsChildren = 4, YGPrintOptionsChildren = 4,
} YGPrintOptions; } YG_ENUM_END(YGPrintOptions);
#define YGEdgeCount 9 #define YGEdgeCount 9
typedef enum YGEdge { typedef YG_ENUM_BEGIN(YGEdge) {
YGEdgeLeft, YGEdgeLeft,
YGEdgeTop, YGEdgeTop,
YGEdgeRight, YGEdgeRight,
@@ -46,79 +46,79 @@ typedef enum YGEdge {
YGEdgeHorizontal, YGEdgeHorizontal,
YGEdgeVertical, YGEdgeVertical,
YGEdgeAll, YGEdgeAll,
} YGEdge; } YG_ENUM_END(YGEdge);
#define YGPositionTypeCount 2 #define YGPositionTypeCount 2
typedef enum YGPositionType { typedef YG_ENUM_BEGIN(YGPositionType) {
YGPositionTypeRelative, YGPositionTypeRelative,
YGPositionTypeAbsolute, YGPositionTypeAbsolute,
} YGPositionType; } YG_ENUM_END(YGPositionType);
#define YGDimensionCount 2 #define YGDimensionCount 2
typedef enum YGDimension { typedef YG_ENUM_BEGIN(YGDimension) {
YGDimensionWidth, YGDimensionWidth,
YGDimensionHeight, YGDimensionHeight,
} YGDimension; } YG_ENUM_END(YGDimension);
#define YGJustifyCount 5 #define YGJustifyCount 5
typedef enum YGJustify { typedef YG_ENUM_BEGIN(YGJustify) {
YGJustifyFlexStart, YGJustifyFlexStart,
YGJustifyCenter, YGJustifyCenter,
YGJustifyFlexEnd, YGJustifyFlexEnd,
YGJustifySpaceBetween, YGJustifySpaceBetween,
YGJustifySpaceAround, YGJustifySpaceAround,
} YGJustify; } YG_ENUM_END(YGJustify);
#define YGDirectionCount 3 #define YGDirectionCount 3
typedef enum YGDirection { typedef YG_ENUM_BEGIN(YGDirection) {
YGDirectionInherit, YGDirectionInherit,
YGDirectionLTR, YGDirectionLTR,
YGDirectionRTL, YGDirectionRTL,
} YGDirection; } YG_ENUM_END(YGDirection);
#define YGLogLevelCount 5 #define YGLogLevelCount 5
typedef enum YGLogLevel { typedef YG_ENUM_BEGIN(YGLogLevel) {
YGLogLevelError, YGLogLevelError,
YGLogLevelWarn, YGLogLevelWarn,
YGLogLevelInfo, YGLogLevelInfo,
YGLogLevelDebug, YGLogLevelDebug,
YGLogLevelVerbose, YGLogLevelVerbose,
} YGLogLevel; } YG_ENUM_END(YGLogLevel);
#define YGWrapCount 2 #define YGWrapCount 2
typedef enum YGWrap { typedef YG_ENUM_BEGIN(YGWrap) {
YGWrapNoWrap, YGWrapNoWrap,
YGWrapWrap, YGWrapWrap,
} YGWrap; } YG_ENUM_END(YGWrap);
#define YGOverflowCount 3 #define YGOverflowCount 3
typedef enum YGOverflow { typedef YG_ENUM_BEGIN(YGOverflow) {
YGOverflowVisible, YGOverflowVisible,
YGOverflowHidden, YGOverflowHidden,
YGOverflowScroll, YGOverflowScroll,
} YGOverflow; } YG_ENUM_END(YGOverflow);
#define YGExperimentalFeatureCount 2 #define YGExperimentalFeatureCount 2
typedef enum YGExperimentalFeature { typedef YG_ENUM_BEGIN(YGExperimentalFeature) {
YGExperimentalFeatureRounding, YGExperimentalFeatureRounding,
YGExperimentalFeatureWebFlexBasis, YGExperimentalFeatureWebFlexBasis,
} YGExperimentalFeature; } YG_ENUM_END(YGExperimentalFeature);
#define YGAlignCount 6 #define YGAlignCount 6
typedef enum YGAlign { typedef YG_ENUM_BEGIN(YGAlign) {
YGAlignAuto, YGAlignAuto,
YGAlignFlexStart, YGAlignFlexStart,
YGAlignCenter, YGAlignCenter,
YGAlignFlexEnd, YGAlignFlexEnd,
YGAlignStretch, YGAlignStretch,
YGAlignBaseline, YGAlignBaseline,
} YGAlign; } YG_ENUM_END(YGAlign);
#define YGUnitCount 3 #define YGUnitCount 3
typedef enum YGUnit { typedef YG_ENUM_BEGIN(YGUnit) {
YGUnitUndefined, YGUnitUndefined,
YGUnitPixel, YGUnitPixel,
YGUnitPercent, YGUnitPercent,
} YGUnit; } YG_ENUM_END(YGUnit);
YG_EXTERN_C_END YG_EXTERN_C_END

View File

@@ -40,3 +40,19 @@
YG_ABORT(); \ YG_ABORT(); \
} }
#endif #endif
#ifndef YG_ENUM_BEGIN
#ifndef NS_ENUM
#define YG_ENUM_BEGIN(name) enum name
#else
#define YG_ENUM_BEGIN(name) NS_ENUM(NSInteger, name)
#endif
#endif
#ifndef YG_ENUM_END
#ifndef NS_ENUM
#define YG_ENUM_END(name) name
#else
#define YG_ENUM_END(name)
#endif
#endif