Rename uikit/CSSLayout to CSSLayoutKit.

Summary:
When trying to integrate this into an Xcode project that already included CSSLayout.[c|h], we were getting a linker error.

Upon digging in, I found out that Xcode was becoming confused because the imports of the uikit library and the c library are both `#import <CSSLayout/CSSLayout.h>`. So, it needed a new name.

Reviewed By: emilsjolander

Differential Revision: D4162621

fbshipit-source-id: b5f7624eb29f1b9eaebbed5104ec9ea8a12ad2e5
This commit is contained in:
Dustin Shahidehpour
2016-11-11 07:36:39 -08:00
committed by Facebook Github Bot
parent 3e2c13f418
commit 70e01a4476
7 changed files with 6 additions and 6 deletions

41
CSSLayoutKit/BUCK Normal file
View File

@@ -0,0 +1,41 @@
# 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')
UIKIT_CSSLAYOUT_COMPILER_FLAGS = ['-fobjc-arc']
apple_library(
name = 'CSSLayoutKit',
compiler_flags = UIKIT_CSSLAYOUT_COMPILER_FLAGS,
tests = [':CSSLayoutKitTests'],
srcs = glob(['*.m']),
exported_headers = glob(['*.h']),
frameworks = [
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
'$SDKROOT/System/Library/Frameworks/UIKit.framework',
],
deps = [
csslayout_dep(':CSSLayout'),
],
visibility = ['PUBLIC'],
)
apple_test(
name = 'CSSLayoutKitTests',
compiler_flags = UIKIT_CSSLAYOUT_COMPILER_FLAGS,
info_plist = 'Tests/Info.plist',
srcs = glob(['Tests/**/*.m']),
frameworks = [
'$SDKROOT/System/Library/Frameworks/CoreGraphics.framework',
'$PLATFORM_DIR/Developer/Library/Frameworks/XCTest.framework',
],
deps = [
':CSSLayoutKit',
],
visibility = ['PUBLIC'],
)

View File

