From f0430a32a7b52a3de55a8490755d76d3c98f7692 Mon Sep 17 00:00:00 2001 From: "pengfei.zhou" Date: Tue, 19 Nov 2019 11:21:49 +0800 Subject: [PATCH] feat:add scrollview on iOS --- .../main/java/pub/doric/shader/GroupNode.java | 6 -- .../main/java/pub/doric/shader/ViewNode.java | 3 + demo/src/ScrollerDemo.ts | 5 +- iOS/Example/Example.xcodeproj/project.pbxproj | 4 + iOS/Example/Example/ViewController.m | 2 +- iOS/Pod/Classes/DoricRegistry.m | 2 + iOS/Pod/Classes/Shader/DoricGroupNode.h | 1 - iOS/Pod/Classes/Shader/DoricGroupNode.m | 1 - iOS/Pod/Classes/Shader/DoricListItemNode.m | 5 ++ iOS/Pod/Classes/Shader/DoricScrollerNode.h | 9 ++ iOS/Pod/Classes/Shader/DoricScrollerNode.m | 90 +++++++++++++++++++ iOS/Pod/Classes/Shader/DoricSuperNode.h | 2 + iOS/Pod/Classes/Shader/DoricViewNode.m | 3 + 13 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 iOS/Pod/Classes/Shader/DoricScrollerNode.h create mode 100644 iOS/Pod/Classes/Shader/DoricScrollerNode.m diff --git a/Android/doric/src/main/java/pub/doric/shader/GroupNode.java b/Android/doric/src/main/java/pub/doric/shader/GroupNode.java index e1316229..0eb3f101 100644 --- a/Android/doric/src/main/java/pub/doric/shader/GroupNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/GroupNode.java @@ -78,9 +78,6 @@ public abstract class GroupNode extends SuperNode { mView.removeView(oldNode.getDoricLayer()); ViewNode newNode = ViewNode.create(getDoricContext(), type); newNode.setId(id); - if (newNode instanceof SuperNode) { - ((SuperNode) newNode).mReusable = this.mReusable; - } newNode.init(this); newNode.blend(model.getProperty("props").asObject()); mChildNodes.add(idx, newNode); @@ -124,9 +121,6 @@ public abstract class GroupNode extends SuperNode { //Insert ViewNode newNode = ViewNode.create(getDoricContext(), type); newNode.setId(id); - if (newNode instanceof SuperNode) { - ((SuperNode) newNode).mReusable = this.mReusable; - } newNode.init(this); newNode.blend(model.getProperty("props").asObject()); mChildNodes.add(newNode); diff --git a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java index 76e8833e..c62f2f0d 100644 --- a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java @@ -55,6 +55,9 @@ public abstract class ViewNode extends DoricContextHolder { private DoricLayer doricLayer; public void init(SuperNode superNode) { + if (this instanceof SuperNode) { + ((SuperNode) this).mReusable = superNode.mReusable; + } this.mSuperNode = superNode; this.mLayoutParams = superNode.generateDefaultLayoutParams(); this.doricLayer = new DoricLayer(getContext()); diff --git a/demo/src/ScrollerDemo.ts b/demo/src/ScrollerDemo.ts index 9373f322..153ffcb8 100644 --- a/demo/src/ScrollerDemo.ts +++ b/demo/src/ScrollerDemo.ts @@ -37,8 +37,7 @@ class ScrollerPanel extends Panel { width: 80, height: 50, })), - ] - )), + ]).also(it => it.space = 20)), hlayout([ ...[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5].map(e => text({ text: colors[e % colors.length], @@ -55,6 +54,6 @@ class ScrollerPanel extends Panel { ] ), ] - ))) + ).also(it => it.space = 20))) } } \ No newline at end of file diff --git a/iOS/Example/Example.xcodeproj/project.pbxproj b/iOS/Example/Example.xcodeproj/project.pbxproj index 0a47f19e..b7439e60 100644 --- a/iOS/Example/Example.xcodeproj/project.pbxproj +++ b/iOS/Example/Example.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ E2334B0822E9D2070098A085 /* ExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E2334B0722E9D2070098A085 /* ExampleTests.m */; }; E2334B1322E9D2070098A085 /* ExampleUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = E2334B1222E9D2070098A085 /* ExampleUITests.m */; }; E2BF7BF7237E8E9F001B0EDC /* ListDemo.js in Resources */ = {isa = PBXBuildFile; fileRef = E2BF7BF6237E8E9F001B0EDC /* ListDemo.js */; }; + E2F447F42383924B00073C7F /* ScrollerDemo.js in Resources */ = {isa = PBXBuildFile; fileRef = E2F447F32383924B00073C7F /* ScrollerDemo.js */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -69,6 +70,7 @@ E2334B1222E9D2070098A085 /* ExampleUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExampleUITests.m; sourceTree = ""; }; E2334B1422E9D2070098A085 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E2BF7BF6237E8E9F001B0EDC /* ListDemo.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = ListDemo.js; sourceTree = ""; }; + E2F447F32383924B00073C7F /* ScrollerDemo.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = ScrollerDemo.js; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -125,6 +127,7 @@ E21DC9D12302865E00660C5C /* src */ = { isa = PBXGroup; children = ( + E2F447F32383924B00073C7F /* ScrollerDemo.js */, E2BF7BF6237E8E9F001B0EDC /* ListDemo.js */, E21DC9D22302865E00660C5C /* Snake.js */, E21DC9D32302865E00660C5C /* Counter.js */, @@ -298,6 +301,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + E2F447F42383924B00073C7F /* ScrollerDemo.js in Resources */, E2BF7BF7237E8E9F001B0EDC /* ListDemo.js in Resources */, E21DC9D42302870000660C5C /* Snake.js in Resources */, E21DC9D52302870000660C5C /* Counter.js in Resources */, diff --git a/iOS/Example/Example/ViewController.m b/iOS/Example/Example/ViewController.m index cd3b433d..25917f3a 100644 --- a/iOS/Example/Example/ViewController.m +++ b/iOS/Example/Example/ViewController.m @@ -26,7 +26,7 @@ @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; - NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"ListDemo" ofType:@"js"]; + NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"ScrollerDemo" ofType:@"js"]; NSString *jsContent = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; self.doricContext = [[DoricContext alloc] initWithScript:jsContent source:@"test.js"]; [self.doricContext.rootNode setupRootView:[[DoricStackView new] also:^(DoricStackView *it) { diff --git a/iOS/Pod/Classes/DoricRegistry.m b/iOS/Pod/Classes/DoricRegistry.m index 81b3b0c2..aad88e40 100644 --- a/iOS/Pod/Classes/DoricRegistry.m +++ b/iOS/Pod/Classes/DoricRegistry.m @@ -30,6 +30,7 @@ #import "DoricImageNode.h" #import "DoricListNode.h" #import "DoricListItemNode.h" +#import "DoricScrollerNode.h" @interface DoricRegistry () @@ -62,6 +63,7 @@ - (void)innerRegister { [self registerViewNode:DoricImageNode.class withName:@"Image"]; [self registerViewNode:DoricListNode.class withName:@"List"]; [self registerViewNode:DoricListItemNode.class withName:@"ListItem"]; + [self registerViewNode:DoricScrollerNode.class withName:@"Scroller"]; } - (void)registerJSBundle:(NSString *)bundle withName:(NSString *)name { diff --git a/iOS/Pod/Classes/Shader/DoricGroupNode.h b/iOS/Pod/Classes/Shader/DoricGroupNode.h index deccf40b..202eb7c2 100644 --- a/iOS/Pod/Classes/Shader/DoricGroupNode.h +++ b/iOS/Pod/Classes/Shader/DoricGroupNode.h @@ -25,7 +25,6 @@ NS_ASSUME_NONNULL_BEGIN @interface DoricGroupNode : DoricSuperNode -@property(nonatomic, assign) BOOL reusable; @end NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/Shader/DoricGroupNode.m b/iOS/Pod/Classes/Shader/DoricGroupNode.m index a8bd3141..c35156fb 100644 --- a/iOS/Pod/Classes/Shader/DoricGroupNode.m +++ b/iOS/Pod/Classes/Shader/DoricGroupNode.m @@ -34,7 +34,6 @@ - (instancetype)initWithContext:(DoricContext *)doricContext { if (self = [super initWithContext:doricContext]) { _childNodes = @[]; _childViewIds = @[]; - _reusable = NO; } return self; } diff --git a/iOS/Pod/Classes/Shader/DoricListItemNode.m b/iOS/Pod/Classes/Shader/DoricListItemNode.m index db72ab85..f08abf9d 100644 --- a/iOS/Pod/Classes/Shader/DoricListItemNode.m +++ b/iOS/Pod/Classes/Shader/DoricListItemNode.m @@ -38,6 +38,11 @@ - (instancetype)initWithContext:(DoricContext *)doricContext { return self; } +- (void)initWithSuperNode:(DoricSuperNode *)superNode { + [super initWithSuperNode:superNode]; + self.reusable = YES; +} + - (DoricStackView *)build { return [DoricListItemView new]; } diff --git a/iOS/Pod/Classes/Shader/DoricScrollerNode.h b/iOS/Pod/Classes/Shader/DoricScrollerNode.h new file mode 100644 index 00000000..a61a48cb --- /dev/null +++ b/iOS/Pod/Classes/Shader/DoricScrollerNode.h @@ -0,0 +1,9 @@ +// +// Created by pengfei.zhou on 2019/11/19. +// + +#import +#import "DoricSuperNode.h" + +@interface DoricScrollerNode : DoricSuperNode +@end \ No newline at end of file diff --git a/iOS/Pod/Classes/Shader/DoricScrollerNode.m b/iOS/Pod/Classes/Shader/DoricScrollerNode.m new file mode 100644 index 00000000..0c48225d --- /dev/null +++ b/iOS/Pod/Classes/Shader/DoricScrollerNode.m @@ -0,0 +1,90 @@ +// +// Created by pengfei.zhou on 2019/11/19. +// + +#import "DoricScrollerNode.h" +#import "DoricExtensions.h" + +@interface DoricScrollView : UIScrollView +@end + +@implementation DoricScrollView + +- (void)layoutSubviews { + [super layoutSubviews]; + if (self.subviews.count > 0) { + UIView *child = self.subviews[0]; + [self setContentSize:child.frame.size]; + } +} + +- (CGSize)sizeThatFits:(CGSize)size { + if (self.subviews.count > 0) { + UIView *child = self.subviews[0]; + CGSize childSize = [child sizeThatFits:size]; + return CGSizeMake(MIN(size.width, childSize.width), MIN(size.height, childSize.height)); + } + return CGSizeZero; +} +@end + +@interface DoricScrollerNode () +@property(nonatomic, strong) DoricViewNode *childNode; +@property(nonatomic, copy) NSString *childViewId; +@end + +@implementation DoricScrollerNode +- (UIScrollView *)build { + return [DoricScrollView new]; +} + +- (void)blend:(NSDictionary *)props { + [super blend:props]; + NSDictionary *childModel = [self subModelOf:self.childViewId]; + if (!childModel) { + return; + } + NSString *viewId = childModel[@"id"]; + NSString *type = childModel[@"type"]; + NSDictionary *childProps = childModel[@"props"]; + if (self.childNode) { + if ([self.childNode.viewId isEqualToString:viewId]) { + //skip + } else { + if (self.reusable && [type isEqualToString:self.childNode.type]) { + [self.childNode also:^(DoricViewNode *it) { + it.viewId = viewId; + [it blend:childProps]; + }]; + } else { + [self.childNode.view removeFromSuperview]; + self.childNode = [[DoricViewNode create:self.doricContext withType:type] also:^(DoricViewNode *it) { + it.viewId = viewId; + [it initWithSuperNode:self]; + [it blend:childProps]; + [self.view addSubview:it.view]; + }]; + } + } + } else { + self.childNode = [[DoricViewNode create:self.doricContext withType:type] also:^(DoricViewNode *it) { + it.viewId = viewId; + [it initWithSuperNode:self]; + [it blend:childProps]; + [self.view addSubview:it.view]; + }]; + } +} + +- (void)blendView:(UIScrollView *)view forPropName:(NSString *)name propValue:(id)prop { + if ([@"content" isEqualToString:name]) { + self.childViewId = prop; + } else { + [super blendView:view forPropName:name propValue:prop]; + } +} + +- (void)blendSubNode:(NSDictionary *)subModel { + [self.childNode blend:subModel]; +} +@end \ No newline at end of file diff --git a/iOS/Pod/Classes/Shader/DoricSuperNode.h b/iOS/Pod/Classes/Shader/DoricSuperNode.h index 1fe10660..42c58b5e 100644 --- a/iOS/Pod/Classes/Shader/DoricSuperNode.h +++ b/iOS/Pod/Classes/Shader/DoricSuperNode.h @@ -21,6 +21,8 @@ #import "DoricViewNode.h" @interface DoricSuperNode : DoricViewNode +@property(nonatomic, assign) BOOL reusable; + - (DoricLayoutConfig *)generateDefaultLayoutParams; - (void)blendSubNode:(DoricViewNode *)subNode layoutConfig:(NSDictionary *)layoutConfig; diff --git a/iOS/Pod/Classes/Shader/DoricViewNode.m b/iOS/Pod/Classes/Shader/DoricViewNode.m index 7049f702..6731c663 100644 --- a/iOS/Pod/Classes/Shader/DoricViewNode.m +++ b/iOS/Pod/Classes/Shader/DoricViewNode.m @@ -81,6 +81,9 @@ - (instancetype)initWithContext:(DoricContext *)doricContext { - (void)initWithSuperNode:(DoricSuperNode *)superNode { + if ([self isKindOfClass:[DoricSuperNode class]]) { + ((DoricSuperNode *) self).reusable = superNode.reusable; + } self.superNode = superNode; self.view = [[self build] also:^(UIView *it) { it.layoutConfig = [superNode generateDefaultLayoutParams];