UIView category
Summary: Add flexbox support to UIViews via a category Reviewed By: dshahidehpour Differential Revision: D4002873 fbshipit-source-id: f89de3acdf8fd89c7801918dcad34ba9011dd025
This commit is contained in:
committed by
Facebook Github Bot
parent
d3fc24e842
commit
8939bcb96d
18
uikit/CSSLayout/BUCK
Normal file
18
uikit/CSSLayout/BUCK
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
include_defs('//CSSLAYOUT_DEFS')
|
||||||
|
|
||||||
|
apple_library(
|
||||||
|
name = 'CSSLayout',
|
||||||
|
srcs = glob(['*.m']),
|
||||||
|
exported_headers = glob(['*.h']),
|
||||||
|
deps = [
|
||||||
|
csslayout_dep(':CSSLayout'),
|
||||||
|
],
|
||||||
|
visibility = ['PUBLIC'],
|
||||||
|
)
|
51
uikit/CSSLayout/UIView+CSSLayout.h
Normal file
51
uikit/CSSLayout/UIView+CSSLayout.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 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 <CSSLayout/CSSLayout.h>
|
||||||
|
|
||||||
|
@interface UIView (CSSLayout)
|
||||||
|
|
||||||
|
- (void)css_setUsesFlexbox:(BOOL)enabled;
|
||||||
|
- (BOOL)css_usesFlexbox;
|
||||||
|
|
||||||
|
- (void)css_setDirection:(CSSDirection)direction;
|
||||||
|
- (void)css_setFlexDirection:(CSSFlexDirection)flexDirection;
|
||||||
|
- (void)css_setJustifyContent:(CSSJustify)justifyContent;
|
||||||
|
- (void)css_setAlignContent:(CSSAlign)alignContent;
|
||||||
|
- (void)css_setAlignItems:(CSSAlign)alignItems;
|
||||||
|
- (void)css_setAlignSelf:(CSSAlign)alignSelf;
|
||||||
|
- (void)css_setPositionType:(CSSPositionType)positionType;
|
||||||
|
- (void)css_setFlexWrap:(CSSWrapType)flexWrap;
|
||||||
|
- (void)css_setOverflow:(CSSOverflow)overflow;
|
||||||
|
|
||||||
|
- (void)css_setFlex:(CGFloat)flex;
|
||||||
|
- (void)css_setFlexGrow:(CGFloat)flexGrow;
|
||||||
|
- (void)css_setFlexShrink:(CGFloat)flexShrink;
|
||||||
|
- (void)css_setFlexBasis:(CGFloat)flexBasis;
|
||||||
|
|
||||||
|
- (void)css_setPosition:(CGFloat)position forEdge:(CSSEdge)edge;
|
||||||
|
- (void)css_setMargin:(CGFloat)margin forEdge:(CSSEdge)edge;
|
||||||
|
- (void)css_setPadding:(CGFloat)padding forEdge:(CSSEdge)edge;
|
||||||
|
- (void)css_setBorder:(CGFloat)border forEdge:(CSSEdge)edge;
|
||||||
|
|
||||||
|
- (void)css_setWidth:(CGFloat)width;
|
||||||
|
- (void)css_setHeight:(CGFloat)height;
|
||||||
|
- (void)css_setMinWidth:(CGFloat)minWidth;
|
||||||
|
- (void)css_setMinHeight:(CGFloat)minHeight;
|
||||||
|
- (void)css_setMaxWidth:(CGFloat)maxWidth;
|
||||||
|
- (void)css_setMaxHeight:(CGFloat)maxHeight;
|
||||||
|
|
||||||
|
// Get the resolved direction of this node. This won't be CSSDirectionInherit
|
||||||
|
- (CSSDirection)css_resolvedDirection;
|
||||||
|
|
||||||
|
// Perform a layout calculation and update the frames of the views in the hierarchy with th results
|
||||||
|
- (void)css_applyLayout;
|
||||||
|
|
||||||
|
@end
|
281
uikit/CSSLayout/UIView+CSSLayout.m
Normal file
281
uikit/CSSLayout/UIView+CSSLayout.m
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
/**
|
||||||
|
* 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 "UIView+CSSLayout.h"
|
||||||
|
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
static CSSSize _measure(
|
||||||
|
void *context,
|
||||||
|
float width,
|
||||||
|
CSSMeasureMode widthMode,
|
||||||
|
float height,
|
||||||
|
CSSMeasureMode heightMode)
|
||||||
|
{
|
||||||
|
UIView *view = (__bridge UIView*) context;
|
||||||
|
CGSize sizeThatFits = [view sizeThatFits:(CGSize) {
|
||||||
|
.width = widthMode == CSSMeasureModeUndefined ? CGFLOAT_MAX : width,
|
||||||
|
.height = heightMode == CSSMeasureModeUndefined ? CGFLOAT_MAX : height,
|
||||||
|
}];
|
||||||
|
|
||||||
|
return (CSSSize) {
|
||||||
|
.width = widthMode == CSSMeasureModeExactly ? width : sizeThatFits.width,
|
||||||
|
.height = heightMode == CSSMeasureModeExactly ? height : sizeThatFits.height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _attachNodesRecursive(UIView *view);
|
||||||
|
static void _updateFrameRecursive(UIView *view);
|
||||||
|
|
||||||
|
@interface CSSNodeBridge : NSObject
|
||||||
|
@property (nonatomic, assign) CSSNodeRef cnode;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation CSSNodeBridge
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
if ([super init]) {
|
||||||
|
_cnode = CSSNodeNew();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[super dealloc];
|
||||||
|
CSSNodeFree(_cnode);
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation UIView (CSSLayout)
|
||||||
|
|
||||||
|
- (CSSNodeRef)cssNode
|
||||||
|
{
|
||||||
|
CSSNodeBridge *node = objc_getAssociatedObject(self, @selector(cssNode));
|
||||||
|
if (!node) {
|
||||||
|
node = [CSSNodeBridge new];
|
||||||
|
CSSNodeSetContext(node.cnode, (__bridge void *) self);
|
||||||
|
objc_setAssociatedObject(self, @selector(cssNode), node, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.cnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setUsesFlexbox:(BOOL)enabled
|
||||||
|
{
|
||||||
|
objc_setAssociatedObject(
|
||||||
|
self,
|
||||||
|
@selector(css_usesFlexbox),
|
||||||
|
@(enabled),
|
||||||
|
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)css_usesFlexbox
|
||||||
|
{
|
||||||
|
if (objc_getAssociatedObject(self, @selector(css_usesFlexbox))) {
|
||||||
|
return YES;
|
||||||
|
} else {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setDirection:(CSSDirection)direction
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetDirection([self cssNode], direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setFlexDirection:(CSSFlexDirection)flexDirection
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetFlexDirection([self cssNode], flexDirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setJustifyContent:(CSSJustify)justifyContent
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetJustifyContent([self cssNode], justifyContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setAlignContent:(CSSAlign)alignContent
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetAlignContent([self cssNode], alignContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setAlignItems:(CSSAlign)alignItems
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetAlignItems([self cssNode], alignItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setAlignSelf:(CSSAlign)alignSelf
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetAlignSelf([self cssNode], alignSelf);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setPositionType:(CSSPositionType)positionType
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetPositionType([self cssNode], positionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setFlexWrap:(CSSWrapType)flexWrap
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetFlexWrap([self cssNode], flexWrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setOverflow:(CSSOverflow)overflow
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetOverflow([self cssNode], overflow);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setFlex:(CGFloat)flex
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetFlex([self cssNode], flex);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setFlexGrow:(CGFloat)flexGrow
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetFlexGrow([self cssNode], flexGrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setFlexShrink:(CGFloat)flexShrink
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetFlexShrink([self cssNode], flexShrink);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setFlexBasis:(CGFloat)flexBasis
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetFlexBasis([self cssNode], flexBasis);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setPosition:(CGFloat)position forEdge:(CSSEdge)edge
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetPosition([self cssNode], edge, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setMargin:(CGFloat)margin forEdge:(CSSEdge)edge
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetMargin([self cssNode], edge, margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setPadding:(CGFloat)padding forEdge:(CSSEdge)edge
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetPadding([self cssNode], edge, padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setBorder:(CGFloat)border forEdge:(CSSEdge)edge
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetBorder([self cssNode], edge, border);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setWidth:(CGFloat)width
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetWidth([self cssNode], width);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setHeight:(CGFloat)height
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetHeight([self cssNode], height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setMinWidth:(CGFloat)minWidth
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetMinWidth([self cssNode], minWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setMinHeight:(CGFloat)minHeight
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetMinHeight([self cssNode], minHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setMaxWidth:(CGFloat)maxWidth
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetMaxWidth([self cssNode], maxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_setMaxHeight:(CGFloat)maxHeight
|
||||||
|
{
|
||||||
|
CSSNodeStyleSetMaxHeight([self cssNode], maxHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CSSDirection)css_resolvedDirection
|
||||||
|
{
|
||||||
|
return CSSNodeLayoutGetDirection([self cssNode]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)css_applyLayout
|
||||||
|
{
|
||||||
|
NSAssert([NSThread isMainThread], @"Method called using a thread other than main!");
|
||||||
|
NSAssert([self css_usesFlexbox], @"css_applyLayout must be called on a node using css styling!");
|
||||||
|
|
||||||
|
_attachNodesRecursive(self);
|
||||||
|
|
||||||
|
CSSNodeRef node = [self cssNode];
|
||||||
|
CSSNodeCalculateLayout(
|
||||||
|
[self cssNode],
|
||||||
|
CSSNodeStyleGetWidth(node),
|
||||||
|
CSSNodeStyleGetHeight(node),
|
||||||
|
CSSNodeStyleGetDirection(node));
|
||||||
|
|
||||||
|
_updateFrameRecursive(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
static void _attachNodesRecursive(UIView *view) {
|
||||||
|
CSSNodeRef node = [view cssNode];
|
||||||
|
const BOOL usesFlexbox = [view css_usesFlexbox];
|
||||||
|
const BOOL isLeaf = !usesFlexbox || view.subviews.count == 0;
|
||||||
|
|
||||||
|
// Only leaf nodes should have a measure function
|
||||||
|
if (isLeaf) {
|
||||||
|
CSSNodeSetMeasureFunc(node, _measure);
|
||||||
|
|
||||||
|
// Clear any children
|
||||||
|
while (CSSNodeChildCount(node) > 0) {
|
||||||
|
CSSNodeRemoveChild(node, CSSNodeGetChild(node, 0));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CSSNodeSetMeasureFunc(node, NULL);
|
||||||
|
|
||||||
|
// Add any children which were added since the last call to css_applyLayout
|
||||||
|
for (NSUInteger i = 0; i < view.subviews.count; i++) {
|
||||||
|
CSSNodeRef childNode = [view.subviews[i] cssNode];
|
||||||
|
if (CSSNodeGetChild(node, i) != childNode) {
|
||||||
|
CSSNodeInsertChild(node, childNode, i);
|
||||||
|
}
|
||||||
|
_attachNodesRecursive(view.subviews[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any children which were removed since the last call to css_applyLayout
|
||||||
|
while (view.subviews.count < CSSNodeChildCount(node)) {
|
||||||
|
CSSNodeRemoveChild(node, CSSNodeGetChild(node, CSSNodeChildCount(node) - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _updateFrameRecursive(UIView *view) {
|
||||||
|
CSSNodeRef node = [view cssNode];
|
||||||
|
const BOOL usesFlexbox = [view css_usesFlexbox];
|
||||||
|
const BOOL isLeaf = !usesFlexbox || view.subviews.count == 0;
|
||||||
|
|
||||||
|
view.frame = (CGRect) {
|
||||||
|
.origin = {
|
||||||
|
.x = CSSNodeLayoutGetLeft(node),
|
||||||
|
.y = CSSNodeLayoutGetTop(node),
|
||||||
|
},
|
||||||
|
.size = {
|
||||||
|
.width = CSSNodeLayoutGetWidth(node),
|
||||||
|
.height = CSSNodeLayoutGetHeight(node),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isLeaf) {
|
||||||
|
for (NSUInteger i = 0; i < view.subviews.count; i++) {
|
||||||
|
_updateFrameRecursive(view.subviews[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user