WIP: Improve the Objective-C and Swift bridge #289

Closed
hartbit wants to merge 8 commits from improve-objc-swift into master
39 changed files with 946 additions and 1804 deletions
Showing only changes of commit 5f5e5df328 - Show all commits

View File

@@ -8,68 +8,10 @@
*/
#import <UIKit/UIKit.h>
#import "YKEnums.h"
#import <YogaKit/YKLayout.h>
@interface UIView (YogaKit)
/**
The property that decides if we should include this view when calculating layout. Defaults to YES.
*/
@property (nonatomic, setter=yk_setIncludeInLayout:) BOOL yk_includeInLayout NS_SWIFT_NAME(includeInLayout);
/**
The property that decides during layout/sizing whether or not yk_* properties should be applied. Defaults to NO.
*/
@property (nonatomic, setter=yk_setUsesYoga:) BOOL yk_usesYoga NS_SWIFT_NAME(usesYoga);
@property (nonatomic, setter=yk_setDirection:) YKDirection yk_direction NS_SWIFT_NAME(layoutDirection);
@property (nonatomic, setter=yk_setFlexDirection:) YKFlexDirection yk_flexDirection NS_SWIFT_NAME(layoutFlexDirection);
@property (nonatomic, setter=yk_setJustifyContent:) YKJustify yk_justifyContent NS_SWIFT_NAME(layoutJustifyContent);
@property (nonatomic, setter=yk_setAlignContent:) YKAlign yk_alignContent NS_SWIFT_NAME(layoutAlignContent);
@property (nonatomic, setter=yk_setAlignItems:) YKAlign yk_alignItems NS_SWIFT_NAME(layoutAlignItems);
@property (nonatomic, setter=yk_setAlignSelf:) YKAlign yk_alignSelf NS_SWIFT_NAME(layoutAlignSelf);
@property (nonatomic, setter=yk_setPositionType:) YKPositionType yk_positionType NS_SWIFT_NAME(layoutPositionType);
@property (nonatomic, setter=yk_setFlexWrap:) YKWrap yk_flexWrap NS_SWIFT_NAME(layoutFlexWrap);
@property (nonatomic, setter=yk_setFlexGrow:) CGFloat yk_flexGrow NS_SWIFT_NAME(layoutFlexGrow);
@property (nonatomic, setter=yk_setFlexShrink:) CGFloat yk_flexShrink NS_SWIFT_NAME(layoutFlexShrink);
@property (nonatomic, setter=yk_setFlexBasis:) CGFloat yk_flexBasis NS_SWIFT_NAME(layoutFlexBasis);
- (void)yk_positionForEdge:(YKEdge)edge;
- (void)yk_setPosition:(CGFloat)position forEdge:(YKEdge)edge;
- (void)yk_marginForEdge:(YKEdge)edge;
- (void)yk_setMargin:(CGFloat)margin forEdge:(YKEdge)edge;
- (void)yk_paddingForEdge:(YKEdge)edge;
- (void)yk_setPadding:(CGFloat)padding forEdge:(YKEdge)edge;
@property (nonatomic, setter=yk_setWidth:) CGFloat yk_width NS_SWIFT_NAME(layoutWidth);
@property (nonatomic, setter=yk_setHeight:) CGFloat yk_height NS_SWIFT_NAME(layoutHeight);
@property (nonatomic, setter=yk_setMinWidth:) CGFloat yk_minWidth NS_SWIFT_NAME(layoutMinWidth);
@property (nonatomic, setter=yk_setMinHeight:) CGFloat yk_minHeight NS_SWIFT_NAME(layoutMinHeight);
@property (nonatomic, setter=yk_setMaxWidth:) CGFloat yk_maxWidth NS_SWIFT_NAME(layoutMaxWidth);
@property (nonatomic, setter=yk_setMaxHeight:) CGFloat yk_maxHeight NS_SWIFT_NAME(layoutMaxHeight);
// Yoga specific properties, not compatible with flexbox specification
@property (nonatomic, setter=yk_setAspectRatio:) CGFloat yk_aspectRatio NS_SWIFT_NAME(layoutAspectRatio);
/**
Get the resolved direction of this node. This won't be YGDirectionInherit
*/
@property (nonatomic, readonly) CGFloat yk_resolvedDirection NS_SWIFT_NAME(layoutResolvedDirection);
/**
Perform a layout calculation and update the frames of the views in the hierarchy with the results
*/
- (void)yk_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) CGSize yk_intrinsicSize NS_SWIFT_NAME(layoutIntrinsicSize);
/**
Returns the number of children that are using Flexbox.
*/
@property (nonatomic, readonly) NSUInteger yk_numberOfChildren NS_SWIFT_NAME(layoutNumberOfChildren);
@property (nonatomic, readonly) YKLayout* layout;
@end

View File