@@ -0,0 +1,110 @@
/**
* 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 <XCTest/XCTest.h>
#import "UIView+CSSLayout.h"
@interface CSSLayoutTests : XCTestCase
@end
@implementation CSSLayoutTests
- (void)testNodesAreDeallocedWithSingleView
{
XCTAssertEqual(0, CSSNodeGetInstanceCount());
UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
[view css_setFlexBasis:1];
XCTAssertEqual(1, CSSNodeGetInstanceCount());
view = nil;
XCTAssertEqual(0, CSSNodeGetInstanceCount());
}
- (void)testNodesAreDeallocedCascade
{
XCTAssertEqual(0, CSSNodeGetInstanceCount());
UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
[view css_setFlexBasis:1];
for (int i=0; i<10; i++) {
UIView *subview = [[UIView alloc] initWithFrame:CGRectZero];
[subview css_setFlexBasis:1];
[view addSubview:subview];
}
XCTAssertEqual(11, CSSNodeGetInstanceCount());
view = nil;
XCTAssertEqual(0, CSSNodeGetInstanceCount());
}
- (void)testUsesFlexbox
{
UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
XCTAssertFalse([view css_usesFlexbox]);
[view css_setUsesFlexbox:YES];
XCTAssertTrue([view css_usesFlexbox]);
[view css_setUsesFlexbox:NO];
XCTAssertFalse([view css_usesFlexbox]);
}
- (void)testSizeThatFitsAsserts
{
UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
XCTAssertThrows([view css_sizeThatFits:CGSizeZero]);
dispatch_sync(dispatch_queue_create("com.facebook.CSSLayout.testing", DISPATCH_QUEUE_SERIAL), ^(void){
XCTAssertThrows([view css_sizeThatFits:CGSizeZero]);
});
}
- (void)testSizeThatFitsSmoke
{
UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
[view css_setUsesFlexbox:YES];
const CGSize constrainedSize = CGSizeMake(50, 50);
const CGSize actualSize = [view css_sizeThatFits:constrainedSize];
XCTAssertTrue(CGSizeEqualToSize(constrainedSize, actualSize), @"Actual Size: %@", NSStringFromCGSize(actualSize));
}
- (void)testFrameAndOriginPlacement
{
const CGSize containerSize = CGSizeMake(320, 50);
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)];
[container css_setUsesFlexbox:YES];
[container css_setFlexDirection:CSSFlexDirectionRow];
for (int i = 0; i < 3; i++) {
UIView *subview = [[UIView alloc] initWithFrame:CGRectZero];
[subview css_setUsesFlexbox:YES];
[subview css_setFlexGrow:1];
[container addSubview:subview];
}
[container css_applyLayout];
XCTAssertFalse(CGRectIntersectsRect([container.subviews objectAtIndex:0].frame, [container.subviews objectAtIndex:1].frame));
XCTAssertFalse(CGRectIntersectsRect([container.subviews objectAtIndex:1].frame, [container.subviews objectAtIndex:2].frame));
XCTAssertFalse(CGRectIntersectsRect([container.subviews objectAtIndex:0].frame, [container.subviews objectAtIndex:2].frame));
CGFloat totalWidth = 0;
for (UIView *view in container.subviews) {
totalWidth += view.bounds.size.width;
}
XCTAssertEqual(containerSize.width, totalWidth, @"The container's width is %.6f, the subviews take up %.6f", containerSize.width, totalWidth);
}
@end

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.facebook.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View 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_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_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;
//! @abstract Perform a layout calculation and update the frames of the views in the hierarchy with th results
- (void)css_applyLayout;
//! @abstract Compute the size of a layout with a constrained size.
- (CGSize)css_sizeThatFits:(CGSize)constrainedSize;
@end

View File

@@ -0,0 +1,311 @@
/**
* 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>
@interface CSSNodeBridge : NSObject
@property (nonatomic, assign, readonly) CSSNodeRef cnode;
@end
@implementation CSSNodeBridge
- (instancetype)init
{
if ([super init]) {
_cnode = CSSNodeNew();
}
return self;
}
- (void)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
{
NSNumber *usesFlexbox = objc_getAssociatedObject(self, @selector(css_usesFlexbox));
return [usesFlexbox boolValue];
}
- (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_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_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]);
}
- (CGSize)css_sizeThatFits:(CGSize)constrainedSize
{
NSAssert([NSThread isMainThread], @"CSS Layout calculation must be done on main.");
NSAssert([self css_usesFlexbox], @"CSS Layout is not enabled for this view.");
_attachNodesRecursive(self);
const CSSNodeRef node = [self cssNode];
CSSNodeCalculateLayout(
node,
constrainedSize.width,
constrainedSize.height,
CSSNodeStyleGetDirection(node));
return (CGSize) {
.width = CSSNodeLayoutGetWidth(node),
.height = CSSNodeLayoutGetHeight(node),
};
}
- (void)css_applyLayout
{
[self css_sizeThatFits:self.bounds.size];
_updateFrameRecursive(self);
}
#pragma mark - Private
static CSSSize _measure(
CSSNodeRef node,
float width,
CSSMeasureMode widthMode,
float height,
CSSMeasureMode heightMode)
{
const CGFloat constrainedWidth = (widthMode == CSSMeasureModeUndefined) ? CGFLOAT_MAX : width;
const CGFloat constrainedHeight = (heightMode == CSSMeasureModeUndefined) ? CGFLOAT_MAX: height;
UIView *view = (__bridge UIView*) CSSNodeGetContext(node);
const CGSize sizeThatFits = [view sizeThatFits:(CGSize) {
.width = constrainedWidth,
.height = constrainedHeight,
}];
return (CSSSize) {
.width = _sanitizeMeasurement(constrainedWidth, sizeThatFits.width, widthMode),
.height = _sanitizeMeasurement(constrainedHeight, sizeThatFits.height, heightMode),
};
}
static CGFloat _sanitizeMeasurement(
CGFloat constrainedSize,
CGFloat measuredSize,
CSSMeasureMode measureMode)
{
CGFloat result;
if (measureMode == CSSMeasureModeExactly) {
result = constrainedSize;
} else if (measureMode == CSSMeasureModeAtMost) {
result = MIN(constrainedSize, measuredSize);
} else {
result = measuredSize;
}
return result;
}
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 (CSSNodeChildCount(node) < i + 1 || 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 CGFloat _roundPixelValue(CGFloat value)
{
static CGFloat scale;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^(){
scale = [UIScreen mainScreen].scale;
});
return round(value * scale) / scale;
}
static void _updateFrameRecursive(UIView *view) {
NSCAssert([NSThread isMainThread], @"Framesetting should only be done on the main thread.");
CSSNodeRef node = [view cssNode];
const CGPoint topLeft = {
CSSNodeLayoutGetLeft(node),
CSSNodeLayoutGetTop(node),
};
const CGPoint bottomRight = {
topLeft.x + CSSNodeLayoutGetWidth(node),
topLeft.y + CSSNodeLayoutGetHeight(node),
};
view.frame = (CGRect) {
.origin = {
.x = _roundPixelValue(topLeft.x),
.y = _roundPixelValue(topLeft.y),
},
.size = {
.width = _roundPixelValue(bottomRight.x) - _roundPixelValue(topLeft.x),
.height = _roundPixelValue(bottomRight.y) - _roundPixelValue(topLeft.y),
},
};
const BOOL isLeaf = ![view css_usesFlexbox] || view.subviews.count == 0;
if (!isLeaf) {
for (NSUInteger i = 0; i < view.subviews.count; i++) {
_updateFrameRecursive(view.subviews[i]);
}
}
}
@end