WIP: Improve the Objective-C and Swift bridge #289
@@ -8,68 +8,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import "YKEnums.h"
|
#import <YogaKit/YKLayout.h>
|
||||||
|
|
||||||
@interface UIView (YogaKit)
|
@interface UIView (YogaKit)
|
||||||
|
|
||||||
/**
|
@property (nonatomic, readonly) YKLayout* layout;
|
||||||
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);
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@@ -7,478 +7,22 @@
|
|||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <YogaKit/UIView+Yoga.h>
|
#import <YogaKit/UIView+YogaKit.h>
|
||||||
#import <YogaKit/yoga.h>
|
#import <YogaKit/YKLayout+Private.h>
|
||||||
#import <objc/runtime.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)
|
@implementation UIView (YogaKit)
|
||||||
|
|
||||||
- (BOOL)yk_usesYoga
|
- (YKLayout *)layout
|
||||||
{
|
{
|
||||||
NSNumber *usesYoga = objc_getAssociatedObject(self, @selector(yk_usesYoga));
|
YKLayout *layout = objc_getAssociatedObject(self, @selector(layout));
|
||||||
return [usesYoga boolValue];
|
if (!layout) {
|
||||||
}
|
layout = [[YKLayout alloc] initWithView:self];
|
||||||
|
objc_setAssociatedObject(self, @selector(layout), layout, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
- (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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
@end
|
||||||
|
@@ -12,24 +12,12 @@ typedef NS_ENUM(NSInteger, YKFlexDirection) {
|
|||||||
YKFlexDirectionColumnReverse,
|
YKFlexDirectionColumnReverse,
|
||||||
YKFlexDirectionRow,
|
YKFlexDirectionRow,
|
||||||
YKFlexDirectionRowReverse,
|
YKFlexDirectionRowReverse,
|
||||||
};
|
} NS_SWIFT_NAME(FlexDirection);
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, YKEdge) {
|
|
||||||
YKEdgeLeft,
|
|
||||||
YKEdgeTop,
|
|
||||||
YKEdgeRight,
|
|
||||||
YKEdgeBottom,
|
|
||||||
YKEdgeStart,
|
|
||||||
YKEdgeEnd,
|
|
||||||
YKEdgeHorizontal,
|
|
||||||
YKEdgeVertical,
|
|
||||||
YKEdgeAll,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, YKPositionType) {
|
typedef NS_ENUM(NSInteger, YKPositionType) {
|
||||||
YKPositionTypeRelative,
|
YKPositionTypeRelative,
|
||||||
YKPositionTypeAbsolute,
|
YKPositionTypeAbsolute,
|
||||||
};
|
} NS_SWIFT_NAME(PositionType);
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, YKJustify) {
|
typedef NS_ENUM(NSInteger, YKJustify) {
|
||||||
YKJustifyFlexStart,
|
YKJustifyFlexStart,
|
||||||
@@ -37,18 +25,18 @@ typedef NS_ENUM(NSInteger, YKJustify) {
|
|||||||
YKJustifyFlexEnd,
|
YKJustifyFlexEnd,
|
||||||
YKJustifySpaceBetween,
|
YKJustifySpaceBetween,
|
||||||
YKJustifySpaceAround,
|
YKJustifySpaceAround,
|
||||||
};
|
} NS_SWIFT_NAME(Justify);
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, YKDirection) {
|
typedef NS_ENUM(NSInteger, YKDirection) {
|
||||||
YKDirectionInherit,
|
YKDirectionInherit,
|
||||||
YKDirectionLeftToRight,
|
YKDirectionLeftToRight,
|
||||||
YKDirectionRightToLeft,
|
YKDirectionRightToLeft,
|
||||||
};
|
} NS_SWIFT_NAME(Direction);
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, YKWrap) {
|
typedef NS_ENUM(NSInteger, YKWrap) {
|
||||||
YKWrapNoWrap,
|
YKWrapNoWrap,
|
||||||
YKWrapWrap,
|
YKWrapWrap,
|
||||||
};
|
} NS_SWIFT_NAME(Wrap);
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, YKAlign) {
|
typedef NS_ENUM(NSInteger, YKAlign) {
|
||||||
YKAlignAuto,
|
YKAlignAuto,
|
||||||
@@ -56,5 +44,5 @@ typedef NS_ENUM(NSInteger, YKAlign) {
|
|||||||
YKAlignCenter,
|
YKAlignCenter,
|
||||||
YKAlignFlexEnd,
|
YKAlignFlexEnd,
|
||||||
YKAlignStretch,
|
YKAlignStretch,
|
||||||
};
|
} NS_SWIFT_NAME(Align);
|
||||||
|
|
||||||
|
16
YogaKit/YKLayout+Private.h
Normal file
16
YogaKit/YKLayout+Private.h
Normal 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
97
YogaKit/YKLayout.h
Normal 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;
|
||||||
|
|||||||
|
@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;
|
||||||
![]() nit across the board, can we explicity add all the properties? i.e.
nit across the board, can we explicity add all the properties? i.e.
```
@property (nonatomic, readwrite, assign) ...
```
![]() 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
294
YogaKit/YKLayout.m
Normal 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
|
@@ -25,6 +25,9 @@
|
|||||||
63EE08511E06EECB00EE5F9A /* UIView+YogaKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 13687D691DF8778F00E7C260 /* UIView+YogaKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
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, ); }; };
|
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 */; };
|
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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -74,6 +77,9 @@
|
|||||||
63EE08401E06ED3D00EE5F9A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -169,6 +175,9 @@
|
|||||||
63EE08521E06F3D100EE5F9A /* YKEnums.h */,
|
63EE08521E06F3D100EE5F9A /* YKEnums.h */,
|
||||||
13687D691DF8778F00E7C260 /* UIView+YogaKit.h */,
|
13687D691DF8778F00E7C260 /* UIView+YogaKit.h */,
|
||||||
13687D6A1DF8778F00E7C260 /* UIView+YogaKit.m */,
|
13687D6A1DF8778F00E7C260 /* UIView+YogaKit.m */,
|
||||||
|
63EE08581E07591A00EE5F9A /* YKLayout.h */,
|
||||||
|
63EE085A1E075AAA00EE5F9A /* YKLayout+Private.h */,
|
||||||
|
63EE08561E07590C00EE5F9A /* YKLayout.m */,
|
||||||
63EE08401E06ED3D00EE5F9A /* Info.plist */,
|
63EE08401E06ED3D00EE5F9A /* Info.plist */,
|
||||||
);
|
);
|
||||||
path = YogaKit;
|
path = YogaKit;
|
||||||
@@ -181,6 +190,8 @@
|
|||||||
isa = PBXHeadersBuildPhase;
|
isa = PBXHeadersBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
63EE085B1E075B0B00EE5F9A /* YKLayout+Private.h in Headers */,
|
||||||
|
63EE08591E07598A00EE5F9A /* YKLayout.h in Headers */,
|
||||||
63EE08411E06ED3D00EE5F9A /* YogaKit.h in Headers */,
|
63EE08411E06ED3D00EE5F9A /* YogaKit.h in Headers */,
|
||||||
63EE08531E06F3D100EE5F9A /* YKEnums.h in Headers */,
|
63EE08531E06F3D100EE5F9A /* YKEnums.h in Headers */,
|
||||||
63EE08511E06EECB00EE5F9A /* UIView+YogaKit.h in Headers */,
|
63EE08511E06EECB00EE5F9A /* UIView+YogaKit.h in Headers */,
|
||||||
@@ -306,6 +317,7 @@
|
|||||||
63EE084A1E06EEB700EE5F9A /* YGNodeList.c in Sources */,
|
63EE084A1E06EEB700EE5F9A /* YGNodeList.c in Sources */,
|
||||||
63EE084B1E06EEB700EE5F9A /* Yoga.c in Sources */,
|
63EE084B1E06EEB700EE5F9A /* Yoga.c in Sources */,
|
||||||
63EE084C1E06EEB700EE5F9A /* UIView+YogaKit.m in Sources */,
|
63EE084C1E06EEB700EE5F9A /* UIView+YogaKit.m in Sources */,
|
||||||
|
63EE08571E07590C00EE5F9A /* YKLayout.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
Binary file not shown.
@@ -13,17 +13,17 @@ class SwiftViewController : UIViewController {
|
|||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
let root = view!
|
let root = view!
|
||||||
root.backgroundColor = .red
|
root.backgroundColor = .red
|
||||||
root.usesYoga = true
|
root.layout.isEnabled = true
|
||||||
root.layoutWidth = view.bounds.size.width
|
root.layout.width = view.bounds.size.width
|
||||||
root.layoutHeight = view.bounds.size.height
|
root.layout.height = view.bounds.size.height
|
||||||
root.layoutAlignItems = .center
|
root.layout.alignItems = .center
|
||||||
root.layoutJustifyContent = .center
|
root.layout.justifyContent = .center
|
||||||
|
|
||||||
let child1 = UIView()
|
let child1 = UIView()
|
||||||
child1.backgroundColor = .blue
|
child1.backgroundColor = .blue
|
||||||
child1.usesYoga = true
|
child1.layout.isEnabled = true
|
||||||
child1.layoutWidth = 100
|
child1.layout.width = 100
|
||||||
child1.layoutHeight = 100
|
child1.layout.height = 100
|
||||||
|
|
||||||
let child2 = UIView()
|
let child2 = UIView()
|
||||||
child2.backgroundColor = .green
|
child2.backgroundColor = .green
|
||||||
@@ -36,6 +36,6 @@ class SwiftViewController : UIViewController {
|
|||||||
child2.addSubview(child3)
|
child2.addSubview(child3)
|
||||||
root.addSubview(child1)
|
root.addSubview(child1)
|
||||||
root.addSubview(child2)
|
root.addSubview(child2)
|
||||||
root.applyLayout()
|
root.layout.apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,17 +19,17 @@
|
|||||||
{
|
{
|
||||||
UIView *root = self.view;
|
UIView *root = self.view;
|
||||||
root.backgroundColor = [UIColor redColor];
|
root.backgroundColor = [UIColor redColor];
|
||||||
[root yk_setUsesYoga:YES];
|
root.layout.isEnabled = YES;
|
||||||
[root yk_setWidth:self.view.bounds.size.width];
|
root.layout.width = self.view.bounds.size.width;
|
||||||
[root yk_setHeight:self.view.bounds.size.height];
|
root.layout.height = self.view.bounds.size.height;
|
||||||
[root yk_setAlignItems:YKAlignCenter];
|
root.layout.alignItems = YKAlignCenter;
|
||||||
[root yk_setJustifyContent:YKJustifyCenter];
|
root.layout.justifyContent = YKJustifyCenter;
|
||||||
|
|
||||||
UIView *child1 = [UIView new];
|
UIView *child1 = [UIView new];
|
||||||
child1.backgroundColor = [UIColor blueColor];
|
child1.backgroundColor = [UIColor blueColor];
|
||||||
[child1 yk_setUsesYoga:YES];
|
child1.layout.isEnabled = YES;
|
||||||
[child1 yk_setWidth:100];
|
child1.layout.width = 100;
|
||||||
[child1 yk_setHeight:100];
|
child1.layout.height = 100;
|
||||||
|
|
||||||
UIView *child2 = [UIView new];
|
UIView *child2 = [UIView new];
|
||||||
child2.backgroundColor = [UIColor greenColor];
|
child2.backgroundColor = [UIColor greenColor];
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
[child2 addSubview:child3];
|
[child2 addSubview:child3];
|
||||||
[root addSubview:child1];
|
[root addSubview:child1];
|
||||||
[root addSubview:child2];
|
[root addSubview:child2];
|
||||||
[root yk_applyLayout];
|
[root.layout apply];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
3
enums.py
3
enums.py
@@ -95,6 +95,7 @@ OBJC_ENUMS = {
|
|||||||
'LeftToRight',
|
'LeftToRight',
|
||||||
'RightToLeft',
|
'RightToLeft',
|
||||||
],
|
],
|
||||||
|
'Edge': None,
|
||||||
'MeasureMode': None,
|
'MeasureMode': None,
|
||||||
'PrintOptions': None,
|
'PrintOptions': None,
|
||||||
'Dimension': 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]))
|
f.write(' YK%s%s = %d,\n' % (name, value[0], value[1]))
|
||||||
else:
|
else:
|
||||||
f.write(' YK%s%s,\n' % (name, value))
|
f.write(' YK%s%s,\n' % (name, value))
|
||||||
f.write('};\n')
|
f.write('} NS_SWIFT_NAME(%s);\n' % name)
|
||||||
f.write('\n')
|
f.write('\n')
|
||||||
|
Reference in New Issue
Block a user
Personally, I find this makes the API much more confusing. There are multiple properties that essentially do the same thing. I could set
positionLeft
andpositionRight
or just simply setpositionHorizontal
. To me, it would be much more idiomatic to Objective-C and Swift if we did something like this.or
where YKEdges is an NSOption.
I would apply this comment to
margin
andpadding
too. cc: @emilsjolander@dshahidehpour I agree the solution I have is not ideal, but I fail to see how
UIEdgeInsets
or aNS_OPTIONS YKEdges
would allow us to express theStart
andEnd
values.@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.@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 itsbounds.size
. Setting one affects the other. And it's the same with it'scenter
, which is the mid-x and mid-y offrame
.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
Usage in Objective-C
Usage in Swift
Pros
Cons
Methods with
NS_ENUM
Usage in Objective-C
Usage in Swift
Pros
Cons
YGEdgeValue
Usage in Objective-C
Usage in Swift
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
, thentop
).Conclusion
On and all, here's my ranking of preference:
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:
Add to that the fact that CSS and React Native users would feel right a home.
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.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(...);