From c2e3ef4f7e7ab17caaf7b336b0e3a83034c97078 Mon Sep 17 00:00:00 2001 From: "pengfei.zhou" Date: Wed, 23 Oct 2019 19:37:06 +0800 Subject: [PATCH] feat:iOS LinearLayout and adapt new layoutconfig --- iOS/Example/Example/ViewController.m | 13 +- iOS/Example/Podfile.lock | 2 +- iOS/Pod/Classes/DoricContext.h | 2 +- iOS/Pod/Classes/DoricContext.m | 9 +- iOS/Pod/Classes/Shader/DoricGroupNode.h | 6 +- iOS/Pod/Classes/Shader/DoricGroupNode.m | 41 +- iOS/Pod/Classes/Shader/DoricHLayoutNode.h | 8 +- iOS/Pod/Classes/Shader/DoricHLayoutNode.m | 110 +---- iOS/Pod/Classes/Shader/DoricImageNode.m | 8 - iOS/Pod/Classes/Shader/DoricLinearLayout.h | 117 +++++ iOS/Pod/Classes/Shader/DoricLinearLayout.m | 505 ++++++++++++++++++++ iOS/Pod/Classes/Shader/DoricRootNode.h | 2 +- iOS/Pod/Classes/Shader/DoricRootNode.m | 8 +- iOS/Pod/Classes/Shader/DoricStackNode.h | 7 +- iOS/Pod/Classes/Shader/DoricStackNode.m | 92 +--- iOS/Pod/Classes/Shader/DoricTextNode.m | 10 +- iOS/Pod/Classes/Shader/DoricVLayoutNode.h | 8 +- iOS/Pod/Classes/Shader/DoricVLayoutNode.m | 101 +--- iOS/Pod/Classes/Shader/DoricViewContainer.h | 101 ---- iOS/Pod/Classes/Shader/DoricViewContainer.m | 108 ----- iOS/Pod/Classes/Shader/DoricViewNode.h | 23 +- iOS/Pod/Classes/Shader/DoricViewNode.m | 151 +----- iOS/Pod/Classes/{ => Shader}/UIView+Doric.h | 0 iOS/Pod/Classes/{ => Shader}/UIView+Doric.m | 0 iOS/Pod/Classes/Util/DoricExtensions.h | 29 ++ iOS/Pod/Classes/Util/DoricExtensions.m | 35 ++ 26 files changed, 813 insertions(+), 683 deletions(-) create mode 100644 iOS/Pod/Classes/Shader/DoricLinearLayout.h create mode 100644 iOS/Pod/Classes/Shader/DoricLinearLayout.m delete mode 100644 iOS/Pod/Classes/Shader/DoricViewContainer.h delete mode 100644 iOS/Pod/Classes/Shader/DoricViewContainer.m rename iOS/Pod/Classes/{ => Shader}/UIView+Doric.h (100%) rename iOS/Pod/Classes/{ => Shader}/UIView+Doric.m (100%) create mode 100644 iOS/Pod/Classes/Util/DoricExtensions.h create mode 100644 iOS/Pod/Classes/Util/DoricExtensions.m diff --git a/iOS/Example/Example/ViewController.m b/iOS/Example/Example/ViewController.m index dabf77b3..806bc11d 100644 --- a/iOS/Example/Example/ViewController.m +++ b/iOS/Example/Example/ViewController.m @@ -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]; diff --git a/iOS/Example/Podfile.lock b/iOS/Example/Podfile.lock index 9c0eaa93..a41b13c0 100644 --- a/iOS/Example/Podfile.lock +++ b/iOS/Example/Podfile.lock @@ -25,7 +25,7 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - Doric: 1dae6e8a19a32a27af975f787509cc0658dde095 + Doric: f96b77d435e836e88cf02319e3c9ebc08cab65f6 GCDWebServer: ead88cd14596dd4eae4f5830b8877c87c8728990 SDWebImage: 920f1a2ff1ca8296ad34f6e0510a1ef1d70ac965 SocketRocket: d57c7159b83c3c6655745cd15302aa24b6bae531 diff --git a/iOS/Pod/Classes/DoricContext.h b/iOS/Pod/Classes/DoricContext.h index 0829fff0..6607ba4b 100644 --- a/iOS/Pod/Classes/DoricContext.h +++ b/iOS/Pod/Classes/DoricContext.h @@ -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; diff --git a/iOS/Pod/Classes/DoricContext.m b/iOS/Pod/Classes/DoricContext.m index 222b131d..fa07d1df 100644 --- a/iOS/Pod/Classes/DoricContext.m +++ b/iOS/Pod/Classes/DoricContext.m @@ -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]; } diff --git a/iOS/Pod/Classes/Shader/DoricGroupNode.h b/iOS/Pod/Classes/Shader/DoricGroupNode.h index cd15a6bc..fe717a3a 100644 --- a/iOS/Pod/Classes/Shader/DoricGroupNode.h +++ b/iOS/Pod/Classes/Shader/DoricGroupNode.h @@ -24,15 +24,13 @@ NS_ASSUME_NONNULL_BEGIN -@interface DoricGroupNode : DoricViewNode +@interface DoricGroupNode : DoricViewNode @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 diff --git a/iOS/Pod/Classes/Shader/DoricGroupNode.m b/iOS/Pod/Classes/Shader/DoricGroupNode.m index 0aefa8dc..ba5c6764 100644 --- a/iOS/Pod/Classes/Shader/DoricGroupNode.m +++ b/iOS/Pod/Classes/Shader/DoricGroupNode.m @@ -20,6 +20,7 @@ // Created by pengfei.zhou on 2019/7/30. // +#import #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]); } } diff --git a/iOS/Pod/Classes/Shader/DoricHLayoutNode.h b/iOS/Pod/Classes/Shader/DoricHLayoutNode.h index 458546f7..d429f102 100644 --- a/iOS/Pod/Classes/Shader/DoricHLayoutNode.h +++ b/iOS/Pod/Classes/Shader/DoricHLayoutNode.h @@ -22,11 +22,5 @@ #import "DoricGroupNode.h" -NS_ASSUME_NONNULL_BEGIN - -@interface DoricHLayoutNode : DoricGroupNode -@property(nonatomic) CGFloat space; -@property(nonatomic) DoricGravity gravity; +@interface DoricHLayoutNode : DoricGroupNode @end - -NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/Shader/DoricHLayoutNode.m b/iOS/Pod/Classes/Shader/DoricHLayoutNode.m index 26fb4d06..29d6a639 100644 --- a/iOS/Pod/Classes/Shader/DoricHLayoutNode.m +++ b/iOS/Pod/Classes/Shader/DoricHLayoutNode.m @@ -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 diff --git a/iOS/Pod/Classes/Shader/DoricImageNode.m b/iOS/Pod/Classes/Shader/DoricImageNode.m index 28c28195..470d8477 100644 --- a/iOS/Pod/Classes/Shader/DoricImageNode.m +++ b/iOS/Pod/Classes/Shader/DoricImageNode.m @@ -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 diff --git a/iOS/Pod/Classes/Shader/DoricLinearLayout.h b/iOS/Pod/Classes/Shader/DoricLinearLayout.h new file mode 100644 index 00000000..300cd46d --- /dev/null +++ b/iOS/Pod/Classes/Shader/DoricLinearLayout.h @@ -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 + + +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 : UIView + +- (T)configForChild:(__kindof UIView *)child; + +- (void)layout; + +- (void)requestLayout; +@end + +@interface Stack : LayoutContainer +@property(nonatomic, assign) DoricGravity gravity; +@end + +@interface LinearLayout : LayoutContainer +@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 *blocks); + +HLayout *hLayoutWithBlock(NSArray *blocks); diff --git a/iOS/Pod/Classes/Shader/DoricLinearLayout.m b/iOS/Pod/Classes/Shader/DoricLinearLayout.m new file mode 100644 index 00000000..7ddc1db8 --- /dev/null +++ b/iOS/Pod/Classes/Shader/DoricLinearLayout.m @@ -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 +#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 *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 *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; +} \ No newline at end of file diff --git a/iOS/Pod/Classes/Shader/DoricRootNode.h b/iOS/Pod/Classes/Shader/DoricRootNode.h index 9fa950b6..cc945e8c 100644 --- a/iOS/Pod/Classes/Shader/DoricRootNode.h +++ b/iOS/Pod/Classes/Shader/DoricRootNode.h @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @interface DoricRootNode : DoricStackNode -- (void)setupRootView:(UIView *)view; +- (void)setupRootView:(Stack *)view; - (void)render:(NSDictionary *)props; @end diff --git a/iOS/Pod/Classes/Shader/DoricRootNode.m b/iOS/Pod/Classes/Shader/DoricRootNode.m index 6df879d9..69f2ad28 100644 --- a/iOS/Pod/Classes/Shader/DoricRootNode.m +++ b/iOS/Pod/Classes/Shader/DoricRootNode.m @@ -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 diff --git a/iOS/Pod/Classes/Shader/DoricStackNode.h b/iOS/Pod/Classes/Shader/DoricStackNode.h index 71719c10..9d6cbe98 100644 --- a/iOS/Pod/Classes/Shader/DoricStackNode.h +++ b/iOS/Pod/Classes/Shader/DoricStackNode.h @@ -22,10 +22,5 @@ #import "DoricGroupNode.h" -NS_ASSUME_NONNULL_BEGIN - -@interface DoricStackNode : DoricGroupNode -@property(nonatomic) DoricGravity gravity; +@interface DoricStackNode : DoricGroupNode @end - -NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/Shader/DoricStackNode.m b/iOS/Pod/Classes/Shader/DoricStackNode.m index 50491d08..4023605a 100644 --- a/iOS/Pod/Classes/Shader/DoricStackNode.m +++ b/iOS/Pod/Classes/Shader/DoricStackNode.m @@ -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 diff --git a/iOS/Pod/Classes/Shader/DoricTextNode.m b/iOS/Pod/Classes/Shader/DoricTextNode.m index 8c7dbd02..d723c4ad 100644 --- a/iOS/Pod/Classes/Shader/DoricTextNode.m +++ b/iOS/Pod/Classes/Shader/DoricTextNode.m @@ -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 diff --git a/iOS/Pod/Classes/Shader/DoricVLayoutNode.h b/iOS/Pod/Classes/Shader/DoricVLayoutNode.h index 54c15fa3..2ed3bfbd 100644 --- a/iOS/Pod/Classes/Shader/DoricVLayoutNode.h +++ b/iOS/Pod/Classes/Shader/DoricVLayoutNode.h @@ -22,11 +22,5 @@ #import "DoricGroupNode.h" -NS_ASSUME_NONNULL_BEGIN - -@interface DoricVLayoutNode : DoricGroupNode -@property(nonatomic) CGFloat space; -@property(nonatomic) DoricGravity gravity; +@interface DoricVLayoutNode : DoricGroupNode @end - -NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/Shader/DoricVLayoutNode.m b/iOS/Pod/Classes/Shader/DoricVLayoutNode.m index 3ce2cacf..94b00765 100644 --- a/iOS/Pod/Classes/Shader/DoricVLayoutNode.m +++ b/iOS/Pod/Classes/Shader/DoricVLayoutNode.m @@ -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 diff --git a/iOS/Pod/Classes/Shader/DoricViewContainer.h b/iOS/Pod/Classes/Shader/DoricViewContainer.h deleted file mode 100644 index d15eb2f1..00000000 --- a/iOS/Pod/Classes/Shader/DoricViewContainer.h +++ /dev/null @@ -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 diff --git a/iOS/Pod/Classes/Shader/DoricViewContainer.m b/iOS/Pod/Classes/Shader/DoricViewContainer.m deleted file mode 100644 index 8abbcdac..00000000 --- a/iOS/Pod/Classes/Shader/DoricViewContainer.m +++ /dev/null @@ -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 - -@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 diff --git a/iOS/Pod/Classes/Shader/DoricViewNode.h b/iOS/Pod/Classes/Shader/DoricViewNode.h index 543f87c0..36dcc378 100644 --- a/iOS/Pod/Classes/Shader/DoricViewNode.h +++ b/iOS/Pod/Classes/Shader/DoricViewNode.h @@ -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 *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; diff --git a/iOS/Pod/Classes/Shader/DoricViewNode.m b/iOS/Pod/Classes/Shader/DoricViewNode.m index de219bd2..e4d4b214 100644 --- a/iOS/Pod/Classes/Shader/DoricViewNode.m +++ b/iOS/Pod/Classes/Shader/DoricViewNode.m @@ -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 *)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 { diff --git a/iOS/Pod/Classes/UIView+Doric.h b/iOS/Pod/Classes/Shader/UIView+Doric.h similarity index 100% rename from iOS/Pod/Classes/UIView+Doric.h rename to iOS/Pod/Classes/Shader/UIView+Doric.h diff --git a/iOS/Pod/Classes/UIView+Doric.m b/iOS/Pod/Classes/Shader/UIView+Doric.m similarity index 100% rename from iOS/Pod/Classes/UIView+Doric.m rename to iOS/Pod/Classes/Shader/UIView+Doric.m diff --git a/iOS/Pod/Classes/Util/DoricExtensions.h b/iOS/Pod/Classes/Util/DoricExtensions.h new file mode 100644 index 00000000..aba09103 --- /dev/null +++ b/iOS/Pod/Classes/Util/DoricExtensions.h @@ -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 + + +@interface NSObject (Doric) +- (id)apply:(id (^)(id it))block; + +- (instancetype)also:(void (^)(id it))block; + +- (void)let:(void (^)(id it))block; +@end diff --git a/iOS/Pod/Classes/Util/DoricExtensions.m b/iOS/Pod/Classes/Util/DoricExtensions.m new file mode 100644 index 00000000..b3b92987 --- /dev/null +++ b/iOS/Pod/Classes/Util/DoricExtensions.m @@ -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