@@ -7,478 +7,22 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <YogaKit/UIView+Yoga.h>
#import <YogaKit/yoga.h>
#import <YogaKit/UIView+YogaKit.h>
#import <YogaKit/YKLayout+Private.h>
#import <objc/runtime.h>
@interface YGNodeBridge : NSObject
@property (nonatomic, assign, readonly) YGNodeRef cnode;
@end
@implementation YGNodeBridge
+ (void)initialize
{
YGSetExperimentalFeatureEnabled(YGExperimentalFeatureWebFlexBasis, true);
}
- (instancetype)init
{
if ([super init]) {
_cnode = YGNodeNew();
}
return self;
}
- (void)dealloc
{
YGNodeFree(_cnode);
}
@end
@implementation UIView (YogaKit)
- (BOOL)yk_usesYoga
- (YKLayout *)layout
{
NSNumber *usesYoga = objc_getAssociatedObject(self, @selector(yk_usesYoga));
return [usesYoga boolValue];
}
- (BOOL)yk_includeInLayout
{
NSNumber *includeInLayout = objc_getAssociatedObject(self, @selector(yk_includeInLayout));
return (includeInLayout != nil) ? [includeInLayout boolValue] : YES;
}
- (NSUInteger)yk_numberOfChildren
{
return YGNodeGetChildCount([self ygNode]);
}
#pragma mark - Setters
- (void)yk_setIncludeInLayout:(BOOL)includeInLayout
{
objc_setAssociatedObject(
self,
@selector(yk_includeInLayout),
@(includeInLayout),
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)yk_setUsesYoga:(BOOL)enabled
{
objc_setAssociatedObject(
self,
@selector(yk_usesYoga),
@(enabled),
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (YKDirection)yk_direction
{
return (YKDirection)YGNodeStyleGetDirection([self ygNode]);
}
- (void)yk_setDirection:(YKDirection)direction
{
YGNodeStyleSetDirection([self ygNode], (YGDirection)direction);
}
- (YKFlexDirection)yk_flexDirection
{
return (YKFlexDirection)YGNodeStyleGetFlexDirection([self ygNode]);
}
- (void)yk_setFlexDirection:(YKFlexDirection)flexDirection
{
YGNodeStyleSetFlexDirection([self ygNode], (YGFlexDirection)flexDirection);
}
- (YKJustify)yk_justifyContent
{
return (YKJustify)YGNodeStyleGetJustifyContent([self ygNode]);
}
- (void)yk_setJustifyContent:(YKJustify)justifyContent
{
YGNodeStyleSetJustifyContent([self ygNode], (YGJustify)justifyContent);
}
- (YKAlign)yk_alignContent
{
return (YKAlign)YGNodeStyleGetAlignContent([self ygNode]);
}
- (void)yk_setAlignContent:(YKAlign)alignContent
{
YGNodeStyleSetAlignContent([self ygNode], (YGAlign)alignContent);
}
- (YKAlign)yk_alignItems
{
return (YKAlign)YGNodeStyleGetAlignItems([self ygNode]);
}
- (void)yk_setAlignItems:(YKAlign)alignItems
{
YGNodeStyleSetAlignItems([self ygNode], (YGAlign)alignItems);
}
- (YKAlign)yk_alignSelf
{
return (YKAlign)YGNodeStyleGetAlignSelf([self ygNode]);
}
- (void)yk_setAlignSelf:(YKAlign)alignSelf
{
YGNodeStyleSetAlignSelf([self ygNode], (YGAlign)alignSelf);
}
- (YKPositionType)yk_positionType
{
return (YKPositionType)YGNodeStyleGetPositionType([self ygNode]);
}
- (void)yk_setPositionType:(YKPositionType)positionType
{
YGNodeStyleSetPositionType([self ygNode], (YGPositionType)positionType);
}
- (YKWrap)yk_flexWrap
{
return (YKWrap)YGNodeStyleGetFlexWrap([self ygNode]);
}
- (void)yk_setFlexWrap:(YKWrap)flexWrap
{
YGNodeStyleSetFlexWrap([self ygNode], (YGWrap)flexWrap);
}
- (CGFloat)yk_flexGrow
{
return YGNodeStyleGetFlexGrow([self ygNode]);
}
- (void)yk_setFlexGrow:(CGFloat)flexGrow
{
YGNodeStyleSetFlexGrow([self ygNode], flexGrow);
}
- (CGFloat)yk_flexShrink
{
return YGNodeStyleGetFlexShrink([self ygNode]);
}
- (void)yk_setFlexShrink:(CGFloat)flexShrink
{
YGNodeStyleSetFlexShrink([self ygNode], flexShrink);
}
- (CGFloat)yk_flexBasis
{
return YGNodeStyleGetFlexBasis([self ygNode]);
}
- (void)yk_setFlexBasis:(CGFloat)flexBasis
{
YGNodeStyleSetFlexBasis([self ygNode], flexBasis);
}
- (CGFloat)yk_positionForEdge:(YKEdge)edge
{
return YGNodeStyleGetPosition([self ygNode], (YGEdge)edge);
}
- (void)yk_setPosition:(CGFloat)position forEdge:(YKEdge)edge
{
YGNodeStyleSetPosition([self ygNode], (YGEdge)edge, position);
}
- (CGFloat)yk_marginForEdge:(YKEdge)edge
{
return YGNodeStyleGetMargin([self ygNode], (YGEdge)edge);
}
- (void)yk_setMargin:(CGFloat)margin forEdge:(YKEdge)edge
{
YGNodeStyleSetMargin([self ygNode], (YGEdge)edge, margin);
}
- (CGFloat)yk_paddingForEdge:(YKEdge)edge
{
return YGNodeStyleGetPadding([self ygNode], (YGEdge)edge);
}
- (void)yk_setPadding:(CGFloat)padding forEdge:(YKEdge)edge
{
YGNodeStyleSetPadding([self ygNode], (YGEdge)edge, padding);
}
- (CGFloat)yk_width
{
return YGNodeStyleGetWidth([self ygNode]);
}
- (void)yk_setWidth:(CGFloat)width
{
YGNodeStyleSetWidth([self ygNode], width);
}
- (CGFloat)yk_height
{
return YGNodeStyleGetHeight([self ygNode]);
}
- (void)yk_setHeight:(CGFloat)height
{
YGNodeStyleSetHeight([self ygNode], height);
}
- (CGFloat)yk_minWidth
{
return YGNodeStyleGetMinWidth([self ygNode]);
}
- (void)yk_setMinWidth:(CGFloat)minWidth
{
YGNodeStyleSetMinWidth([self ygNode], minWidth);
}
- (CGFloat)yk_minHeight
{
return YGNodeStyleGetMinHeight([self ygNode]);
}
- (void)yk_setMinHeight:(CGFloat)minHeight
{
YGNodeStyleSetMinHeight([self ygNode], minHeight);
}
- (CGFloat)yk_maxWidth
{
return YGNodeStyleGetMaxWidth([self ygNode]);
}
- (void)yk_setMaxWidth:(CGFloat)maxWidth
{
YGNodeStyleSetMaxWidth([self ygNode], maxWidth);
}
- (CGFloat)yk_maxHeight
{
return YGNodeStyleGetMaxHeight([self ygNode]);
}
- (void)yk_setMaxHeight:(CGFloat)maxHeight
{
YGNodeStyleSetMaxHeight([self ygNode], maxHeight);
}
- (CGFloat)yk_aspectRatio
{
return YGNodeStyleGetAspectRatio([self ygNode]);
}
- (void)yk_setAspectRatio:(CGFloat)aspectRatio
{
YGNodeStyleSetAspectRatio([self ygNode], aspectRatio);
}
#pragma mark - Layout and Sizing
- (YKDirection)yk_resolvedDirection
{
return (YKDirection)YGNodeLayoutGetDirection([self ygNode]);
}
- (void)yk_applyLayout
{
[self calculateLayoutWithSize:self.bounds.size];
YKApplyLayoutToViewHierarchy(self);
}
- (CGSize)yk_intrinsicSize
{
const CGSize constrainedSize = {
.width = YGUndefined,
.height = YGUndefined,
};
return [self calculateLayoutWithSize:constrainedSize];
}
#pragma mark - Private
- (YGNodeRef)ygNode
{
YGNodeBridge *node = objc_getAssociatedObject(self, @selector(ygNode));
if (!node) {
node = [YGNodeBridge new];
YGNodeSetContext(node.cnode, (__bridge void *) self);
objc_setAssociatedObject(self, @selector(ygNode), node, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
YKLayout *layout = objc_getAssociatedObject(self, @selector(layout));
if (!layout) {
layout = [[YKLayout alloc] initWithView:self];
objc_setAssociatedObject(self, @selector(layout), layout, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return node.cnode;
return layout;
}
- (CGSize)calculateLayoutWithSize:(CGSize)size
{
NSAssert([NSThread isMainThread], @"YG Layout calculation must be done on main.");
NSAssert([self yk_usesYoga], @"YG Layout is not enabled for this view.");
YKAttachNodesFromViewHierachy(self);
const YGNodeRef node = [self ygNode];
YGNodeCalculateLayout(
node,
size.width,
size.height,
YGNodeStyleGetDirection(node));
return (CGSize) {
.width = YGNodeLayoutGetWidth(node),
.height = YGNodeLayoutGetHeight(node),
};
}
static YGSize YKMeasureView(
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 = YKSanitizeMeasurement(constrainedWidth, sizeThatFits.width, widthMode),
.height = YKSanitizeMeasurement(constrainedHeight, sizeThatFits.height, heightMode),
};
}
static CGFloat YKSanitizeMeasurement(
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 void YKAttachNodesFromViewHierachy(UIView *view) {
YGNodeRef node = [view ygNode];
// Only leaf nodes should have a measure function
if (![view yk_usesYoga] || view.subviews.count == 0) {
YGNodeSetMeasureFunc(node, YKMeasureView);
YKRemoveAllChildren(node);
} else {
YGNodeSetMeasureFunc(node, NULL);
// Create a list of all the subviews that we are going to use for layout.
NSMutableArray<UIView *> *subviewsToInclude = [[NSMutableArray alloc] initWithCapacity:view.subviews.count];
for (UIView *subview in view.subviews) {
if ([subview yk_includeInLayout]) {
[subviewsToInclude addObject:subview];
}
}
BOOL shouldReconstructChildList = NO;
if (YGNodeGetChildCount(node) != subviewsToInclude.count) {
shouldReconstructChildList = YES;
} else {
for (int i = 0; i < subviewsToInclude.count; i++) {
if (YGNodeGetChild(node, i) != [subviewsToInclude[i] ygNode]) {
shouldReconstructChildList = YES;
break;
}
}
}
if (shouldReconstructChildList) {
YKRemoveAllChildren(node);
for (int i = 0 ; i < subviewsToInclude.count; i++) {
UIView *const subview = subviewsToInclude[i];
YGNodeInsertChild(node, [subview ygNode], i);
YKAttachNodesFromViewHierachy(subview);
}
}
}
}
static void YKRemoveAllChildren(const YGNodeRef node)
{
if (node == NULL) {
return;
}
while (YGNodeGetChildCount(node) > 0) {
YGNodeRemoveChild(node, YGNodeGetChild(node, YGNodeGetChildCount(node) - 1));
}
}
static CGFloat YKRoundPixelValue(CGFloat value)
{
static CGFloat scale;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^(){
scale = [UIScreen mainScreen].scale;
});
return round(value * scale) / scale;
}
static void YKApplyLayoutToViewHierarchy(UIView *view) {
NSCAssert([NSThread isMainThread], @"Framesetting should only be done on the main thread.");
if (![view yk_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 = YKRoundPixelValue(topLeft.x),
.y = YKRoundPixelValue(topLeft.y),
},
.size = {
.width = YKRoundPixelValue(bottomRight.x) - YKRoundPixelValue(topLeft.x),
.height = YKRoundPixelValue(bottomRight.y) - YKRoundPixelValue(topLeft.y),
},
};
const BOOL isLeaf = ![view yk_usesYoga] || view.subviews.count == 0;
if (!isLeaf) {
for (NSUInteger i = 0; i < view.subviews.count; i++) {
YKApplyLayoutToViewHierarchy(view.subviews[i]);
}
}
}
@end

View File

@@ -12,24 +12,12 @@ typedef NS_ENUM(NSInteger, YKFlexDirection) {
YKFlexDirectionColumnReverse,
YKFlexDirectionRow,
YKFlexDirectionRowReverse,
};
typedef NS_ENUM(NSInteger, YKEdge) {
YKEdgeLeft,
YKEdgeTop,
YKEdgeRight,
YKEdgeBottom,
YKEdgeStart,
YKEdgeEnd,
YKEdgeHorizontal,
YKEdgeVertical,
YKEdgeAll,
};
} NS_SWIFT_NAME(FlexDirection);
typedef NS_ENUM(NSInteger, YKPositionType) {
YKPositionTypeRelative,
YKPositionTypeAbsolute,
};
} NS_SWIFT_NAME(PositionType);
typedef NS_ENUM(NSInteger, YKJustify) {
YKJustifyFlexStart,
@@ -37,18 +25,18 @@ typedef NS_ENUM(NSInteger, YKJustify) {
YKJustifyFlexEnd,
YKJustifySpaceBetween,
YKJustifySpaceAround,
};
} NS_SWIFT_NAME(Justify);
typedef NS_ENUM(NSInteger, YKDirection) {
YKDirectionInherit,
YKDirectionLeftToRight,
YKDirectionRightToLeft,
};
} NS_SWIFT_NAME(Direction);
typedef NS_ENUM(NSInteger, YKWrap) {
YKWrapNoWrap,
YKWrapWrap,
};
} NS_SWIFT_NAME(Wrap);
typedef NS_ENUM(NSInteger, YKAlign) {
YKAlignAuto,
@@ -56,5 +44,5 @@ typedef NS_ENUM(NSInteger, YKAlign) {
YKAlignCenter,
YKAlignFlexEnd,
YKAlignStretch,
};
} NS_SWIFT_NAME(Align);

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 <YogaKit/YKLayout.h>
@interface YKLayout ()
- (instancetype)initWithView:(UIView*)view;
@end

97
YogaKit/YKLayout.h Normal file
View File

@@ -0,0 +1,97 @@
/**
* 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 "YKEnums.h"
@interface YKLayout : NSObject
/**
The property that decides if we should include this view when calculating layout. Defaults to YES.
*/
@property (nonatomic, setter=setIncluded:) BOOL isIncluded;
/**
The property that decides during layout/sizing whether or not yk_* properties should be applied. Defaults to NO.
*/
@property (nonatomic, setter=setEnabled:) BOOL isEnabled;
@property (nonatomic) YKDirection direction;
@property (nonatomic) YKFlexDirection flexDirection;
@property (nonatomic) YKJustify justifyContent;
@property (nonatomic) YKAlign alignContent;
@property (nonatomic) YKAlign alignItems;
@property (nonatomic) YKAlign alignSelf;
@property (nonatomic) YKPositionType positionType;
@property (nonatomic) YKWrap flexWrap;
@property (nonatomic) CGFloat flexGrow;
@property (nonatomic) CGFloat flexShrink;
@property (nonatomic) CGFloat flexBasis;
@property (nonatomic) CGFloat positionLeft;
@property (nonatomic) CGFloat positionTop;
@property (nonatomic) CGFloat positionRight;
@property (nonatomic) CGFloat positionBottom;
@property (nonatomic) CGFloat positionStart;
@property (nonatomic) CGFloat positionEnd;
@property (nonatomic) CGFloat positionHorizontal;
d16r commented 2016-12-20 09:00:23 -08:00 (Migrated from github.com)
Review

Personally, I find this makes the API much more confusing. There are multiple properties that essentially do the same thing. I could set positionLeft and positionRight or just simply set positionHorizontal. To me, it would be much more idiomatic to Objective-C and Swift if we did something like this.

@property (nonatomic, readwrite, assign) UIEdgeInsets position;

or

- (void)setMargin:(CGFloat)margin forEdges:(YKEdges)edges;

where YKEdges is an NSOption.

I would apply this comment to margin and padding too. cc: @emilsjolander

Personally, I find this makes the API much more confusing. There are multiple properties that essentially do the same thing. I could set `positionLeft` and `positionRight` or just simply set `positionHorizontal`. To me, it would be much more idiomatic to Objective-C and Swift if we did something like this. ```objc @property (nonatomic, readwrite, assign) UIEdgeInsets position; ``` or ```objc - (void)setMargin:(CGFloat)margin forEdges:(YKEdges)edges; ``` where YKEdges is an NSOption. I would apply this comment to `margin` and `padding` too. cc: @emilsjolander
hartbit commented 2016-12-20 13:00:44 -08:00 (Migrated from github.com)
Review

@dshahidehpour I agree the solution I have is not ideal, but I fail to see how UIEdgeInsets or a NS_OPTIONS YKEdges would allow us to express the Start and End values.

@dshahidehpour I agree the solution I have is not ideal, but I fail to see how `UIEdgeInsets` or a `NS_OPTIONS YKEdges` would allow us to express the `Start` and `End` values.
d16r commented 2016-12-20 15:29:12 -08:00 (Migrated from github.com)
Review

@hartbit Whoops, I didn't even notice those, do we think they need to part of YogaKit? I purposely left some properties out at first because I didn't see a huge use-case for them with UIKit.

@hartbit Whoops, I didn't even notice those, do we think they need to part of `YogaKit`? I purposely left some properties out at first because I didn't see a huge use-case for them with UIKit.
hartbit commented 2016-12-20 16:21:51 -08:00 (Migrated from github.com)
Review

@dshahidehpour The Yoga website says: "We believe that Right-to-Left (RTL) should be a first class citizen when it comes to layout" and I strongly agree. UIKit also supports it by default with leading and trailing constraints in Auto Layout and I'd be very sorry to have them out of YogaKit.

You mention that there are multiple properties that do the same thing. But it's not unheard of in Cocoa. For example, a UIView's frame.size is the same as its bounds.size. Setting one affects the other. And it's the same with it's center, which is the mid-x and mid-y of frame.

I know, this is a difficult design decision. But I think there is no perfect solution. For sake of discussion, here are the solutions I see, as well as their pros and cons.

Properties

@property (nonatomic) CGFloat paddingLeft;
@property (nonatomic) CGFloat paddingRight;
@property (nonatomic) CGFloat paddingTop;
@property (nonatomic) CGFloat paddingBottom;
@property (nonatomic) CGFloat paddingStart;
@property (nonatomic) CGFloat paddingEnd;
@property (nonatomic) CGFloat paddingHorizontal;
@property (nonatomic) CGFloat paddingVertical;
@property (nonatomic) CGFloat padding; // I've removed the All because it's a change I'd like to do

Usage in Objective-C

view.layout.padding = 15;
// OR
view.layout.paddingTop = 10;
// OR
view.layout.paddingStart = 10;
// OR
view.layout.paddingHorizontal = 10;
view.layout.paddingTop = 15;

Usage in Swift

view.layout.padding = 15
// OR
view.layout.paddingTop = 10
// OR
view.layout.paddingStart = 10
// OR
view.layout.paddingHorizontal = 10
view.layout.paddingTop = 10

Pros

  • Recognizable API for people from CSS and React Native (it's actually the exact same API as React Native).
  • Auto-completion makes this API version fast to use, as for all other properties.

Cons

  • Makes the API surface larger, not very DRY.

Methods with NS_ENUM

- (CGFloat)paddingForEdge:(YGEdge)edge;
- (void)setPadding:(CGFloat)padding forEdge:(YGEdge)edge;

Usage in Objective-C

[view.layout setPadding:15 forEdge:YGEdgeAll];
// OR
[view.layout setPadding:10 forEdge:YGEdgeTop];
// OR
// NO WAY TO SET START
[view.layout setPadding:10 forEdge:YKEdgeHorizontal];
[view.layout setPadding:10 forEdge:YKEdgeTop];

Usage in Swift

view.layout.setPadding(15, for: .all)
// OR
view.layout.setPadding(10, for: .top)
// OR
// NO WAY TO SET START
// OR
view.layout.setPadding(10, for: .horizontal)
view.layout.setPadding(10, for: .top)

Pros

  • The usage looks cleaner by having a smaller API surface area.

Cons

  • We can't use dot-notation like all other properties, which breaks the reading flow. Perhaps it's my personal taste, but I see that as a big minus.
root.layout.width = 5;
[root.layout setPadding:10 forEdge:YGEdgeTop];
root.layout.flexGrow = 1;

YGEdgeValue


@interface YGEdgeValue
@property (nonatomic) CGFloat left;
@property (nonatomic) CGFloat right;
@property (nonatomic) CGFloat top;
@property (nonatomic) CGFloat bottom;
@property (nonatomic) CGFloat start;
@property (nonatomic) CGFloat end;
@property (nonatomic) CGFloat horizontal;
@property (nonatomic) CGFloat vertical;
@property (nonatomic) CGFloat all;
@end

@inteface YGLayout
@property (nonatomic, readonly) YGEdgeValue *padding;
@end

Usage in Objective-C

view.layout.padding.all = 15;
// OR
view.layout.padding.top = 10;
// OR
view.layout.padding.start = 10;
// OR
view.layout.padding.horizontal = 10;
view.layout.padding.top = 15;

Usage in Swift

view.layout.padding.all = 15
// OR
view.layout.padding.top = 10
// OR
view.layout.padding.start = 10
// OR
view.layout.padding.horizontal = 10
view.layout.padding.top = 10

Pros and Cons

This is basically a different flavor of properties. I'm not a hug fan of it because it multiplies the number of objects on the heap that yoga generates by 4 for a small gain in readability and API surface and a small loss in usage typing speed because having to go through two auto-completion hints (padding, then top).

Conclusion

On and all, here's my ranking of preference:

  1. Properties
  2. YGEdgeValue
  3. Methods
@dshahidehpour The Yoga website says: "We believe that Right-to-Left (RTL) should be a first class citizen when it comes to layout" and I strongly agree. UIKit also supports it by default with leading and trailing constraints in Auto Layout and I'd be very sorry to have them out of YogaKit. You mention that there are multiple properties that do the same thing. But it's not unheard of in Cocoa. For example, a UIView's `frame.size` is the same as its `bounds.size`. Setting one affects the other. And it's the same with it's `center`, which is the mid-x and mid-y of `frame`. I know, this is a difficult design decision. But I think there is no perfect solution. For sake of discussion, here are the solutions I see, as well as their pros and cons. # Properties ``` @property (nonatomic) CGFloat paddingLeft; @property (nonatomic) CGFloat paddingRight; @property (nonatomic) CGFloat paddingTop; @property (nonatomic) CGFloat paddingBottom; @property (nonatomic) CGFloat paddingStart; @property (nonatomic) CGFloat paddingEnd; @property (nonatomic) CGFloat paddingHorizontal; @property (nonatomic) CGFloat paddingVertical; @property (nonatomic) CGFloat padding; // I've removed the All because it's a change I'd like to do ``` ## Usage in Objective-C ``` view.layout.padding = 15; // OR view.layout.paddingTop = 10; // OR view.layout.paddingStart = 10; // OR view.layout.paddingHorizontal = 10; view.layout.paddingTop = 15; ``` ## Usage in Swift ``` view.layout.padding = 15 // OR view.layout.paddingTop = 10 // OR view.layout.paddingStart = 10 // OR view.layout.paddingHorizontal = 10 view.layout.paddingTop = 10 ``` ## Pros * Recognizable API for people from CSS and React Native (it's actually the exact same API as React Native). * Auto-completion makes this API version fast to use, as for all other properties. ## Cons * Makes the API surface larger, not very DRY. # Methods with `NS_ENUM` ``` - (CGFloat)paddingForEdge:(YGEdge)edge; - (void)setPadding:(CGFloat)padding forEdge:(YGEdge)edge; ``` ## Usage in Objective-C ``` [view.layout setPadding:15 forEdge:YGEdgeAll]; // OR [view.layout setPadding:10 forEdge:YGEdgeTop]; // OR // NO WAY TO SET START [view.layout setPadding:10 forEdge:YKEdgeHorizontal]; [view.layout setPadding:10 forEdge:YKEdgeTop]; ``` ## Usage in Swift ``` view.layout.setPadding(15, for: .all) // OR view.layout.setPadding(10, for: .top) // OR // NO WAY TO SET START // OR view.layout.setPadding(10, for: .horizontal) view.layout.setPadding(10, for: .top) ``` ## Pros * The usage looks cleaner by having a smaller API surface area. ## Cons * We can't use dot-notation like all other properties, which breaks the reading flow. Perhaps it's my personal taste, but I see that as a big minus. ``` root.layout.width = 5; [root.layout setPadding:10 forEdge:YGEdgeTop]; root.layout.flexGrow = 1; ``` # `YGEdgeValue` ``` @interface YGEdgeValue @property (nonatomic) CGFloat left; @property (nonatomic) CGFloat right; @property (nonatomic) CGFloat top; @property (nonatomic) CGFloat bottom; @property (nonatomic) CGFloat start; @property (nonatomic) CGFloat end; @property (nonatomic) CGFloat horizontal; @property (nonatomic) CGFloat vertical; @property (nonatomic) CGFloat all; @end @inteface YGLayout @property (nonatomic, readonly) YGEdgeValue *padding; @end ``` ## Usage in Objective-C ``` view.layout.padding.all = 15; // OR view.layout.padding.top = 10; // OR view.layout.padding.start = 10; // OR view.layout.padding.horizontal = 10; view.layout.padding.top = 15; ``` ## Usage in Swift ``` view.layout.padding.all = 15 // OR view.layout.padding.top = 10 // OR view.layout.padding.start = 10 // OR view.layout.padding.horizontal = 10 view.layout.padding.top = 10 ``` ## Pros and Cons This is basically a different flavor of properties. I'm not a hug fan of it because it multiplies the number of objects on the heap that yoga generates by 4 for a small gain in readability and API surface and a small loss in usage typing speed because having to go through two auto-completion hints (`padding`, then `top`). # Conclusion On and all, here's my ranking of preference: 1. Properties 2. YGEdgeValue 3. Methods
hartbit commented 2016-12-20 16:34:24 -08:00 (Migrated from github.com)
Review

As an additional support for the properties, I don't think it would surprise UIKit users because it is fairly idiomatic of the AutoLayout APIs:

screen shot 2016-12-21 at 01 31 09

Add to that the fact that CSS and React Native users would feel right a home.

As an additional support for the properties, I don't think it would surprise UIKit users because it is fairly idiomatic of the AutoLayout APIs: <img width="368" alt="screen shot 2016-12-21 at 01 31 09" src="https://cloud.githubusercontent.com/assets/325519/21373209/5f647db0-c71d-11e6-8cac-5853287cbeec.png"> Add to that the fact that CSS and React Native users would feel right a home.
d16r commented 2016-12-22 06:20:39 -08:00 (Migrated from github.com)
Review

Hey @hartbit, sorry I took so long to respond. I really appreciate the time you put into that response, and I support all of it 100%.

@emilsjolander I know we talked about YGEdgeValue, but, I think @hartbit makes a compelling case for why we should put these properties on the layout object.

  1. Matches other UIKit APIs.
  2. Less objects on the heap.
  3. One less method lookup when we are getting/setting this on the objc runtime.
Hey @hartbit, sorry I took so long to respond. I really appreciate the time you put into that response, and I support all of it 100%. @emilsjolander I know we talked about `YGEdgeValue`, but, I think @hartbit makes a compelling case for why we should put these properties on the layout object. 1. Matches other UIKit APIs. 2. Less objects on the heap. 3. One less method lookup when we are getting/setting this on the objc runtime.
eklipse2k8 commented 2017-01-03 11:37:41 -08:00 (Migrated from github.com)
Review

I like the YGEdgeValue the best, but why does it need to be a full class? In C/C++, the pattern would be lightweight structs for this kind of data set. That way I can do something like:

view.layout.padding = {.top = 10, .bottom = 10}

Or a hypothetical
view.layout.padding = YGCreateEdgeValues(...);

I like the YGEdgeValue the best, but why does it need to be a full class? In C/C++, the pattern would be lightweight structs for this kind of data set. That way I can do something like: `view.layout.padding = {.top = 10, .bottom = 10}` Or a hypothetical `view.layout.padding = YGCreateEdgeValues(...);`
@property (nonatomic) CGFloat positionVertical;
@property (nonatomic) CGFloat positionAll;
@property (nonatomic) CGFloat marginLeft;
@property (nonatomic) CGFloat marginTop;
@property (nonatomic) CGFloat marginRight;
@property (nonatomic) CGFloat marginBottom;
@property (nonatomic) CGFloat marginStart;
@property (nonatomic) CGFloat marginEnd;
@property (nonatomic) CGFloat marginHorizontal;
@property (nonatomic) CGFloat marginVertical;
@property (nonatomic) CGFloat marginAll;
@property (nonatomic) CGFloat paddingLeft;
@property (nonatomic) CGFloat paddingTop;
@property (nonatomic) CGFloat paddingRight;
@property (nonatomic) CGFloat paddingBottom;
@property (nonatomic) CGFloat paddingStart;
d16r commented 2016-12-20 09:02:54 -08:00 (Migrated from github.com)
Review

nit across the board, can we explicity add all the properties? i.e.

@property (nonatomic, readwrite, assign) ...
nit across the board, can we explicity add all the properties? i.e. ``` @property (nonatomic, readwrite, assign) ... ```
hartbit commented 2016-12-20 13:00:55 -08:00 (Migrated from github.com)
Review

Sure!

Sure!
@property (nonatomic) CGFloat paddingEnd;
@property (nonatomic) CGFloat paddingHorizontal;
@property (nonatomic) CGFloat paddingVertical;
@property (nonatomic) CGFloat paddingAll;
@property (nonatomic) CGFloat width;
@property (nonatomic) CGFloat height;
@property (nonatomic) CGFloat minWidth;
@property (nonatomic) CGFloat minHeight;
@property (nonatomic) CGFloat maxWidth;
@property (nonatomic) CGFloat maxHeight;
// Yoga specific properties, not compatible with flexbox specification
@property (nonatomic) CGFloat aspectRatio;
/**
Get the resolved direction of this node. This won't be YGDirectionInherit
*/
@property (nonatomic, readonly) YKDirection resolvedDirection;
/**
Perform a layout calculation and update the frames of the views in the hierarchy with the results
*/
- (void)apply;
/**
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) CGSize intrinsicSize;
/**
Returns the number of children that are using Flexbox.
*/
@property (nonatomic, readonly) NSUInteger numberOfChildren;
@end

294
YogaKit/YKLayout.m Normal file
View File

@@ -0,0 +1,294 @@
/**
* 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 <YogaKit/UIView+YogaKit.h>
#import <YogaKit/YKLayout.h>
#import <YogaKit/yoga.h>
#define YK_STYLE_PROPERTY_IMPL(objc_type, c_type, lowercased_name, capitalized_name) \
- (objc_type)lowercased_name \
{ \
return (objc_type)YGNodeStyleGet##capitalized_name(_node); \
} \
\
- (void)set##capitalized_name:(objc_type)lowercased_name \
{ \
YGNodeStyleSet##capitalized_name(_node, (c_type)lowercased_name); \
}
#define _YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, edge) \
- (CGFloat)lowercased_name##edge \
{ \
return YGNodeStyleGet##capitalized_name(_node, YGEdge##edge); \
} \
\
- (void)set##capitalized_name##edge:(CGFloat)lowercased_name##edge \
{ \
YGNodeStyleSet##capitalized_name(_node, YGEdge##edge, lowercased_name##edge); \
}
#define YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name) \
_YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, Left) \
_YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, Top) \
_YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, Right) \
_YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, Bottom) \
_YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, Start) \
_YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, End) \
_YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, Horizontal) \
_YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, Vertical) \
_YK_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, All)
@interface YKLayout ()
@property (nonatomic, weak, readonly) UIView* view;
@property (nonatomic, assign, readonly) YGNodeRef node;
@end
@implementation YKLayout
@synthesize isEnabled=_isEnabled;
@synthesize isIncluded=_isIncluded;
+ (void)initialize
{
YGSetExperimentalFeatureEnabled(YGExperimentalFeatureWebFlexBasis, true);
}
- (instancetype)initWithView:(UIView*)view
{
if ([super init]) {
_view = view;
_node = YGNodeNew();
YGNodeSetContext(_node, (__bridge void *) view);
_isEnabled = NO;
_isIncluded = YES;
}
return self;
}
- (void)dealloc
{
YGNodeFree(_node);
}
- (NSUInteger)numberOfChildren
{
return YGNodeGetChildCount(_node);
}
YK_STYLE_PROPERTY_IMPL(YKDirection, YGDirection, direction, Direction)
YK_STYLE_PROPERTY_IMPL(YKFlexDirection, YGFlexDirection, flexDirection, FlexDirection)
YK_STYLE_PROPERTY_IMPL(YKJustify, YGJustify, justifyContent, JustifyContent)
YK_STYLE_PROPERTY_IMPL(YKAlign, YGAlign, alignContent, AlignContent)
YK_STYLE_PROPERTY_IMPL(YKAlign, YGAlign, alignItems, AlignItems)
YK_STYLE_PROPERTY_IMPL(YKAlign, YGAlign, alignSelf, AlignSelf)
YK_STYLE_PROPERTY_IMPL(YKPositionType, YGPositionType, positionType, PositionType)
YK_STYLE_PROPERTY_IMPL(YKWrap, YGWrap, flexWrap, FlexWrap)
YK_STYLE_PROPERTY_IMPL(CGFloat, CGFloat, flexGrow, FlexGrow)
YK_STYLE_PROPERTY_IMPL(CGFloat, CGFloat, flexShrink, FlexShrink)
YK_STYLE_PROPERTY_IMPL(CGFloat, CGFloat, flexBasis, FlexBasis)
YK_STYLE_EDGE_PROPERTY_IMPL(position, Position)
YK_STYLE_EDGE_PROPERTY_IMPL(margin, Margin)
YK_STYLE_EDGE_PROPERTY_IMPL(padding, Padding)
YK_STYLE_PROPERTY_IMPL(CGFloat, CGFloat, width, Width)
YK_STYLE_PROPERTY_IMPL(CGFloat, CGFloat, height, Height)
YK_STYLE_PROPERTY_IMPL(CGFloat, CGFloat, minWidth, MinWidth)
YK_STYLE_PROPERTY_IMPL(CGFloat, CGFloat, minHeight, MinHeight)
YK_STYLE_PROPERTY_IMPL(CGFloat, CGFloat, maxWidth, MaxWidth)
YK_STYLE_PROPERTY_IMPL(CGFloat, CGFloat, maxHeight, MaxHeight)
YK_STYLE_PROPERTY_IMPL(CGFloat, CGFloat, aspectRatio, AspectRatio)
#pragma mark - Layout and Sizing
- (YKDirection)resolvedDirection
{
return (YKDirection)YGNodeLayoutGetDirection(_node);
}
- (void)apply
{
[self calculateLayoutWithSize:self.view.bounds.size];
YKApplyLayoutToViewHierarchy(self);
}
- (CGSize)intrinsicSize
{
const CGSize constrainedSize = {
.width = YGUndefined,
.height = YGUndefined,
};
return [self calculateLayoutWithSize:constrainedSize];
}
#pragma mark - Private
- (CGSize)calculateLayoutWithSize:(CGSize)size
{
NSAssert([NSThread isMainThread], @"YG Layout calculation must be done on main.");
NSAssert([self isEnabled], @"YG Layout is not enabled for this view.");
YKAttachNodesFromViewHierachy(self);
const YGNodeRef node = _node;
YGNodeCalculateLayout(
node,
size.width,
size.height,
YGNodeStyleGetDirection(node));
return (CGSize) {
.width = YGNodeLayoutGetWidth(node),
.height = YGNodeLayoutGetHeight(node),
};
}
static YGSize YKMeasureView(
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 = YKSanitizeMeasurement(constrainedWidth, sizeThatFits.width, widthMode),
.height = YKSanitizeMeasurement(constrainedHeight, sizeThatFits.height, heightMode),
};
}
static CGFloat YKSanitizeMeasurement(
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 void YKAttachNodesFromViewHierachy(YKLayout *layout) {
YGNodeRef node = layout.node;
UIView *view = layout.view;
// Only leaf nodes should have a measure function
if (![layout isEnabled] || view.subviews.count == 0) {
YGNodeSetMeasureFunc(node, YKMeasureView);
YKRemoveAllChildren(node);
} else {
YGNodeSetMeasureFunc(node, NULL);
// Create a list of all the subviews that we are going to use for layout.
NSMutableArray<UIView *> *subviewsToInclude = [[NSMutableArray alloc] initWithCapacity:view.subviews.count];
for (UIView *subview in view.subviews) {
if (subview.layout.isIncluded) {
[subviewsToInclude addObject:subview];
}
}
BOOL shouldReconstructChildList = NO;
if (YGNodeGetChildCount(node) != subviewsToInclude.count) {
shouldReconstructChildList = YES;
} else {
for (int i = 0; i < subviewsToInclude.count; i++) {
if (YGNodeGetChild(node, i) != subviewsToInclude[i].layout.node) {
shouldReconstructChildList = YES;
break;
}
}
}
if (shouldReconstructChildList) {
YKRemoveAllChildren(node);
for (int i = 0 ; i < subviewsToInclude.count; i++) {
UIView *const subview = subviewsToInclude[i];
YGNodeInsertChild(node, subview.layout.node, i);
YKAttachNodesFromViewHierachy(subview.layout);
}
}
}
}
static void YKRemoveAllChildren(const YGNodeRef node)
{
if (node == NULL) {
return;
}
while (YGNodeGetChildCount(node) > 0) {
YGNodeRemoveChild(node, YGNodeGetChild(node, YGNodeGetChildCount(node) - 1));
}
}
static CGFloat YKRoundPixelValue(CGFloat value)
{
static CGFloat scale;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^(){
scale = [UIScreen mainScreen].scale;
});
return round(value * scale) / scale;
}
static void YKApplyLayoutToViewHierarchy(YKLayout *layout) {
NSCAssert([NSThread isMainThread], @"Framesetting should only be done on the main thread.");
if (!layout.isIncluded) {
return;
}
YGNodeRef node = layout.node;
const CGPoint topLeft = {
YGNodeLayoutGetLeft(node),
YGNodeLayoutGetTop(node),
};
const CGPoint bottomRight = {
topLeft.x + YGNodeLayoutGetWidth(node),
topLeft.y + YGNodeLayoutGetHeight(node),
};
UIView *view = layout.view;
view.frame = (CGRect) {
.origin = {
.x = YKRoundPixelValue(topLeft.x),
.y = YKRoundPixelValue(topLeft.y),
},
.size = {
.width = YKRoundPixelValue(bottomRight.x) - YKRoundPixelValue(topLeft.x),
.height = YKRoundPixelValue(bottomRight.y) - YKRoundPixelValue(topLeft.y),
},
};
const BOOL isLeaf = !layout.isEnabled || view.subviews.count == 0;
if (!isLeaf) {
for (NSUInteger i = 0; i < view.subviews.count; i++) {
YKApplyLayoutToViewHierarchy(view.subviews[i].layout);
}
}
}
@end

View File

@@ -25,6 +25,9 @@
63EE08511E06EECB00EE5F9A /* UIView+YogaKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 13687D691DF8778F00E7C260 /* UIView+YogaKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
63EE08531E06F3D100EE5F9A /* YKEnums.h in Headers */ = {isa = PBXBuildFile; fileRef = 63EE08521E06F3D100EE5F9A /* YKEnums.h */; settings = {ATTRIBUTES = (Public, ); }; };
63EE08551E072EF800EE5F9A /* SwiftViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63EE08541E072EF800EE5F9A /* SwiftViewController.swift */; };
63EE08571E07590C00EE5F9A /* YKLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 63EE08561E07590C00EE5F9A /* YKLayout.m */; };
63EE08591E07598A00EE5F9A /* YKLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 63EE08581E07591A00EE5F9A /* YKLayout.h */; settings = {ATTRIBUTES = (Public, ); }; };
63EE085B1E075B0B00EE5F9A /* YKLayout+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 63EE085A1E075AAA00EE5F9A /* YKLayout+Private.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -74,6 +77,9 @@
63EE08401E06ED3D00EE5F9A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
63EE08521E06F3D100EE5F9A /* YKEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YKEnums.h; path = ../../YKEnums.h; sourceTree = "<group>"; };
63EE08541E072EF800EE5F9A /* SwiftViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftViewController.swift; sourceTree = "<group>"; };
63EE08561E07590C00EE5F9A /* YKLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = YKLayout.m; path = ../../YKLayout.m; sourceTree = "<group>"; };
63EE08581E07591A00EE5F9A /* YKLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = YKLayout.h; path = ../../YKLayout.h; sourceTree = "<group>"; };
63EE085A1E075AAA00EE5F9A /* YKLayout+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "YKLayout+Private.h"; path = "../../YKLayout+Private.h"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -169,6 +175,9 @@
63EE08521E06F3D100EE5F9A /* YKEnums.h */,
13687D691DF8778F00E7C260 /* UIView+YogaKit.h */,
13687D6A1DF8778F00E7C260 /* UIView+YogaKit.m */,
63EE08581E07591A00EE5F9A /* YKLayout.h */,
63EE085A1E075AAA00EE5F9A /* YKLayout+Private.h */,
63EE08561E07590C00EE5F9A /* YKLayout.m */,
63EE08401E06ED3D00EE5F9A /* Info.plist */,
);
path = YogaKit;
@@ -181,6 +190,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
63EE085B1E075B0B00EE5F9A /* YKLayout+Private.h in Headers */,
63EE08591E07598A00EE5F9A /* YKLayout.h in Headers */,
63EE08411E06ED3D00EE5F9A /* YogaKit.h in Headers */,
63EE08531E06F3D100EE5F9A /* YKEnums.h in Headers */,
63EE08511E06EECB00EE5F9A /* UIView+YogaKit.h in Headers */,
@@ -306,6 +317,7 @@
63EE084A1E06EEB700EE5F9A /* YGNodeList.c in Sources */,
63EE084B1E06EEB700EE5F9A /* Yoga.c in Sources */,
63EE084C1E06EEB700EE5F9A /* UIView+YogaKit.m in Sources */,
63EE08571E07590C00EE5F9A /* YKLayout.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -13,17 +13,17 @@ class SwiftViewController : UIViewController {
override func viewDidLoad() {
let root = view!
root.backgroundColor = .red
root.usesYoga = true
root.layoutWidth = view.bounds.size.width
root.layoutHeight = view.bounds.size.height
root.layoutAlignItems = .center
root.layoutJustifyContent = .center
root.layout.isEnabled = true
root.layout.width = view.bounds.size.width
root.layout.height = view.bounds.size.height
root.layout.alignItems = .center
root.layout.justifyContent = .center
let child1 = UIView()
child1.backgroundColor = .blue
child1.usesYoga = true
child1.layoutWidth = 100
child1.layoutHeight = 100
child1.layout.isEnabled = true
child1.layout.width = 100
child1.layout.height = 100
let child2 = UIView()
child2.backgroundColor = .green
@@ -36,6 +36,6 @@ class SwiftViewController : UIViewController {
child2.addSubview(child3)
root.addSubview(child1)
root.addSubview(child2)
root.applyLayout()
root.layout.apply()
}
}

View File

@@ -19,17 +19,17 @@
{
UIView *root = self.view;
root.backgroundColor = [UIColor redColor];
[root yk_setUsesYoga:YES];
[root yk_setWidth:self.view.bounds.size.width];
[root yk_setHeight:self.view.bounds.size.height];
[root yk_setAlignItems:YKAlignCenter];
[root yk_setJustifyContent:YKJustifyCenter];
root.layout.isEnabled = YES;
root.layout.width = self.view.bounds.size.width;
root.layout.height = self.view.bounds.size.height;
root.layout.alignItems = YKAlignCenter;
root.layout.justifyContent = YKJustifyCenter;
UIView *child1 = [UIView new];
child1.backgroundColor = [UIColor blueColor];
[child1 yk_setUsesYoga:YES];
[child1 yk_setWidth:100];
[child1 yk_setHeight:100];
child1.layout.isEnabled = YES;
child1.layout.width = 100;
child1.layout.height = 100;
UIView *child2 = [UIView new];
child2.backgroundColor = [UIColor greenColor];
@@ -52,7 +52,7 @@
[child2 addSubview:child3];
[root addSubview:child1];
[root addSubview:child2];
[root yk_applyLayout];
[root.layout apply];
}

View File

@@ -95,6 +95,7 @@ OBJC_ENUMS = {
'LeftToRight',
'RightToLeft',
],
'Edge': None,
'MeasureMode': None,
'PrintOptions': None,
'Dimension': None,
@@ -215,5 +216,5 @@ with open(root + '/YogaKit/YKEnums.h', 'w') as f:
f.write(' YK%s%s = %d,\n' % (name, value[0], value[1]))
else:
f.write(' YK%s%s,\n' % (name, value))
f.write('};\n')
f.write('} NS_SWIFT_NAME(%s);\n' % name)
f.write('\n')