From ae9f89b5d16dd28c225158ba0d7d119be2d0a8f1 Mon Sep 17 00:00:00 2001 From: Dustin Shahidehpour Date: Mon, 13 Feb 2017 09:16:22 -0800 Subject: [PATCH] Add helper function for bulk updates. Summary: Currently, our configuration of Views looks like this: ```objc UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; view.yoga.isEnabled = YES; view.yoga.height = 50; view.yoga.width = 50; ``` Every time that we access `view.yoga` we have to access the associated object on `UIView` to get the `YGLayout`. This adds an extra `objc_msgSend` which increases binary size, and is slight perf impact. This diff creates a way to modify the `YGLayout` with only a single `objc_msgSend`. Here's the new syntax: ```objc UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; [view configureLayoutWithBlock:^void(YGLayout *layout){ layout.isEnabled = YES layout.height = 50; layout.width = 50; }]; ``` Here's the Swift version: ```swift let view = UIView(frame: .zero) view.configureLayout { (layout) in layout.isEnabled = true layout.height = 50 layout.width = 50 } ``` Closes https://github.com/facebook/yoga/pull/393 Reviewed By: emilsjolander Differential Revision: D4550382 Pulled By: dshahidehpour fbshipit-source-id: 76d797d1e0de8e5dc767e02180a7fc440a70212e --- YogaKit/CHANGELOG.md | 17 +++++++++++++++++ YogaKit/Source/UIView+Yoga.h | 16 ++++++++++++++++ YogaKit/Source/UIView+Yoga.m | 6 ++++++ YogaKit/Tests/YogaKitTests.m | 19 +++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/YogaKit/CHANGELOG.md b/YogaKit/CHANGELOG.md index 49c46f2f..58f41d9b 100644 --- a/YogaKit/CHANGELOG.md +++ b/YogaKit/CHANGELOG.md @@ -25,3 +25,20 @@ view.yoga.marginLeft // 0 ### Enhancements - Pixel Rounding now uses `roundf()` instead of `round()`. + +- There is now a method that allows "bulk" updates to YGLayout. +```objc +[view configureLayoutWithBlock:^(YGLayout *layout) { + layout.isEnabled = YES; + layout.width = 50; + layout.height = 50; +}]; +``` + +```swift +view.configureLayout { (layout) in + layout.isEnabled = true + layout.width = 50 + layout.height = 50 +} +``` diff --git a/YogaKit/Source/UIView+Yoga.h b/YogaKit/Source/UIView+Yoga.h index 1af3581b..e75119c0 100644 --- a/YogaKit/Source/UIView+Yoga.h +++ b/YogaKit/Source/UIView+Yoga.h @@ -10,8 +10,24 @@ #import "YGLayout.h" #import +NS_ASSUME_NONNULL_BEGIN + +typedef void (^YGLayoutConfigurationBlock)(YGLayout *); + @interface UIView (Yoga) +/** + The YGLayout that is attached to this view. It is lazily created. + */ @property (nonatomic, readonly, strong) YGLayout *yoga; +/** + In ObjC land, every time you access `view.yoga.*` you are adding another `objc_msgSend` + to your code. If you plan on making multiple changes to YGLayout, it's more performant + to use this method, which uses a single objc_msgSend call. + */ +- (void)configureLayoutWithBlock:(YGLayoutConfigurationBlock)block NS_SWIFT_NAME(configureLayout(block:)); + @end + +NS_ASSUME_NONNULL_END diff --git a/YogaKit/Source/UIView+Yoga.m b/YogaKit/Source/UIView+Yoga.m index 66b7eed0..4448b377 100644 --- a/YogaKit/Source/UIView+Yoga.m +++ b/YogaKit/Source/UIView+Yoga.m @@ -26,5 +26,11 @@ static const void *kYGYogaAssociatedKey = &kYGYogaAssociatedKey; return yoga; } +- (void)configureLayoutWithBlock:(YGLayoutConfigurationBlock)block +{ + if (block != nil) { + block(self.yoga); + } +} @end diff --git a/YogaKit/Tests/YogaKitTests.m b/YogaKit/Tests/YogaKitTests.m index dcb089e8..5a1d56f1 100644 --- a/YogaKit/Tests/YogaKitTests.m +++ b/YogaKit/Tests/YogaKitTests.m @@ -18,6 +18,25 @@ @implementation YogaKitTests +- (void)testConfigureLayoutIsNoOpWithNilBlock +{ + UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; + XCTAssertNoThrow([view configureLayoutWithBlock:nil]); +} + +- (void)testConfigureLayoutBlockWorksWithValidBlock +{ + UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; + [view configureLayoutWithBlock:^(YGLayout *layout){ + XCTAssertNotNil(layout); + layout.isEnabled = YES; + layout.width = 25; + }]; + + XCTAssertTrue(view.yoga.isEnabled); + XCTAssertEqual(view.yoga.width, 25); +} + - (void)testNodesAreDeallocedWithSingleView { __weak YGLayout *layoutRef = nil;