feat:iOS LinearLayout and adapt new layoutconfig

This commit is contained in:
pengfei.zhou 2019-10-23 19:37:06 +08:00
parent f78f3ed7d8
commit c2e3ef4f7e
26 changed files with 813 additions and 683 deletions

View File

@ -13,21 +13,26 @@
#import "DoricNativePlugin.h"
#import "DoricRootNode.h"
#import "DoricLocalServer.h"
#import "DoricLinearLayout.h"
#import "DoricExtensions.h"
@interface ViewController ()
@property (nonatomic,strong) DoricContext *doricContext;
@property (nonatomic,strong) DoricLocalServer *localServer;
@property(nonatomic, strong) DoricContext *doricContext;
@property(nonatomic, strong) DoricLocalServer *localServer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"Snake" ofType:@"js"];
NSString *jsContent = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
self.doricContext = [[DoricContext alloc] initWithScript:jsContent source:@"test.js"];
self.doricContext.rootNode.view = self.view;
[self.doricContext.rootNode setupRootView:[[Stack new] also:^(Stack *it) {
it.layoutConfig = [[StackLayoutConfig alloc] initWithWidth:LayoutParamAtMost height:LayoutParamAtMost];
[self.view addSubview:it];
}]];
[self.doricContext initContextWithWidth:self.view.width height:self.view.height];
[self.doricContext.driver connectDevKit:@"ws://192.168.11.38:7777"];
self.localServer = [[DoricLocalServer alloc] init];

View File

@ -25,7 +25,7 @@ EXTERNAL SOURCES:
:path: "../"
SPEC CHECKSUMS:
Doric: 1dae6e8a19a32a27af975f787509cc0658dde095
Doric: f96b77d435e836e88cf02319e3c9ebc08cab65f6
GCDWebServer: ead88cd14596dd4eae4f5830b8877c87c8728990
SDWebImage: 920f1a2ff1ca8296ad34f6e0510a1ef1d70ac965
SocketRocket: d57c7159b83c3c6655745cd15302aa24b6bae531

View File

@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, strong) DoricRootNode *rootNode;
@property(nonatomic, strong) NSString *source;
@property(nonatomic, strong) NSString *script;;
@property(nonatomic, strong) NSDictionary *initialParams;
@property(nonatomic, strong) NSMutableDictionary *initialParams;
- (instancetype)initWithScript:(NSString *)script source:(NSString *)source;

View File

@ -24,6 +24,7 @@
#import "DoricContextManager.h"
#import "DoricRootNode.h"
#import "DoricConstant.h"
#import "DoricExtensions.h"
@implementation DoricContext
@ -35,7 +36,7 @@ - (instancetype)initWithScript:(NSString *)script source:(NSString *)source {
_rootNode = [[DoricRootNode alloc] initWithContext:self];
_script = script;
_source = source;
_initialParams = [@{@"width": @(LAYOUT_MATCH_PARENT), @"height": @(LAYOUT_MATCH_PARENT)} mutableCopy];
_initialParams = [@{@"width": @(0), @"height": @(0)} mutableCopy];
[self callEntity:DORIC_ENTITY_CREATE, nil];
}
return self;
@ -63,8 +64,10 @@ - (DoricAsyncResult *)callEntity:(NSString *)method withArgumentsArray:(NSArray
}
- (void)initContextWithWidth:(CGFloat)width height:(CGFloat)height {
[self.initialParams setValue:@(width) forKey:@"width"];
[self.initialParams setValue:@(height) forKey:@"height"];
[self.initialParams also:^(NSMutableDictionary *it) {
it[@"width"] = @(width);
it[@"height"] = @(height);
}];
[self callEntity:DORIC_ENTITY_INIT, self.initialParams, nil];
}

View File

@ -24,15 +24,13 @@
NS_ASSUME_NONNULL_BEGIN
@interface DoricGroupNode <V:UIView *, P:LayoutParams *> : DoricViewNode<V>
@interface DoricGroupNode <V:UIView *, P:LayoutConfig *> : DoricViewNode<V>
@property(nonatomic, strong) NSMutableDictionary *children;
@property(nonatomic, strong) NSMutableArray *indexedChildren;
@property(nonatomic) CGFloat desiredWidth;
@property(nonatomic) CGFloat desiredHeight;
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig;
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig;
- (P)generateDefaultLayoutParams;
@end

View File

@ -20,6 +20,7 @@
// Created by pengfei.zhou on 2019/7/30.
//
#import <Doric/DoricExtensions.h>
#import "DoricGroupNode.h"
@implementation DoricGroupNode
@ -72,10 +73,10 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop
[tobeRemoved addObject:old];
}
LayoutParams *params = node.layoutParams;
LayoutConfig *params = node.layoutConfig;
if (params == nil) {
params = [self generateDefaultLayoutParams];
node.layoutParams = params;
node.layoutConfig = params;
}
[node blend:val[@"props"]];
if (self.indexedChildren.count <= i) {
@ -108,21 +109,35 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop
}
}
- (LayoutParams *)generateDefaultLayoutParams {
LayoutParams *params = [[LayoutParams alloc] init];
- (LayoutConfig *)generateDefaultLayoutParams {
LayoutConfig *params = [[LayoutConfig alloc] init];
return params;
}
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig {
LayoutParams *params = child.layoutParams;
if ([params isKindOfClass:MarginLayoutParams.class]) {
MarginLayoutParams *marginParams = (MarginLayoutParams *) params;
NSDictionary *margin = layoutconfig[@"margin"];
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig {
LayoutConfig *params = child.layoutConfig;
[layoutConfig[@"widthSpec"] also:^(NSNumber *it) {
if (it) {
params.widthSpec = (LayoutParam) [it integerValue];
}
}];
[layoutConfig[@"heightSpec"] also:^(NSNumber *it) {
if (it) {
params.heightSpec = (LayoutParam) [it integerValue];
}
}];
if ([params isKindOfClass:MarginLayoutConfig.class]) {
MarginLayoutConfig *marginParams = (MarginLayoutConfig *) params;
NSDictionary *margin = layoutConfig[@"margin"];
if (margin) {
marginParams.margin.top = [(NSNumber *) margin[@"top"] floatValue];
marginParams.margin.left = [(NSNumber *) margin[@"left"] floatValue];
marginParams.margin.right = [(NSNumber *) margin[@"right"] floatValue];
marginParams.margin.bottom = [(NSNumber *) margin[@"bottom"] floatValue];
marginParams.margin = MarginMake(
[(NSNumber *) margin[@"left"] floatValue],
[(NSNumber *) margin[@"top"] floatValue],
[(NSNumber *) margin[@"right"] floatValue],
[(NSNumber *) margin[@"bottom"] floatValue]);
}
}

View File

@ -22,11 +22,5 @@
#import "DoricGroupNode.h"
NS_ASSUME_NONNULL_BEGIN
@interface DoricHLayoutNode : DoricGroupNode<UIView *, VHLayoutParams *>
@property(nonatomic) CGFloat space;
@property(nonatomic) DoricGravity gravity;
@interface DoricHLayoutNode : DoricGroupNode<HLayout *, LinearLayoutConfig *>
@end
NS_ASSUME_NONNULL_END

View File

@ -24,114 +24,42 @@
#import "DoricUtil.h"
@implementation DoricHLayoutNode
- (instancetype)init {
if (self = [super init]) {
_space = 0;
_gravity = 0;
}
return self;
- (HLayout *)build:(NSDictionary *)props {
return [HLayout new];
}
- (void)blendView:(id)view forPropName:(NSString *)name propValue:(id)prop {
- (void)blendView:(HLayout *)view forPropName:(NSString *)name propValue:(id)prop {
if ([name isEqualToString:@"gravity"]) {
self.gravity = [(NSNumber *)prop integerValue];
view.gravity = (DoricGravity) [(NSNumber *) prop integerValue];
} else if ([name isEqualToString:@"space"]) {
self.space = [(NSNumber *)prop floatValue];
view.space = [(NSNumber *) prop floatValue];
} else {
[super blendView:view forPropName:name propValue:prop];
}
}
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig {
[super blendChild:child layoutConfig:layoutconfig];
if (![child.layoutParams isKindOfClass:VHLayoutParams.class]) {
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig {
[super blendChild:child layoutConfig:layoutConfig];
if (![child.layoutConfig isKindOfClass:LinearLayoutConfig.class]) {
DoricLog(@"blend HLayout child error,layout params not match");
return;
}
VHLayoutParams *params = (VHLayoutParams *)child.layoutParams;
NSDictionary *margin = [layoutconfig objectForKey:@"margin"];
LinearLayoutConfig *params = (LinearLayoutConfig *) child.layoutConfig;
NSDictionary *margin = layoutConfig[@"margin"];
if (margin) {
params.margin.top = [(NSNumber *)[margin objectForKey:@"top"] floatValue];
params.margin.left = [(NSNumber *)[margin objectForKey:@"left"] floatValue];
params.margin.right = [(NSNumber *)[margin objectForKey:@"right"] floatValue];
params.margin.bottom = [(NSNumber *)[margin objectForKey:@"bottom"] floatValue];
params.margin = MarginMake(
[(NSNumber *) margin[@"left"] floatValue],
[(NSNumber *) margin[@"top"] floatValue],
[(NSNumber *) margin[@"right"] floatValue],
[(NSNumber *) margin[@"bottom"] floatValue]);
}
NSNumber *alignment = [layoutconfig objectForKey:@"alignment"];
NSNumber *alignment = layoutConfig[@"alignment"];
if (alignment) {
params.alignment = [alignment integerValue];
params.alignment = (DoricGravity) [alignment integerValue];
}
}
- (VHLayoutParams *)generateDefaultLayoutParams {
return [[VHLayoutParams alloc] init];
- (LinearLayoutConfig *)generateDefaultLayoutParams {
return [[LinearLayoutConfig alloc] init];
}
- (void)measureByParent:(DoricGroupNode *)parent {
DoricLayoutDesc widthSpec = self.layoutParams.width;
DoricLayoutDesc heightSpec = self.layoutParams.height;
CGFloat maxWidth = 0,maxHeight = 0;
for (DoricViewNode *child in self.indexedChildren) {
[child measureByParent:self];
CGFloat placeWidth = child.measuredWidth;
CGFloat placeHeight = child.measuredHeight;
maxWidth += placeWidth + self.space;
maxHeight = MAX(maxHeight, placeHeight);
}
maxWidth -= self.space;
self.desiredWidth = maxWidth;
self.desiredHeight = maxHeight;
if (widthSpec == LAYOUT_WRAP_CONTENT) {
self.width = maxWidth;
}
if (heightSpec == LAYOUT_WRAP_CONTENT) {
self.height = maxHeight;
}
}
- (void)layoutByParent:(DoricGroupNode *)parent {
if (self.layoutParams.width == LAYOUT_MATCH_PARENT) {
self.width = parent.width;
}
if (self.layoutParams.height == LAYOUT_MATCH_PARENT) {
self.height = parent.height;
}
// layout child
CGFloat xStart = 0;
if ((self.gravity & LEFT) == LEFT) {
xStart = 0;
} else if ((self.gravity & RIGHT) == RIGHT) {
xStart = self.width - self.desiredWidth;
} else if ((self.gravity & CENTER_X) == CENTER_X) {
xStart = (self.width -self.desiredWidth)/2;
}
for (DoricViewNode *child in self.indexedChildren) {
if (child.layoutParams.width == LAYOUT_MATCH_PARENT) {
child.width = self.width;
}
if (child.layoutParams.height == LAYOUT_MATCH_PARENT) {
child.height = self.height;
}
if ([child.layoutParams isKindOfClass:VHLayoutParams.class]) {
VHLayoutParams *layoutParams = (VHLayoutParams *)child.layoutParams;
DoricGravity gravity = layoutParams.alignment | self.gravity;
if ((gravity & TOP) == TOP) {
child.top = 0;
} else if ((gravity & BOTTOM) == BOTTOM) {
child.bottom = self.height;
} else if ((gravity & CENTER_Y) == CENTER_Y) {
child.centerY = self.height/2;
}
}
child.left = xStart;
xStart = child.right + self.space;
[child layoutByParent:self];
}
}
@end

View File

@ -40,12 +40,4 @@ - (void)blendView:(UIImageView *)view forPropName:(NSString *)name propValue:(id
[super blendView:view forPropName:name propValue:prop];
}
}
- (void)measureByParent:(DoricGroupNode *)parent {
DoricLayoutDesc widthSpec = self.layoutParams.width;
DoricLayoutDesc heightSpec = self.layoutParams.height;
if (widthSpec == LAYOUT_WRAP_CONTENT || heightSpec == LAYOUT_WRAP_CONTENT) {
[self.view sizeToFit];
}
}
@end

View File

@ -0,0 +1,117 @@
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Created by pengfei.zhou on 2019/10/23.
//
#import <Foundation/Foundation.h>
struct Margin {
CGFloat left;
CGFloat right;
CGFloat top;
CGFloat bottom;
};
typedef struct Margin Margin;
Margin MarginMake(CGFloat left, CGFloat top, CGFloat right, CGFloat bottom);
typedef NS_ENUM(NSInteger, LayoutParam) {
LayoutParamExact,
LayoutParamWrapContent,
LayoutParamAtMost,
};
typedef NS_ENUM(NSInteger, DoricGravity) {
SPECIFIED = 1,
START = 1 << 1,
END = 1 << 2,
SHIFT_X = 0,
SHIFT_Y = 4,
LEFT = (START | SPECIFIED) << SHIFT_X,
RIGHT = (END | SPECIFIED) << SHIFT_X,
TOP = (START | SPECIFIED) << SHIFT_Y,
BOTTOM = (END | SPECIFIED) << SHIFT_Y,
CENTER_X = SPECIFIED << SHIFT_X,
CENTER_Y = SPECIFIED << SHIFT_Y,
CENTER = CENTER_X | CENTER_Y,
};
@interface LayoutConfig : NSObject
@property(nonatomic, assign) LayoutParam widthSpec;
@property(nonatomic, assign) LayoutParam heightSpec;
@property(nonatomic, assign) DoricGravity alignment;
- (instancetype)init;
- (instancetype)initWithWidth:(LayoutParam)width height:(LayoutParam)height;
@end
@interface StackLayoutConfig : LayoutConfig
@end
@interface MarginLayoutConfig : LayoutConfig
@property(nonatomic) Margin margin;
- (instancetype)initWithWidth:(LayoutParam)width height:(LayoutParam)height margin:(Margin)margin;
@end
@interface LinearLayoutConfig : MarginLayoutConfig
@property(nonatomic, assign) NSUInteger weight;
@end
@interface LayoutContainer <T :LayoutConfig *> : UIView
- (T)configForChild:(__kindof UIView *)child;
- (void)layout;
- (void)requestLayout;
@end
@interface Stack : LayoutContainer<StackLayoutConfig *>
@property(nonatomic, assign) DoricGravity gravity;
@end
@interface LinearLayout : LayoutContainer<LinearLayoutConfig *>
@property(nonatomic, assign) DoricGravity gravity;
@property(nonatomic, assign) CGFloat space;
@end
@interface VLayout : LinearLayout
@end
@interface HLayout : LinearLayout
@end
@interface UIView (LayoutConfig)
@property(nonatomic, strong) LayoutConfig *layoutConfig;
@property(nonatomic, copy) NSString *tagString;
- (UIView *)viewWithTagString:(NSString *)tagString;
@end
VLayout *vLayout(NSArray <__kindof UIView *> *views);
HLayout *hLayout(NSArray <__kindof UIView *> *views);
VLayout *vLayoutWithBlock(NSArray <UIView *(^)()> *blocks);
HLayout *hLayoutWithBlock(NSArray <UIView *(^)()> *blocks);

View File

@ -0,0 +1,505 @@
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Created by pengfei.zhou on 2019/10/23.
//
#import "DoricLinearLayout.h"
#import <objc/runtime.h>
#import "UIView+Doric.h"
Margin MarginMake(CGFloat left, CGFloat top, CGFloat right, CGFloat bottom) {
Margin margin;
margin.left = left;
margin.top = top;
margin.right = right;
margin.bottom = bottom;
return margin;
}
@implementation LayoutConfig
- (instancetype)init {
if (self = [super init]) {
_widthSpec = LayoutParamExact;
_heightSpec = LayoutParamExact;
}
return self;
}
- (instancetype)initWithWidth:(LayoutParam)width height:(LayoutParam)height {
if (self = [super init]) {
_widthSpec = width;
_heightSpec = height;
}
return self;
}
@end
@implementation MarginLayoutConfig
- (instancetype)init {
if (self = [super init]) {
_margin = MarginMake(0, 0, 0, 0);
}
return self;
}
- (instancetype)initWithWidth:(LayoutParam)width height:(LayoutParam)height margin:(Margin)margin {
if (self = [super initWithWidth:width height:height]) {
_margin = margin;
}
return self;
}
@end
@implementation StackLayoutConfig
@end
@implementation LinearLayoutConfig
@end
@interface LayoutContainer ()
@property(nonatomic, assign) BOOL waitingLayout;
@end
@implementation LayoutContainer
- (instancetype)init {
if (self = [super init]) {
_waitingLayout = NO;
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
_waitingLayout = NO;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
_waitingLayout = NO;
}
return self;
}
- (LayoutConfig *)configForChild:(UIView *)child {
LayoutConfig *config = child.layoutConfig;
if (!config) {
config = [[LayoutConfig alloc] init];
}
return config;
}
- (void)requestLayout {
if ([self.superview isKindOfClass:[LinearLayout class]]) {
[(LinearLayout *) self.superview requestLayout];
return;
}
if (self.waitingLayout) {
return;
}
self.waitingLayout = YES;
dispatch_async(dispatch_get_main_queue(), ^{
self.waitingLayout = NO;
[self sizeToFit];
[self layout];
});
}
- (void)layoutSubviews {
[super layoutSubviews];
[self requestLayout];
}
- (void)layout {
[self.subviews enumerateObjectsUsingBlock:^(__kindof UIView *child, NSUInteger idx, BOOL *stop) {
if ([child isKindOfClass:[LayoutContainer class]]) {
[(LayoutContainer *) child layout];
}
}];
}
@end
@interface Stack ()
@property(nonatomic, assign) CGFloat contentWidth;
@property(nonatomic, assign) CGFloat contentHeight;
@end
@implementation Stack
- (StackLayoutConfig *)configForChild:(UIView *)child {
StackLayoutConfig *config = (StackLayoutConfig *) child.layoutConfig;
if (!config) {
config = [[StackLayoutConfig alloc] init];
}
return config;
}
- (void)sizeToFit {
LayoutConfig *config = self.layoutConfig;
self.contentWidth = 0;
self.contentHeight = 0;
for (UIView *child in self.subviews) {
if (child.isHidden) {
continue;
}
StackLayoutConfig *childConfig = [self configForChild:child];
if ([child isKindOfClass:[LayoutContainer class]]
|| childConfig.widthSpec == LayoutParamWrapContent
|| childConfig.heightSpec == LayoutParamWrapContent) {
[child sizeToFit];
}
self.contentWidth = MAX(self.contentWidth, child.width);
self.contentHeight = MAX(self.contentHeight, child.height);
}
if (config.widthSpec == LayoutParamWrapContent) {
self.width = self.contentWidth;
} else if (config.widthSpec == LayoutParamAtMost) {
self.width = self.superview.width;
}
if (config.heightSpec == LayoutParamWrapContent) {
self.height = self.contentHeight;
} else if (config.heightSpec == LayoutParamAtMost) {
self.height = self.superview.height;
}
}
- (void)layout {
for (UIView *child in self.subviews) {
if (child.isHidden) {
continue;
}
StackLayoutConfig *childConfig = [self configForChild:child];
DoricGravity gravity = childConfig.alignment | self.gravity;
if ((gravity & LEFT) == LEFT) {
child.left = 0;
} else if ((gravity & RIGHT) == RIGHT) {
child.right = self.width;
} else if ((gravity & CENTER_X) == CENTER_X) {
child.centerX = self.width / 2;
}
if ((gravity & TOP) == TOP) {
child.top = 0;
} else if ((gravity & BOTTOM) == BOTTOM) {
child.bottom = self.height;
} else if ((gravity & CENTER_Y) == CENTER_Y) {
child.centerY = self.height / 2;
}
if (childConfig.widthSpec == LayoutParamAtMost) {
child.width = self.width;
}
if (childConfig.heightSpec == LayoutParamAtMost) {
child.height = self.height;
}
if ([child isKindOfClass:[LayoutContainer class]]) {
[(LayoutContainer *) child layout];
}
}
}
@end
@interface LinearLayout ()
@property(nonatomic, assign) CGFloat contentWidth;
@property(nonatomic, assign) CGFloat contentHeight;
@property(nonatomic, assign) NSUInteger contentWeight;
@end
@implementation LinearLayout
- (LinearLayoutConfig *)configForChild:(UIView *)child {
LinearLayoutConfig *config = (LinearLayoutConfig *) child.layoutConfig;
if (!config) {
config = [[LinearLayoutConfig alloc] init];
}
return config;
}
@end
@implementation VLayout
- (void)sizeToFit {
LayoutConfig *config = self.layoutConfig;
self.contentWidth = 0;
self.contentHeight = 0;
self.contentWeight = 0;
for (UIView *child in self.subviews) {
if (child.isHidden) {
continue;
}
LinearLayoutConfig *childConfig = [self configForChild:child];
if ([child isKindOfClass:[LayoutContainer class]]
|| childConfig.widthSpec == LayoutParamWrapContent
|| childConfig.heightSpec == LayoutParamWrapContent) {
[child sizeToFit];
}
self.contentWidth = MAX(self.contentWidth, child.width + childConfig.margin.left + childConfig.margin.right);
self.contentHeight += child.height + self.space + childConfig.margin.top + childConfig.margin.bottom;
self.contentWeight += childConfig.weight;
}
self.contentHeight -= self.space;
if (config.widthSpec == LayoutParamWrapContent) {
self.width = self.contentWidth;
} else if (config.widthSpec == LayoutParamAtMost) {
self.width = self.superview.width;
}
if (config.heightSpec == LayoutParamWrapContent) {
self.height = self.contentHeight;
} else if (config.heightSpec == LayoutParamAtMost) {
self.height = self.superview.height;
}
if (self.contentWeight) {
CGFloat remain = self.height - self.contentHeight;
for (UIView *child in self.subviews) {
if (child.isHidden) {
continue;
}
LinearLayoutConfig *childConfig = [self configForChild:child];
if (childConfig.weight) {
child.height += remain / self.contentWeight * childConfig.weight;
}
}
self.contentHeight = self.height;
}
}
- (void)layout {
CGFloat yStart = 0;
if ((self.gravity & TOP) == TOP) {
yStart = 0;
} else if ((self.gravity & BOTTOM) == BOTTOM) {
yStart = self.height - self.contentHeight;
} else if ((self.gravity & CENTER_Y) == CENTER_Y) {
yStart = (self.height - self.contentHeight) / 2;
}
for (UIView *child in self.subviews) {
if (child.isHidden) {
continue;
}
LinearLayoutConfig *childConfig = [self configForChild:child];
DoricGravity gravity = childConfig.alignment | self.gravity;
if ((gravity & LEFT) == LEFT) {
child.left = 0;
} else if ((gravity & RIGHT) == RIGHT) {
child.right = self.width;
} else if ((gravity & CENTER_X) == CENTER_X) {
child.centerX = self.width / 2;
} else {
if (childConfig.margin.left) {
child.left = childConfig.margin.left;
} else if (childConfig.margin.right) {
child.right = self.width - childConfig.margin.right;
}
}
if (childConfig.widthSpec == LayoutParamAtMost) {
child.width = self.width;
}
if (childConfig.heightSpec == LayoutParamAtMost) {
child.height = self.height - yStart - childConfig.margin.top - childConfig.margin.bottom - self.space;
}
if (childConfig.margin.top) {
yStart += childConfig.margin.top;
}
child.top = yStart;
yStart = child.bottom + self.space;
if (childConfig.margin.bottom) {
yStart += childConfig.margin.bottom;
}
if ([child isKindOfClass:[LayoutContainer class]]) {
[(LayoutContainer *) child layout];
}
}
}
@end
@implementation HLayout
- (void)sizeToFit {
LinearLayoutConfig *config;
if ([self.superview isKindOfClass:[LinearLayout class]]) {
config = [(LinearLayout *) self.superview configForChild:self];
} else {
config = (LinearLayoutConfig *) self.layoutConfig;
if (!config) {
config = [[LinearLayoutConfig alloc] init];
}
}
self.contentWidth = 0;
self.contentHeight = 0;
self.contentWeight = 0;
for (UIView *child in self.subviews) {
if (child.isHidden) {
continue;
}
LinearLayoutConfig *childConfig = [self configForChild:child];
if ([child isKindOfClass:[LayoutContainer class]]
|| childConfig.widthSpec == LayoutParamWrapContent
|| childConfig.heightSpec == LayoutParamWrapContent) {
[child sizeToFit];
}
self.contentHeight = MAX(self.contentHeight, child.height + childConfig.margin.top + childConfig.margin.bottom);
self.contentWidth += child.width + self.space + childConfig.margin.left + childConfig.margin.right;
self.contentWeight += childConfig.weight;
}
self.contentWidth -= self.space;
if (config.widthSpec == LayoutParamWrapContent) {
self.width = self.contentWidth;
} else if (config.widthSpec == LayoutParamAtMost) {
self.width = self.superview.width;
}
if (config.heightSpec == LayoutParamWrapContent) {
self.height = self.contentHeight;
} else if (config.heightSpec == LayoutParamAtMost) {
self.height = self.superview.height;
}
if (self.contentWeight) {
CGFloat remain = self.width - self.contentWidth;
for (UIView *child in self.subviews) {
if (child.isHidden) {
continue;
}
LinearLayoutConfig *childConfig = [self configForChild:child];
if (childConfig.weight) {
child.width += remain / self.contentWeight * childConfig.weight;
}
}
self.contentWidth = self.width;
}
}
- (void)layout {
CGFloat xStart = 0;
if ((self.gravity & LEFT) == LEFT) {
xStart = 0;
} else if ((self.gravity & RIGHT) == RIGHT) {
xStart = self.width - self.contentWidth;
} else if ((self.gravity & CENTER_X) == CENTER_X) {
xStart = (self.width - self.contentWidth) / 2;
}
for (UIView *child in self.subviews) {
if (child.isHidden) {
continue;
}
LinearLayoutConfig *childConfig = [self configForChild:child];
DoricGravity gravity = childConfig.alignment | self.gravity;
if ((gravity & TOP) == TOP) {
child.top = 0;
} else if ((gravity & BOTTOM) == BOTTOM) {
child.bottom = self.height;
} else if ((gravity & CENTER_Y) == CENTER_Y) {
child.centerY = self.height / 2;
} else {
if (childConfig.margin.top) {
child.top = childConfig.margin.top;
} else if (childConfig.margin.bottom) {
child.bottom = self.height - childConfig.margin.bottom;
}
}
if (childConfig.heightSpec == LayoutParamAtMost) {
child.height = self.height;
}
if (childConfig.widthSpec == LayoutParamAtMost) {
child.width = self.width - xStart - childConfig.margin.right - childConfig.margin.left - self.space;
}
if (childConfig.margin.left) {
xStart += childConfig.margin.left;
}
child.left = xStart;
xStart = child.right + self.space;
if (childConfig.margin.right) {
xStart += childConfig.margin.right;
}
if ([child isKindOfClass:[LayoutContainer class]]) {
[(LayoutContainer *) child layout];
}
}
}
@end
static const void *kLayoutConfig = &kLayoutConfig;
static const void *kTagString = &kTagString;
@implementation UIView (LayoutConfig)
@dynamic layoutConfig;
- (void)setLayoutConfig:(LayoutConfig *)layoutConfig {
objc_setAssociatedObject(self, kLayoutConfig, layoutConfig, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (LayoutConfig *)layoutConfig {
return objc_getAssociatedObject(self, kLayoutConfig);
}
- (void)setTagString:(NSString *)tagString {
objc_setAssociatedObject(self, kTagString, tagString, OBJC_ASSOCIATION_COPY_NONATOMIC);
self.tag = [tagString hash];
}
- (NSString *)tagString {
return objc_getAssociatedObject(self, kTagString);
}
- (UIView *)viewWithTagString:(NSString *)tagString {
// notice the potential hash collision
return [self viewWithTag:[tagString hash]];
}
@end
VLayout *vLayout(NSArray <__kindof UIView *> *views) {
VLayout *layout = [[VLayout alloc] initWithFrame:CGRectZero];
for (__kindof UIView *uiView in views) {
[layout addSubview:uiView];
}
layout.layoutConfig = [[LayoutConfig alloc] initWithWidth:LayoutParamWrapContent height:LayoutParamWrapContent];
return layout;
}
HLayout *hLayout(NSArray <__kindof UIView *> *views) {
HLayout *layout = [[HLayout alloc] initWithFrame:CGRectZero];
for (__kindof UIView *uiView in views) {
[layout addSubview:uiView];
}
layout.layoutConfig = [[LayoutConfig alloc] initWithWidth:LayoutParamWrapContent height:LayoutParamWrapContent];
return layout;
}
VLayout *vLayoutWithBlock(NSArray <UIView *(^)()> *blocks) {
VLayout *layout = [[VLayout alloc] initWithFrame:CGRectZero];
UIView *(^block)();
for (block in blocks) {
[layout addSubview:block()];
}
layout.layoutConfig = [[LayoutConfig alloc] initWithWidth:LayoutParamWrapContent height:LayoutParamWrapContent];
return layout;
}
HLayout *hLayoutWithBlock(NSArray <UIView *(^)()> *blocks) {
HLayout *layout = [[HLayout alloc] initWithFrame:CGRectZero];
UIView *(^block)();
for (block in blocks) {
[layout addSubview:block()];
}
layout.layoutConfig = [[LayoutConfig alloc] initWithWidth:LayoutParamWrapContent height:LayoutParamWrapContent];
return layout;
}

View File

@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface DoricRootNode : DoricStackNode
- (void)setupRootView:(UIView *)view;
- (void)setupRootView:(Stack *)view;
- (void)render:(NSDictionary *)props;
@end

View File

@ -23,17 +23,15 @@
#import "DoricRootNode.h"
@implementation DoricRootNode
- (void)setupRootView:(UIView *)view {
- (void)setupRootView:(Stack *)view {
self.view = view;
self.layoutConfig = view.layoutConfig;
}
- (void)render:(NSDictionary *)props {
[self blend:props];
[self requestLayout];
}
- (void)requestLayout {
[self measureByParent:self];
[self layoutByParent:self];
[self.view requestLayout];
}
@end

View File

@ -22,10 +22,5 @@
#import "DoricGroupNode.h"
NS_ASSUME_NONNULL_BEGIN
@interface DoricStackNode : DoricGroupNode<UIView *, StackLayoutParams *>
@property(nonatomic) DoricGravity gravity;
@interface DoricStackNode : DoricGroupNode<Stack *, StackLayoutConfig *>
@end
NS_ASSUME_NONNULL_END

View File

@ -25,93 +25,33 @@
@implementation DoricStackNode
- (instancetype)init {
if (self = [super init]) {
_gravity = 0;
}
return self;
- (Stack *)build:(NSDictionary *)props {
return [Stack new];
}
- (void)measureByParent:(DoricGroupNode *)parent {
DoricLayoutDesc widthSpec = self.layoutParams.width;
DoricLayoutDesc heightSpec = self.layoutParams.height;
CGFloat maxWidth = 0, maxHeight = 0;
for (DoricViewNode *child in self.indexedChildren) {
[child measureByParent:self];
CGFloat placeWidth = child.measuredWidth;
CGFloat placeHeight = child.measuredHeight;
maxWidth = MAX(maxWidth, placeWidth);
maxHeight = MAX(maxHeight, placeHeight);
}
self.desiredWidth = maxWidth;
self.desiredHeight = maxHeight;
if (widthSpec == LAYOUT_WRAP_CONTENT) {
self.width = maxWidth;
}
if (heightSpec == LAYOUT_WRAP_CONTENT) {
self.height = maxHeight;
- (void)blendView:(Stack *)view forPropName:(NSString *)name propValue:(id)prop {
if ([name isEqualToString:@"gravity"]) {
view.gravity = (DoricGravity) [(NSNumber *) prop integerValue];
} else {
[super blendView:view forPropName:name propValue:prop];
}
}
- (LayoutParams *)generateDefaultLayoutParams {
return [[StackLayoutParams alloc] init];
- (StackLayoutConfig *)generateDefaultLayoutParams {
return [[StackLayoutConfig alloc] init];
}
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig {
[super blendChild:child layoutConfig:layoutconfig];
if (![child.layoutParams isKindOfClass:StackLayoutParams.class]) {
DoricLog(@"blend Stack child error,layout params not match");
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig {
[super blendChild:child layoutConfig:layoutConfig];
if (![child.layoutConfig isKindOfClass:StackLayoutConfig.class]) {
DoricLog(@"blend HLayout child error,layout params not match");
return;
}
StackLayoutParams *params = (StackLayoutParams *) child.layoutParams;
// NSDictionary *margin = [layoutconfig objectForKey:@"margin"];
// if (margin) {
// params.margin.top = [(NSNumber *)[margin objectForKey:@"top"] floatValue];
// params.margin.left = [(NSNumber *)[margin objectForKey:@"left"] floatValue];
// params.margin.right = [(NSNumber *)[margin objectForKey:@"right"] floatValue];
// params.margin.bottom = [(NSNumber *)[margin objectForKey:@"bottom"] floatValue];
// }
NSNumber *alignment = layoutconfig[@"alignment"];
StackLayoutConfig *params = (StackLayoutConfig *) child.layoutConfig;
NSNumber *alignment = layoutConfig[@"alignment"];
if (alignment) {
params.alignment = [alignment integerValue];
params.alignment = (DoricGravity) [alignment integerValue];
}
}
- (void)layoutByParent:(DoricGroupNode *)parent {
for (DoricViewNode *child in self.indexedChildren) {
if (child.layoutParams.width == LAYOUT_MATCH_PARENT) {
child.width = self.width;
}
if (child.layoutParams.height == LAYOUT_MATCH_PARENT) {
child.height = self.height;
}
DoricGravity gravity = self.gravity;
if ([child.layoutParams isKindOfClass:StackLayoutParams.class]) {
StackLayoutParams *layoutParams = (StackLayoutParams *) child.layoutParams;
gravity |= layoutParams.alignment;
}
if ((gravity & LEFT) == LEFT) {
child.left = self.left;
}
if ((gravity & RIGHT) == RIGHT) {
child.right = self.right;
}
if ((gravity & TOP) == TOP) {
child.top = self.top;
}
if ((gravity & BOTTOM) == BOTTOM) {
child.bottom = self.bottom;
}
if ((gravity & CENTER_X) == CENTER_X) {
child.centerX = self.centerX;
}
if ((gravity & CENTER_Y) == CENTER_Y) {
child.centerY = self.centerY;
}
[child layoutByParent:self];
}
}
@end

View File

@ -37,7 +37,7 @@ - (void)blendView:(UILabel *)view forPropName:(NSString *)name propValue:(id)pro
} else if ([name isEqualToString:@"textColor"]) {
view.textColor = DoricColor(prop);
} else if ([name isEqualToString:@"textAlignment"]) {
DoricGravity gravity = [(NSNumber *) prop integerValue];
DoricGravity gravity = (DoricGravity) [(NSNumber *) prop integerValue];
NSTextAlignment alignment = NSTextAlignmentCenter;
switch (gravity) {
case LEFT:
@ -54,12 +54,4 @@ - (void)blendView:(UILabel *)view forPropName:(NSString *)name propValue:(id)pro
[super blendView:view forPropName:name propValue:prop];
}
}
- (void)measureByParent:(DoricGroupNode *)parent {
DoricLayoutDesc widthSpec = self.layoutParams.width;
DoricLayoutDesc heightSpec = self.layoutParams.height;
if (widthSpec == LAYOUT_WRAP_CONTENT || heightSpec == LAYOUT_WRAP_CONTENT) {
[self.view sizeToFit];
}
}
@end

View File

@ -22,11 +22,5 @@
#import "DoricGroupNode.h"
NS_ASSUME_NONNULL_BEGIN
@interface DoricVLayoutNode : DoricGroupNode<UIView *, VHLayoutParams *>
@property(nonatomic) CGFloat space;
@property(nonatomic) DoricGravity gravity;
@interface DoricVLayoutNode : DoricGroupNode<VLayout *, LinearLayoutConfig *>
@end
NS_ASSUME_NONNULL_END

View File

@ -24,19 +24,16 @@
#import "DoricUtil.h"
@implementation DoricVLayoutNode
- (instancetype)init {
if (self = [super init]) {
_space = 0;
_gravity = 0;
}
return self;
- (VLayout *)build:(NSDictionary *)props {
return [VLayout new];
}
- (void)blendView:(id)view forPropName:(NSString *)name propValue:(id)prop {
- (void)blendView:(VLayout *)view forPropName:(NSString *)name propValue:(id)prop {
if ([name isEqualToString:@"gravity"]) {
self.gravity = [(NSNumber *) prop integerValue];
view.gravity = (DoricGravity) [(NSNumber *) prop integerValue];
} else if ([name isEqualToString:@"space"]) {
self.space = [(NSNumber *) prop floatValue];
view.space = [(NSNumber *) prop floatValue];
} else {
[super blendView:view forPropName:name propValue:prop];
}
@ -44,92 +41,26 @@ - (void)blendView:(id)view forPropName:(NSString *)name propValue:(id)prop {
- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig {
[super blendChild:child layoutConfig:layoutconfig];
if (![child.layoutParams isKindOfClass:VHLayoutParams.class]) {
if (![child.layoutConfig isKindOfClass:LinearLayoutConfig.class]) {
DoricLog(@"blend VLayout child error,layout params not match");
return;
}
VHLayoutParams *params = (VHLayoutParams *) child.layoutParams;
LinearLayoutConfig *params = (LinearLayoutConfig *) child.layoutConfig;
NSDictionary *margin = layoutconfig[@"margin"];
if (margin) {
params.margin.top = [(NSNumber *) margin[@"top"] floatValue];
params.margin.left = [(NSNumber *) margin[@"left"] floatValue];
params.margin.right = [(NSNumber *) margin[@"right"] floatValue];
params.margin.bottom = [(NSNumber *) margin[@"bottom"] floatValue];
params.margin = MarginMake(
[(NSNumber *) margin[@"left"] floatValue],
[(NSNumber *) margin[@"top"] floatValue],
[(NSNumber *) margin[@"right"] floatValue],
[(NSNumber *) margin[@"bottom"] floatValue]);
}
NSNumber *alignment = layoutconfig[@"alignment"];
if (alignment) {
params.alignment = [alignment integerValue];
params.alignment = (DoricGravity) [alignment integerValue];
}
}
- (LayoutParams *)generateDefaultLayoutParams {
return [[VHLayoutParams alloc] init];
}
- (void)measureByParent:(DoricGroupNode *)parent {
DoricLayoutDesc widthSpec = self.layoutParams.width;
DoricLayoutDesc heightSpec = self.layoutParams.height;
CGFloat maxWidth = 0, maxHeight = 0;
for (DoricViewNode *child in self.indexedChildren) {
[child measureByParent:self];
CGFloat placeWidth = child.measuredWidth;
CGFloat placeHeight = child.measuredHeight;
maxWidth = MAX(maxWidth, placeWidth);
maxHeight += placeHeight + self.space;
}
maxHeight -= self.space;
self.desiredWidth = maxWidth;
self.desiredHeight = maxHeight;
if (widthSpec == LAYOUT_WRAP_CONTENT) {
self.width = maxWidth;
}
if (heightSpec == LAYOUT_WRAP_CONTENT) {
self.height = maxHeight;
}
}
- (void)layoutByParent:(DoricGroupNode *)parent {
if (self.layoutParams.width == LAYOUT_MATCH_PARENT) {
self.width = parent.width;
}
if (self.layoutParams.height == LAYOUT_MATCH_PARENT) {
self.height = parent.height;
}
// layout child
CGFloat yStart = 0;
if ((self.gravity & TOP) == TOP) {
yStart = 0;
} else if ((self.gravity & BOTTOM) == BOTTOM) {
yStart = self.height - self.desiredHeight;
} else if ((self.gravity & CENTER_Y) == CENTER_Y) {
yStart = (self.height - self.desiredHeight) / 2;
}
for (DoricViewNode *child in self.indexedChildren) {
if (child.layoutParams.width == LAYOUT_MATCH_PARENT) {
child.width = self.width;
}
if (child.layoutParams.height == LAYOUT_MATCH_PARENT) {
child.height = self.height;
}
if ([child.layoutParams isKindOfClass:VHLayoutParams.class]) {
VHLayoutParams *layoutParams = (VHLayoutParams *) child.layoutParams;
DoricGravity gravity = layoutParams.alignment | self.gravity;
if ((gravity & LEFT) == LEFT) {
child.left = 0;
} else if ((gravity & RIGHT) == RIGHT) {
child.right = self.width;
} else if ((gravity & CENTER_X) == CENTER_X) {
child.centerX = self.width / 2;
}
}
child.top = yStart;
yStart = child.bottom + self.space;
[child layoutByParent:self];
}
- (LinearLayoutConfig *)generateDefaultLayoutParams {
return [[LinearLayoutConfig alloc] init];
}
@end

View File

@ -1,101 +0,0 @@
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// DoricViewContainer.h
// Doric
//
// Created by pengfei.zhou on 2019/7/30.
//
#import "UIView+Doric.h"
typedef NS_ENUM(NSInteger, DoricGravity) {
SPECIFIED = 1,
START = 1 << 1,
END = 1 << 2,
SHIFT_X = 0,
SHIFT_Y = 4,
LEFT = (START | SPECIFIED) << SHIFT_X,
RIGHT = (END | SPECIFIED) << SHIFT_X,
TOP = (START | SPECIFIED) << SHIFT_Y,
BOTTOM = (END | SPECIFIED) << SHIFT_Y,
CENTER_X = SPECIFIED << SHIFT_X,
CENTER_Y = SPECIFIED << SHIFT_Y,
CENTER = CENTER_X | CENTER_Y,
};
typedef NS_ENUM(NSInteger, DoricLayoutDesc) {
LAYOUT_ABSOLUTE = 0,
LAYOUT_MATCH_PARENT = -1,
LAYOUT_WRAP_CONTENT = -2,
};
NS_ASSUME_NONNULL_BEGIN
@interface DoricRect : NSObject
@property(nonatomic) CGFloat left;
@property(nonatomic) CGFloat right;
@property(nonatomic) CGFloat top;
@property(nonatomic) CGFloat bottom;
@end
@interface LayoutParams : NSObject
@property(nonatomic) DoricLayoutDesc width;
@property(nonatomic) DoricLayoutDesc height;
@end
@interface MarginLayoutParams : LayoutParams
@property(nonatomic, strong) DoricRect *margin;
@end
@interface StackLayoutParams : LayoutParams
@property(nonatomic) DoricGravity alignment;
@end
@interface VHLayoutParams : MarginLayoutParams
@property(nonatomic) DoricGravity alignment;
@property(nonatomic) NSInteger weight;
@end
@interface UIView (DoricContainer)
@property(nonatomic, strong) LayoutParams *layoutParams;
- (void)layout;
- (void)measure;
@end
@interface Stack : UIView
@property(nonatomic) DoricGravity gravity;
@end
@interface LinearLayout : UIView
@property(nonatomic) DoricGravity gravity;
@property(nonatomic) CGFloat space;
@end
@interface VLayout : LinearLayout
@end
@interface HLayout : LinearLayout
@end
NS_ASSUME_NONNULL_END

View File

@ -1,108 +0,0 @@
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// DoricViewContainer.m
// Doric
//
// Created by pengfei.zhou on 2019/7/30.
//
#import "DoricViewContainer.h"
#import <objc/runtime.h>
@implementation DoricRect
- (instancetype)init {
if (self = [super init]) {
_left = 0;
_right = 0;
_top = 0;
_bottom = 0;
}
return self;
}
@end
@implementation LayoutParams
- (instancetype)init {
if (self = [super init]) {
_width = LAYOUT_WRAP_CONTENT;
_height = LAYOUT_WRAP_CONTENT;
}
return self;
}
@end
@implementation MarginLayoutParams
- (instancetype)init {
if (self = [super init]) {
_margin = [[DoricRect alloc] init];
}
return self;
}
@end
@implementation StackLayoutParams
- (instancetype)init {
if (self = [super init]) {
_alignment = 0;
}
return self;
}
@end
@implementation VHLayoutParams
- (instancetype)init {
if (self = [super init]) {
_alignment = 0;
_weight = 0;
}
return self;
}
@end
@implementation UIView (DoricContainer)
- (LayoutParams *)layoutParams {
return objc_getAssociatedObject(self, _cmd);
}
- (void)setLayoutParams:(LayoutParams *)layoutParams {
objc_setAssociatedObject(self, @selector(layoutParams), layoutParams, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)layout {
}
- (void)measure {
if (self.layoutParams) {
}
}
@end
@implementation Stack
@end
@implementation LinearLayout
@end
@implementation VLayout
@end
@implementation HLayout
@end

View File

@ -21,8 +21,7 @@
//
#import "DoricContextHolder.h"
#import "DoricViewContainer.h"
#import "DoricLinearLayout.h"
#import "UIView+Doric.h"
NS_ASSUME_NONNULL_BEGIN
@ -37,24 +36,10 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, strong) NSString *viewId;
@property(nonatomic, strong) LayoutParams *layoutParams;
@property(nonatomic, strong) LayoutConfig *layoutConfig;
@property(nonatomic, strong, readonly) NSArray<NSString *> *idList;
@property(nonatomic) CGFloat x;
@property(nonatomic) CGFloat y;
@property(nonatomic) CGFloat width;
@property(nonatomic) CGFloat height;
@property(nonatomic) CGFloat centerX;
@property(nonatomic) CGFloat centerY;
@property(nonatomic) CGFloat top;
@property(nonatomic) CGFloat left;
@property(nonatomic) CGFloat right;
@property(nonatomic) CGFloat bottom;
@property(nonatomic, readonly) CGFloat measuredWidth;
@property(nonatomic, readonly) CGFloat measuredHeight;
- (V)build:(NSDictionary *)props;
- (void)blend:(NSDictionary *)props;
@ -63,10 +48,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)callJSResponse:(NSString *)funcId, ...;
- (void)measureByParent:(DoricGroupNode *)parent;
- (void)layoutByParent:(DoricGroupNode *)parent;
+ (DoricViewNode *)create:(DoricContext *)context withType:(NSString *)type;
- (void)requestLayout;

View File

@ -85,6 +85,7 @@ - (void)blend:(NSDictionary *)props {
if (self.view == nil) {
self.view = [self build:props];
}
self.view.layoutConfig = self.layoutConfig;
for (NSString *key in props) {
id value = props[key];
[self blendView:self.view forPropName:key propValue:value];
@ -94,18 +95,12 @@ - (void)blend:(NSDictionary *)props {
- (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop {
if ([name isEqualToString:@"width"]) {
NSNumber *width = (NSNumber *) prop;
if ([width integerValue] < 0) {
self.layoutParams.width = [width integerValue];
} else {
self.layoutParams.width = LAYOUT_ABSOLUTE;
if ([width floatValue] >= 0) {
view.width = [width floatValue];
}
} else if ([name isEqualToString:@"height"]) {
NSNumber *height = (NSNumber *) prop;
if ([height integerValue] < 0) {
self.layoutParams.height = [height integerValue];
} else {
self.layoutParams.height = LAYOUT_ABSOLUTE;
if ([height floatValue] >= 0) {
view.height = [height floatValue];
}
} else if ([name isEqualToString:@"x"]) {
@ -119,14 +114,14 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop
[self.parent blendChild:self layoutConfig:prop];
}
} else if ([name isEqualToString:@"onClick"]) {
[self.callbackIds setObject:prop forKey:@"onClick"];
self.callbackIds[@"onClick"] = prop;
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesturRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClick:)];
[view addGestureRecognizer:tapGesturRecognizer];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClick:)];
[view addGestureRecognizer:tapGestureRecognizer];
} else if ([name isEqualToString:@"border"]) {
NSDictionary *dic = prop;
CGFloat width = [(NSNumber *) [dic objectForKey:@"width"] floatValue];
UIColor *color = DoricColor((NSNumber *) [dic objectForKey:@"color"]);
CGFloat width = [(NSNumber *) dic[@"width"] floatValue];
UIColor *color = DoricColor((NSNumber *) dic[@"color"]);
view.layer.borderWidth = width;
view.layer.borderColor = color.CGColor;
} else if ([name isEqualToString:@"corners"]) {
@ -134,10 +129,10 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop
view.layer.cornerRadius = [(NSNumber *) prop floatValue];
} else if ([prop isKindOfClass:NSDictionary.class]) {
NSDictionary *dic = prop;
CGFloat leftTop = [(NSNumber *) [dic objectForKey:@"leftTop"] floatValue];
CGFloat rightTop = [(NSNumber *) [dic objectForKey:@"rightTop"] floatValue];
CGFloat rightBottom = [(NSNumber *) [dic objectForKey:@"rightBottom"] floatValue];
CGFloat leftBottom = [(NSNumber *) [dic objectForKey:@"leftBottom"] floatValue];
CGFloat leftTop = [(NSNumber *) dic[@"leftTop"] floatValue];
CGFloat rightTop = [(NSNumber *) dic[@"rightTop"] floatValue];
CGFloat rightBottom = [(NSNumber *) dic[@"rightBottom"] floatValue];
CGFloat leftBottom = [(NSNumber *) dic[@"leftBottom"] floatValue];
CALayer *mask = nil;
if (ABS(leftTop - rightTop) > CGFLOAT_MIN
|| ABS(leftTop - rightBottom) > CGFLOAT_MIN
@ -155,14 +150,14 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop
}
} else if ([name isEqualToString:@"shadow"]) {
NSDictionary *dic = prop;
CGFloat opacity = [(NSNumber *) [dic objectForKey:@"opacity"] floatValue];
CGFloat opacity = [(NSNumber *) dic[@"opacity"] floatValue];
if (opacity > CGFLOAT_MIN) {
view.clipsToBounds = NO;
UIColor *color = DoricColor((NSNumber *) [dic objectForKey:@"color"]);
UIColor *color = DoricColor((NSNumber *) dic[@"color"]);
view.layer.shadowColor = color.CGColor;
view.layer.shadowRadius = [(NSNumber *) [dic objectForKey:@"radius"] floatValue];
view.layer.shadowOffset = CGSizeMake([(NSNumber *) [dic objectForKey:@"offsetX"] floatValue], [(NSNumber *) [dic objectForKey:@"offsetY"] floatValue]);
view.layer.shadowOpacity = opacity;
view.layer.shadowRadius = [(NSNumber *) dic[@"radius"] floatValue];
view.layer.shadowOffset = CGSizeMake([(NSNumber *) dic[@"offsetX"] floatValue], [(NSNumber *) dic[@"offsetY"] floatValue]);
view.layer.shadowOpacity = (float) opacity;
} else {
view.clipsToBounds = YES;
}
@ -173,35 +168,7 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop
}
- (void)onClick:(UIView *)view {
[self callJSResponse:[self.callbackIds objectForKey:@"onClick"], nil];
}
- (CGFloat)measuredWidth {
if ([self.layoutParams isKindOfClass:MarginLayoutParams.class]) {
MarginLayoutParams *marginParams = (MarginLayoutParams *) self.layoutParams;
return self.width + marginParams.margin.left + marginParams.margin.right;
}
return self.width;
}
- (CGFloat)measuredHeight {
if ([self.layoutParams isKindOfClass:MarginLayoutParams.class]) {
MarginLayoutParams *marginParams = (MarginLayoutParams *) self.layoutParams;
return self.height + marginParams.margin.top + marginParams.margin.bottom;
}
return self.height;
}
- (void)measureByParent:(DoricGroupNode *)parent {
DoricLayoutDesc widthSpec = self.layoutParams.width;
DoricLayoutDesc heightSpec = self.layoutParams.height;
if (widthSpec == LAYOUT_WRAP_CONTENT || heightSpec == LAYOUT_WRAP_CONTENT) {
[self.view sizeToFit];
}
}
- (void)layoutByParent:(DoricGroupNode *)parent {
[self callJSResponse:self.callbackIds[@"onClick"], nil];
}
- (NSArray<NSString *> *)idList {
@ -232,87 +199,7 @@ - (void)callJSResponse:(NSString *)funcId, ... {
+ (DoricViewNode *)create:(DoricContext *)context withType:(NSString *)type {
DoricRegistry *registry = context.driver.registry;
Class clz = [registry acquireViewNode:type];
return [[clz alloc] initWithContext:context];
}
- (CGFloat)x {
return ((UIView *) self.view).x;
}
- (CGFloat)y {
return ((UIView *) self.view).y;
}
- (CGFloat)width {
return ((UIView *) self.view).width;
}
- (CGFloat)height {
return ((UIView *) self.view).height;
}
- (CGFloat)top {
return ((UIView *) self.view).top;
}
- (CGFloat)bottom {
return ((UIView *) self.view).bottom;
}
- (CGFloat)left {
return ((UIView *) self.view).left;
}
- (CGFloat)right {
return ((UIView *) self.view).right;
}
- (CGFloat)centerX {
return ((UIView *) self.view).centerX;
}
- (CGFloat)centerY {
return ((UIView *) self.view).centerY;
}
- (void)setX:(CGFloat)x {
((UIView *) self.view).x = x;
}
- (void)setY:(CGFloat)y {
((UIView *) self.view).y = y;
}
- (void)setWidth:(CGFloat)width {
((UIView *) self.view).width = width;
}
- (void)setHeight:(CGFloat)height {
((UIView *) self.view).height = height;
}
- (void)setLeft:(CGFloat)left {
((UIView *) self.view).left = left;
}
- (void)setRight:(CGFloat)right {
((UIView *) self.view).right = right;
}
- (void)setTop:(CGFloat)top {
((UIView *) self.view).top = top;
}
- (void)setBottom:(CGFloat)bottom {
((UIView *) self.view).bottom = bottom;
}
- (void)setCenterX:(CGFloat)centerX {
((UIView *) self.view).centerX = centerX;
}
- (void)setCenterY:(CGFloat)centerY {
((UIView *) self.view).centerY = centerY;
return [(DoricViewNode *) [clz alloc] initWithContext:context];
}
- (void)requestLayout {

View File

@ -0,0 +1,29 @@
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Created by pengfei.zhou on 2019/10/23.
//
#import <Foundation/Foundation.h>
@interface NSObject (Doric)
- (id)apply:(id (^)(id it))block;
- (instancetype)also:(void (^)(id it))block;
- (void)let:(void (^)(id it))block;
@end

View File

@ -0,0 +1,35 @@
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Created by pengfei.zhou on 2019/10/23.
//
#import "DoricExtensions.h"
@implementation NSObject (Doric)
- (id)apply:(id (^)(id it))block {
return block(self);
}
- (instancetype)also:(void (^)(id it))block {
block(self);
return self;
}
- (void)let:(void (^)(id it))block {
block(self);
}
@end