You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by ac...@apache.org on 2018/04/26 12:39:53 UTC
[01/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Repository: incubator-weex
Updated Branches:
refs/heads/master bf14bcdb5 -> b77b42599
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.m b/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.m
deleted file mode 100644
index 66d6ad5..0000000
--- a/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.m
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXComponent+ViewManagement.h"
-#import "WXComponent_internal.h"
-#import "WXComponent+BoxShadow.h"
-#import "WXAssert.h"
-#import "WXView.h"
-#import "WXSDKInstance_private.h"
-#import "WXTransform.h"
-#import "WXTracingManager.h"
-#import "WXSDKManager.h"
-
-#define WX_BOARD_RADIUS_RESET_ALL(key)\
-do {\
- if (styles && [styles containsObject:@#key]) {\
- _borderTopLeftRadius = _borderTopRightRadius = _borderBottomLeftRadius = _borderBottomRightRadius = 0;\
- [self setNeedsDisplay];\
- }\
-} while(0);
-
-#define WX_BOARD_RADIUS_RESET(key)\
-do {\
- if (styles && [styles containsObject:@#key]) {\
- _##key = 0;\
- [self setNeedsDisplay];\
- }\
-} while(0);
-
-#define WX_BOARD_WIDTH_RESET_ALL(key)\
-do {\
- if (styles && [styles containsObject:@#key]) {\
- _borderTopWidth = _borderLeftWidth = _borderRightWidth = _borderBottomWidth = 0;\
- [self setNeedsLayout];\
- }\
-} while(0);
-
-#define WX_BOARD_WIDTH_RESET(key)\
-do {\
- if (styles && [styles containsObject:@#key]) {\
- _##key = 0;\
- [self setNeedsLayout];\
- }\
-} while(0);
-
-#define WX_BOARD_RADIUS_COLOR_RESET_ALL(key)\
-do {\
- if (styles && [styles containsObject:@#key]) {\
- _borderTopColor = _borderLeftColor = _borderRightColor = _borderBottomColor = [UIColor blackColor];\
- [self setNeedsDisplay];\
- }\
-} while(0);
-
-#define WX_BOARD_COLOR_RESET(key)\
-do {\
- if (styles && [styles containsObject:@#key]) {\
- _##key = [UIColor blackColor];\
- [self setNeedsDisplay];\
- }\
-} while(0);
-
-#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
-
-@implementation WXComponent (ViewManagement)
-
-#pragma mark Public
-
-- (UIView *)loadView
-{
- return [[WXView alloc] init];
-}
-
-- (BOOL)isViewLoaded
-{
- return (_view != nil);
-}
-
-- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- WXAssertMainThread();
-
- if (subcomponent.displayType == WXDisplayTypeNone) {
- return;
- }
-
- WX_CHECK_COMPONENT_TYPE(self.componentType)
- if (subcomponent->_positionType == WXPositionTypeFixed) {
- [self.weexInstance.rootView addSubview:subcomponent.view];
- return;
- }
-
- // use _lazyCreateView to forbid component like cell's view creating
- if(_lazyCreateView) {
- subcomponent->_lazyCreateView = YES;
- }
-
- if (!subcomponent->_lazyCreateView || (self->_lazyCreateView && [self isViewLoaded])) {
- [self.view insertSubview:subcomponent.view atIndex:index];
- }
-}
-
-- (void)willRemoveSubview:(WXComponent *)component
-{
- WXAssertMainThread();
-}
-
-- (void)removeFromSuperview
-{
- WXAssertMainThread();
-
- if ([self isViewLoaded]) {
- [self.view removeFromSuperview];
- }
-}
-
-- (void)moveToSuperview:(WXComponent *)newSupercomponent atIndex:(NSUInteger)index
-{
- WX_CHECK_COMPONENT_TYPE(self.componentType)
- [self removeFromSuperview];
- [newSupercomponent insertSubview:self atIndex:index];
-}
-
-- (void)viewWillLoad
-{
- WXAssertMainThread();
-}
-
-- (void)viewDidLoad
-{
- WXAssertMainThread();
-}
-
-- (void)viewWillUnload
-{
- WXAssertMainThread();
-}
-
-- (void)viewDidUnload
-{
- WXAssertMainThread();
-}
-
-#pragma mark Private
-
-- (void)_initViewPropertyWithStyles:(NSDictionary *)styles
-{
- _backgroundColor = styles[@"backgroundColor"] ? [WXConvert UIColor:styles[@"backgroundColor"]] : [UIColor clearColor];
- _backgroundImage = styles[@"backgroundImage"] ? [WXConvert NSString:styles[@"backgroundImage"]]: nil;
- _opacity = styles[@"opacity"] ? [WXConvert CGFloat:styles[@"opacity"]] : 1.0;
- _clipToBounds = styles[@"overflow"] ? [WXConvert WXClipType:styles[@"overflow"]] : NO;
- _visibility = styles[@"visibility"] ? [WXConvert WXVisibility:styles[@"visibility"]] : WXVisibilityShow;
- _positionType = styles[@"position"] ? [WXConvert WXPositionType:styles[@"position"]] : WXPositionTypeRelative;
- _transform = styles[@"transform"] || styles[@"transformOrigin"] ?
- [[WXTransform alloc] initWithCSSValue:[WXConvert NSString:styles[@"transform"]] origin:[WXConvert NSString:styles[@"transformOrigin"]] instance:self.weexInstance] :
- [[WXTransform alloc] initWithCSSValue:nil origin:nil instance:self.weexInstance];
- _boxShadow = styles[@"boxShadow"]?[WXConvert WXBoxShadow:styles[@"boxShadow"] scaleFactor:self.weexInstance.pixelScaleFactor]:nil;
- if (_boxShadow) {
- _lastBoxShadow = _boxShadow;
- }
-}
-- (void)_transitionUpdateViewProperty:(NSDictionary *)styles
-{
- WX_CHECK_COMPONENT_TYPE(self.componentType)
- if (styles[@"backgroundColor"]) {
- _backgroundColor = [WXConvert UIColor:styles[@"backgroundColor"]];
- }
- if (styles[@"opacity"]) {
- _opacity = [WXConvert CGFloat:styles[@"opacity"]];
- }
-}
-
-- (void)_updateViewStyles:(NSDictionary *)styles
-{
- WX_CHECK_COMPONENT_TYPE(self.componentType)
- if (styles[@"boxShadow"]) {
- _lastBoxShadow = _boxShadow;
- _boxShadow = styles[@"boxShadow"]?[WXConvert WXBoxShadow:styles[@"boxShadow"] scaleFactor:self.weexInstance.pixelScaleFactor]:nil;
- [self configBoxShadow:_boxShadow];
- [self setNeedsDisplay];
- }
-
- if (styles[@"backgroundColor"]) {
- _backgroundColor = [WXConvert UIColor:styles[@"backgroundColor"]];
- [self setNeedsDisplay];
- }
-
- if (styles[@"backgroundImage"]) {
- _backgroundImage = styles[@"backgroundImage"] ? [WXConvert NSString:styles[@"backgroundImage"]]: nil;
- if (_backgroundImage) {
- [self setGradientLayer];
- }
- }
-
- if (styles[@"opacity"]) {
- _opacity = [WXConvert CGFloat:styles[@"opacity"]];
- _layer.opacity = _opacity;
- }
-
- if (styles[@"overflow"]) {
- _clipToBounds = [WXConvert WXClipType:styles[@"overflow"]];
- _view.clipsToBounds = _clipToBounds;
- }
-
- if (styles[@"position"]) {
- WXPositionType positionType = [WXConvert WXPositionType:styles[@"position"]];
- if (positionType == WXPositionTypeSticky) {
- [self.ancestorScroller addStickyComponent:self];
- } else if (_positionType == WXPositionTypeSticky) {
- [self.ancestorScroller removeStickyComponent:self];
- }
-
- WXPerformBlockOnComponentThread(^{
- if (positionType == WXPositionTypeFixed) {
- [self.weexInstance.componentManager addFixedComponent:self];
- _isNeedJoinLayoutSystem = NO;
- [self.supercomponent _recomputeCSSNodeChildren];
- } else if (_positionType == WXPositionTypeFixed) {
- [self.weexInstance.componentManager removeFixedComponent:self];
- _isNeedJoinLayoutSystem = YES;
- [self.supercomponent _recomputeCSSNodeChildren];
- }
-
- _positionType = positionType;
- });
- }
-
- if (styles[@"visibility"]) {
- _visibility = [WXConvert WXVisibility:styles[@"visibility"]];
- if (_visibility == WXVisibilityShow) {
- self.view.hidden = NO;
- }
- else {
- self.view.hidden = YES;
- }
- }
- if (styles[@"transform"]) {
- _transform = [[WXTransform alloc] initWithCSSValue:[WXConvert NSString:styles[@"transform"]] origin:[WXConvert NSString:self.styles[@"transformOrigin"]] instance:self.weexInstance];
- if (!CGRectEqualToRect(self.calculatedFrame, CGRectZero)) {
- [_transform applyTransformForView:_view];
- [_layer setNeedsDisplay];
- }
- }
-
- if (styles[@"transformOrigin"]) {
- [_transform setTransformOrigin:[WXConvert NSString:styles[@"transformOrigin"]]];
- if (!CGRectEqualToRect(self.calculatedFrame, CGRectZero)) {
- [_transform applyTransformForView:_view];
- [_layer setNeedsDisplay];
- }
- }
-}
-
--(void)resetBorder:(NSArray *)styles
-{
- WX_BOARD_RADIUS_RESET_ALL(borderRadius);
- WX_BOARD_RADIUS_RESET(borderTopLeftRadius);
- WX_BOARD_RADIUS_RESET(borderTopRightRadius);
- WX_BOARD_RADIUS_RESET(borderBottomLeftRadius);
- WX_BOARD_RADIUS_RESET(borderBottomRightRadius);
-
- WX_BOARD_WIDTH_RESET_ALL(borderWidth);
- WX_BOARD_WIDTH_RESET(borderTopWidth);
- WX_BOARD_WIDTH_RESET(borderLeftWidth);
- WX_BOARD_WIDTH_RESET(borderRightWidth);
- WX_BOARD_WIDTH_RESET(borderBottomWidth);
-
- WX_BOARD_RADIUS_COLOR_RESET_ALL(borderColor);
- WX_BOARD_COLOR_RESET(borderTopColor);
- WX_BOARD_COLOR_RESET(borderLeftColor);
- WX_BOARD_COLOR_RESET(borderRightColor);
- WX_BOARD_COLOR_RESET(borderBottomColor);
-}
-
--(void)_resetStyles:(NSArray *)styles
-{
- if (styles && [styles containsObject:@"backgroundColor"]) {
- _backgroundColor = [UIColor clearColor];
- [self setNeedsDisplay];
- }
- if (styles && [styles containsObject:@"boxShadow"]) {
- _lastBoxShadow = _boxShadow;
- _boxShadow = nil;
- [self setNeedsDisplay];
- }
- if (styles && [styles containsObject:@"backgroundImage"]) {
- _backgroundImage = @"linear-gradient(to left,rgba(255,255,255,0),rgba(255,255,255,0))"; // if backgroundImage is nil, give defalut color value.
- [self setGradientLayer];
- }
-
- [self resetBorder:styles];
-}
-
-- (void)_unloadViewWithReusing:(BOOL)isReusing
-{
- WXAssertMainThread();
-
- if (isReusing && self->_positionType == WXPositionTypeFixed) {
- return;
- }
-
- [self viewWillUnload];
-
- _view.gestureRecognizers = nil;
-
- [self _removeAllEvents];
-
- if(self.ancestorScroller){
- [self.ancestorScroller removeStickyComponent:self];
- [self.ancestorScroller removeScrollToListener:self];
- }
-
- for (WXComponent *subcomponents in [self.subcomponents reverseObjectEnumerator]) {
- [subcomponents _unloadViewWithReusing:isReusing];
- }
-
- if ([_view superview]) {
- [_view removeFromSuperview];
- }
-
- if (self->_isTemplate && self.attributes[@"@templateId"]) {
- [[WXSDKManager bridgeMgr] callComponentHook:self.weexInstance.instanceId componentId:self.attributes[@"@templateId"] type:@"lifecycle" hook:@"detach" args:nil competion:nil];
- }
- _view = nil;
- [_layer removeFromSuperlayer];
- _layer = nil;
-
- [self viewDidUnload];
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm b/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm
new file mode 100644
index 0000000..29d5d87
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm
@@ -0,0 +1,347 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXComponent+ViewManagement.h"
+#import "WXComponent_internal.h"
+#import "WXComponent+BoxShadow.h"
+#import "WXAssert.h"
+#import "WXView.h"
+#import "WXSDKInstance_private.h"
+#import "WXTransform.h"
+#import "WXTracingManager.h"
+#import "WXSDKManager.h"
+#import "WXComponent+Layout.h"
+
+#define WX_BOARD_RADIUS_RESET_ALL(key)\
+do {\
+ if (styles && [styles containsObject:@#key]) {\
+ _borderTopLeftRadius = _borderTopRightRadius = _borderBottomLeftRadius = _borderBottomRightRadius = 0;\
+ [self setNeedsDisplay];\
+ }\
+} while(0);
+
+#define WX_BOARD_RADIUS_RESET(key)\
+do {\
+ if (styles && [styles containsObject:@#key]) {\
+ _##key = 0;\
+ [self setNeedsDisplay];\
+ }\
+} while(0);
+
+#define WX_BOARD_WIDTH_RESET_ALL(key)\
+do {\
+ if (styles && [styles containsObject:@#key]) {\
+ _borderTopWidth = _borderLeftWidth = _borderRightWidth = _borderBottomWidth = 0;\
+ [self setNeedsLayout];\
+ }\
+} while(0);
+
+#define WX_BOARD_WIDTH_RESET(key)\
+do {\
+ if (styles && [styles containsObject:@#key]) {\
+ _##key = 0;\
+ [self setNeedsLayout];\
+ }\
+} while(0);
+
+#define WX_BOARD_RADIUS_COLOR_RESET_ALL(key)\
+do {\
+ if (styles && [styles containsObject:@#key]) {\
+ _borderTopColor = _borderLeftColor = _borderRightColor = _borderBottomColor = [UIColor blackColor];\
+ [self setNeedsDisplay];\
+ }\
+} while(0);
+
+#define WX_BOARD_COLOR_RESET(key)\
+do {\
+ if (styles && [styles containsObject:@#key]) {\
+ _##key = [UIColor blackColor];\
+ [self setNeedsDisplay];\
+ }\
+} while(0);
+
+#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+
+@implementation WXComponent (ViewManagement)
+
+#pragma mark Public
+
+- (UIView *)loadView
+{
+ return [[WXView alloc] init];
+}
+
+- (BOOL)isViewLoaded
+{
+ return (_view != nil);
+}
+
+- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ WXAssertMainThread();
+
+ if (subcomponent.displayType == WXDisplayTypeNone) {
+ return;
+ }
+
+ WX_CHECK_COMPONENT_TYPE(self.componentType)
+ if (subcomponent->_positionType == WXPositionTypeFixed) {
+ [self.weexInstance.rootView addSubview:subcomponent.view];
+ return;
+ }
+
+ // use _lazyCreateView to forbid component like cell's view creating
+ if(_lazyCreateView) {
+ subcomponent->_lazyCreateView = YES;
+ }
+
+ if (!subcomponent->_lazyCreateView || (self->_lazyCreateView && [self isViewLoaded])) {
+ [self.view insertSubview:subcomponent.view atIndex:index];
+ }
+}
+
+- (void)willRemoveSubview:(WXComponent *)component
+{
+ WXAssertMainThread();
+}
+
+- (void)removeFromSuperview
+{
+ WXAssertMainThread();
+
+ if ([self isViewLoaded]) {
+ [self.view removeFromSuperview];
+ }
+}
+
+- (void)moveToSuperview:(WXComponent *)newSupercomponent atIndex:(NSUInteger)index
+{
+ WX_CHECK_COMPONENT_TYPE(self.componentType)
+ [self removeFromSuperview];
+ [newSupercomponent insertSubview:self atIndex:index];
+}
+
+- (void)viewWillLoad
+{
+ WXAssertMainThread();
+}
+
+- (void)viewDidLoad
+{
+ WXAssertMainThread();
+}
+
+- (void)viewWillUnload
+{
+ WXAssertMainThread();
+}
+
+- (void)viewDidUnload
+{
+ WXAssertMainThread();
+}
+
+#pragma mark Private
+
+- (void)_initViewPropertyWithStyles:(NSDictionary *)styles
+{
+ _backgroundColor = styles[@"backgroundColor"] ? [WXConvert UIColor:styles[@"backgroundColor"]] : [UIColor clearColor];
+ _backgroundImage = styles[@"backgroundImage"] ? [WXConvert NSString:styles[@"backgroundImage"]]: nil;
+ _opacity = styles[@"opacity"] ? [WXConvert CGFloat:styles[@"opacity"]] : 1.0;
+ _clipToBounds = styles[@"overflow"] ? [WXConvert WXClipType:styles[@"overflow"]] : NO;
+ _visibility = styles[@"visibility"] ? [WXConvert WXVisibility:styles[@"visibility"]] : WXVisibilityShow;
+ _positionType = styles[@"position"] ? [WXConvert WXPositionType:styles[@"position"]] : WXPositionTypeRelative;
+ _transform = styles[@"transform"] || styles[@"transformOrigin"] ?
+ [[WXTransform alloc] initWithCSSValue:[WXConvert NSString:styles[@"transform"]] origin:[WXConvert NSString:styles[@"transformOrigin"]] instance:self.weexInstance] :
+ [[WXTransform alloc] initWithCSSValue:nil origin:nil instance:self.weexInstance];
+ _boxShadow = styles[@"boxShadow"]?[WXConvert WXBoxShadow:styles[@"boxShadow"] scaleFactor:self.weexInstance.pixelScaleFactor]:nil;
+ if (_boxShadow) {
+ _lastBoxShadow = _boxShadow;
+ }
+}
+- (void)_transitionUpdateViewProperty:(NSDictionary *)styles
+{
+ WX_CHECK_COMPONENT_TYPE(self.componentType)
+ if (styles[@"backgroundColor"]) {
+ _backgroundColor = [WXConvert UIColor:styles[@"backgroundColor"]];
+ }
+ if (styles[@"opacity"]) {
+ _opacity = [WXConvert CGFloat:styles[@"opacity"]];
+ }
+}
+
+- (void)_updateViewStyles:(NSDictionary *)styles
+{
+ WX_CHECK_COMPONENT_TYPE(self.componentType)
+ if (styles[@"boxShadow"]) {
+ _lastBoxShadow = _boxShadow;
+ _boxShadow = styles[@"boxShadow"]?[WXConvert WXBoxShadow:styles[@"boxShadow"] scaleFactor:self.weexInstance.pixelScaleFactor]:nil;
+ [self configBoxShadow:_boxShadow];
+ [self setNeedsDisplay];
+ }
+
+ if (styles[@"backgroundColor"]) {
+ _backgroundColor = [WXConvert UIColor:styles[@"backgroundColor"]];
+ [self setNeedsDisplay];
+ }
+
+ if (styles[@"backgroundImage"]) {
+ _backgroundImage = styles[@"backgroundImage"] ? [WXConvert NSString:styles[@"backgroundImage"]]: nil;
+ if (_backgroundImage) {
+ [self setGradientLayer];
+ }
+ }
+
+ if (styles[@"opacity"]) {
+ _opacity = [WXConvert CGFloat:styles[@"opacity"]];
+ _layer.opacity = _opacity;
+ }
+
+ if (styles[@"overflow"]) {
+ _clipToBounds = [WXConvert WXClipType:styles[@"overflow"]];
+ _view.clipsToBounds = _clipToBounds;
+ }
+
+ if (styles[@"position"]) {
+ WXPositionType positionType = [WXConvert WXPositionType:styles[@"position"]];
+ if (positionType == WXPositionTypeSticky) {
+ [self.ancestorScroller addStickyComponent:self];
+ } else if (_positionType == WXPositionTypeSticky) {
+ [self.ancestorScroller removeStickyComponent:self];
+ }
+
+ WXPerformBlockOnComponentThread(^{
+ if (positionType == WXPositionTypeFixed) {
+ [self.weexInstance.componentManager addFixedComponent:self];
+ _isNeedJoinLayoutSystem = NO;
+ [self.supercomponent _recomputeCSSNodeChildren];
+ } else if (_positionType == WXPositionTypeFixed) {
+ [self.weexInstance.componentManager removeFixedComponent:self];
+ _isNeedJoinLayoutSystem = YES;
+ [self.supercomponent _recomputeCSSNodeChildren];
+ }
+
+ _positionType = positionType;
+ });
+ }
+
+ if (styles[@"visibility"]) {
+ _visibility = [WXConvert WXVisibility:styles[@"visibility"]];
+ if (_visibility == WXVisibilityShow) {
+ self.view.hidden = NO;
+ }
+ else {
+ self.view.hidden = YES;
+ }
+ }
+ if (styles[@"transform"]) {
+ _transform = [[WXTransform alloc] initWithCSSValue:[WXConvert NSString:styles[@"transform"]] origin:[WXConvert NSString:self.styles[@"transformOrigin"]] instance:self.weexInstance];
+ if (!CGRectEqualToRect(self.calculatedFrame, CGRectZero)) {
+ [_transform applyTransformForView:_view];
+ [_layer setNeedsDisplay];
+ }
+ }
+
+ if (styles[@"transformOrigin"]) {
+ [_transform setTransformOrigin:[WXConvert NSString:styles[@"transformOrigin"]]];
+ if (!CGRectEqualToRect(self.calculatedFrame, CGRectZero)) {
+ [_transform applyTransformForView:_view];
+ [_layer setNeedsDisplay];
+ }
+ }
+}
+
+-(void)resetBorder:(NSArray *)styles
+{
+ WX_BOARD_RADIUS_RESET_ALL(borderRadius);
+ WX_BOARD_RADIUS_RESET(borderTopLeftRadius);
+ WX_BOARD_RADIUS_RESET(borderTopRightRadius);
+ WX_BOARD_RADIUS_RESET(borderBottomLeftRadius);
+ WX_BOARD_RADIUS_RESET(borderBottomRightRadius);
+
+ WX_BOARD_WIDTH_RESET_ALL(borderWidth);
+ WX_BOARD_WIDTH_RESET(borderTopWidth);
+ WX_BOARD_WIDTH_RESET(borderLeftWidth);
+ WX_BOARD_WIDTH_RESET(borderRightWidth);
+ WX_BOARD_WIDTH_RESET(borderBottomWidth);
+
+ WX_BOARD_RADIUS_COLOR_RESET_ALL(borderColor);
+ WX_BOARD_COLOR_RESET(borderTopColor);
+ WX_BOARD_COLOR_RESET(borderLeftColor);
+ WX_BOARD_COLOR_RESET(borderRightColor);
+ WX_BOARD_COLOR_RESET(borderBottomColor);
+}
+
+-(void)_resetStyles:(NSArray *)styles
+{
+ if (styles && [styles containsObject:@"backgroundColor"]) {
+ _backgroundColor = [UIColor clearColor];
+ [self setNeedsDisplay];
+ }
+ if (styles && [styles containsObject:@"boxShadow"]) {
+ _lastBoxShadow = _boxShadow;
+ _boxShadow = nil;
+ [self setNeedsDisplay];
+ }
+ if (styles && [styles containsObject:@"backgroundImage"]) {
+ _backgroundImage = @"linear-gradient(to left,rgba(255,255,255,0),rgba(255,255,255,0))"; // if backgroundImage is nil, give defalut color value.
+ [self setGradientLayer];
+ }
+
+ [self resetBorder:styles];
+}
+
+- (void)_unloadViewWithReusing:(BOOL)isReusing
+{
+ WXAssertMainThread();
+
+ if (isReusing && self->_positionType == WXPositionTypeFixed) {
+ return;
+ }
+
+ [self viewWillUnload];
+
+ _view.gestureRecognizers = nil;
+
+ [self _removeAllEvents];
+
+ if(self.ancestorScroller){
+ [self.ancestorScroller removeStickyComponent:self];
+ [self.ancestorScroller removeScrollToListener:self];
+ }
+
+ for (WXComponent *subcomponents in [self.subcomponents reverseObjectEnumerator]) {
+ [subcomponents _unloadViewWithReusing:isReusing];
+ }
+
+ if ([_view superview]) {
+ [_view removeFromSuperview];
+ }
+
+ if (self->_isTemplate && self.attributes[@"@templateId"]) {
+ [[WXSDKManager bridgeMgr] callComponentHook:self.weexInstance.instanceId componentId:self.attributes[@"@templateId"] type:@"lifecycle" hook:@"detach" args:nil competion:nil];
+ }
+ _view = nil;
+ [_layer removeFromSuperlayer];
+ _layer = nil;
+
+ [self viewDidUnload];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/View/WXRootView.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/View/WXRootView.m b/ios/sdk/WeexSDK/Sources/View/WXRootView.m
index 11fb48d..4d35f48 100644
--- a/ios/sdk/WeexSDK/Sources/View/WXRootView.m
+++ b/ios/sdk/WeexSDK/Sources/View/WXRootView.m
@@ -50,7 +50,7 @@
- (BOOL)isHasEvent
{
- return false;
+ return _mHasEvent;
}
@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/WeexSDK.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/WeexSDK.h b/ios/sdk/WeexSDK/Sources/WeexSDK.h
index 9d5ddcd..dfc0b42 100644
--- a/ios/sdk/WeexSDK/Sources/WeexSDK.h
+++ b/ios/sdk/WeexSDK/Sources/WeexSDK.h
@@ -59,10 +59,14 @@
#import "WXErrorView.h"
#import "WXDefine.h"
#import "WXDebugTool.h"
+#import "WXCoreStyle.h"
+#import "WXCoreLayout.h"
+#import "WXCoreFlexEnum.h"
#import "WXConvert.h"
#import "WXConfigCenterProtocol.h"
#import "WXComponentManager.h"
#import "WXComponent.h"
+#import "WXComponent+Layout.h"
#import "WXBridgeProtocol.h"
#import "WXBridgeManager.h"
#import "WXBaseViewController.h"
[09/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.m
deleted file mode 100644
index 5bfee0a..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.m
+++ /dev/null
@@ -1,1803 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXSliderNeighborComponent.h"
-#import "WXConvert.h"
-#import "WXUtility.h"
-#import "WXComponent_internal.h"
-#import "WXIndicatorComponent.h"
-#import "WXSDKInstance.h"
-#import "NSTimer+Weex.h"
-
-#define MAX_VISIBLE_ITEMS 30
-#define MIN_TOGGLE_DURATION 0.2
-#define MAX_TOGGLE_DURATION 0.4
-#define SCROLL_DURATION 0.4
-#define INSERT_DURATION 0.4
-#define DECELERATE_THRESHOLD 0.1
-#define SCROLL_SPEED_THRESHOLD 2.0
-#define SCROLL_DISTANCE_THRESHOLD 0.1
-#define DECELERATION_MULTIPLIER 30.0
-#define FLOAT_ERROR_MARGIN 0.000001
-
-@protocol WXSliderNeighborDelegate, WXSliderNeighborDataSource;
-
-@interface WXSliderNeighborView: UIView
-@property (nonatomic, strong) WXIndicatorView *indicator;
-@property (nonatomic, weak) id<WXSliderNeighborDelegate> delegate;
-@property (nonatomic, weak) id<WXSliderNeighborDataSource> dataSource;
-@property (nonatomic, assign) CGFloat perspective;
-@property (nonatomic, readonly, getter = isDragging) BOOL dragging;
-@property (nonatomic, readonly, getter = isScrolling) BOOL scrolling;
-@property (nonatomic, assign) CGFloat autoscroll;
-@property (nonatomic, assign) CGFloat scrollOffset;
-@property (nonatomic, assign) CGFloat previousTranslation;
-@property (nonatomic, assign, getter = isVertical) BOOL vertical;
-@property (nonatomic, assign) CGFloat decelerationRate;
-@property (nonatomic, assign, getter = isScrollEnabled) BOOL scrollEnabled;
-@property (nonatomic, assign, getter = isPagingEnabled) BOOL pagingEnabled;
-@property (nonatomic, assign) BOOL bounces;
-@property (nonatomic, assign) BOOL didDrag;
-@property (nonatomic, readonly, getter = isDecelerating) BOOL decelerating;
-@property (nonatomic, strong, readonly) UIView *contentView;
-@property (nonatomic, strong) NSMutableDictionary *itemViews;
-@property (nonatomic, readonly) NSInteger numberOfVisibleItems;
-@property (nonatomic, readonly) NSInteger numberOfItems;
-@property (nonatomic, readonly) NSInteger numberOfPlaceholders;
-@property (nonatomic, strong) NSMutableSet *itemViewPool;
-@property (nonatomic, strong) NSMutableSet *placeholderViewPool;
-@property (nonatomic, assign) NSTimeInterval startTime;
-@property (nonatomic, assign) NSTimeInterval lastTime;
-@property (nonatomic, assign) CGFloat startVelocity;
-@property (nonatomic, assign) CGFloat offsetMultiplier;
-@property (nonatomic, assign) CGFloat startOffset;
-@property (nonatomic, assign) CGFloat endOffset;
-@property (nonatomic, assign) CGSize contentOffset;
-@property (nonatomic, assign) CGSize viewpointOffset;
-@property (nonatomic, assign) CGFloat scrollSpeed;
-@property (nonatomic, assign) CGFloat bounceDistance;
-@property (nonatomic, assign) BOOL stopAtItemBoundary;
-@property (nonatomic, assign) BOOL scrollToItemBoundary;
-@property (nonatomic, assign) BOOL centerItemWhenSelected;
-@property (nonatomic, assign) NSTimeInterval scrollDuration;
-@property (nonatomic, readonly, getter = isWrapEnabled) BOOL wrapEnabled;
-@property (nonatomic, strong) NSTimer *timer;
-@property (nonatomic, assign) NSInteger numberOfPlaceholdersToShow;
-@property (nonatomic, assign) CGFloat previousScrollOffset;
-@property (nonatomic, assign) NSTimeInterval toggleTime;
-@property (nonatomic, readonly) CGFloat toggle;
-@property (nonatomic, assign) NSInteger previousItemIndex;
-@property (nonatomic, readonly) CGFloat itemWidth;
-@property (nonatomic, assign) BOOL inited;
-@end
-
-@implementation WXSliderNeighborView
-
-- (instancetype)init {
-
- self = [super init];
- if (!self) {
- }
- _decelerationRate = 0.01;
- _scrollEnabled = YES;
- _bounces = YES;
- _offsetMultiplier = 1.0;
- _perspective = -1.0/500.0;
- _contentOffset = CGSizeZero;
- _viewpointOffset = CGSizeZero;
- _scrollSpeed = 1.0;
- _bounceDistance = 1.0;
- _stopAtItemBoundary = YES;
- _scrollToItemBoundary = YES;
- _centerItemWhenSelected = YES;
- _inited = NO;
-
- _contentView = [[UIView alloc] initWithFrame:self.bounds];
-
- //pan gesture recognizer
- self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didPan:)];
- panGesture.delegate = (id <UIGestureRecognizerDelegate>)self;
- [_contentView addGestureRecognizer:panGesture];
-
- //tap gesture recognizer
- UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap:)];
- tapGesture.delegate = (id <UIGestureRecognizerDelegate>)self;
- [_contentView addGestureRecognizer:tapGesture];
-
- self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction;
- self.isAccessibilityElement = YES;
-
- [self addSubview:_contentView];
-
- if (_dataSource) {
- [self reloadData];
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- [self stopAnimation];
-}
-
-- (void)accessibilityIncrement
-{
- [self setCurrentItemIndex: [self lastItemIndex]];
-}
-
-- (void)accessibilityDecrement
-{
- [self setCurrentItemIndex:[self nextItemIndex]];
-}
-
-- (void)setDataSource:(id<WXSliderNeighborDataSource>)dataSource
-{
- if (_dataSource != dataSource) {
- _dataSource = dataSource;
- if (_dataSource) {
- [self reloadData];
- }
- }
-}
-
-- (void)setDelegate:(id<WXSliderNeighborDelegate>)delegate
-{
- if (_delegate != delegate) {
- _delegate = delegate;
- if (_delegate && _dataSource) {
- [self setNeedsLayout];
- }
- }
-}
-
-- (void)setVertical:(BOOL)vertical
-{
- if (_vertical != vertical)
- {
- _vertical = vertical;
- [self layOutItemViews];
- }
-}
-
-#pragma clang diagnostic push
-#pragma GCC diagnostic ignored "-Wundeclared-selector"
-- (void)setCurrentItemIndex:(NSInteger)currentItemIndex
-{
- if ([self currentItemIndex] == currentItemIndex) return;
-
- [self setScrollOffset:currentItemIndex];
- [self.indicator setCurrentPoint:currentItemIndex];
-
- if (self.delegate && [self.delegate respondsToSelector:@selector(sliderView:didScrollToItemAtIndex:)]) {
- [self.delegate sliderNeighbor:self didScrollToItemAtIndex:currentItemIndex];
- }
- [self scroll2ItemViewAtIndex:currentItemIndex animated:YES];
-
-}
-#pragma clang diagnostic pop
-
-- (void)updateItemWidth
-{
- _itemWidth = [_delegate sliderNeighborItemWidth:self] ?: _itemWidth;
- if (_numberOfItems > 0) {
- if ([_itemViews count] == 0) {
- [self loadViewAtIndex:0];
- }
- } else if (_numberOfPlaceholders > 0) {
- if ([_itemViews count] == 0) {
- [self loadViewAtIndex:-1];
- }
- }
-}
-
-- (NSInteger)circularCarouselItemCount
-{
- NSInteger count = 0;
- CGFloat spacing = [self valueForOption:WXSliderNeighborOptionSpacing withDefault:1.0];
- CGFloat width = _vertical ? self.bounds.size.height: self.bounds.size.width;
- count = MIN(MAX_VISIBLE_ITEMS, MAX(12, ceil(width / (spacing * _itemWidth)) * M_PI));
- count = MIN(_numberOfItems + _numberOfPlaceholdersToShow, count);
- return [self valueForOption:WXSliderNeighborOptionCount withDefault:count];
-}
-
-- (void)updateNumberOfVisibleItems
-{
- //get number of visible items
- //based on count value
- CGFloat spacing = [self valueForOption:WXSliderNeighborOptionSpacing withDefault:1.0];
- CGFloat width = _vertical ? self.bounds.size.height: self.bounds.size.width;
- CGFloat itemWidth = _itemWidth * spacing;
- _numberOfVisibleItems = ceil(width / itemWidth) + 2;
- _numberOfVisibleItems = MIN(MAX_VISIBLE_ITEMS, _numberOfVisibleItems);
- _numberOfVisibleItems = [self valueForOption:WXSliderNeighborOptionVisibleItems withDefault:_numberOfVisibleItems];
- _numberOfVisibleItems = MAX(0, MIN(_numberOfVisibleItems, _numberOfItems + _numberOfPlaceholdersToShow));
-
-}
-
-- (CGFloat)offsetForItemAtIndex:(NSInteger)index
-{
- //calculate relative position
- CGFloat offset = index - _scrollOffset;
- if (_wrapEnabled) {
- if (offset > _numberOfItems/2.0) {
- offset -= _numberOfItems;
- } else if (offset < -_numberOfItems/2.0) {
- offset += _numberOfItems;
- }
- }
-
- return offset;
-}
-
-- (CGFloat)alphaForItemWithOffset:(CGFloat)offset
-{
- CGFloat fadeMin = -INFINITY;
- CGFloat fadeMax = INFINITY;
- CGFloat fadeRange = 1.0;
- CGFloat fadeMinAlpha = 0.0;
- fadeMin = [self valueForOption:WXSliderNeighborOptionFadeMin withDefault:fadeMin];
- fadeMax = [self valueForOption:WXSliderNeighborOptionFadeMax withDefault:fadeMax];
- fadeRange = [self valueForOption:WXSliderNeighborOptionFadeRange withDefault:fadeRange];
- fadeMinAlpha = [self valueForOption:WXSliderNeighborOptionFadeMinAlpha withDefault:fadeMinAlpha];
-
- CGFloat factor = 0.0;
- if (offset > fadeMax) {
- factor = offset - fadeMax;
- } else if (offset < fadeMin) {
- factor = fadeMin - offset;
- }
- return 1.0 - MIN(factor, fadeRange) / fadeRange * (1.0 - fadeMinAlpha);
-}
-
-
-- (UIView *)containView:(UIView *)view
-{
- //set item width
- if (!_itemWidth) {
- _itemWidth = _vertical? view.frame.size.height: view.frame.size.width;
- }
-
- //set container frame
- CGRect frame = view.frame;
- frame.size.width = _vertical? frame.size.width: _itemWidth;
- frame.size.height = _vertical? _itemWidth: frame.size.height;
- UIView *containerView = [[UIView alloc] initWithFrame:frame];
-
- //set view frame
- frame = view.frame;
- frame.origin.x = (containerView.bounds.size.width - frame.size.width) / 2.0;
- frame.origin.y = (containerView.bounds.size.height - frame.size.height) / 2.0;
- view.frame = frame;
- [containerView addSubview:view];
- containerView.layer.opacity = 0;
- containerView.transform = CGAffineTransformMakeScale(0.8, 1.0);
- return containerView;
-}
-
-- (void)transformItemViews
-{
- for (NSNumber *number in _itemViews)
- {
- NSInteger index = [number integerValue];
- UIView *view = _itemViews[number];
- [self transformItemView:view atIndex:index];
- }
-}
-
-- (CATransform3D)transformForItemViewWithOffset:(CGFloat)offset
-{
- //set up base transform
- CATransform3D transform = CATransform3DIdentity;
- transform.m34 = _perspective;
- transform = CATransform3DTranslate(transform, -_viewpointOffset.width, -_viewpointOffset.height, 0.0);
-
- //perform transforms
- CGFloat spacing = [self valueForOption:WXSliderNeighborOptionSpacing withDefault:1.0];
- if (_vertical) {
- return CATransform3DTranslate(transform, 0.0, offset * _itemWidth * spacing, 0.0);
- } else {
- return CATransform3DTranslate(transform, offset * _itemWidth * spacing, 0.0, 0.0);
- }
-}
-
-- (void)transformItemView:(UIView *)view atIndex:(NSInteger)index
-{
- //calculate offset
- CGFloat offset = [self offsetForItemAtIndex:index];
-
- //update alpha
- view.superview.layer.opacity = [self alphaForItemWithOffset:offset];
-
- //center view
- view.superview.center = CGPointMake(self.bounds.size.width/2.0 + _contentOffset.width,
- self.bounds.size.height/2.0 + _contentOffset.height);
-
- //enable/disable interaction
- view.superview.userInteractionEnabled = (!_centerItemWhenSelected || index == self.currentItemIndex);
- view.superview.layer.rasterizationScale = [UIScreen mainScreen].scale;
-
- [view layoutIfNeeded];
-
- CGFloat clampedOffset = MAX(-1.0, MIN(1.0, offset));
- if (_decelerating || (_scrolling && !_dragging && !_didDrag) || (_autoscroll && !_dragging) ||
- (!_wrapEnabled && (_scrollOffset < 0 || _scrollOffset >= _numberOfItems - 1))) {
- if (offset > 0) {
- _toggle = (offset <= 0.5)? -clampedOffset: (1.0 - clampedOffset);
- } else {
- _toggle = (offset > -0.5)? -clampedOffset: (- 1.0 - clampedOffset);
- }
- }
-
- //calculate transform
- CATransform3D transform = [self transformForItemViewWithOffset:offset];
-
- //transform view
- view.superview.layer.transform = transform;
-
- //backface culling
- BOOL showBackfaces = view.layer.doubleSided;
- if (showBackfaces) {
- showBackfaces = YES;
- }
- showBackfaces = !![self valueForOption:WXSliderNeighborOptionShowBackfaces withDefault:showBackfaces];
-
- view.superview.hidden = !(showBackfaces ?: (transform.m33 > 0.0));
-}
-
-NSComparisonResult sliderNeighorCompareViewDepth(UIView *view1, UIView *view2, WXSliderNeighborView *self)
-{
- //compare depths
- CATransform3D t1 = view1.superview.layer.transform;
- CATransform3D t2 = view2.superview.layer.transform;
- CGFloat z1 = t1.m13 + t1.m23 + t1.m33 + t1.m43;
- CGFloat z2 = t2.m13 + t2.m23 + t2.m33 + t2.m43;
- CGFloat difference = z1 - z2;
-
- //if depths are equal, compare distance from current view
- if (difference == 0.0) {
- CATransform3D t3 = [self currentItemView].superview.layer.transform;
- if (self.vertical) {
- CGFloat y1 = t1.m12 + t1.m22 + t1.m32 + t1.m42;
- CGFloat y2 = t2.m12 + t2.m22 + t2.m32 + t2.m42;
- CGFloat y3 = t3.m12 + t3.m22 + t3.m32 + t3.m42;
- difference = fabs(y2 - y3) - fabs(y1 - y3);
- } else {
- CGFloat x1 = t1.m11 + t1.m21 + t1.m31 + t1.m41;
- CGFloat x2 = t2.m11 + t2.m21 + t2.m31 + t2.m41;
- CGFloat x3 = t3.m11 + t3.m21 + t3.m31 + t3.m41;
- difference = fabs(x2 - x3) - fabs(x1 - x3);
- }
- }
- return (difference < 0.0)? NSOrderedAscending: NSOrderedDescending;
-}
-
-- (void)depthSortViews
-{
- for (UIView *view in [[_itemViews allValues] sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))sliderNeighorCompareViewDepth context:(__bridge void *)self])
- {
- [_contentView bringSubviewToFront:(UIView *__nonnull)view.superview];
- }
-}
-
-- (void)layOutItemViews
-{
- //bail out if not set up yet
- if (!_dataSource || !_contentView) {
- return;
- }
-
- //update wrap
- _wrapEnabled = YES;
- _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
-
- //no placeholders on wrapped carousels
- _numberOfPlaceholdersToShow = _wrapEnabled? 0: _numberOfPlaceholders;
-
- //set item width
- [self updateItemWidth];
-
- //update number of visible items
- [self updateNumberOfVisibleItems];
-
- //prevent false index changed event
- _previousScrollOffset = self.scrollOffset;
-
- //update offset multiplier
- _offsetMultiplier = 1.0;
- _offsetMultiplier = [self valueForOption:WXSliderNeighborOptionOffsetMultiplier withDefault:_offsetMultiplier];
-
- //align
- if (!_scrolling && !_decelerating && !_autoscroll) {
- if (_scrollToItemBoundary && self.currentItemIndex != -1) {
- [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
- } else {
- _scrollOffset = [self clampedOffset:_scrollOffset];
- }
- }
- //update views
- if (_inited) {
- [self didScroll];
- }
-
- _inited = YES;
-}
-
-- (CGFloat)valueForOption:(WXSliderNeighborOption)option withDefault:(CGFloat)value
-{
- if (_delegate && [_delegate respondsToSelector:@selector(sliderNeighbor:valueForOption:withDefault:)]) {
- return [_delegate sliderNeighbor:self valueForOption:option withDefault:value];
- }
- return value;
-}
-
-#pragma mark -
-#pragma mark Gestures and taps
-
-- (NSInteger)viewOrSuperviewIndex:(UIView *)view
-{
- if (view == nil || view == _contentView) {
- return NSNotFound;
- }
- NSInteger index = [self indexOfItemView:view];
- if (index == NSNotFound) {
- return [self viewOrSuperviewIndex:view.superview];
- }
- return index;
-}
-
-- (BOOL)viewOrSuperview:(UIView *)view implementsSelector:(SEL)selector
-{
- if (!view || view == self.contentView) {
- return NO;
- }
-
- Class viewClass = [view class];
- while (viewClass && viewClass != [UIView class])
- {
- unsigned int numberOfMethods;
- Method *methods = class_copyMethodList(viewClass, &numberOfMethods);
- for (unsigned int i = 0; i < numberOfMethods; i++)
- {
- if (method_getName(methods[i]) == selector) {
- free(methods);
- return YES;
- }
- }
- if (methods) free(methods);
- viewClass = [viewClass superclass];
- }
-
- return [self viewOrSuperview:view.superview implementsSelector:selector];
-}
-
-- (id)viewOrSuperview:(UIView *)view ofClass:(Class)class
-{
- if (!view || view == self.contentView) {
- return nil;
- } else if ([view isKindOfClass:class]) {
- return view;
- }
- return [self viewOrSuperview:view.superview ofClass:class];
-}
-
-- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch
-{
- if (_scrollEnabled) {
- _dragging = NO;
- _scrolling = NO;
- _decelerating = NO;
- }
-
- if ([gesture isKindOfClass:[UITapGestureRecognizer class]]) {
- //handle tap
- NSInteger index = [self viewOrSuperviewIndex:touch.view];
- if (index == NSNotFound && _centerItemWhenSelected) {
- //view is a container view
- index = [self viewOrSuperviewIndex:[touch.view.subviews lastObject]];
- }
- if (index != NSNotFound) {
- if ([self viewOrSuperview:touch.view implementsSelector:@selector(touchesBegan:withEvent:)]) {
- return NO;
- }
- }
- } else if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
- if (!_scrollEnabled) {
- return NO;
- } else if ([self viewOrSuperview:touch.view implementsSelector:@selector(touchesMoved:withEvent:)]) {
- UIScrollView *scrollView = [self viewOrSuperview:touch.view ofClass:[UIScrollView class]];
- if (scrollView) {
- return !scrollView.scrollEnabled ||
- (self.vertical && scrollView.contentSize.height <= scrollView.frame.size.height) ||
- (!self.vertical && scrollView.contentSize.width <= scrollView.frame.size.width);
- }
- if ([self viewOrSuperview:touch.view ofClass:[UIButton class]] ||
- [self viewOrSuperview:touch.view ofClass:[UIBarButtonItem class]]) {
- return YES;
- }
- return NO;
- }
- }
- return YES;
-}
-
-- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
- //if the view which the otherGestureRecognizer works on is a scrollview and also it is scrollEnabled vertically ,at this time,we should not block the guesture from being recognized by the otherGestureRecognize
- if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
- if ([otherGestureRecognizer.view isKindOfClass:[UIScrollView class]]) {
- UIScrollView* scrollview = (UIScrollView *)otherGestureRecognizer.view;
- if (scrollview.scrollEnabled) {
- UIPanGestureRecognizer* panRcgn= (UIPanGestureRecognizer *)gestureRecognizer;
- //check offset for confirming vertival movement
- if (fabs([panRcgn translationInView:panRcgn.view].y) > fabs([panRcgn translationInView:panRcgn.view].x)*16) {
- return YES;
- }
- }
- }
- }
- return NO;
-}
-
-
-- (void)didPan:(UIPanGestureRecognizer *)panGesture
-{
- if (_scrollEnabled && _numberOfItems) {
- switch (panGesture.state)
- {
- case UIGestureRecognizerStateBegan:
- {
- _dragging = YES;
- _scrolling = NO;
- _decelerating = NO;
- _previousTranslation = _vertical? [panGesture translationInView:self].y: [panGesture translationInView:self].x;
-
- [_delegate sliderNeighborWillBeginDragging:self];
- break;
- }
- case UIGestureRecognizerStateEnded:
- case UIGestureRecognizerStateCancelled:
- case UIGestureRecognizerStateFailed:
- {
- _dragging = NO;
- _didDrag = YES;
- if ([self shouldDecelerate]) {
- _didDrag = NO;
- [self startDecelerating];
- }
-
- [self pushAnimationState:YES];
- [_delegate sliderNeighborDidEndDragging:self willDecelerate:_decelerating];
- [self popAnimationState];
-
- if (!_decelerating) {
- if ((_scrollToItemBoundary || fabs(_scrollOffset - [self clampedOffset:_scrollOffset]) > FLOAT_ERROR_MARGIN) && !_autoscroll) {
- if (fabs(_scrollOffset - self.currentItemIndex) < FLOAT_ERROR_MARGIN) {
- //call scroll to trigger events for legacy support reasons
- //even though technically we don't need to scroll at all
- [self scroll2ItemViewAtIndex:self.currentItemIndex duration:0.01];
- } else {
- [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
- }
- } else {
- [self depthSortViews];
- }
- } else {
- [self pushAnimationState:YES];
- [_delegate sliderNeighborWillBeginDecelerating:self];
- [self popAnimationState];
- }
- break;
- }
- case UIGestureRecognizerStateChanged:
- {
- CGFloat translation = _vertical? [panGesture translationInView:self].y: [panGesture translationInView:self].x;
- CGFloat velocity = _vertical? [panGesture velocityInView:self].y: [panGesture velocityInView:self].x;
-
- CGFloat factor = 1.0;
- if (!_wrapEnabled && _bounces) {
- factor = 1.0 - MIN(fabs(_scrollOffset - [self clampedOffset:_scrollOffset]),
- _bounceDistance) / _bounceDistance;
- }
-
- _startVelocity = -velocity * factor * _scrollSpeed / _itemWidth;
- _scrollOffset -= (translation - _previousTranslation) * factor * _offsetMultiplier / _itemWidth;
- _previousTranslation = translation;
- [self didScroll];
- break;
- }
- case UIGestureRecognizerStatePossible:
- {
- //do nothing
- break;
- }
- }
- }
-}
-
-- (void)didTap:(UITapGestureRecognizer *)tapGesture
-{
- //check for tapped view
- NSInteger index = [self indexOfItemView:[self itemViewAtPoint:[tapGesture locationInView:_contentView]]];
- if (index != NSNotFound) {
- if (!_delegate || [_delegate sliderNeighbor:self shouldSelectItemAtIndex:index]) {
- if ((index != self.currentItemIndex && _centerItemWhenSelected) ||
- (index == self.currentItemIndex && _scrollToItemBoundary)) {
- [self scroll2ItemViewAtIndex:index animated:YES];
- }
- [_delegate sliderNeighbor:self didSelectItemAtIndex:index];
- } else if (_scrollEnabled && _scrollToItemBoundary && _autoscroll) {
- [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
- }
- } else {
- [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
- }
-}
-
-- (void)pushAnimationState:(BOOL)enabled
-{
- [CATransaction begin];
- [CATransaction setDisableActions:!enabled];
-}
-
-- (void)popAnimationState
-{
- [CATransaction commit];
-}
-
-- (void)reloadData
-{
- //remove old views
- for (UIView *view in [_itemViews allValues])
- {
- [view.superview removeFromSuperview];
- }
-
- //bail out if not set up yet
- if (!_dataSource || !_contentView)
- {
- return;
- }
-
- //get number of items and placeholders
- _numberOfVisibleItems = 0;
- _numberOfItems = [_dataSource numberOfItemsInSliderNeighbor:self];
- if ([_dataSource respondsToSelector:@selector(numberOfPlaceholdersInsliderNeighbor:)]) {
- _numberOfPlaceholders = [_dataSource numberOfPlaceholdersInsliderNeighbor:self];
- }
-
- //reset view pools
- self.itemViews = [NSMutableDictionary dictionary];
- self.itemViewPool = [NSMutableSet set];
- self.placeholderViewPool = [NSMutableSet setWithCapacity:_numberOfPlaceholders];
-
- //layout views
- [self setNeedsLayout];
-
- //fix scroll offset
- if (_numberOfItems > 0 && _scrollOffset < 0.0)
- {
- [self scroll2ItemViewAtIndex:0 animated:(_numberOfPlaceholders > 0)];
- }
-}
-
-#pragma mark scrolling
-
-- (NSInteger)currentItemIndex
-{
- return [self clampedIndex:round(_scrollOffset)];
-}
-
-- (NSInteger)nextItemIndex
-{
- return ([self currentItemIndex]+1)%_numberOfItems;
-}
-
-- (NSInteger)lastItemIndex
-{
- if ([self currentItemIndex] == 0) {
- return _numberOfItems - 1;
- }
- return ((NSInteger)round(abs((int)[self currentItemIndex] - 1))) % _numberOfItems;
-}
-
-- (NSInteger)minScrollDistanceFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex
-{
- NSInteger directDistance = toIndex - fromIndex;
- if (_wrapEnabled)
- {
- NSInteger wrappedDistance = MIN(toIndex, fromIndex) + _numberOfItems - MAX(toIndex, fromIndex);
- if (fromIndex < toIndex)
- {
- wrappedDistance = -wrappedDistance;
- }
- return (ABS(directDistance) <= ABS(wrappedDistance))? directDistance: wrappedDistance;
- }
- return directDistance;
-}
-
-- (NSInteger)clampedIndex:(NSInteger)index
-{
- if (_numberOfItems == 0)
- {
- return -1;
- } else if (_wrapEnabled) {
- return index - floor((CGFloat)index / (CGFloat)_numberOfItems) * _numberOfItems;
- } else {
- return MIN(MAX(0, index), MAX(0, _numberOfItems - 1));
- }
-}
-
-- (CGFloat)clampedOffset:(CGFloat)offset
-{
- if (_numberOfItems == 0) {
- return -1.0;
- } else if (_wrapEnabled) {
- return offset - floor(offset / (CGFloat)_numberOfItems) * _numberOfItems;
- } else {
- return MIN(MAX(0.0, offset), MAX(0.0, (CGFloat)_numberOfItems - 1.0));
- }
-}
-
-- (CGFloat)minScrollDistanceFromOffset:(CGFloat)fromOffset toOffset:(CGFloat)toOffset
-{
- CGFloat directDistance = toOffset - fromOffset;
- if (_wrapEnabled) {
- CGFloat wrappedDistance = MIN(toOffset, fromOffset) + _numberOfItems - MAX(toOffset, fromOffset);
- if (fromOffset < toOffset) {
- wrappedDistance = -wrappedDistance;
- }
- return (fabs(directDistance) <= fabs(wrappedDistance))? directDistance: wrappedDistance;
- }
- return directDistance;
-}
-
-- (void)scrollByOffset:(CGFloat)offset duration:(NSTimeInterval)duration
-{
- if (duration > 0.0) {
- _decelerating = NO;
- _scrolling = YES;
- _startTime = CACurrentMediaTime();
- _startOffset = _scrollOffset;
- _scrollDuration = duration;
- _endOffset = _startOffset + offset;
- if (!_wrapEnabled) {
- _endOffset = [self clampedOffset:_endOffset];
- }
- [_delegate sliderNeighborWillBeginScrollingAnimation:self];
- [self startAnimation];
- } else {
- self.scrollOffset += offset;
- }
-}
-
-- (void)scroll2Offset:(CGFloat)offset duration:(NSTimeInterval)duration
-{
- [self scrollByOffset:[self minScrollDistanceFromOffset:_scrollOffset toOffset:offset] duration:duration];
- if (_delegate && [_delegate respondsToSelector:@selector(sliderNeighbor:didScrollToItemAtIndex:)]) {
- [_delegate sliderNeighbor:self didScrollToItemAtIndex:offset];
- }
-}
-
-- (void)scroll2ItemViewAtIndex:(NSInteger)index duration:(NSTimeInterval)duration
-{
- [self scroll2Offset:index duration:duration];
-}
-
-- (void)scroll2ItemViewAtIndex:(NSInteger)index animated:(BOOL)animated
-{
- [self scroll2ItemViewAtIndex:index duration:animated? 0.6: 0];
-}
-
-- (void)scrollByNumberOfItems:(NSInteger)itemCount duration:(NSTimeInterval)duration
-{
- if (duration > 0.0) {
- CGFloat offset = 0.0;
- if (itemCount > 0) {
- offset = (floor(_scrollOffset) + itemCount) - _scrollOffset;
- } else if (itemCount < 0) {
- offset = (ceil(_scrollOffset) + itemCount) - _scrollOffset;
- } else {
- offset = round(_scrollOffset) - _scrollOffset;
- }
- [self scrollByOffset:offset duration:duration];
- } else {
- self.scrollOffset = [self clampedIndex:_previousItemIndex + itemCount];
- }
-}
-
-- (void)removeItemAtIndex:(NSInteger)index animated:(BOOL)animated
-{
- index = [self clampedIndex:index];
- UIView *itemView = [self itemViewAtIndex:index];
-
- if (animated) {
- [UIView beginAnimations:nil context:nil];
- [UIView setAnimationDuration:0.1];
- [UIView setAnimationDelegate:itemView.superview];
- [UIView setAnimationDidStopSelector:@selector(removeFromSuperview)];
- [self performSelector:@selector(queueItemView:) withObject:itemView afterDelay:0.1];
- itemView.superview.layer.opacity = 0.0;
- [UIView commitAnimations];
-
- [UIView beginAnimations:nil context:nil];
- [UIView setAnimationDelay:0.1];
- [UIView setAnimationDuration:INSERT_DURATION];
- [UIView setAnimationDelegate:self];
- [UIView setAnimationDidStopSelector:@selector(depthSortViews)];
- [self removeViewAtIndex:index];
- _numberOfItems --;
- _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
- [self updateNumberOfVisibleItems];
- _scrollOffset = self.currentItemIndex;
- [self didScroll];
- [UIView commitAnimations];
- } else {
- [self pushAnimationState:NO];
- [self queueItemView:itemView];
- [itemView.superview removeFromSuperview];
- [self removeViewAtIndex:index];
- _numberOfItems --;
- _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
- _scrollOffset = self.currentItemIndex;
- [self didScroll];
- [self depthSortViews];
- [self popAnimationState];
- }
-}
-
-- (void)insertItemAtIndex:(NSInteger)index animated:(BOOL)animated
-{
- _numberOfItems ++;
- _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
- [self updateNumberOfVisibleItems];
-
- index = [self clampedIndex:index];
- [self insertView:nil atIndex:index];
- [self loadViewAtIndex:index];
-
- if (fabs(_itemWidth) < FLOAT_ERROR_MARGIN) {
- [self updateItemWidth];
- }
-
- if (animated) {
- [UIView beginAnimations:nil context:nil];
- [UIView setAnimationDuration:INSERT_DURATION];
- [UIView setAnimationDelegate:self];
- [UIView setAnimationDidStopSelector:@selector(didScroll)];
- [self transformItemViews];
- [UIView commitAnimations];
- } else {
- [self pushAnimationState:NO];
- [self didScroll];
- [self popAnimationState];
- }
-
- if (_scrollOffset < 0.0) {
- [self scroll2ItemViewAtIndex:0 animated:(animated && _numberOfPlaceholders)];
- }
-}
-
-- (void)reloadItemAtIndex:(NSInteger)index animated:(BOOL)animated
-{
- //get container view
- UIView *containerView = [[self itemViewAtIndex:index] superview];
- if (containerView) {
- if (animated) {
- //fade transition
- CATransition *transition = [CATransition animation];
- transition.duration = INSERT_DURATION;
- transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
- transition.type = kCATransitionFade;
- [containerView.layer addAnimation:transition forKey:nil];
- }
-
- //reload view
- [self loadViewAtIndex:index withContainerView:containerView];
- }
-}
-
-#pragma mark Animation
-- (void)step
-{
- [self pushAnimationState:NO];
- NSTimeInterval currentTime = CACurrentMediaTime();
- double delta = currentTime - _lastTime;
- _lastTime = currentTime;
-
- if (_scrolling && !_dragging) {
- NSTimeInterval time = MIN(1.0, (currentTime - _startTime) / _scrollDuration);
- delta = [self easeInOut:time];
- _scrollOffset = _startOffset + (_endOffset - _startOffset) * delta;
- [self didScroll];
- if (time >= 1.0) {
- _scrolling = NO;
- [self depthSortViews];
- [self pushAnimationState:YES];
- [_delegate sliderNeighborDidEndScrollingAnimation:self];
- [self popAnimationState];
- }
- } else if (_decelerating) {
- CGFloat time = MIN(_scrollDuration, currentTime - _startTime);
- CGFloat acceleration = -_startVelocity/_scrollDuration;
- CGFloat distance = _startVelocity * time + 0.5 * acceleration * pow(time, 2.0);
- _scrollOffset = _startOffset + distance;
- [self didScroll];
- if (fabs(time - _scrollDuration) < FLOAT_ERROR_MARGIN) {
- _decelerating = NO;
- [self pushAnimationState:YES];
- [_delegate sliderNeighborDidEndDecelerating:self];
- [self popAnimationState];
- if ((_scrollToItemBoundary || fabs(_scrollOffset - [self clampedOffset:_scrollOffset]) > FLOAT_ERROR_MARGIN) && !_autoscroll) {
- if (fabs(_scrollOffset - self.currentItemIndex) < FLOAT_ERROR_MARGIN) {
- //call scroll to trigger events for legacy support reasons
- //even though technically we don't need to scroll at all
- [self scroll2ItemViewAtIndex:self.currentItemIndex duration:0.01];
- } else {
- [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
- }
- } else {
- CGFloat difference = round(_scrollOffset) - _scrollOffset;
- if (difference > 0.5) {
- difference = difference - 1.0;
- } else if (difference < -0.5) {
- difference = 1.0 + difference;
- }
- _toggleTime = currentTime - MAX_TOGGLE_DURATION * fabs(difference);
- _toggle = MAX(-1.0, MIN(1.0, -difference));
- }
- }
- } else if (_autoscroll && !_dragging) {
- //autoscroll goes backwards from what you'd expect, for historical reasons
- self.scrollOffset = [self clampedOffset:_scrollOffset - delta * _autoscroll];
- } else if (fabs(_toggle) > FLOAT_ERROR_MARGIN) {
- NSTimeInterval toggleDuration = _startVelocity? MIN(1.0, MAX(0.0, 1.0 / fabs(_startVelocity))): 1.0;
- toggleDuration = MIN_TOGGLE_DURATION + (MAX_TOGGLE_DURATION - MIN_TOGGLE_DURATION) * toggleDuration;
- NSTimeInterval time = MIN(1.0, (currentTime - _toggleTime) / toggleDuration);
- delta = [self easeInOut:time];
- _toggle = (_toggle < 0.0)? (delta - 1.0): (1.0 - delta);
- [self didScroll];
- } else if (!_autoscroll) {
- [self stopAnimation];
- }
-
- [self popAnimationState];
-}
-
-- (CGFloat)easeInOut:(CGFloat)time
-{
- return (time < 0.5)? 0.5 * pow(time * 2.0, 3.0): 0.5 * pow(time * 2.0 - 2.0, 3.0) + 1.0;
-}
-
-- (CGFloat)decelerationDistance
-{
- CGFloat acceleration = -_startVelocity * DECELERATION_MULTIPLIER * (1.0 - _decelerationRate);
- return -pow(_startVelocity, 2.0) / (2.0 * acceleration);
-}
-
-- (void)startDecelerating
-{
- CGFloat distance = [self decelerationDistance];
- _startOffset = _scrollOffset;
- _endOffset = _startOffset + distance;
- if (_pagingEnabled) {
- if (distance > 0.0) {
- _endOffset = ceil(_startOffset);
- } else {
- _endOffset = floor(_startOffset);
- }
- } else if (_stopAtItemBoundary) {
- if (distance > 0.0) {
- _endOffset = ceil(_endOffset);
- } else {
- _endOffset = floor(_endOffset);
- }
- }
- if (!_wrapEnabled) {
- if (_bounces) {
- _endOffset = MAX(-_bounceDistance, MIN(_numberOfItems - 1.0 + _bounceDistance, _endOffset));
- } else {
- _endOffset = [self clampedOffset:_endOffset];
- }
- }
- distance = _endOffset - _startOffset;
-
- _startTime = CACurrentMediaTime();
- _scrollDuration = fabs(distance) / fabs(0.5 * _startVelocity);
-
- if (distance != 0.0) {
- _decelerating = YES;
- [self startAnimation];
- }
-}
-
-
-- (BOOL)shouldDecelerate
-{
- return (fabs(_startVelocity) > SCROLL_SPEED_THRESHOLD) &&
- (fabs([self decelerationDistance]) > DECELERATE_THRESHOLD);
-}
-
-- (BOOL)shouldScroll
-{
- return (fabs(_startVelocity) > SCROLL_SPEED_THRESHOLD) &&
- (fabs(_scrollOffset - self.currentItemIndex) > SCROLL_DISTANCE_THRESHOLD);
-}
-
-- (void)didScroll
-{
- if (_wrapEnabled || !_bounces) {
- _scrollOffset = [self clampedOffset:_scrollOffset];
- } else {
- CGFloat min = -_bounceDistance;
- CGFloat max = MAX(_numberOfItems - 1, 0.0) + _bounceDistance;
- if (_scrollOffset < min) {
- _scrollOffset = min;
- _startVelocity = 0.0;
- } else if (_scrollOffset > max) {
- _scrollOffset = max;
- _startVelocity = 0.0;
- }
- }
-
- //check if index has changed
- NSInteger difference = [self minScrollDistanceFromIndex:self.currentItemIndex toIndex:self.previousItemIndex];
- if (difference) {
- _toggleTime = CACurrentMediaTime();
- _toggle = MAX(-1, MIN(1, difference));
-
- [self startAnimation];
- }
-
- [self loadUnloadViews];
- [self transformItemViews];
-
- //notify delegate of offset change
- if (fabs(_scrollOffset - _previousScrollOffset) > 0.000001)
- {
- [self pushAnimationState:YES];
- [_delegate sliderNeighborDidScroll:self];
- [self popAnimationState];
- }
-
- //notify delegate of index change
- if (_previousItemIndex != self.currentItemIndex)
- {
- [self pushAnimationState:YES];
- if([_delegate respondsToSelector:@selector(sliderNeighborCurrentItemIndexDidChange:from:to:)]) {
- [_delegate sliderNeighborCurrentItemIndexDidChange:self from:_previousItemIndex to:self.currentItemIndex];
- }
- [self popAnimationState];
- }
-
- //update previous index
- _previousScrollOffset = _scrollOffset;
- _previousItemIndex = self.currentItemIndex;
- [self.indicator setCurrentPoint:self.currentItemIndex];
-}
-
-- (void)startAnimation
-{
- if (!_timer) {
- self.timer = [NSTimer timerWithTimeInterval:1.0/60.0
- target:self
- selector:@selector(step)
- userInfo:nil
- repeats:YES];
-
- [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
- [[NSRunLoop mainRunLoop] addTimer:_timer forMode:UITrackingRunLoopMode];
-
- }
-}
-
-- (void)stopAnimation
-{
- [_timer invalidate];
- _timer = nil;
-}
-
-#pragma mark -
-#pragma mark view management
-
-- (NSArray *)indexesForVisibleItems
-{
- return [[_itemViews allKeys] sortedArrayUsingSelector:@selector(compare:)];
-}
-
-- (NSArray *)visibleItemViews
-{
- NSArray *indexes = [self indexesForVisibleItems];
- return [_itemViews objectsForKeys:indexes notFoundMarker:[NSNull null]];
-}
-
-- (UIView *)itemViewAtIndex:(NSInteger)index
-{
- return _itemViews[@(index)];
-}
-
-- (UIView *)currentItemView
-{
- return [self itemViewAtIndex:self.currentItemIndex];
-}
-
-- (NSInteger)indexOfItemView:(UIView *)view
-{
- NSInteger index = [[_itemViews allValues] indexOfObject:view];
- if (index != NSNotFound) {
- return [[_itemViews allKeys][index] integerValue];
- }
- return NSNotFound;
-}
-
-- (NSInteger)indexOfItemViewOrSubview:(UIView *)view
-{
- NSInteger index = [self indexOfItemView:view];
- if (index == NSNotFound && view != nil && view != _contentView) {
- return [self indexOfItemViewOrSubview:view.superview];
- }
- return index;
-}
-
-- (UIView *)itemViewAtPoint:(CGPoint)point
-{
- for (UIView *view in [[[_itemViews allValues] sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))sliderNeighorCompareViewDepth context:(__bridge void *)self] reverseObjectEnumerator])
- {
- if ([view.superview.layer hitTest:point]) {
- return view;
- }
- }
- return nil;
-}
-
-- (void)setItemView:(UIView *)view forIndex:(NSInteger)index
-{
- _itemViews[@(index)] = view;
-}
-
-- (void)removeViewAtIndex:(NSInteger)index
-{
- NSMutableDictionary *newItemViews = [NSMutableDictionary dictionaryWithCapacity:[_itemViews count] - 1];
- for (NSNumber *number in [self indexesForVisibleItems])
- {
- NSInteger i = [number integerValue];
- if (i < index) {
- newItemViews[number] = _itemViews[number];
- } else if (i > index) {
- newItemViews[@(i - 1)] = _itemViews[number];
- }
- }
- self.itemViews = newItemViews;
- [self.indicator setPointCount:self.itemViews.count];
-}
-
-- (void)insertView:(UIView *)view atIndex:(NSInteger)index
-{
- NSMutableDictionary *newItemViews = [NSMutableDictionary dictionaryWithCapacity:[_itemViews count] + 1];
- for (NSNumber *number in [self indexesForVisibleItems])
- {
- NSInteger i = [number integerValue];
- if (i < index) {
- newItemViews[number] = _itemViews[number];
- } else {
- newItemViews[@(i + 1)] = _itemViews[number];
- }
- }
- if (view) {
- [self setItemView:view forIndex:index];
- }
- self.itemViews = newItemViews;
-
- [self.indicator setPointCount:self.itemViews.count];
-}
-
-#pragma mark -
-#pragma mark View loading
-
-- (UIView *)loadViewAtIndex:(NSInteger)index withContainerView:(UIView *)containerView
-{
- [self pushAnimationState:NO];
-
- UIView *view = nil;
- if (index < 0) {
- view = [_dataSource sliderNeighbor:self placeholderViewAtIndex:(NSInteger)(ceil((CGFloat)_numberOfPlaceholdersToShow/2.0)) + index reusingView:[self dequeuePlaceholderView]];
- } else if (index >= _numberOfItems) {
- view = [_dataSource sliderNeighbor:self placeholderViewAtIndex:_numberOfPlaceholdersToShow/2.0 + index - _numberOfItems reusingView:[self dequeuePlaceholderView]];
- } else {
- view = [_dataSource sliderNeighbor:self viewForItemAtIndex:index reusingView:[self dequeueItemView]];
- }
-
- if (view == nil) {
- view = [[UIView alloc] init];
- }
-
- [self setItemView:view forIndex:index];
- if (containerView) {
- //get old item view
- UIView *oldItemView = [containerView.subviews lastObject];
- if (index < 0 || index >= _numberOfItems) {
- [self queuePlaceholderView:oldItemView];
- } else {
- [self queueItemView:oldItemView];
- }
-
- //set container frame
- CGRect frame = containerView.bounds;
- if(_vertical) {
- frame.size.width = view.frame.size.width;
- frame.size.height = MIN(_itemWidth, view.frame.size.height);
- } else {
- frame.size.width = MIN(_itemWidth, view.frame.size.width);
- frame.size.height = view.frame.size.height;
- }
- containerView.bounds = frame;
-
- //set view frame
- frame = view.frame;
- frame.origin.x = (containerView.bounds.size.width - frame.size.width) / 2.0;
- frame.origin.y = (containerView.bounds.size.height - frame.size.height) / 2.0;
- view.frame = frame;
-
- //switch views
- [oldItemView removeFromSuperview];
- [containerView addSubview:view];
- } else {
- [_contentView addSubview:[self containView:view]];
- }
- view.superview.layer.opacity = 0.0;
- [self transformItemView:view atIndex:index];
-
- [self popAnimationState];
-
- return view;
-}
-
-- (UIView *)loadViewAtIndex:(NSInteger)index
-{
- return [self loadViewAtIndex:index withContainerView:nil];
-}
-
-- (void)loadUnloadViews
-{
- //set item width
- [self updateItemWidth];
-
- //update number of visible items
- [self updateNumberOfVisibleItems];
-
- //calculate visible view indices
- NSMutableSet *visibleIndices = [NSMutableSet setWithCapacity:_numberOfVisibleItems];
- NSInteger min = -(NSInteger)(ceil((CGFloat)_numberOfPlaceholdersToShow/2.0));
- NSInteger max = _numberOfItems - 1 + _numberOfPlaceholdersToShow/2;
- NSInteger offset = self.currentItemIndex - _numberOfVisibleItems/2;
- if (!_wrapEnabled) {
- offset = MAX(min, MIN(max - _numberOfVisibleItems + 1, offset));
- }
- for (NSInteger i = 0; i < _numberOfVisibleItems; i++)
- {
- NSInteger index = i + offset;
- if (_wrapEnabled) {
- index = [self clampedIndex:index];
- }
- CGFloat alpha = [self alphaForItemWithOffset:[self offsetForItemAtIndex:index]];
- if (alpha) {
- //only add views with alpha > 0
- [visibleIndices addObject:@(index)];
- }
- }
-
- //remove offscreen views
- for (NSNumber *number in [_itemViews allKeys])
- {
- if (![visibleIndices containsObject:number]) {
- UIView *view = _itemViews[number];
- if ([number integerValue] < 0 || [number integerValue] >= _numberOfItems) {
- [self queuePlaceholderView:view];
- } else {
- [self queueItemView:view];
- }
- [view.superview removeFromSuperview];
- [(NSMutableDictionary *)_itemViews removeObjectForKey:number];
- }
- }
-
- //add onscreen views
- for (NSNumber *number in visibleIndices)
- {
- UIView *view = _itemViews[number];
- if (view == nil) {
- [self loadViewAtIndex:[number integerValue]];
- }
- }
-}
-
-#pragma mark View queing
-
-- (void)queueItemView:(UIView *)view
-{
- if (view) {
- [_itemViewPool addObject:view];
- }
-}
-
-- (void)queuePlaceholderView:(UIView *)view
-{
- if (view) {
- [_placeholderViewPool addObject:view];
- }
-}
-
-- (UIView *)dequeueItemView
-{
- UIView *view = [_itemViewPool anyObject];
- if (view) {
- [_itemViewPool removeObject:view];
- }
- return view;
-}
-
-- (UIView *)dequeuePlaceholderView
-{
- UIView *view = [_placeholderViewPool anyObject];
- if (view) {
- [_placeholderViewPool removeObject:view];
- }
- return view;
-}
-
-- (void)setIndicator:(WXIndicatorView *)indicator
-{
- _indicator = indicator;
- [_indicator setPointCount:self.numberOfItems];
- [_indicator setCurrentPoint:[self currentItemIndex]];
-}
-
-- (void)layoutSubviews
-{
- [super layoutSubviews];
- _contentView.frame = self.bounds;
- [self layOutItemViews];
-}
-
-@end
-
-@implementation NSObject (WXSliderNeighborView)
-
-- (NSUInteger)numberOfPlaceholdersInSliderNeighborView:(__unused WXSliderNeighborView *)sliderNeighbor { return 0; }
-- (void)sliderNeighborWillBeginScrollingAnimation:(__unused WXSliderNeighborView *)sliderNeighbor {}
-- (void)sliderNeighborDidEndScrollingAnimation:(__unused WXSliderNeighborView *)sliderNeighbor {}
-- (void)sliderNeighborDidScroll:(__unused WXSliderNeighborView *)sliderNeighbor {}
-
-- (void)sliderNeighborCurrentItemIndexDidChange:(__unused WXSliderNeighborView *)sliderNeighbor {}
-- (void)sliderNeighborWillBeginDragging:(__unused WXSliderNeighborView *)sliderNeighbor {}
-- (void)sliderNeighborDidEndDragging:(__unused WXSliderNeighborView *)sliderNeighbor willDecelerate:(__unused BOOL)decelerate {}
-- (void)sliderNeighborWillBeginDecelerating:(__unused WXSliderNeighborView *)sliderNeighbor {}
-- (void)sliderNeighborDidEndDecelerating:(__unused WXSliderNeighborView *)sliderNeighbor {}
-
-- (BOOL)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor shouldSelectItemAtIndex:(__unused NSInteger)index { return YES; }
-- (void)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor didSelectItemAtIndex:(__unused NSInteger)index {}
-
-- (CGFloat)sliderNeighborItemWidth:(__unused WXSliderNeighborView *)sliderNeighbor { return 0; }
-- (CATransform3D)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor
- itemTransformForOffset:(__unused CGFloat)offset
- baseTransform:(CATransform3D)transform { return transform; }
-- (CGFloat)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor
- valueForOption:(__unused WXSliderNeighborOption)option
- withDefault:(CGFloat)value { return value; }
-
-@end
-
-@interface WXSliderNeighborComponent () <WXIndicatorComponentDelegate>{
- WXPixelType neighborSpace;
- CGFloat neighborAlpha;
- CGFloat neighborScale;
- CGFloat currentItemScale;
-}
-
-@property (nonatomic, strong) WXSliderNeighborView *sliderView;
-@property (nonatomic, assign) BOOL autoPlay;
-@property (nonatomic, assign) NSUInteger interval;
-@property (nonatomic, assign) NSInteger index;
-@property (nonatomic, strong) NSMutableArray *items;
-@property (nonatomic, strong) NSTimer *autoTimer;
-@property (nonatomic, assign) BOOL sliderChangeEvent;
-@property (nonatomic, assign) NSInteger currentIndex;
-@property (nonatomic) CGRect itemRect;
-@property (nonatomic, assign) BOOL scrollable;
-
-@end
-
-#define DEFAULT_NEIGHBOR_ITEM_SCALE 0.8
-#define DEFAULT_CURRENT_ITEM_SCALE 0.9
-#define DEFAULT_NEIGHBOR_ALPHA 0.6
-#define DEFAULT_ANIMATION_DURATION 0.3
-#define DEFAULT_NEIGHBOR_SPACE 25
-
-
-@implementation WXSliderNeighborComponent
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance {
- if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
- _sliderChangeEvent = NO;
- _interval = 3000;
- _items = [NSMutableArray array];
- _itemRect = CGRectNull;
- self->neighborAlpha = DEFAULT_NEIGHBOR_ALPHA;
- self->neighborScale = DEFAULT_NEIGHBOR_ITEM_SCALE;
- self->currentItemScale = DEFAULT_CURRENT_ITEM_SCALE;
- self->neighborSpace = [WXConvert WXPixelType:@(DEFAULT_NEIGHBOR_SPACE) scaleFactor:self.weexInstance.pixelScaleFactor];
- _scrollable = YES;
- [self setAttributes:attributes];
-
- _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
- }
- self.cssNode->style.flex_direction = CSS_FLEX_DIRECTION_ROW;
-
- return self;
-}
-
-
-#pragma mark life circle
-
-- (UIView *)loadView
-{
- _sliderView = [[WXSliderNeighborView alloc] init];
- return _sliderView;
-}
-
-- (void)dealloc
-{
- _sliderView.delegate = nil;
- _sliderView.dataSource = nil;
- if (_autoPlay) {
- [self _stopAutoPlayTimer];
- }
- _sliderView = nil;
- [self.items removeAllObjects];
-}
-
-- (void)viewDidUnload
-{
- [self.items removeAllObjects];
-}
-
-- (void)viewDidLoad
-{
- _sliderView = (WXSliderNeighborView *)self.view;
- _sliderView.delegate = self;
- _sliderView.dataSource = self;
- _sliderView.contentView.clipsToBounds = YES;
- _sliderView.scrollEnabled = _scrollable;
- UIAccessibilityTraits traits = UIAccessibilityTraitAdjustable;
-
- if (_autoPlay) {
- traits |= UIAccessibilityTraitUpdatesFrequently;
- [self _startAutoPlayTimer];
- } else {
- [self _stopAutoPlayTimer];
- }
- _sliderView.accessibilityTraits = traits;
-}
-
-- (void)layoutDidFinish {
-
- [_sliderView setCurrentItemIndex:_index];
-}
-
-- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- if (subcomponent->_positionType == WXPositionTypeFixed) {
- [self.weexInstance.rootView addSubview:subcomponent.view];
- return;
- }
-
- // use _lazyCreateView to forbid component like cell's view creating
- if(_lazyCreateView) {
- subcomponent->_lazyCreateView = YES;
- }
-
- if (!subcomponent->_lazyCreateView || (self->_lazyCreateView && [self isViewLoaded])) {
- UIView *view = subcomponent.view;
-
- WXSliderNeighborView *sliderView = (WXSliderNeighborView *)self.view;
- if ([view isKindOfClass:[WXIndicatorView class]]) {
- ((WXIndicatorComponent *)subcomponent).delegate = self;
- [sliderView addSubview:view];
- return;
- }
-
- if(index < 0) {
- [self.items addObject:view];
- } else {
- [self.items insertObject:view atIndex:index];
- }
-
- subcomponent.isViewFrameSyncWithCalculated = NO;
-
- if (index == -1) {
- [sliderView insertView:view atIndex:index];
- } else {
- NSInteger offset = 0;
- for (int i = 0; i < [self.items count]; ++i) {
- if (index == i) break;
-
- if ([self.items[i] isKindOfClass:[WXIndicatorView class]]) {
- offset++;
- }
- }
- [sliderView insertView:view atIndex:index - offset];
- }
-
- [sliderView reloadData];
- }
-}
-
-#pragma mark attributes update
-- (void)setAttributes:(NSDictionary *)attributes
-{
- if (attributes[@"index"]) {
- _index = [attributes[@"index"] integerValue];
- _currentIndex = _index;
- }
- if (attributes[@"autoPlay"]) {
- _autoPlay = [attributes[@"autoPlay"] boolValue];
- if (_autoPlay) {
- [self _startAutoPlayTimer];
- } else {
- [self _stopAutoPlayTimer];
- }
- }
- if (attributes[@"interval"]) {
- _interval = [attributes[@"interval"] integerValue];
-
- [self _stopAutoPlayTimer];
-
- if (_autoPlay) {
- [self _startAutoPlayTimer];
- }
- }
- if (attributes[@"neighborScale"]) {
- [self setNeighborScale:attributes];
- }
- if (attributes[@"currentItemScale"]) {
- [self setCurrentItemScale:attributes];
- }
- if (attributes[@"neighborAlpha"]) {
- [self setNeighborAlpha:attributes];
- }
- if (attributes[@"neighborSpace"]) {
- [self setNeighborSpace:attributes];
- }
- if (attributes[@"scrollable"]) {
- _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
- ((WXSliderNeighborView *)self.view).scrollEnabled = _scrollable;
- }
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- [self setAttributes:attributes];
- [self.sliderView setCurrentItemIndex:_index];
- [self updateSliderPage:YES];
-}
-
-#pragma mark styles update
-- (void)updateStyles:(NSDictionary *)styles {
-
-}
-
-#pragma mark event
-- (void)addEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"change"]) {
- _sliderChangeEvent = YES;
- }
-}
-
-- (void)removeEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"change"]) {
- _sliderChangeEvent = NO;
- }
-}
-
-#pragma mark WXIndicatorComponentDelegate Methods
-
-- (void)setIndicatorView:(WXIndicatorView *)indicatorView
-{
- NSAssert(_sliderView, @"");
- [_sliderView setIndicator:indicatorView];
-}
-
-- (void)setNeighborSpace:(NSDictionary *)attributes{
- if(attributes[@"neighborSpace"]) {
- self->neighborSpace = [WXConvert WXPixelType:attributes[@"neighborSpace"] scaleFactor:self.weexInstance.pixelScaleFactor];
- }
-}
-
-- (void)setNeighborAlpha:(NSDictionary *)attributes {
- if (attributes[@"neighborAlpha"]) {
- self->neighborAlpha = [WXConvert CGFloat:attributes[@"neighborAlpha"]];
- self->neighborAlpha = self->neighborAlpha >= 0 ? self->neighborAlpha : 0;
- self->neighborAlpha = self->neighborAlpha <= 1 ? self->neighborAlpha: 1;
- }
-}
-
-- (void)setCurrentItemScale:(NSDictionary *)attributes {
- if (attributes[@"currentItemScale"]) {
- self->currentItemScale = [WXConvert CGFloat:attributes[@"currentItemScale"]];
- self->currentItemScale = self->currentItemScale >= 0 ? self->currentItemScale : 0;
- self->currentItemScale = self->currentItemScale <= 1 ? self->currentItemScale: 1;
- }
-}
-
-- (void)setNeighborScale:(NSDictionary *)attributes
-{
- if (attributes[@"neighborScale"]) {
- self->neighborScale = [WXConvert CGFloat:attributes[@"neighborScale"]];
- self->neighborScale = self->neighborScale >= 0? self->neighborScale : 0;
- self->neighborScale = self->neighborScale <= 1? self->neighborScale :1;
- }
-}
-
-#pragma mark Private Methods
-
-- (void)_startAutoPlayTimer
-{
- if (!self.autoTimer || ![self.autoTimer isValid]) {
- __weak __typeof__(self) weakSelf = self;
- self.autoTimer = [NSTimer wx_scheduledTimerWithTimeInterval:_interval/1000.0f block:^() {
- [weakSelf _autoPlayOnTimer];
- } repeats:YES];
- [[NSRunLoop currentRunLoop] addTimer:self.autoTimer forMode:NSRunLoopCommonModes];
- }
-}
-
-- (void)_stopAutoPlayTimer
-{
- if (self.autoTimer && [self.autoTimer isValid]) {
- [self.autoTimer invalidate];
- self.autoTimer = nil;
- }
-}
-
-- (void)_autoPlayOnTimer
-{
- WXSliderNeighborView *sliderNeighborView = (WXSliderNeighborView *)self.view;
-
- int indicatorCnt = 0;
- for (int i = 0; i < [self.items count]; ++i) {
- if ([self.items[i] isKindOfClass:[WXIndicatorView class]]) {
- indicatorCnt++;
- }
- }
-
- self.currentIndex ++;
- if (self.currentIndex >= self.items.count - indicatorCnt) {
- self.currentIndex = 0;
- }
-
- [sliderNeighborView setCurrentItemIndex:self.currentIndex];
-}
-
-#pragma mark sliderNeighbor Delegate && dataSource
-
-- (NSInteger)numberOfItemsInSliderNeighbor:(WXSliderNeighborView *)sliderNeighbor {
- return [self.items count];
-}
-
-- (NSInteger)numberOfPlaceholdersInsliderNeighbor:(WXSliderNeighborView *)sliderNeighbor {
- return 2;
-}
-
-- (UIView *)sliderNeighbor:(WXSliderNeighborView *)sliderNeighbor viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view {
-
- if (!view) {
- if (index < [self.items count]) {
- view = self.items[index];
- }else {
- return nil;
- }
- } else {
- view.tag = 1;
- }
-
- if (CGRectIsNull(_itemRect)) {
- _itemRect = view.frame;
- }
- if (index == [self.items count] - 1) {
- [self updateSliderPage:NO];
- }
-
- return view;
-}
-
-- (BOOL)sliderNeighbor:(WXSliderNeighborView *)sliderNeighbor shouldSelectItemAtIndex:(NSInteger)index {
-
- return YES;
-}
-
-- (UIView *)sliderNeighbor:(WXSliderNeighborView *)sliderNeighbor placeholderViewAtIndex:(NSInteger)index reusingView:(UIView *)view
-{
-
- return view;
-}
-
-- (void)sliderNeighborDidEndScrollingAnimation:(WXSliderNeighborView *)sliderNeighbor
-{
- [self updateSliderPage:YES];
-}
-
-- (void)sliderNeighbor:(WXSliderNeighborView *)sliderView didScrollToItemAtIndex:(NSInteger)index
-{
- self.currentIndex = index;
-
- if (_sliderChangeEvent) {
- [self fireEvent:@"change" params:@{@"index":@(index)} domChanges:@{@"attrs": @{@"index": @(index)}}];
- }
-}
-
-- (void)sliderNeighborWillBeginDragging:(WXSliderNeighborView *)sliderNeighbor
-{
- [self _stopAutoPlayTimer];
-}
-
-- (void)sliderNeighborDidEndDragging:(WXSliderNeighborView *)sliderNeighbor willDecelerate:(BOOL)decelerate {
- if (_autoPlay) {
- [self _startAutoPlayTimer];
- }
-}
-
-- (void)updateSliderPage:(BOOL)animate
-{
- __block UIView * currentView = [self.sliderView itemViewAtIndex:[_sliderView currentItemIndex]];
- __block UIView * lastView = [self.sliderView itemViewAtIndex:[_sliderView lastItemIndex]];
- __block UIView * nextView = [self.sliderView itemViewAtIndex:[_sliderView nextItemIndex]];
-
- __block CGAffineTransform transfrom = CGAffineTransformIdentity;
- float duration = 0;
- __weak typeof(self) weakSelf = self;
- if (animate) {
- duration = DEFAULT_ANIMATION_DURATION;
- }
-
- [UIView animateWithDuration:duration animations:^{
- __strong typeof(self) strongSelf = weakSelf;
- if (strongSelf) {
- currentView.alpha = 1.0;
- if (fabs(strongSelf->currentItemScale) > CGFLOAT_MIN) {
- transfrom = CGAffineTransformConcat(transfrom,CGAffineTransformMakeScale(strongSelf->currentItemScale, strongSelf->currentItemScale));
- }
- currentView.transform = transfrom;
- transfrom = CGAffineTransformIdentity;
- if (fabs(strongSelf->neighborScale) <= CGFLOAT_MIN) {
- strongSelf->neighborScale = DEFAULT_NEIGHBOR_ITEM_SCALE;
- }
-
- CGFloat tx = 0.5*_itemRect.size.width*((1-self->neighborScale)+(1-self->currentItemScale))-self->neighborSpace;
- transfrom = CGAffineTransformConcat(transfrom, CGAffineTransformMakeScale(strongSelf->neighborScale, strongSelf->neighborScale));
- nextView.transform = CGAffineTransformConcat(transfrom, CGAffineTransformMakeTranslation(-tx, 0));
- lastView.transform = CGAffineTransformConcat(transfrom, CGAffineTransformMakeTranslation(tx, 0));
- lastView.alpha = strongSelf->neighborAlpha;
- nextView.alpha = strongSelf->neighborAlpha;
- }
- }];
-}
-
-@end
[12/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXEditComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXEditComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXEditComponent.m
deleted file mode 100644
index f2d6c99..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXEditComponent.m
+++ /dev/null
@@ -1,918 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXEditComponent.h"
-#import "WXConvert.h"
-#import "WXUtility.h"
-#import "WXSDKInstance.h"
-#import "WXSDKInstance_private.h"
-#import "WXDefine.h"
-#import "WXAssert.h"
-#import "WXComponent_internal.h"
-#import "WXComponent+PseudoClassManagement.h"
-#import "WXTextInputComponent.h"
-
-@interface WXEditComponent()
-
-//@property (nonatomic, strong) WXTextInputView *inputView;
-@property (nonatomic, strong) WXDatePickerManager *datePickerManager;
-@property (nonatomic, strong) NSDictionary *attr;
-//attribute
-@property (nonatomic) NSNumber *maxLength;
-@property (nonatomic) NSString * value;
-@property (nonatomic) BOOL autofocus;
-@property(nonatomic) UIReturnKeyType returnKeyType;
-@property (nonatomic) BOOL disabled;
-@property (nonatomic, copy) NSString *inputType;
-@property (nonatomic) NSUInteger rows;
-@property (nonatomic) BOOL hideDoneButton;
-
-//style
-@property (nonatomic) WXPixelType fontSize;
-@property (nonatomic) WXTextStyle fontStyle;
-@property (nonatomic) CGFloat fontWeight;
-@property (nonatomic, strong) NSString *fontFamily;
-@property (nonatomic, strong) UIColor *colorForStyle;
-@property (nonatomic)NSTextAlignment textAlignForStyle;
-
-//event
-@property (nonatomic) BOOL inputEvent;
-@property (nonatomic) BOOL clickEvent;
-@property (nonatomic) BOOL focusEvent;
-@property (nonatomic) BOOL blurEvent;
-@property (nonatomic) BOOL changeEvent;
-@property (nonatomic) BOOL returnEvent;
-@property (nonatomic) BOOL keyboardEvent;
-@property (nonatomic, strong) NSString *changeEventString;
-@property (nonatomic, assign) CGSize keyboardSize;
-
-// formatter
-@property (nonatomic, strong) NSString * formatRule;
-@property (nonatomic, strong) NSString * formatReplace;
-@property (nonatomic, strong) NSString * recoverRule;
-@property (nonatomic, strong) NSString * recoverReplace;
-@property (nonatomic, strong) NSDictionary * formaterData;
-
-// disable move rootView up as the keyboard show up.
-@property (nonatomic, assign) BOOL disableMoveViewUp;
-
-@end
-
-@implementation WXEditComponent
-{
- UIEdgeInsets _border;
- UIEdgeInsets _padding;
-}
-
-WX_EXPORT_METHOD(@selector(focus))
-WX_EXPORT_METHOD(@selector(blur))
-WX_EXPORT_METHOD(@selector(setSelectionRange:selectionEnd:))
-WX_EXPORT_METHOD(@selector(getSelectionRange:))
-WX_EXPORT_METHOD(@selector(setTextFormatter:))
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
- self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
- if (self) {
- _inputEvent = NO;
- _focusEvent = NO;
- _blurEvent = NO;
- _changeEvent = NO;
- _returnEvent = NO;
- _clickEvent = NO;
- _keyboardEvent = NO;
- // handle attributes
- _autofocus = [attributes[@"autofocus"] boolValue];
- _disabled = [attributes[@"disabled"] boolValue];
- _value = [WXConvert NSString:attributes[@"value"]]?:@"";
- _placeholderString = [WXConvert NSString:attributes[@"placeholder"]]?:@"";
- if(attributes[@"type"]) {
- _inputType = [WXConvert NSString:attributes[@"type"]];
- _attr = attributes;
- }
- if (attributes[@"maxlength"]) {
- _maxLength = [NSNumber numberWithUnsignedInteger:[attributes[@"maxlength"] integerValue]];
- }
- if (attributes[@"returnKeyType"]) {
- _returnKeyType = [WXConvert UIReturnKeyType:attributes[@"returnKeyType"]];
- }
- if (attributes[@"rows"]) {
- _rows = [attributes[@"rows"] integerValue];
- } else {
- _rows = 2;
- }
-
- if (attributes[@"hideDoneButton"]) {
- _hideDoneButton = [attributes[@"hideDoneButton"] boolValue];
- }
- if (attributes[@"disableMoveViewUp"]) {
- _disableMoveViewUp = [WXConvert BOOL:attributes[@"disableMoveViewUp"]];
- }
-
- // handle styles
- if (styles[@"color"]) {
- _colorForStyle = [WXConvert UIColor:styles[@"color"]];
- }
- if (styles[@"fontSize"]) {
- _fontSize = [WXConvert WXPixelType:styles[@"fontSize"] scaleFactor:self.weexInstance.pixelScaleFactor];
- }
- if (styles[@"fontWeight"]) {
- _fontWeight = [WXConvert WXTextWeight:styles[@"fontWeight"]];
- }
- if (styles[@"fontStyle"]) {
- _fontStyle = [WXConvert WXTextStyle:styles[@"fontStyle"]];
- }
- if (styles[@"fontFamily"]) {
- _fontFamily = styles[@"fontFamily"];
- }
- if (styles[@"textAlign"]) {
- _textAlignForStyle = [WXConvert NSTextAlignment:styles[@"textAlign"]];
- }
- if (styles[@"placeholderColor"]) {
- _placeholderColor = [WXConvert UIColor:styles[@"placeholderColor"]];
- }else {
- _placeholderColor = [UIColor colorWithRed:0x99/255.0 green:0x99/255.0 blue:0x99/255.0 alpha:1.0];
- }
- }
-
- return self;
-}
-
-#pragma mark - lifeCircle
-
-- (void)viewDidLoad
-{
- UIView * view = self.view;
- if ([view isKindOfClass:[UITextField class]]){
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFiledEditChanged:)
- name:UITextFieldTextDidChangeNotification
- object:view];
- }
- _padding = UIEdgeInsetsZero;
- _border = UIEdgeInsetsZero;
- self.userInteractionEnabled = YES;
- [self setType];
- [self setAutofocus:_autofocus];
- [self setTextFont];
- [self setPlaceholderAttributedString];
- [self setTextAlignment:_textAlignForStyle];
- [self setTextColor:_colorForStyle];
- [self setText:_value];
- [self setEnabled:!_disabled];
- [self setRows:_rows];
- [self setReturnKeyType:_returnKeyType];
- [self updatePattern];
-
- if (!self.hideDoneButton) {
- UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeKeyboard)];
- UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
- UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 0, 44)];
- toolbar.items = [NSArray arrayWithObjects:space, barButton, nil];
-
- self.inputAccessoryView = toolbar;
- }
-
- [self handlePseudoClass];
-}
-
-- (void)viewWillLoad
-{
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(keyboardWasShown:)
- name:UIKeyboardWillShowNotification
- object:nil];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(keyboardWillHide:)
- name:UIKeyboardWillHideNotification
- object:nil];
-}
-
-- (void)dealloc
-{
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-}
-
--(void)focus
-{
- if(self.view) {
- [self.view becomeFirstResponder];
- }
-}
-
--(void)blur
-{
- if(self.view) {
- [self.view resignFirstResponder];
- }
-}
-
--(void)setSelectionRange:(NSInteger)selectionStart selectionEnd:(NSInteger)selectionEnd
-{
- if(selectionStart>self.text.length || selectionEnd>self.text.length) {
- return;
- }
- [self.view becomeFirstResponder];
- [self setEditSelectionRange:selectionStart selectionEnd:selectionEnd];
-}
-
--(void)getSelectionRange:(WXKeepAliveCallback)callback
-{
- NSDictionary *res = [self getEditSelectionRange];
- if(callback) {
- callback(res,NO);
- }
-}
-
-- (void)setTextFormatter:(NSDictionary*)formater
-{
- _formaterData = formater;
- if (formater[@"formatRule"]) {;
- _formatRule = [self _preProcessInputTextFormatter:[WXConvert NSString:formater[@"formatRule"]]];
- }
- if (formater[@"formatReplace"]) {
- _formatReplace = [self _preProcessInputTextFormatter:[WXConvert NSString:formater[@"formatReplace"]]];
- }
- if (formater[@"recoverRule"]) {
- _recoverRule = [self _preProcessInputTextFormatter:[WXConvert NSString:formater[@"recoverRule"]]];
- }
- if (formater[@"recoverReplace"]) {
- _recoverReplace = [self _preProcessInputTextFormatter:[WXConvert NSString:formater[@"recoverReplace"]]];
- }
-}
-
-- (NSString*)_preProcessInputTextFormatter:(NSString*)formater
-{
- NSRange start = [formater rangeOfString:@"/"];
- NSRange end = [formater rangeOfString:@"/g"];
- if (start.location == NSNotFound || end.location == NSNotFound || end.location < start.location) {
- return formater;
- }
- NSRange subStringRange = NSMakeRange(start.location+1, end.location - start.location-1);
-
- return [formater substringWithRange:subStringRange];
-}
-
-#pragma mark - Overwrite Method
--(NSString *)text
-{
- return @"";
-}
-
-- (void)setText:(NSString *)text
-{
-}
-
--(void)setTextColor:(UIColor *)color
-{
-}
-
--(void)setTextAlignment:(NSTextAlignment)textAlignForStyle
-{
-}
-
--(void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
-{
-}
-
--(void)setEnabled:(BOOL)enabled
-{
-}
-
--(void)setReturnKeyType:(UIReturnKeyType)returnKeyType
-{
-}
-
--(void)setInputAccessoryView:(UIView *)inputAccessoryView
-{
-}
-
--(void)setEditSelectionRange:(NSInteger)selectionStart selectionEnd:(NSInteger)selectionEnd
-{
-}
-
--(NSDictionary *)getEditSelectionRange
-{
- return @{};
-}
-
--(void)setKeyboardType:(UIKeyboardType)keyboardType
-{
-}
-
--(void)setSecureTextEntry:(BOOL)secureTextEntry
-{
-}
-
--(void)setEditPadding:(UIEdgeInsets)padding
-{
-}
-
--(void)setEditBorder:(UIEdgeInsets)border
-{
-}
-
--(void)setAttributedPlaceholder:(NSMutableAttributedString *)attributedString font:(UIFont *)font
-{
-}
-
--(void)setFont:(UIFont *)font
-{
-}
-
--(void)setRows:(NSUInteger)rows
-{
-}
-
-#pragma mark - Add Event
-- (void)addEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"input"]) {
- _inputEvent = YES;
- }
- if ([eventName isEqualToString:@"focus"]) {
- _focusEvent = YES;
- }
- if ([eventName isEqualToString:@"blur"]) {
- _blurEvent = YES;
- }
- if ([eventName isEqualToString:@"change"]) {
- _changeEvent = YES;
- }
- if ([eventName isEqualToString:@"return"]) {
- _returnEvent = YES;
- }
- if ([eventName isEqualToString:@"click"]) {
- _clickEvent = YES;
- }
- if ([eventName isEqualToString:@"keyboard"]) {
- _keyboardEvent = YES;
- }
-}
-
-#pragma Remove Event
-
--(void)removeEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"input"]) {
- _inputEvent = NO;
- }
- if ([eventName isEqualToString:@"focus"]) {
- _focusEvent = NO;
- }
- if ([eventName isEqualToString:@"blur"]) {
- _blurEvent = NO;
- }
- if ([eventName isEqualToString:@"change"]) {
- _changeEvent = NO;
- }
- if ([eventName isEqualToString:@"return"]) {
- _returnEvent = NO;
- }
- if ([eventName isEqualToString:@"click"]) {
- _clickEvent = NO;
- }
- if ([eventName isEqualToString:@"keyboard"]) {
- _keyboardEvent = NO;
- }
-}
-
-#pragma mark - upate attributes
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- _attr = attributes;
- if (attributes[@"type"]) {
- _inputType = [WXConvert NSString:attributes[@"type"]];
- [self setType];
- }
- if (attributes[@"autofocus"]) {
- self.autofocus = [attributes[@"autofocus"] boolValue];
- }
- if (attributes[@"disabled"]) {
- _disabled = [attributes[@"disabled"] boolValue];
- [self setEnabled:!_disabled];
- }
- if (attributes[@"maxlength"]) {
- _maxLength = [NSNumber numberWithInteger:[attributes[@"maxlength"] integerValue]];
- }
-
- if (attributes[@"disableMoveViewUp"]) {
- _disableMoveViewUp = [WXConvert BOOL:attributes[@"disableMoveViewUp"]];
- }
- if (attributes[@"value"]) {
- _value = [WXConvert NSString:attributes[@"value"]]?:@"";
- if (_maxLength && [_value length] > [_maxLength integerValue]&& [_maxLength integerValue] >= 0) {
- _value = [_value substringToIndex:([_maxLength integerValue])];
- }
- [self setText:_value];
- }
- if (attributes[@"placeholder"]) {
- _placeholderString = [WXConvert NSString:attributes[@"placeholder"]]?:@"";
- [self setPlaceholderAttributedString];
- if(_value.length > 0){
- _placeHolderLabel.text = @"";
- }
- }
- if (attributes[@"returnKeyType"]) {
- _returnKeyType = [WXConvert UIReturnKeyType:attributes[@"returnKeyType"]];
- [self setReturnKeyType:_returnKeyType];
- }
- if (attributes[@"rows"]) {
- _rows = [attributes[@"rows"] integerValue];
- [self setRows:_rows];
- } else {
- _rows = 2;
- [self setRows:_rows];
- }
-}
-
-#pragma mark - upate styles
-
-- (void)updateStyles:(NSDictionary *)styles
-{
- if (styles[@"color"]) {
- _colorForStyle = [WXConvert UIColor:styles[@"color"]];
- [self setTextColor:_colorForStyle];
- }
- if (styles[@"fontSize"]) {
- _fontSize = [WXConvert WXPixelType:styles[@"fontSize"] scaleFactor:self.weexInstance.pixelScaleFactor];
- }
- if (styles[@"fontWeight"]) {
- _fontWeight = [WXConvert WXTextWeight:styles[@"fontWeight"]];
- }
- if (styles[@"fontStyle"]) {
- _fontStyle = [WXConvert WXTextStyle:styles[@"fontStyle"]];
- }
- if (styles[@"fontFamily"]) {
- _fontFamily = [WXConvert NSString:styles[@"fontFamily"]];
- }
- [self setTextFont];
-
- if (styles[@"textAlign"]) {
- _textAlignForStyle = [WXConvert NSTextAlignment:styles[@"textAlign"]];
- [self setTextAlignment:_textAlignForStyle] ;
- }
- if (styles[@"placeholderColor"]) {
- _placeholderColor = [WXConvert UIColor:styles[@"placeholderColor"]];
- }else {
- _placeholderColor = [UIColor colorWithRed:0x99/255.0 green:0x99/255.0 blue:0x99/255.0 alpha:1.0];
- }
- [self setPlaceholderAttributedString];
- [self updatePattern];
-}
-
--(void)updatePattern
-{
- UIEdgeInsets padding = UIEdgeInsetsMake(self.cssNode->style.padding[CSS_TOP], self.cssNode->style.padding[CSS_LEFT], self.cssNode->style.padding[CSS_BOTTOM], self.cssNode->style.padding[CSS_RIGHT]);
- if (!UIEdgeInsetsEqualToEdgeInsets(padding, _padding)) {
- [self setPadding:padding];
- }
-
- UIEdgeInsets border = UIEdgeInsetsMake(self.cssNode->style.border[CSS_TOP], self.cssNode->style.border[CSS_LEFT], self.cssNode->style.border[CSS_BOTTOM], self.cssNode->style.border[CSS_RIGHT]);
- if (!UIEdgeInsetsEqualToEdgeInsets(border, _border)) {
- [self setBorder:border];
- }
-}
-
-- (CGSize (^)(CGSize))measureBlock
-{
- __weak typeof(self) weakSelf = self;
- return ^CGSize (CGSize constrainedSize) {
-
- CGSize computedSize = [[[NSString alloc] init]sizeWithAttributes:nil];
- //TODO:more elegant way to use max and min constrained size
- if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) {
- computedSize.width = MAX(computedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]);
- }
-
- if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) {
- computedSize.width = MIN(computedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]);
- }
-
- if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_HEIGHT])) {
- computedSize.height = MAX(computedSize.height, weakSelf.cssNode->style.minDimensions[CSS_HEIGHT]);
- }
-
- if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT])) {
- computedSize.height = MIN(computedSize.height, weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT]);
- }
-
- return (CGSize) {
- WXCeilPixelValue(computedSize.width),
- WXCeilPixelValue(computedSize.height)
- };
- };
-}
-
-#pragma mark WXDatePickerManagerDelegate
-
--(void)fetchDatePickerValue:(NSString *)value
-{
- self.text = value;
- if (_changeEvent) {
- if (![[self text] isEqualToString:_changeEventString]) {
- [self fireEvent:@"change" params:@{@"value":[self text]} domChanges:@{@"attrs":@{@"value":[self text]}}];
- }
- }
-}
-
-#pragma mark UITextFieldDelegate
-
-- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
- if([self isDateType])
- {
- [[[UIApplication sharedApplication] keyWindow] endEditing:YES];
- _changeEventString = [textField text];
- [_datePickerManager show];
- return NO;
- }
- return YES;
-}
-
-#pragma mark UITextFieldDelegate
-
-- (void)textFieldDidBeginEditing:(UITextField *)textField
-{
- _changeEventString = [textField text];
- if (_focusEvent) {
- [self fireEvent:@"focus" params:nil];
- }
- [self handlePseudoClass];
-}
-
-- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
-{
- if (!string.length) {
- ((WXTextInputView*)textField).deleteWords = YES;
- ((WXTextInputView*)textField).editWords = [textField.text substringWithRange:range];
- } else {
- ((WXTextInputView*)textField).deleteWords = FALSE;
- ((WXTextInputView*)textField).editWords = string;
- }
-
- if (_maxLength) {
- NSUInteger oldLength = [textField.text length];
- NSUInteger replacementLength = [string length];
- NSUInteger rangeLength = range.length;
-
- NSUInteger newLength = oldLength - rangeLength + replacementLength;
-
- return newLength <= [_maxLength integerValue] ;
- }
-
- return YES;
-}
-
-- (void)textFieldDidEndEditing:(UITextField *)textField
-{
- if (_changeEvent) {
- if (![[textField text] isEqualToString:_changeEventString]) {
- [self fireEvent:@"change" params:@{@"value":[textField text]} domChanges:@{@"attrs":@{@"value":[textField text]}}];
- }
- }
- if (_blurEvent) {
- [self fireEvent:@"blur" params:nil];
- }
- if(self.pseudoClassStyles && [self.pseudoClassStyles count]>0){
- [self recoveryPseudoStyles:self.styles];
- }
-}
-
-- (BOOL)textFieldShouldReturn:(UITextField *)textField
-{
- if (_returnEvent) {
- NSString *typeStr = [WXUtility returnKeyType:_returnKeyType];
- [self fireEvent:@"return" params:@{@"value":[textField text],@"returnKeyType":typeStr} domChanges:@{@"attrs":@{@"value":[textField text]}}];
- }
- [self blur];
- return YES;
-}
-
-- (void)textFiledEditChanged:(NSNotification *)notifi
-{
- WXTextInputView *textField = (WXTextInputView *)notifi.object;
- if (_formaterData && _recoverRule && _recoverReplace && _formatRule && _formatReplace) {
- UITextRange * textRange = textField.selectedTextRange;
- NSInteger cursorPosition = [textField offsetFromPosition:textField.beginningOfDocument toPosition:textRange.start];
- NSMutableString * preText = [[textField.text substringToIndex:cursorPosition] mutableCopy];
- NSMutableString * lastText = [[textField.text substringFromIndex:cursorPosition] mutableCopy];
-
- NSRegularExpression *recoverRule = [NSRegularExpression regularExpressionWithPattern:_recoverRule options:NSRegularExpressionCaseInsensitive error:NULL];
- [recoverRule replaceMatchesInString:preText options:0 range:NSMakeRange(0, preText.length) withTemplate:_recoverReplace];
- [recoverRule replaceMatchesInString:lastText options:0 range:NSMakeRange(0, lastText.length) withTemplate:_recoverReplace];
- NSMutableString * newString = [NSMutableString stringWithFormat:@"%@%@", preText, lastText];
- NSRegularExpression *formatRule = [NSRegularExpression regularExpressionWithPattern:_formatRule options:NSRegularExpressionCaseInsensitive error:NULL];
- [formatRule replaceMatchesInString:newString options:0 range:NSMakeRange(0, newString.length) withTemplate:_formatReplace];
- NSString * oldText = textField.text;
- NSInteger adjust = 0;
-
- if (cursorPosition == textField.text.length) {
- adjust = newString.length-oldText.length;
- }
- if (textField.deleteWords &&[textField.editWords isKindOfClass:[NSString class]] && [_recoverRule isEqualToString:textField.editWords]) {
- // do nothing
- } else {
- textField.text = [newString copy];
- UITextPosition * newPosition = [textField positionFromPosition:textField.beginningOfDocument offset:cursorPosition+adjust];
-
- textField.selectedTextRange = [textField textRangeFromPosition:newPosition toPosition:newPosition];
- }
-
- }
- if (_inputEvent) {
- // bind each other , the key must be attrs
- [self fireEvent:@"input" params:@{@"value":[textField text]} domChanges:@{@"attrs":@{@"value":[textField text]}}];
- }
-}
-
-- (void)setViewMovedUp:(BOOL)movedUp
-{
- UIView *rootView = self.weexInstance.rootView;
- CGRect rect = self.weexInstance.frame;
- CGRect rootViewFrame = rootView.frame;
- CGRect inputFrame = [self.view.superview convertRect:self.view.frame toView:rootView];
- if (movedUp) {
- CGFloat offset = inputFrame.origin.y-(rootViewFrame.size.height-_keyboardSize.height-inputFrame.size.height);
- if (offset > 0) {
- rect = (CGRect){
- .origin.x = 0.f,
- .origin.y = rect.origin.y - offset,
- .size = rootViewFrame.size
- };
- }
- }
- self.weexInstance.rootView.frame = rect;
-}
-
-#pragma mark textview Delegate
-- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
-{
- if([self isDateType])
- {
- [[[UIApplication sharedApplication] keyWindow] endEditing:YES];
- _changeEventString = [textView text];
- [_datePickerManager show];
- return NO;
- }
- return YES;
-}
-
-- (void)textViewDidBeginEditing:(UITextView *)textView
-{
- _changeEventString = [textView text];
- if (_focusEvent) {
- [self fireEvent:@"focus" params:nil];
- }
- if (_clickEvent) {
- [self fireEvent:@"click" params:nil];
- }
- [textView becomeFirstResponder];
- [self handlePseudoClass];
-}
-
-- (void)textViewDidChange:(UITextView *)textView
-{
- if(textView.text && [textView.text length] > 0) {
- self.placeHolderLabel.text = @"";
- }else{
- [self setPlaceholderAttributedString];
- }
- if (_inputEvent) {
- [self fireEvent:@"input" params:@{@"value":[textView text]} domChanges:@{@"attrs":@{@"value":[textView text]}}];
- }
-}
-
-- (void)textViewDidEndEditing:(UITextView *)textView
-{
- if (![textView.text length]) {
- [self setPlaceholderAttributedString];
- }
- if (_changeEvent) {
- if (![[textView text] isEqualToString:_changeEventString]) {
- [self fireEvent:@"change" params:@{@"value":[textView text]} domChanges:@{@"attrs":@{@"value":[textView text]}}];
- }
- }
- if (_blurEvent) {
- [self fireEvent:@"blur" params:nil];
- }
- if(self.pseudoClassStyles && [self.pseudoClassStyles count]>0){
- [self recoveryPseudoStyles:self.styles];
- }
-}
-
-- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
-{
- if ([text isEqualToString:@"\n"]) {
- NSString *typeStr = [WXUtility returnKeyType:_returnKeyType];
- if (_returnEvent) {
- [self fireEvent:@"return" params:@{@"value":[textView text],@"returnKeyType":typeStr} domChanges:@{@"attrs":@{@"value":[textView text]}}];
- }
- if(typeStr.length > 0 && ![@"default" isEqualToString:typeStr]){
- [self blur];
- return NO;
- }
- }
-
- if (_maxLength) {
- NSUInteger oldLength = [textView.text length];
- NSUInteger replacementLength = [text length];
- NSUInteger rangeLength = range.length;
- NSUInteger newLength = oldLength - rangeLength + replacementLength;
- return newLength <= [_maxLength integerValue] ;
- }
-
- return YES;
-}
-
-#pragma mark private method
-
-- (BOOL)isDateType
-{
- if([_inputType isEqualToString:@"date"] || [_inputType isEqualToString:@"time"])
- return YES;
- return NO;
-}
-
-- (void)setPlaceholderAttributedString
-{
- NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:_placeholderString];
- [attributedString addAttribute:NSForegroundColorAttributeName value:_placeholderColor range:NSMakeRange(0, _placeholderString.length)];
- UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor];
- [self setAttributedPlaceholder:attributedString font:font];
-}
-
-- (void)setTextFont
-{
- UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor];
- [self setFont:font];
-}
-
-- (void)setAutofocus:(BOOL)b
-{
- if (b) {
- if([self isDateType])
- {
- [_datePickerManager show];
- }else
- {
- [self.view becomeFirstResponder];
- }
- } else {
- if([self isDateType])
- {
- [_datePickerManager hide];
- }else
- {
- [self.view resignFirstResponder];
- }
- }
-}
-
-- (void)setType
-{
- [self setKeyboardType:UIKeyboardTypeDefault];
- [self setSecureTextEntry:NO];
- if ([_inputType isEqualToString:@"text"]) {
- [self setKeyboardType:UIKeyboardTypeDefault];
- }else if ([_inputType isEqualToString:@"password"]) {
- [self setSecureTextEntry:YES];
- }else if ([_inputType isEqualToString:@"tel"]) {
- [self setKeyboardType:UIKeyboardTypePhonePad];
- }else if ([_inputType isEqualToString:@"email"]) {
- [self setKeyboardType:UIKeyboardTypeEmailAddress];
- }else if ([_inputType isEqualToString:@"url"]) {
- [self setKeyboardType:UIKeyboardTypeURL];
- }else if ([_inputType isEqualToString:@"number"]) {
- [self setKeyboardType:UIKeyboardTypeDecimalPad];
- }else if ([self isDateType]) {
- if (!_datePickerManager) {
- _datePickerManager = [[WXDatePickerManager alloc] init];
- _datePickerManager.delegate = self;
- }
- [_datePickerManager updateDatePicker:_attr];
- }
-}
-
-- (void)setPadding:(UIEdgeInsets)padding
-{
- _padding = padding;
- [self setEditPadding:padding];
-}
-
-- (void)setBorder:(UIEdgeInsets)border
-{
- _border = border;
- [self setEditBorder:border];
-}
-
-#pragma mark update touch styles
--(void)handlePseudoClass
-{
- NSMutableDictionary *styles = [NSMutableDictionary new];
- NSMutableDictionary *recordStyles = [NSMutableDictionary new];
- if(_disabled){
- recordStyles = [self getPseudoClassStylesByKeys:@[@"disabled"]];
- [styles addEntriesFromDictionary:recordStyles];
- }else {
- recordStyles = [NSMutableDictionary new];
- recordStyles = [self getPseudoClassStylesByKeys:@[@"enabled"]];
- [styles addEntriesFromDictionary:recordStyles];
- }
- if ([self.view isFirstResponder]){
- recordStyles = [NSMutableDictionary new];
- recordStyles = [self getPseudoClassStylesByKeys:@[@"focus"]];
- [styles addEntriesFromDictionary:recordStyles];
- }
- NSString *disabledStr = @"enabled";
- if (_disabled){
- disabledStr = @"disabled";
- }
- if ([self.view isFirstResponder]) {
- NSString *focusStr = @"focus";
- recordStyles = [NSMutableDictionary new];
- recordStyles = [self getPseudoClassStylesByKeys:@[focusStr,disabledStr]];
- [styles addEntriesFromDictionary:recordStyles];
- }
- [self updatePseudoClassStyles:styles];
-}
-
-#pragma mark keyboard
-- (void)keyboardWasShown:(NSNotification*)notification
-{
- if(![self.view isFirstResponder]) {
- return;
- }
- if (!_disableMoveViewUp) {
- CGRect end = [[[notification userInfo] objectForKey:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
- _keyboardSize = end.size;
- UIView * rootView = self.weexInstance.rootView;
- CGRect screenRect = [[UIScreen mainScreen] bounds];
- CGRect keyboardRect = (CGRect){
- .origin.x = 0,
- .origin.y = CGRectGetMaxY(screenRect) - _keyboardSize.height - 54,
- .size = _keyboardSize
- };
- CGRect inputFrame = [self.view.superview convertRect:self.view.frame toView:rootView];
- if (keyboardRect.origin.y - inputFrame.size.height <= inputFrame.origin.y) {
- [self setViewMovedUp:YES];
- self.weexInstance.isRootViewFrozen = YES;
- }
- }
-
- if (_keyboardEvent) {
- [self fireEvent:@"keyboard" params:@{ @"isShow": @YES }];
- }
-}
-
-- (void)keyboardWillHide:(NSNotification*)notification
-{
- if (![self.view isFirstResponder]) {
- return;
- }
- if (!_disableMoveViewUp) {
- UIView * rootView = self.weexInstance.rootView;
- if (!CGRectEqualToRect(self.weexInstance.frame, rootView.frame)) {
- [self setViewMovedUp:NO];
- self.weexInstance.isRootViewFrozen = NO;
- }
- }
- if (_keyboardEvent) {
- [self fireEvent:@"keyboard" params:@{ @"isShow": @NO }];
- }
-}
-
-- (void)closeKeyboard
-{
- [self.view resignFirstResponder];
-}
-
-#pragma mark -reset color
-- (void)resetStyles:(NSArray *)styles
-{
- if ([styles containsObject:@"color"]) {
- [self setTextColor:[UIColor blackColor]];
- }
- if ([styles containsObject:@"fontSize"]) {
- _fontSize = WX_TEXT_FONT_SIZE;
- [self setTextFont];
- }
-}
-@end
-
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXEditComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXEditComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXEditComponent.mm
new file mode 100644
index 0000000..43e7538
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXEditComponent.mm
@@ -0,0 +1,971 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXEditComponent.h"
+#import "WXConvert.h"
+#import "WXUtility.h"
+#import "WXSDKInstance.h"
+#import "WXSDKInstance_private.h"
+#import "WXDefine.h"
+#import "WXAssert.h"
+#import "WXComponent_internal.h"
+#import "WXComponent+PseudoClassManagement.h"
+#import "WXTextInputComponent.h"
+#import "WXComponent+Layout.h"
+
+@interface WXEditComponent()
+
+//@property (nonatomic, strong) WXTextInputView *inputView;
+@property (nonatomic, strong) WXDatePickerManager *datePickerManager;
+@property (nonatomic, strong) NSDictionary *attr;
+//attribute
+@property (nonatomic) NSNumber *maxLength;
+@property (nonatomic) NSString * value;
+@property (nonatomic) BOOL autofocus;
+@property(nonatomic) UIReturnKeyType returnKeyType;
+@property (nonatomic) BOOL disabled;
+@property (nonatomic, copy) NSString *inputType;
+@property (nonatomic) NSUInteger rows;
+@property (nonatomic) BOOL hideDoneButton;
+
+//style
+@property (nonatomic) WXPixelType fontSize;
+@property (nonatomic) WXTextStyle fontStyle;
+@property (nonatomic) CGFloat fontWeight;
+@property (nonatomic, strong) NSString *fontFamily;
+@property (nonatomic, strong) UIColor *colorForStyle;
+@property (nonatomic)NSTextAlignment textAlignForStyle;
+
+//event
+@property (nonatomic) BOOL inputEvent;
+@property (nonatomic) BOOL clickEvent;
+@property (nonatomic) BOOL focusEvent;
+@property (nonatomic) BOOL blurEvent;
+@property (nonatomic) BOOL changeEvent;
+@property (nonatomic) BOOL returnEvent;
+@property (nonatomic) BOOL keyboardEvent;
+@property (nonatomic, strong) NSString *changeEventString;
+@property (nonatomic, assign) CGSize keyboardSize;
+
+// formatter
+@property (nonatomic, strong) NSString * formatRule;
+@property (nonatomic, strong) NSString * formatReplace;
+@property (nonatomic, strong) NSString * recoverRule;
+@property (nonatomic, strong) NSString * recoverReplace;
+@property (nonatomic, strong) NSDictionary * formaterData;
+
+// disable move rootView up as the keyboard show up.
+@property (nonatomic, assign) BOOL disableMoveViewUp;
+
+@end
+
+@implementation WXEditComponent
+{
+ UIEdgeInsets _border;
+ UIEdgeInsets _padding;
+}
+
+WX_EXPORT_METHOD(@selector(focus))
+WX_EXPORT_METHOD(@selector(blur))
+WX_EXPORT_METHOD(@selector(setSelectionRange:selectionEnd:))
+WX_EXPORT_METHOD(@selector(getSelectionRange:))
+WX_EXPORT_METHOD(@selector(setTextFormatter:))
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+ self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
+ if (self) {
+ _inputEvent = NO;
+ _focusEvent = NO;
+ _blurEvent = NO;
+ _changeEvent = NO;
+ _returnEvent = NO;
+ _clickEvent = NO;
+ _keyboardEvent = NO;
+ // handle attributes
+ _autofocus = [attributes[@"autofocus"] boolValue];
+ _disabled = [attributes[@"disabled"] boolValue];
+ _value = [WXConvert NSString:attributes[@"value"]]?:@"";
+ _placeholderString = [WXConvert NSString:attributes[@"placeholder"]]?:@"";
+ if(attributes[@"type"]) {
+ _inputType = [WXConvert NSString:attributes[@"type"]];
+ _attr = attributes;
+ }
+ if (attributes[@"maxlength"]) {
+ _maxLength = [NSNumber numberWithUnsignedInteger:[attributes[@"maxlength"] integerValue]];
+ }
+ if (attributes[@"returnKeyType"]) {
+ _returnKeyType = [WXConvert UIReturnKeyType:attributes[@"returnKeyType"]];
+ }
+ if (attributes[@"rows"]) {
+ _rows = [attributes[@"rows"] integerValue];
+ } else {
+ _rows = 2;
+ }
+
+ if (attributes[@"hideDoneButton"]) {
+ _hideDoneButton = [attributes[@"hideDoneButton"] boolValue];
+ }
+ if (attributes[@"disableMoveViewUp"]) {
+ _disableMoveViewUp = [WXConvert BOOL:attributes[@"disableMoveViewUp"]];
+ }
+
+ // handle styles
+ if (styles[@"color"]) {
+ _colorForStyle = [WXConvert UIColor:styles[@"color"]];
+ }
+ if (styles[@"fontSize"]) {
+ _fontSize = [WXConvert WXPixelType:styles[@"fontSize"] scaleFactor:self.weexInstance.pixelScaleFactor];
+ }
+ if (styles[@"fontWeight"]) {
+ _fontWeight = [WXConvert WXTextWeight:styles[@"fontWeight"]];
+ }
+ if (styles[@"fontStyle"]) {
+ _fontStyle = [WXConvert WXTextStyle:styles[@"fontStyle"]];
+ }
+ if (styles[@"fontFamily"]) {
+ _fontFamily = styles[@"fontFamily"];
+ }
+ if (styles[@"textAlign"]) {
+ _textAlignForStyle = [WXConvert NSTextAlignment:styles[@"textAlign"]];
+ }
+ if (styles[@"placeholderColor"]) {
+ _placeholderColor = [WXConvert UIColor:styles[@"placeholderColor"]];
+ }else {
+ _placeholderColor = [UIColor colorWithRed:0x99/255.0 green:0x99/255.0 blue:0x99/255.0 alpha:1.0];
+ }
+ }
+
+ return self;
+}
+
+#pragma mark - lifeCircle
+
+- (void)viewDidLoad
+{
+ UIView * view = self.view;
+ if ([view isKindOfClass:[UITextField class]]){
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFiledEditChanged:)
+ name:UITextFieldTextDidChangeNotification
+ object:view];
+ }
+ _padding = UIEdgeInsetsZero;
+ _border = UIEdgeInsetsZero;
+ self.userInteractionEnabled = YES;
+ [self setType];
+ [self setAutofocus:_autofocus];
+ [self setTextFont];
+ [self setPlaceholderAttributedString];
+ [self setTextAlignment:_textAlignForStyle];
+ [self setTextColor:_colorForStyle];
+ [self setText:_value];
+ [self setEnabled:!_disabled];
+ [self setRows:_rows];
+ [self setReturnKeyType:_returnKeyType];
+ [self updatePattern];
+
+ if (!self.hideDoneButton) {
+ UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeKeyboard)];
+ UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
+ UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 0, 44)];
+ toolbar.items = [NSArray arrayWithObjects:space, barButton, nil];
+
+ self.inputAccessoryView = toolbar;
+ }
+
+ [self handlePseudoClass];
+}
+
+- (void)viewWillLoad
+{
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(keyboardWasShown:)
+ name:UIKeyboardWillShowNotification
+ object:nil];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(keyboardWillHide:)
+ name:UIKeyboardWillHideNotification
+ object:nil];
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+-(void)focus
+{
+ if(self.view) {
+ [self.view becomeFirstResponder];
+ }
+}
+
+-(void)blur
+{
+ if(self.view) {
+ [self.view resignFirstResponder];
+ }
+}
+
+-(void)setSelectionRange:(NSInteger)selectionStart selectionEnd:(NSInteger)selectionEnd
+{
+ if(selectionStart>self.text.length || selectionEnd>self.text.length) {
+ return;
+ }
+ [self.view becomeFirstResponder];
+ [self setEditSelectionRange:selectionStart selectionEnd:selectionEnd];
+}
+
+-(void)getSelectionRange:(WXKeepAliveCallback)callback
+{
+ NSDictionary *res = [self getEditSelectionRange];
+ if(callback) {
+ callback(res,NO);
+ }
+}
+
+- (void)setTextFormatter:(NSDictionary*)formater
+{
+ _formaterData = formater;
+ if (formater[@"formatRule"]) {;
+ _formatRule = [self _preProcessInputTextFormatter:[WXConvert NSString:formater[@"formatRule"]]];
+ }
+ if (formater[@"formatReplace"]) {
+ _formatReplace = [self _preProcessInputTextFormatter:[WXConvert NSString:formater[@"formatReplace"]]];
+ }
+ if (formater[@"recoverRule"]) {
+ _recoverRule = [self _preProcessInputTextFormatter:[WXConvert NSString:formater[@"recoverRule"]]];
+ }
+ if (formater[@"recoverReplace"]) {
+ _recoverReplace = [self _preProcessInputTextFormatter:[WXConvert NSString:formater[@"recoverReplace"]]];
+ }
+}
+
+- (NSString*)_preProcessInputTextFormatter:(NSString*)formater
+{
+ NSRange start = [formater rangeOfString:@"/"];
+ NSRange end = [formater rangeOfString:@"/g"];
+ if (start.location == NSNotFound || end.location == NSNotFound || end.location < start.location) {
+ return formater;
+ }
+ NSRange subStringRange = NSMakeRange(start.location+1, end.location - start.location-1);
+
+ return [formater substringWithRange:subStringRange];
+}
+
+#pragma mark - Overwrite Method
+-(NSString *)text
+{
+ return @"";
+}
+
+- (void)setText:(NSString *)text
+{
+}
+
+-(void)setTextColor:(UIColor *)color
+{
+}
+
+-(void)setTextAlignment:(NSTextAlignment)textAlignForStyle
+{
+}
+
+-(void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
+{
+}
+
+-(void)setEnabled:(BOOL)enabled
+{
+}
+
+-(void)setReturnKeyType:(UIReturnKeyType)returnKeyType
+{
+}
+
+-(void)setInputAccessoryView:(UIView *)inputAccessoryView
+{
+}
+
+-(void)setEditSelectionRange:(NSInteger)selectionStart selectionEnd:(NSInteger)selectionEnd
+{
+}
+
+-(NSDictionary *)getEditSelectionRange
+{
+ return @{};
+}
+
+-(void)setKeyboardType:(UIKeyboardType)keyboardType
+{
+}
+
+-(void)setSecureTextEntry:(BOOL)secureTextEntry
+{
+}
+
+-(void)setEditPadding:(UIEdgeInsets)padding
+{
+}
+
+-(void)setEditBorder:(UIEdgeInsets)border
+{
+}
+
+-(void)setAttributedPlaceholder:(NSMutableAttributedString *)attributedString font:(UIFont *)font
+{
+}
+
+-(void)setFont:(UIFont *)font
+{
+}
+
+-(void)setRows:(NSUInteger)rows
+{
+}
+
+#pragma mark - Add Event
+- (void)addEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"input"]) {
+ _inputEvent = YES;
+ }
+ if ([eventName isEqualToString:@"focus"]) {
+ _focusEvent = YES;
+ }
+ if ([eventName isEqualToString:@"blur"]) {
+ _blurEvent = YES;
+ }
+ if ([eventName isEqualToString:@"change"]) {
+ _changeEvent = YES;
+ }
+ if ([eventName isEqualToString:@"return"]) {
+ _returnEvent = YES;
+ }
+ if ([eventName isEqualToString:@"click"]) {
+ _clickEvent = YES;
+ }
+ if ([eventName isEqualToString:@"keyboard"]) {
+ _keyboardEvent = YES;
+ }
+}
+
+#pragma Remove Event
+
+-(void)removeEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"input"]) {
+ _inputEvent = NO;
+ }
+ if ([eventName isEqualToString:@"focus"]) {
+ _focusEvent = NO;
+ }
+ if ([eventName isEqualToString:@"blur"]) {
+ _blurEvent = NO;
+ }
+ if ([eventName isEqualToString:@"change"]) {
+ _changeEvent = NO;
+ }
+ if ([eventName isEqualToString:@"return"]) {
+ _returnEvent = NO;
+ }
+ if ([eventName isEqualToString:@"click"]) {
+ _clickEvent = NO;
+ }
+ if ([eventName isEqualToString:@"keyboard"]) {
+ _keyboardEvent = NO;
+ }
+}
+
+#pragma mark - upate attributes
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ _attr = attributes;
+ if (attributes[@"type"]) {
+ _inputType = [WXConvert NSString:attributes[@"type"]];
+ [self setType];
+ }
+ if (attributes[@"autofocus"]) {
+ self.autofocus = [attributes[@"autofocus"] boolValue];
+ }
+ if (attributes[@"disabled"]) {
+ _disabled = [attributes[@"disabled"] boolValue];
+ [self setEnabled:!_disabled];
+ }
+ if (attributes[@"maxlength"]) {
+ _maxLength = [NSNumber numberWithInteger:[attributes[@"maxlength"] integerValue]];
+ }
+
+ if (attributes[@"disableMoveViewUp"]) {
+ _disableMoveViewUp = [WXConvert BOOL:attributes[@"disableMoveViewUp"]];
+ }
+ if (attributes[@"value"]) {
+ _value = [WXConvert NSString:attributes[@"value"]]?:@"";
+ if (_maxLength && [_value length] > [_maxLength integerValue]&& [_maxLength integerValue] >= 0) {
+ _value = [_value substringToIndex:([_maxLength integerValue])];
+ }
+ [self setText:_value];
+ }
+ if (attributes[@"placeholder"]) {
+ _placeholderString = [WXConvert NSString:attributes[@"placeholder"]]?:@"";
+ [self setPlaceholderAttributedString];
+ if(_value.length > 0){
+ _placeHolderLabel.text = @"";
+ }
+ }
+ if (attributes[@"returnKeyType"]) {
+ _returnKeyType = [WXConvert UIReturnKeyType:attributes[@"returnKeyType"]];
+ [self setReturnKeyType:_returnKeyType];
+ }
+ if (attributes[@"rows"]) {
+ _rows = [attributes[@"rows"] integerValue];
+ [self setRows:_rows];
+ } else {
+ _rows = 2;
+ [self setRows:_rows];
+ }
+}
+
+#pragma mark - upate styles
+
+- (void)updateStyles:(NSDictionary *)styles
+{
+ if (styles[@"color"]) {
+ _colorForStyle = [WXConvert UIColor:styles[@"color"]];
+ [self setTextColor:_colorForStyle];
+ }
+ if (styles[@"fontSize"]) {
+ _fontSize = [WXConvert WXPixelType:styles[@"fontSize"] scaleFactor:self.weexInstance.pixelScaleFactor];
+ }
+ if (styles[@"fontWeight"]) {
+ _fontWeight = [WXConvert WXTextWeight:styles[@"fontWeight"]];
+ }
+ if (styles[@"fontStyle"]) {
+ _fontStyle = [WXConvert WXTextStyle:styles[@"fontStyle"]];
+ }
+ if (styles[@"fontFamily"]) {
+ _fontFamily = [WXConvert NSString:styles[@"fontFamily"]];
+ }
+ [self setTextFont];
+
+ if (styles[@"textAlign"]) {
+ _textAlignForStyle = [WXConvert NSTextAlignment:styles[@"textAlign"]];
+ [self setTextAlignment:_textAlignForStyle] ;
+ }
+ if (styles[@"placeholderColor"]) {
+ _placeholderColor = [WXConvert UIColor:styles[@"placeholderColor"]];
+ }else {
+ _placeholderColor = [UIColor colorWithRed:0x99/255.0 green:0x99/255.0 blue:0x99/255.0 alpha:1.0];
+ }
+ [self setPlaceholderAttributedString];
+ [self updatePattern];
+}
+
+-(void)updatePattern
+{
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ UIEdgeInsets padding = UIEdgeInsetsMake(self.cssNode->style.padding[CSS_TOP], self.cssNode->style.padding[CSS_LEFT], self.cssNode->style.padding[CSS_BOTTOM], self.cssNode->style.padding[CSS_RIGHT]);
+ if (!UIEdgeInsetsEqualToEdgeInsets(padding, _padding)) {
+ [self setPadding:padding];
+ }
+
+ UIEdgeInsets border = UIEdgeInsetsMake(self.cssNode->style.border[CSS_TOP], self.cssNode->style.border[CSS_LEFT], self.cssNode->style.border[CSS_BOTTOM], self.cssNode->style.border[CSS_RIGHT]);
+ if (!UIEdgeInsetsEqualToEdgeInsets(border, _border)) {
+ [self setBorder:border];
+ }
+
+ }
+//#else
+ else{
+ UIEdgeInsets padding_flex = UIEdgeInsetsMake(
+ self.flexCssNode->getPaddingTop(),
+ self.flexCssNode->getPaddingLeft(),
+ self.flexCssNode->getPaddingBottom(),
+ self.flexCssNode->getPaddingRight()
+ );
+
+ if (!UIEdgeInsetsEqualToEdgeInsets(padding_flex, _padding)) {
+ [self setPadding:padding_flex];
+ }
+
+
+ UIEdgeInsets border_flex = UIEdgeInsetsMake(self.flexCssNode->getBorderWidthTop(), self.flexCssNode->getBorderWidthLeft(), self.flexCssNode->getBorderWidthBottom(), self.flexCssNode->getBorderWidthRight());
+
+
+
+
+ if (!UIEdgeInsetsEqualToEdgeInsets(border_flex, _border)) {
+ [self setBorder:border_flex];
+ }
+ }
+//#endif
+}
+
+- (CGSize (^)(CGSize))measureBlock
+{
+ __weak typeof(self) weakSelf = self;
+ return ^CGSize (CGSize constrainedSize) {
+
+ CGSize computedSize = [[[NSString alloc] init]sizeWithAttributes:nil];
+
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ //TODO:more elegant way to use max and min constrained size
+ if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) {
+ computedSize.width = MAX(computedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) {
+ computedSize.width = MIN(computedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_HEIGHT])) {
+ computedSize.height = MAX(computedSize.height, weakSelf.cssNode->style.minDimensions[CSS_HEIGHT]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT])) {
+ computedSize.height = MIN(computedSize.height, weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT]);
+ }
+ }
+//#else
+ else
+ {
+ if (!isnan(weakSelf.flexCssNode->getMinWidth())) {
+ computedSize.width = MAX(computedSize.width, weakSelf.flexCssNode->getMinWidth());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMaxWidth())) {
+ computedSize.width = MIN(computedSize.width, weakSelf.flexCssNode->getMaxWidth());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMinHeight())) {
+ computedSize.height = MAX(computedSize.height, weakSelf.flexCssNode->getMinHeight());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMaxHeight())) {
+ computedSize.height = MIN(computedSize.height, weakSelf.flexCssNode->getMaxHeight());
+ }
+ }
+
+//#endif
+ return (CGSize) {
+ WXCeilPixelValue(computedSize.width),
+ WXCeilPixelValue(computedSize.height)
+ };
+ };
+}
+
+#pragma mark WXDatePickerManagerDelegate
+
+-(void)fetchDatePickerValue:(NSString *)value
+{
+ self.text = value;
+ if (_changeEvent) {
+ if (![[self text] isEqualToString:_changeEventString]) {
+ [self fireEvent:@"change" params:@{@"value":[self text]} domChanges:@{@"attrs":@{@"value":[self text]}}];
+ }
+ }
+}
+
+#pragma mark UITextFieldDelegate
+
+- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
+ if([self isDateType])
+ {
+ [[[UIApplication sharedApplication] keyWindow] endEditing:YES];
+ _changeEventString = [textField text];
+ [_datePickerManager show];
+ return NO;
+ }
+ return YES;
+}
+
+#pragma mark UITextFieldDelegate
+
+- (void)textFieldDidBeginEditing:(UITextField *)textField
+{
+ _changeEventString = [textField text];
+ if (_focusEvent) {
+ [self fireEvent:@"focus" params:nil];
+ }
+ [self handlePseudoClass];
+}
+
+- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
+{
+ if (!string.length) {
+ ((WXTextInputView*)textField).deleteWords = YES;
+ ((WXTextInputView*)textField).editWords = [textField.text substringWithRange:range];
+ } else {
+ ((WXTextInputView*)textField).deleteWords = FALSE;
+ ((WXTextInputView*)textField).editWords = string;
+ }
+
+ if (_maxLength) {
+ NSUInteger oldLength = [textField.text length];
+ NSUInteger replacementLength = [string length];
+ NSUInteger rangeLength = range.length;
+
+ NSUInteger newLength = oldLength - rangeLength + replacementLength;
+
+ return newLength <= [_maxLength integerValue] ;
+ }
+
+ return YES;
+}
+
+- (void)textFieldDidEndEditing:(UITextField *)textField
+{
+ if (_changeEvent) {
+ if (![[textField text] isEqualToString:_changeEventString]) {
+ [self fireEvent:@"change" params:@{@"value":[textField text]} domChanges:@{@"attrs":@{@"value":[textField text]}}];
+ }
+ }
+ if (_blurEvent) {
+ [self fireEvent:@"blur" params:nil];
+ }
+ if(self.pseudoClassStyles && [self.pseudoClassStyles count]>0){
+ [self recoveryPseudoStyles:self.styles];
+ }
+}
+
+- (BOOL)textFieldShouldReturn:(UITextField *)textField
+{
+ if (_returnEvent) {
+ NSString *typeStr = [WXUtility returnKeyType:_returnKeyType];
+ [self fireEvent:@"return" params:@{@"value":[textField text],@"returnKeyType":typeStr} domChanges:@{@"attrs":@{@"value":[textField text]}}];
+ }
+ [self blur];
+ return YES;
+}
+
+- (void)textFiledEditChanged:(NSNotification *)notifi
+{
+ WXTextInputView *textField = (WXTextInputView *)notifi.object;
+ if (_formaterData && _recoverRule && _recoverReplace && _formatRule && _formatReplace) {
+ UITextRange * textRange = textField.selectedTextRange;
+ NSInteger cursorPosition = [textField offsetFromPosition:textField.beginningOfDocument toPosition:textRange.start];
+ NSMutableString * preText = [[textField.text substringToIndex:cursorPosition] mutableCopy];
+ NSMutableString * lastText = [[textField.text substringFromIndex:cursorPosition] mutableCopy];
+
+ NSRegularExpression *recoverRule = [NSRegularExpression regularExpressionWithPattern:_recoverRule options:NSRegularExpressionCaseInsensitive error:NULL];
+ [recoverRule replaceMatchesInString:preText options:0 range:NSMakeRange(0, preText.length) withTemplate:_recoverReplace];
+ [recoverRule replaceMatchesInString:lastText options:0 range:NSMakeRange(0, lastText.length) withTemplate:_recoverReplace];
+ NSMutableString * newString = [NSMutableString stringWithFormat:@"%@%@", preText, lastText];
+ NSRegularExpression *formatRule = [NSRegularExpression regularExpressionWithPattern:_formatRule options:NSRegularExpressionCaseInsensitive error:NULL];
+ [formatRule replaceMatchesInString:newString options:0 range:NSMakeRange(0, newString.length) withTemplate:_formatReplace];
+ NSString * oldText = textField.text;
+ NSInteger adjust = 0;
+
+ if (cursorPosition == textField.text.length) {
+ adjust = newString.length-oldText.length;
+ }
+ if (textField.deleteWords &&[textField.editWords isKindOfClass:[NSString class]] && [_recoverRule isEqualToString:textField.editWords]) {
+ // do nothing
+ } else {
+ textField.text = [newString copy];
+ UITextPosition * newPosition = [textField positionFromPosition:textField.beginningOfDocument offset:cursorPosition+adjust];
+
+ textField.selectedTextRange = [textField textRangeFromPosition:newPosition toPosition:newPosition];
+ }
+
+ }
+ if (_inputEvent) {
+ // bind each other , the key must be attrs
+ [self fireEvent:@"input" params:@{@"value":[textField text]} domChanges:@{@"attrs":@{@"value":[textField text]}}];
+ }
+}
+
+- (void)setViewMovedUp:(BOOL)movedUp
+{
+ UIView *rootView = self.weexInstance.rootView;
+ CGRect rect = self.weexInstance.frame;
+ CGRect rootViewFrame = rootView.frame;
+ CGRect inputFrame = [self.view.superview convertRect:self.view.frame toView:rootView];
+ if (movedUp) {
+ CGFloat offset = inputFrame.origin.y-(rootViewFrame.size.height-_keyboardSize.height-inputFrame.size.height);
+ if (offset > 0) {
+ rect = (CGRect){
+ .origin.x = 0.f,
+ .origin.y = rect.origin.y - offset,
+ .size = rootViewFrame.size
+ };
+ }
+ }
+ self.weexInstance.rootView.frame = rect;
+}
+
+#pragma mark textview Delegate
+- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
+{
+ if([self isDateType])
+ {
+ [[[UIApplication sharedApplication] keyWindow] endEditing:YES];
+ _changeEventString = [textView text];
+ [_datePickerManager show];
+ return NO;
+ }
+ return YES;
+}
+
+- (void)textViewDidBeginEditing:(UITextView *)textView
+{
+ _changeEventString = [textView text];
+ if (_focusEvent) {
+ [self fireEvent:@"focus" params:nil];
+ }
+ if (_clickEvent) {
+ [self fireEvent:@"click" params:nil];
+ }
+ [textView becomeFirstResponder];
+ [self handlePseudoClass];
+}
+
+- (void)textViewDidChange:(UITextView *)textView
+{
+ if(textView.text && [textView.text length] > 0) {
+ self.placeHolderLabel.text = @"";
+ }else{
+ [self setPlaceholderAttributedString];
+ }
+ if (_inputEvent) {
+ [self fireEvent:@"input" params:@{@"value":[textView text]} domChanges:@{@"attrs":@{@"value":[textView text]}}];
+ }
+}
+
+- (void)textViewDidEndEditing:(UITextView *)textView
+{
+ if (![textView.text length]) {
+ [self setPlaceholderAttributedString];
+ }
+ if (_changeEvent) {
+ if (![[textView text] isEqualToString:_changeEventString]) {
+ [self fireEvent:@"change" params:@{@"value":[textView text]} domChanges:@{@"attrs":@{@"value":[textView text]}}];
+ }
+ }
+ if (_blurEvent) {
+ [self fireEvent:@"blur" params:nil];
+ }
+ if(self.pseudoClassStyles && [self.pseudoClassStyles count]>0){
+ [self recoveryPseudoStyles:self.styles];
+ }
+}
+
+- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
+{
+ if ([text isEqualToString:@"\n"]) {
+ NSString *typeStr = [WXUtility returnKeyType:_returnKeyType];
+ if (_returnEvent) {
+ [self fireEvent:@"return" params:@{@"value":[textView text],@"returnKeyType":typeStr} domChanges:@{@"attrs":@{@"value":[textView text]}}];
+ }
+ if(typeStr.length > 0 && ![@"default" isEqualToString:typeStr]){
+ [self blur];
+ return NO;
+ }
+ }
+
+ if (_maxLength) {
+ NSUInteger oldLength = [textView.text length];
+ NSUInteger replacementLength = [text length];
+ NSUInteger rangeLength = range.length;
+ NSUInteger newLength = oldLength - rangeLength + replacementLength;
+ return newLength <= [_maxLength integerValue] ;
+ }
+
+ return YES;
+}
+
+#pragma mark private method
+
+- (BOOL)isDateType
+{
+ if([_inputType isEqualToString:@"date"] || [_inputType isEqualToString:@"time"])
+ return YES;
+ return NO;
+}
+
+- (void)setPlaceholderAttributedString
+{
+ NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:_placeholderString];
+ [attributedString addAttribute:NSForegroundColorAttributeName value:_placeholderColor range:NSMakeRange(0, _placeholderString.length)];
+ UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor];
+ [self setAttributedPlaceholder:attributedString font:font];
+}
+
+- (void)setTextFont
+{
+ UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor];
+ [self setFont:font];
+}
+
+- (void)setAutofocus:(BOOL)b
+{
+ if (b) {
+ if([self isDateType])
+ {
+ [_datePickerManager show];
+ }else
+ {
+ [self.view becomeFirstResponder];
+ }
+ } else {
+ if([self isDateType])
+ {
+ [_datePickerManager hide];
+ }else
+ {
+ [self.view resignFirstResponder];
+ }
+ }
+}
+
+- (void)setType
+{
+ [self setKeyboardType:UIKeyboardTypeDefault];
+ [self setSecureTextEntry:NO];
+ if ([_inputType isEqualToString:@"text"]) {
+ [self setKeyboardType:UIKeyboardTypeDefault];
+ }else if ([_inputType isEqualToString:@"password"]) {
+ [self setSecureTextEntry:YES];
+ }else if ([_inputType isEqualToString:@"tel"]) {
+ [self setKeyboardType:UIKeyboardTypePhonePad];
+ }else if ([_inputType isEqualToString:@"email"]) {
+ [self setKeyboardType:UIKeyboardTypeEmailAddress];
+ }else if ([_inputType isEqualToString:@"url"]) {
+ [self setKeyboardType:UIKeyboardTypeURL];
+ }else if ([_inputType isEqualToString:@"number"]) {
+ [self setKeyboardType:UIKeyboardTypeDecimalPad];
+ }else if ([self isDateType]) {
+ if (!_datePickerManager) {
+ _datePickerManager = [[WXDatePickerManager alloc] init];
+ _datePickerManager.delegate = self;
+ }
+ [_datePickerManager updateDatePicker:_attr];
+ }
+}
+
+- (void)setPadding:(UIEdgeInsets)padding
+{
+ _padding = padding;
+ [self setEditPadding:padding];
+}
+
+- (void)setBorder:(UIEdgeInsets)border
+{
+ _border = border;
+ [self setEditBorder:border];
+}
+
+#pragma mark update touch styles
+-(void)handlePseudoClass
+{
+ NSMutableDictionary *styles = [NSMutableDictionary new];
+ NSMutableDictionary *recordStyles = [NSMutableDictionary new];
+ if(_disabled){
+ recordStyles = [self getPseudoClassStylesByKeys:@[@"disabled"]];
+ [styles addEntriesFromDictionary:recordStyles];
+ }else {
+ recordStyles = [NSMutableDictionary new];
+ recordStyles = [self getPseudoClassStylesByKeys:@[@"enabled"]];
+ [styles addEntriesFromDictionary:recordStyles];
+ }
+ if ([self.view isFirstResponder]){
+ recordStyles = [NSMutableDictionary new];
+ recordStyles = [self getPseudoClassStylesByKeys:@[@"focus"]];
+ [styles addEntriesFromDictionary:recordStyles];
+ }
+ NSString *disabledStr = @"enabled";
+ if (_disabled){
+ disabledStr = @"disabled";
+ }
+ if ([self.view isFirstResponder]) {
+ NSString *focusStr = @"focus";
+ recordStyles = [NSMutableDictionary new];
+ recordStyles = [self getPseudoClassStylesByKeys:@[focusStr,disabledStr]];
+ [styles addEntriesFromDictionary:recordStyles];
+ }
+ [self updatePseudoClassStyles:styles];
+}
+
+#pragma mark keyboard
+- (void)keyboardWasShown:(NSNotification*)notification
+{
+ if(![self.view isFirstResponder]) {
+ return;
+ }
+ if (!_disableMoveViewUp) {
+ CGRect end = [[[notification userInfo] objectForKey:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
+ _keyboardSize = end.size;
+ UIView * rootView = self.weexInstance.rootView;
+ CGRect screenRect = [[UIScreen mainScreen] bounds];
+ CGRect keyboardRect = (CGRect){
+ .origin.x = 0,
+ .origin.y = CGRectGetMaxY(screenRect) - _keyboardSize.height - 54,
+ .size = _keyboardSize
+ };
+ CGRect inputFrame = [self.view.superview convertRect:self.view.frame toView:rootView];
+ if (keyboardRect.origin.y - inputFrame.size.height <= inputFrame.origin.y) {
+ [self setViewMovedUp:YES];
+ self.weexInstance.isRootViewFrozen = YES;
+ }
+ }
+
+ if (_keyboardEvent) {
+ [self fireEvent:@"keyboard" params:@{ @"isShow": @YES }];
+ }
+}
+
+- (void)keyboardWillHide:(NSNotification*)notification
+{
+ if (![self.view isFirstResponder]) {
+ return;
+ }
+ if (!_disableMoveViewUp) {
+ UIView * rootView = self.weexInstance.rootView;
+ if (!CGRectEqualToRect(self.weexInstance.frame, rootView.frame)) {
+ [self setViewMovedUp:NO];
+ self.weexInstance.isRootViewFrozen = NO;
+ }
+ }
+ if (_keyboardEvent) {
+ [self fireEvent:@"keyboard" params:@{ @"isShow": @NO }];
+ }
+}
+
+- (void)closeKeyboard
+{
+ [self.view resignFirstResponder];
+}
+
+#pragma mark -reset color
+- (void)resetStyles:(NSArray *)styles
+{
+ if ([styles containsObject:@"color"]) {
+ [self setTextColor:[UIColor blackColor]];
+ }
+ if ([styles containsObject:@"fontSize"]) {
+ _fontSize = WX_TEXT_FONT_SIZE;
+ [self setTextFont];
+ }
+}
+@end
+
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.m
deleted file mode 100644
index dbfe3dc..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.m
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXHeaderComponent.h"
-#import "WXComponent_internal.h"
-
-@implementation WXHeaderComponent
-{
- BOOL _isUseContainerWidth;
-}
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
- self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
-
- if (self) {
- _async = YES;
- _isNeedJoinLayoutSystem = NO;
- _keepScrollPosition = attributes[@"keepScrollPosition"] ? [WXConvert BOOL:attributes[@"keepScrollPosition"]] : NO;
- }
-
- return self;
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- if (attributes[@"keepScrollPosition"]) {
- _keepScrollPosition = [WXConvert BOOL:attributes[@"keepScrollPosition"]];
- }
-}
-
-- (BOOL)isSticky
-{
- return _positionType == WXPositionTypeSticky;
-}
-
-- (void)_frameDidCalculated:(BOOL)isChanged
-{
- [super _frameDidCalculated:isChanged];
-
- if (isChanged) {
- [self.delegate headerDidLayout:self];
- }
-}
-
-- (void)_removeFromSupercomponent
-{
- [super _removeFromSupercomponent];
-
- [self.delegate headerDidRemove:self];
-}
-
-- (void)_calculateFrameWithSuperAbsolutePosition:(CGPoint)superAbsolutePosition gatherDirtyComponents:(NSMutableSet<WXComponent *> *)dirtyComponents
-{
- if (self.delegate && (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH]) || _isUseContainerWidth)) {
- self.cssNode->style.dimensions[CSS_WIDTH] = [self.delegate headerWidthForLayout:self];
- //TODO: set _isUseContainerWidth to NO if updateStyles have width
- _isUseContainerWidth = YES;
- }
-
- if ([self needsLayout]) {
- layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
- if ([WXLog logLevel] >= WXLogLevelDebug) {
- print_css_node(self.cssNode, CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN);
- }
- }
-
- [super _calculateFrameWithSuperAbsolutePosition:superAbsolutePosition gatherDirtyComponents:dirtyComponents];
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.mm
new file mode 100644
index 0000000..97e4252
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXHeaderComponent.mm
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXHeaderComponent.h"
+#import "WXComponent_internal.h"
+#import "WXComponent+Layout.h"
+
+@implementation WXHeaderComponent
+{
+ BOOL _isUseContainerWidth;
+}
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+ self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
+
+ if (self) {
+ _async = YES;
+ _isNeedJoinLayoutSystem = NO;
+ _keepScrollPosition = attributes[@"keepScrollPosition"] ? [WXConvert BOOL:attributes[@"keepScrollPosition"]] : NO;
+ }
+
+ return self;
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ if (attributes[@"keepScrollPosition"]) {
+ _keepScrollPosition = [WXConvert BOOL:attributes[@"keepScrollPosition"]];
+ }
+}
+
+- (BOOL)isSticky
+{
+ return _positionType == WXPositionTypeSticky;
+}
+
+- (void)_frameDidCalculated:(BOOL)isChanged
+{
+ [super _frameDidCalculated:isChanged];
+
+ if (isChanged) {
+ [self.delegate headerDidLayout:self];
+ }
+}
+
+- (void)_removeFromSupercomponent
+{
+ [super _removeFromSupercomponent];
+
+ [self.delegate headerDidRemove:self];
+}
+
+- (void)_calculateFrameWithSuperAbsolutePosition:(CGPoint)superAbsolutePosition gatherDirtyComponents:(NSMutableSet<WXComponent *> *)dirtyComponents
+{
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex]){
+ if (self.delegate && (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH]) || _isUseContainerWidth)) {
+ self.cssNode->style.dimensions[CSS_WIDTH] = [self.delegate headerWidthForLayout:self];
+ //TODO: set _isUseContainerWidth to NO if updateStyles have width
+ _isUseContainerWidth = YES;
+ }
+
+ if ([self needsLayout]) {
+ layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
+ if ([WXLog logLevel] >= WXLogLevelDebug) {
+ print_css_node(self.cssNode, (css_print_options_t)(CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN));
+ }
+ }
+ }
+
+//#else
+ else
+ {
+ if (self.delegate && ( isnan(self.flexCssNode->getStyleWidth()) || _isUseContainerWidth)) {
+ self.flexCssNode->setStyleWidth([self.delegate headerWidthForLayout:self],NO);
+ //TODO: set _isUseContainerWidth to NO if updateStyles have width
+ _isUseContainerWidth = YES;
+ }
+
+ if ([self needsLayout]) {
+ std::pair<float, float> renderPageSize;
+ renderPageSize.first = self.weexInstance.frame.size.width;
+ renderPageSize.second = self.weexInstance.frame.size.height;
+ self.flexCssNode->calculateLayout(renderPageSize);
+ }
+ }
+
+
+//#endif
+ [super _calculateFrameWithSuperAbsolutePosition:superAbsolutePosition gatherDirtyComponents:dirtyComponents];
+}
+
+@end
[11/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m
deleted file mode 100644
index d73531c..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.m
+++ /dev/null
@@ -1,975 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXListComponent.h"
-#import "WXCellComponent.h"
-#import "WXHeaderComponent.h"
-#import "WXComponent.h"
-#import "WXComponent_internal.h"
-#import "NSArray+Weex.h"
-#import "WXAssert.h"
-#import "WXMonitor.h"
-#import "WXUtility.h"
-#import "NSObject+WXSwizzle.h"
-#import "WXSDKInstance_private.h"
-#import "WXRefreshComponent.h"
-#import "WXLoadingComponent.h"
-
-@interface WXTableView : UITableView
-
-@end
-
-@implementation WXTableView
-
-+ (BOOL)requiresConstraintBasedLayout
-{
- return NO;
-}
-
-- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
-{
- if ([(id <WXScrollerProtocol>) self.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) {
- return [(id <WXScrollerProtocol>) self.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
- }
- else{
- return YES;
- }
-}
-
-- (void)layoutSubviews
-{
- [super layoutSubviews];
- [self.wx_component layoutDidFinish];
-}
-
-- (void)setContentOffset:(CGPoint)contentOffset
-{
- // FIXME: side effect caused by hooking _adjustContentOffsetIfNecessary.
- // When UITableView is pulled down and finger releases,contentOffset will be set from -xxxx to about -0.5(greater than -0.5), then contentOffset will be reset to zero by calling _adjustContentOffsetIfNecessary.
- // So hooking _adjustContentOffsetIfNecessary will always cause remaining 1px space between list's top and navigator.
- // Demo: http://dotwe.org/895630945793a9a044e49abe39cbb77f
- // Have to reset contentOffset to zero manually here.
- if (fabs(contentOffset.y) < 0.5) {
- contentOffset.y = 0;
- }
- if (isnan(contentOffset.x)) {
- contentOffset.x = 0;
- }
- if(isnan(contentOffset.y)) {
- contentOffset.y = 0;
- }
-
- [super setContentOffset:contentOffset];
-}
-
-@end
-
-// WXText is a non-public is not permitted
-@interface WXSectionComponent : NSObject<NSCopying>
-
-@property (nonatomic, strong) WXHeaderComponent *header;
-@property (nonatomic, strong) NSMutableArray<WXCellComponent *> *rows;
-
-@end
-
-@implementation WXSectionComponent
-
-- (instancetype)init
-{
- if (self = [super init]) {
- _rows = [NSMutableArray array];
- }
-
- return self;
-}
-
-- (id)copyWithZone:(NSZone *)zone
-{
- WXSectionComponent *newSection = [[[self class] allocWithZone:zone] init];
- newSection.header = _header;
- newSection.rows = [_rows mutableCopyWithZone:zone];
-
- return newSection;
-}
-
-- (NSString *)description
-{
- return [NSString stringWithFormat:@"%@\n%@", [_header description], [_rows description]];
-}
-@end
-
-@interface WXListComponent () <UITableViewDataSource, UITableViewDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate>
-
-@property (nonatomic, assign) NSUInteger currentTopVisibleSection;
-
-@end
-
-@implementation WXListComponent
-{
- __weak UITableView * _tableView;
-
- // Only accessed on component thread
- NSMutableArray<WXSectionComponent *> *_sections;
- // Only accessed on main thread
- NSMutableArray<WXSectionComponent *> *_completedSections;
- NSUInteger _previousLoadMoreRowNumber;
- // insert & reload & batch
- NSString *_updataType;
-
- BOOL _isUpdating;
- NSMutableArray<void(^)(void)> *_updates;
- NSTimeInterval _reloadInterval;
-}
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
- if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
- _sections = [NSMutableArray array];
- _completedSections = [NSMutableArray array];
- _reloadInterval = attributes[@"reloadInterval"] ? [WXConvert CGFloat:attributes[@"reloadInterval"]]/1000 : 0;
- _updataType = [WXConvert NSString:attributes[@"updataType"]]?:@"insert";
- [self fixFlicker];
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- if (_tableView) {
- _tableView.delegate = nil;
- _tableView.dataSource = nil;
- }
-}
-
-- (UIView *)loadView
-{
- return [[WXTableView alloc] init];
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
-
- _tableView = (UITableView *)self.view;
- _tableView.allowsSelection = NO;
- _tableView.allowsMultipleSelection = NO;
- _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
- _tableView.delegate = self;
- _tableView.dataSource = self;
- _tableView.userInteractionEnabled = YES;
-
- _tableView.estimatedRowHeight = 0;
- _tableView.estimatedSectionFooterHeight = 0;
- _tableView.estimatedSectionHeaderHeight = 0;
-}
-
-- (void)viewWillUnload
-{
- [super viewWillUnload];
-
- _tableView.delegate = nil;
- _tableView.dataSource = nil;
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- [super updateAttributes:attributes];
-
- if (attributes[@"reloadInterval"]) {
- _reloadInterval = [WXConvert CGFloat:attributes[@"reloadInterval"]] / 1000;
- }
- if (attributes[@"updataType"]) {
- _updataType = [WXConvert NSString:attributes[@"updataType"]];
- }
-}
-
-- (void)setContentSize:(CGSize)contentSize
-{
- // Do Nothing
-}
-
-- (void)_handleFirstScreenTime
-{
- // Do Nothing, firstScreenTime is set by cellDidRendered:
-}
-
-- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated
-{
- CGPoint contentOffset = _tableView.contentOffset;
- CGFloat contentOffsetY = 0;
-
- WXComponent *cellComponent = component;
- CGRect cellRect;
- while (cellComponent) {
- if ([cellComponent isKindOfClass:[WXCellComponent class]]) {
- NSIndexPath *toIndexPath = [self indexPathForCell:(WXCellComponent*)cellComponent sections:_completedSections];
- cellRect = [_tableView rectForRowAtIndexPath:toIndexPath];
- break;
- }
- if ([cellComponent isKindOfClass:[WXHeaderComponent class]]) {
- NSUInteger toIndex = [self indexForHeader:(WXHeaderComponent *)cellComponent sections:_completedSections];
- cellRect = [_tableView rectForSection:toIndex];
- break;
- }
- contentOffsetY += cellComponent.calculatedFrame.origin.y;
- cellComponent = cellComponent.supercomponent;
- }
-
- contentOffsetY += cellRect.origin.y;
- contentOffsetY += offset * self.weexInstance.pixelScaleFactor;
-
- if (_tableView.contentSize.height >= _tableView.frame.size.height && contentOffsetY > _tableView.contentSize.height - _tableView.frame.size.height) {
- contentOffset.y = _tableView.contentSize.height - _tableView.frame.size.height;
- } else {
- contentOffset.y = contentOffsetY;
- }
-
- [_tableView setContentOffset:contentOffset animated:animated];
-}
-
-
-#pragma mark - Inheritance
-
-- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- if ([subcomponent isKindOfClass:[WXCellComponent class]]) {
- ((WXCellComponent *)subcomponent).delegate = self;
- } else if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
- ((WXHeaderComponent *)subcomponent).delegate = self;
- } else if (![subcomponent isKindOfClass:[WXRefreshComponent class]]
- && ![subcomponent isKindOfClass:[WXLoadingComponent class]]
- && subcomponent->_positionType != WXPositionTypeFixed) {
- WXLogError(@"list only support cell/header/refresh/loading/fixed-component as child.");
- return;
- }
-
- [super _insertSubcomponent:subcomponent atIndex:index];
-
- if (![subcomponent isKindOfClass:[WXHeaderComponent class]]
- && ![subcomponent isKindOfClass:[WXCellComponent class]]) {
- // Don't insert section if subcomponent is not header or cell
- return;
- }
-
- NSIndexPath *indexPath = [self indexPathForSubIndex:index];
-
- if ([subcomponent isKindOfClass:[WXHeaderComponent class]] || _sections.count <= indexPath.section) {
- // conditions to insert section: insert a header or insert first cell of table view
- // this will be updated by recycler's update controller in the future
- WXSectionComponent *insertSection = [WXSectionComponent new];
- BOOL keepScrollPosition = NO;
- if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
- WXHeaderComponent *header = (WXHeaderComponent*)subcomponent;
- insertSection.header = header;
- keepScrollPosition = header.keepScrollPosition;
- }
-
- NSUInteger insertIndex = indexPath.section;
- WXSectionComponent *reloadSection;
- if (insertIndex > 0 && insertIndex <= _sections.count
- && [subcomponent isKindOfClass:[WXHeaderComponent class]]) {
- // insert a header in the middle, one section may divide into two
- // so the original section need to be reloaded
- NSIndexPath *indexPathBeforeHeader = [self indexPathForSubIndex:index - 1];
- if (_sections[insertIndex - 1].rows.count != 0 && indexPathBeforeHeader.row < _sections[insertIndex - 1].rows.count - 1) {
- reloadSection = _sections[insertIndex - 1];
- NSArray *rowsToSeparate = reloadSection.rows;
- insertSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(indexPathBeforeHeader.row + 1, rowsToSeparate.count - indexPathBeforeHeader.row - 1)] mutableCopy];
- reloadSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(0, indexPathBeforeHeader.row + 1)] mutableCopy];
- }
- }
-
- [_sections insertObject:insertSection atIndex:insertIndex];
- WXSectionComponent *completedInsertSection = [insertSection copy];
- WXSectionComponent *completedReloadSection;
- if (reloadSection) {
- completedReloadSection = [reloadSection copy];
- }
-
- [self.weexInstance.componentManager _addUITask:^{
- WXLogDebug(@"Insert section:%ld", (unsigned long)insertIndex);
-
- [UIView performWithoutAnimation:^{
-
- @try {
- [_tableView beginUpdates];
-
- [_completedSections insertObject:completedInsertSection atIndex:insertIndex];
- if (completedReloadSection) {
- WXLogDebug(@"Reload section:%lu", (unsigned long)(insertIndex - 1));
- _completedSections[insertIndex - 1] = completedReloadSection;
- }
-
- [self _insertTableViewSectionAtIndex:insertIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone];
-
- if (completedReloadSection) {
- [_tableView reloadSections:[NSIndexSet indexSetWithIndex:insertIndex - 1] withRowAnimation:UITableViewRowAnimationNone];
- }
-
- [_tableView endUpdates];
- } @catch (NSException *exception) {
- WXLogError(@"list insert component occurs exception %@", exception);
- } @finally {
- // nothing
- }
-
- }];
-
- }];
-
- }
-}
-
-- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- //Here will not insert cell or header's view again
- if (![subcomponent isKindOfClass:[WXCellComponent class]]
- && ![subcomponent isKindOfClass:[WXHeaderComponent class]]) {
- [super insertSubview:subcomponent atIndex:index];
- }
-}
-
-#pragma mark - WXHeaderRenderDelegate
-
-- (float)headerWidthForLayout:(WXHeaderComponent *)cell
-{
- return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
-}
-
-- (void)headerDidLayout:(WXHeaderComponent *)header
-{
- [self.weexInstance.componentManager _addUITask:^{
- // trigger section header update
- [UIView performWithoutAnimation:^{
- [_tableView beginUpdates];
-
- NSUInteger reloadIndex = [self indexForHeader:header sections:_completedSections];
- [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone];
-
- [_tableView endUpdates];
- }];
- }];
-}
-
-- (void)headerDidRemove:(WXHeaderComponent *)header
-{
- NSUInteger headerIndex = [self indexForHeader:header sections:_sections];
- // this will be updated by recycler's update controller in the future
- WXSectionComponent *headerSection = _sections[headerIndex];
- WXSectionComponent *reloadSection;
- NSUInteger reloadIndex = -1;
- BOOL isDeleteSection = NO;
- if (headerIndex == 0 && headerSection.rows.count > 0) {
- // delete a header in the first section and the section still has cells
- // reload the first section
- reloadIndex = 0;
- reloadSection = _sections[reloadIndex];
- _sections[reloadIndex].header = nil;
- } else if (headerIndex > 0 && headerSection.rows.count > 0) {
- // delete a header in the middle, two sections merge into one
- // so one section need to be deleted and the other should be relo
- isDeleteSection = YES;
- reloadIndex = headerIndex - 1;
- reloadSection = _sections[reloadIndex];
- reloadSection.rows = [[reloadSection.rows arrayByAddingObjectsFromArray:headerSection.rows] mutableCopy];
- [_sections removeObjectAtIndex:headerIndex];
- } else {
- // delete a header with no cell in that section
- // just delete the section
- isDeleteSection = YES;
- [_sections removeObjectAtIndex:headerIndex];
- }
-
- WXSectionComponent *completedReloadSection;
- if (reloadSection) {
- completedReloadSection = [reloadSection copy];
- }
- BOOL keepScrollPosition = header.keepScrollPosition;
-
- [self.weexInstance.componentManager _addUITask:^{
- if (isDeleteSection) {
- WXLogDebug(@"delete section:%lu", (unsigned long)headerIndex);
- [_completedSections removeObjectAtIndex:headerIndex];
- }
-
- if (reloadIndex == 0 && !isDeleteSection) {
- _completedSections[reloadIndex].header = nil;
- }
-
- if (completedReloadSection) {
- WXLogDebug(@"Reload section:%lu", (unsigned long)reloadIndex);
- _completedSections[reloadIndex] = completedReloadSection;
- }
-
- [UIView performWithoutAnimation:^{
- [_tableView beginUpdates];
- if (isDeleteSection) {
- [self _deleteTableViewSectionAtIndex:headerIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone];
- }
-
- if (completedReloadSection) {
- [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone];
- }
-
- [_tableView endUpdates];
- }];
-
- }];
-}
-
-#pragma mark - WXCellRenderDelegate
-
-- (float)containerWidthForLayout:(WXCellComponent *)cell
-{
- return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
-}
-
-- (void)cellDidRemove:(WXCellComponent *)cell
-{
- WXAssertComponentThread();
-
- NSIndexPath *indexPath = [self indexPathForCell:cell sections:_sections];
- [self removeCellForIndexPath:indexPath withSections:_sections];
-
- [self.weexInstance.componentManager _addUITask:^{
- [self removeCellForIndexPath:indexPath withSections:_completedSections];
-
- WXLogDebug(@"Delete cell:%@ at indexPath:%@", cell.ref, indexPath);
- if (cell.deleteAnimation == UITableViewRowAnimationNone) {
- [UIView performWithoutAnimation:^{
- [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone];
- }];
- } else {
- [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.deleteAnimation];
- }
- }];
-}
-
-- (void)cellDidLayout:(WXCellComponent *)cell
-{
- WXAssertComponentThread() ;
-
- NSUInteger index = [self.subcomponents indexOfObject:cell];
- NSIndexPath *indexPath = [self indexPathForSubIndex:index];
-
- NSInteger sectionNum = indexPath.section;
- if (sectionNum >= [_sections count] || sectionNum < 0) {
- // try to protect sectionNum out of range.
- return;
- }
- NSInteger row = indexPath.row;
- NSMutableArray *sections = _sections;
- WXSectionComponent *section = sections[sectionNum];
- WXAssert(section, @"no section found for section number:%ld", sectionNum);
- NSMutableArray *completedSections;
- BOOL isReload = [section.rows containsObject:cell];
- if (!isReload && row > [section.rows count]) {
- // protect crash when row out of bounds
- return ;
- }
- if (!isReload) {
- [section.rows insertObject:cell atIndex:row];
- // deep copy
- completedSections = [[NSMutableArray alloc] initWithArray:sections copyItems:YES];;
- }
-
- [self.weexInstance.componentManager _addUITask:^{
- if (!isReload) {
- WXLogDebug(@"Insert cell:%@ at indexPath:%@", cell.ref, indexPath);
- _completedSections = completedSections;
- if (cell.insertAnimation == UITableViewRowAnimationNone) {
- [UIView performWithoutAnimation:^{
- [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone];
- }];
- } else {
- [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.insertAnimation];
- }
- } else {
- WXLogInfo(@"Reload cell:%@ at indexPath:%@", cell.ref, indexPath);
- [UIView performWithoutAnimation:^{
- [_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
- [self handleAppear];
- }];
- }
- }];
-}
-
-- (void)cellDidRendered:(WXCellComponent *)cell
-{
- WXAssertMainThread();
-
- if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance) && !self.weexInstance.onRenderProgress) {
- // improve performance
- return;
- }
-
- NSIndexPath *indexPath = [self indexPathForCell:cell sections:_completedSections];
- if (!indexPath || indexPath.section >= [_tableView numberOfSections] ||
- indexPath.row < 0 || indexPath.row >= [_tableView numberOfRowsInSection:indexPath.section]) {
- WXLogWarning(@"Rendered cell:%@ out of range, sections:%@", cell, _completedSections);
- return;
- }
-
- CGRect cellRect = [_tableView rectForRowAtIndexPath:indexPath];
- if (cellRect.origin.y + cellRect.size.height >= _tableView.frame.size.height) {
- WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance);
- }
-
- if (self.weexInstance.onRenderProgress) {
- CGRect renderRect = [_tableView convertRect:cellRect toView:self.weexInstance.rootView];
- self.weexInstance.onRenderProgress(renderRect);
- }
-}
-
-- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index
-{
- WXAssertComponentThread();
-
- NSIndexPath *fromIndexPath = [self indexPathForCell:cell sections:_sections];
- NSIndexPath *toIndexPath = [self indexPathForSubIndex:index];
- if (toIndexPath.row > [_sections[toIndexPath.section].rows count] || toIndexPath.row < 0) {
- WXLogError(@"toIndexPath %@ is out of range as the current is %lu",toIndexPath ,(unsigned long)[_sections[toIndexPath.section].rows count]);
- return;
- }
- [self removeCellForIndexPath:fromIndexPath withSections:_sections];
- [self insertCell:cell forIndexPath:toIndexPath withSections:_sections];
-
- [self.weexInstance.componentManager _addUITask:^{
- if (_reloadInterval > 0) {
- // use [UITableView reloadData] to do batch updates, will move to recycler's update controller
- __weak typeof(self) weakSelf = self;
- if (!_updates) {
- _updates = [NSMutableArray array];
- }
- [_updates addObject:^{
- __strong typeof(weakSelf) strongSelf = weakSelf;
- [strongSelf removeCellForIndexPath:fromIndexPath withSections:strongSelf->_completedSections];
- [strongSelf insertCell:cell forIndexPath:toIndexPath withSections:strongSelf->_completedSections];
- }];
-
- [self checkReloadData];
- } else {
- [self removeCellForIndexPath:fromIndexPath withSections:_completedSections];
- [self insertCell:cell forIndexPath:toIndexPath withSections:_completedSections];
- [UIView performWithoutAnimation:^{
- @try {
- [_tableView beginUpdates];
- [_tableView moveRowAtIndexPath:fromIndexPath toIndexPath:toIndexPath];
- [self handleAppear];
- [_tableView endUpdates];
- }@catch(NSException * exception){
- WXLogDebug(@"move cell exception: %@", [exception description]);
- }@finally {
- // do nothing
- }
- }];
- }
- }];
-}
-
-- (void)checkReloadData
-{
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_reloadInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- if (_isUpdating || _updates.count == 0) {
- return ;
- }
-
- _isUpdating = YES;
- NSArray *updates = [_updates copy];
- [_updates removeAllObjects];
- for (void(^update)(void) in updates) {
- update();
- }
- [_tableView reloadData];
- _isUpdating = NO;
-
- [self checkReloadData];
- });
-}
-
-- (void)addStickyComponent:(WXComponent *)sticky
-{
-
-}
-
-- (void)removeStickyComponent:(WXComponent *)sticky
-{
-
-}
-#pragma mark - TableView delegate
-
-- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
-{
-
-}
-
-- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
-{
- WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath);
- NSArray *visibleIndexPaths = [tableView indexPathsForVisibleRows];
- if (![visibleIndexPaths containsObject:indexPath]) {
- if (cell.contentView.subviews.count > 0) {
- UIView *wxCellView = [cell.contentView.subviews firstObject];
- // Must invoke synchronously otherwise it will remove the view just added.
- WXCellComponent *cellComponent = (WXCellComponent *)wxCellView.wx_component;
- if (cellComponent.isRecycle) {
- [wxCellView.wx_component _unloadViewWithReusing:YES];
- }
- }
- }
-}
-
-- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
-{
- WXCellComponent *cell = [self cellForIndexPath:indexPath];
- return cell.calculatedFrame.size.height;
-}
-
-- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
-{
- WXHeaderComponent *header = ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).header;
- if (header) {
- return header.calculatedFrame.size.height;
- } else {
- return 0.0;
- }
-}
-
-- (void)scrollViewDidScroll:(UIScrollView *)scrollView
-{
- [super scrollViewDidScroll:scrollView];
- if ([[_tableView indexPathsForVisibleRows] count] > 0) {
- NSIndexPath *topCellPath = [[_tableView indexPathsForVisibleRows] objectAtIndex:0];
- if (self.currentTopVisibleSection != topCellPath.section) {
- if (self.currentTopVisibleSection) {
- WXSectionComponent *removeSection = [_sections wx_safeObjectAtIndex:self.currentTopVisibleSection];
- if (removeSection.header && [removeSection.header.events containsObject:@"unsticky"]) {
- [removeSection.header fireEvent:@"unsticky" params:nil];
- }
- }
- self.currentTopVisibleSection = topCellPath.section;
- WXSectionComponent *showSection = [_sections wx_safeObjectAtIndex:topCellPath.section];
- if (showSection.header && [showSection.header.events containsObject:@"sticky"]) {
- [showSection.header fireEvent:@"sticky" params:nil];
- }
- }
- }
-}
-
-- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
-{
- WXHeaderComponent *header = ((WXSectionComponent *)_completedSections[section]).header;
- WXLogDebug(@"header view for section %ld:%@", (long)section, header.view);
- return header.view;
-}
-
-#pragma mark - TableView Data Source
-
-- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
-{
- return _completedSections.count;
-}
-
-- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
-{
- return ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).rows.count;
-}
-
-- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
-{
- WXLogDebug(@"Getting cell at indexPath:%@", indexPath);
- static NSString *reuseIdentifier = @"WXTableViewCell";
-
- UITableViewCell *cellView = [_tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
- if (!cellView) {
- cellView = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
- cellView.backgroundColor = [UIColor clearColor];
- }
-
- WXCellComponent *cell = [self cellForIndexPath:indexPath];
-
- if (cell.zIndex) {
- cellView.layer.zPosition = [WXConvert CGFloat:cell.zIndex];
- }
-
- if (!cell) {
- return cellView;
- }
-
- if (cell.view.superview == cellView.contentView) {
- return cellView;
- }
-
- for (UIView *view in cellView.contentView.subviews) {
- [view removeFromSuperview];
- }
-
- [cellView.contentView addSubview:cell.view];
-
- [cellView setAccessibilityIdentifier:cell.view.accessibilityIdentifier];
-
- WXLogDebug(@"Created cell:%@ view:%@ cellView:%@ at indexPath:%@", cell.ref, cell.view, cellView, indexPath);
- return cellView;
-}
-
-#pragma mark - Load More Event
-
-- (void)setLoadmoreretry:(NSUInteger)loadmoreretry
-{
- if (loadmoreretry != self.loadmoreretry) {
- _previousLoadMoreRowNumber = 0;
- }
-
- [super setLoadmoreretry:loadmoreretry];
-}
-
-- (void)loadMore
-{
- [super loadMore];
-
- _previousLoadMoreRowNumber = [self totalNumberOfRows];
-}
-
-- (BOOL)isNeedLoadMore
-{
- BOOL superNeedLoadMore = [super isNeedLoadMore];
- return superNeedLoadMore && _previousLoadMoreRowNumber != [self totalNumberOfRows];
-}
-
-- (NSUInteger)totalNumberOfRows
-{
- NSUInteger rowNumber = 0;
- NSUInteger sectionCount = [_tableView numberOfSections];
- for (int section = 0; section < sectionCount; section ++) {
- rowNumber += [_tableView numberOfRowsInSection:section];
- }
-
- return rowNumber;
-}
-
-- (void)resetLoadmore{
- [super resetLoadmore];
- _previousLoadMoreRowNumber=0;
-}
-
-#pragma mark Private
-
-- (WXCellComponent *)cellForIndexPath:(NSIndexPath *)indexPath
-{
- WXSectionComponent *section = [_completedSections wx_safeObjectAtIndex:indexPath.section];
- if (!section) {
- WXLogError(@"No section found for num:%ld, completed sections:%ld", (long)indexPath.section, (unsigned long)_completedSections.count);
- return nil;
- }
-
- WXCellComponent *cell = [section.rows wx_safeObjectAtIndex:indexPath.row];
- if (!cell) {
- WXLogError(@"No cell found for num:%ld, completed rows:%ld", (long)indexPath.row, (unsigned long)section.rows.count);
- return nil;
- }
-
- return cell;
-}
-
-- (void)insertCell:(WXCellComponent *)cell forIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections
-{
- WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section];
- if (indexPath.row > [section.rows count] || indexPath.row < 0) {
- WXLogError(@"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
- return;
- }
- WXAssert(section, @"inserting cell at indexPath:%@ section has not been inserted to list before, sections:%@", indexPath, sections);
- WXAssert(indexPath.row <= section.rows.count, @"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
- [section.rows insertObject:cell atIndex:indexPath.row];
-}
-
-- (void)removeCellForIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections
-{
- WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section];
- if (0 == [section.rows count]) {
- return;
- }
- WXAssert(section, @"Removing cell at indexPath:%@ has not been inserted to cell list before, sections:%@", indexPath, sections);
- WXAssert(indexPath.row < section.rows.count, @"Removing cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
- [section.rows removeObjectAtIndex:indexPath.row];
-}
-
-- (NSIndexPath *)indexPathForCell:(WXCellComponent *)cell sections:(NSMutableArray<WXSectionComponent *> *)sections
-{
- __block NSIndexPath *indexPath;
- [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull sectionStop) {
- [section.rows enumerateObjectsUsingBlock:^(WXCellComponent * _Nonnull row, NSUInteger rowIndex, BOOL * _Nonnull stop) {
- if (row == cell) {
- indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
- *stop = YES;
- *sectionStop = YES;
- }
- }];
- }];
-
- return indexPath;
-}
-
-- (NSUInteger)indexForHeader:(WXHeaderComponent *)header sections:(NSMutableArray<WXSectionComponent *> *)sections
-{
- __block NSUInteger index;
- [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull stop) {
- if (section.header == header) {
- index = sectionIndex;
- *stop = YES;
- }
- }];
-
- return index;
-}
-
-- (NSIndexPath *)indexPathForSubIndex:(NSUInteger)index
-{
- NSInteger section = 0;
- NSInteger row = -1;
- WXComponent *firstComponent;
- for (int i = 0; i <= index; i++) {
- WXComponent* component = [self.subcomponents wx_safeObjectAtIndex:i];
- if (!component) {
- continue;
- }
- if (([component isKindOfClass:[WXHeaderComponent class]]
- || [component isKindOfClass:[WXCellComponent class]])
- && !firstComponent) {
- firstComponent = component;
- }
-
- if (component != firstComponent && [component isKindOfClass:[WXHeaderComponent class]]) {
- section ++;
- row = -1;
- }
-
- if ([component isKindOfClass:[WXCellComponent class]]) {
- row ++;
- }
- }
-
- return [NSIndexPath indexPathForRow:row inSection:section];
-}
-
-- (void)_performUpdates:(void(^)(void))updates withKeepScrollPosition:(BOOL)keepScrollPosition adjustmentBlock:(CGFloat(^)(NSIndexPath *topVisibleCell))adjustmentBlock
-{
- CGFloat adjustment = 0;
-
- // keep the scroll position when inserting or deleting sections/rows by adjusting the content offset
- if (keepScrollPosition) {
- NSIndexPath *top = _tableView.indexPathsForVisibleRows.firstObject;
- adjustment = adjustmentBlock(top);
- }
-
- updates();
-
- if (keepScrollPosition) {
- CGPoint afterContentOffset = _tableView.contentOffset;
- CGPoint newContentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + adjustment);
- _tableView.contentOffset = newContentOffset;
- }
-
- [self handleAppear];
-}
-
-- (void)_insertTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
-{
- [self _performUpdates:^{
- [_tableView insertSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation];
- } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
- if (section <= top.section) {
- return [self tableView:_tableView heightForHeaderInSection:section];
- } else {
- return 0.0;
- }
- }];
-}
-
-- (void)_deleteTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
-{
- [self _performUpdates:^{
- [_tableView deleteSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation];
- } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
- if (section <= top.section) {
- return [self tableView:_tableView heightForHeaderInSection:section];
- } else {
- return 0.0;
- }
- }];
-}
-
-- (void)_insertTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
-{
- [self _performUpdates:^{
- if ([_updataType isEqual: @"reload"]) {
- [_tableView reloadData];
- } else {
- [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation];
- }
- } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
- if (([indexPath compare:top] <= 0) || [_updataType isEqual: @"reload"]) {
- return [self tableView:_tableView heightForRowAtIndexPath:indexPath];
- } else {
- return 0.0;
- }
- }];
-}
-
-- (void)_deleteTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
-{
- if (!indexPath) {
- return ;
- }
- [self _performUpdates:^{
- [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation];
- } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
- if ([indexPath compare:top] <= 0) {
- return [self tableView:_tableView heightForRowAtIndexPath:indexPath];
- } else {
- return 0.0;
- }
- }];
-}
-
-- (void)fixFlicker
-{
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- // FIXME:(ง •̀_•́)ง┻━┻ Stupid scoll view, always reset content offset to zero by calling _adjustContentOffsetIfNecessary after insert cells.
- // So if you pull down list while list is rendering, the list will be flickering.
- // Demo:
- // Have to hook _adjustContentOffsetIfNecessary here.
- // Any other more elegant way?
- NSString *a = @"ntOffsetIfNe";
- NSString *b = @"adjustConte";
-
- NSString *originSelector = [NSString stringWithFormat:@"_%@%@cessary", b, a];
- [[self class] weex_swizzle:[WXTableView class] Method:NSSelectorFromString(originSelector) withMethod:@selector(fixedFlickerSelector)];
- });
-}
-
-- (void)fixedFlickerSelector
-{
- // DO NOT delete this method.
-}
-
-
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm
new file mode 100644
index 0000000..bd4eb64
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm
@@ -0,0 +1,997 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXListComponent.h"
+#import "WXCellComponent.h"
+#import "WXHeaderComponent.h"
+#import "WXComponent.h"
+#import "WXComponent_internal.h"
+#import "NSArray+Weex.h"
+#import "WXAssert.h"
+#import "WXMonitor.h"
+#import "WXUtility.h"
+#import "NSObject+WXSwizzle.h"
+#import "WXSDKInstance_private.h"
+#import "WXRefreshComponent.h"
+#import "WXLoadingComponent.h"
+#import "WXScrollerComponent+Layout.h"
+
+@interface WXTableView : UITableView
+
+@end
+
+@implementation WXTableView
+
++ (BOOL)requiresConstraintBasedLayout
+{
+ return NO;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
+{
+ if ([(id <WXScrollerProtocol>) self.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) {
+ return [(id <WXScrollerProtocol>) self.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
+ }
+ else{
+ return YES;
+ }
+}
+
+- (void)layoutSubviews
+{
+ [super layoutSubviews];
+ [self.wx_component layoutDidFinish];
+}
+
+- (void)setContentOffset:(CGPoint)contentOffset
+{
+ // FIXME: side effect caused by hooking _adjustContentOffsetIfNecessary.
+ // When UITableView is pulled down and finger releases,contentOffset will be set from -xxxx to about -0.5(greater than -0.5), then contentOffset will be reset to zero by calling _adjustContentOffsetIfNecessary.
+ // So hooking _adjustContentOffsetIfNecessary will always cause remaining 1px space between list's top and navigator.
+ // Demo: http://dotwe.org/895630945793a9a044e49abe39cbb77f
+ // Have to reset contentOffset to zero manually here.
+ if (fabs(contentOffset.y) < 0.5) {
+ contentOffset.y = 0;
+ }
+ if (isnan(contentOffset.x)) {
+ contentOffset.x = 0;
+ }
+ if(isnan(contentOffset.y)) {
+ contentOffset.y = 0;
+ }
+
+ [super setContentOffset:contentOffset];
+}
+
+@end
+
+// WXText is a non-public is not permitted
+@interface WXSectionComponent : NSObject<NSCopying>
+
+@property (nonatomic, strong) WXHeaderComponent *header;
+@property (nonatomic, strong) NSMutableArray<WXCellComponent *> *rows;
+
+@end
+
+@implementation WXSectionComponent
+
+- (instancetype)init
+{
+ if (self = [super init]) {
+ _rows = [NSMutableArray array];
+ }
+
+ return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ WXSectionComponent *newSection = [[[self class] allocWithZone:zone] init];
+ newSection.header = _header;
+ newSection.rows = [_rows mutableCopyWithZone:zone];
+
+ return newSection;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"%@\n%@", [_header description], [_rows description]];
+}
+@end
+
+@interface WXListComponent () <UITableViewDataSource, UITableViewDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate>
+
+@property (nonatomic, assign) NSUInteger currentTopVisibleSection;
+
+@end
+
+@implementation WXListComponent
+{
+ __weak UITableView * _tableView;
+
+ // Only accessed on component thread
+ NSMutableArray<WXSectionComponent *> *_sections;
+ // Only accessed on main thread
+ NSMutableArray<WXSectionComponent *> *_completedSections;
+ NSUInteger _previousLoadMoreRowNumber;
+ // insert & reload & batch
+ NSString *_updataType;
+
+ BOOL _isUpdating;
+ NSMutableArray<void(^)(void)> *_updates;
+ NSTimeInterval _reloadInterval;
+}
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+ if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
+ _sections = [NSMutableArray array];
+ _completedSections = [NSMutableArray array];
+ _reloadInterval = attributes[@"reloadInterval"] ? [WXConvert CGFloat:attributes[@"reloadInterval"]]/1000 : 0;
+ _updataType = [WXConvert NSString:attributes[@"updataType"]]?:@"insert";
+ [self fixFlicker];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ if (_tableView) {
+ _tableView.delegate = nil;
+ _tableView.dataSource = nil;
+ }
+}
+
+- (UIView *)loadView
+{
+ return [[WXTableView alloc] init];
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ _tableView = (UITableView *)self.view;
+ _tableView.allowsSelection = NO;
+ _tableView.allowsMultipleSelection = NO;
+ _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+ _tableView.delegate = self;
+ _tableView.dataSource = self;
+ _tableView.userInteractionEnabled = YES;
+
+ _tableView.estimatedRowHeight = 0;
+ _tableView.estimatedSectionFooterHeight = 0;
+ _tableView.estimatedSectionHeaderHeight = 0;
+}
+
+- (void)viewWillUnload
+{
+ [super viewWillUnload];
+
+ _tableView.delegate = nil;
+ _tableView.dataSource = nil;
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ [super updateAttributes:attributes];
+
+ if (attributes[@"reloadInterval"]) {
+ _reloadInterval = [WXConvert CGFloat:attributes[@"reloadInterval"]] / 1000;
+ }
+ if (attributes[@"updataType"]) {
+ _updataType = [WXConvert NSString:attributes[@"updataType"]];
+ }
+}
+
+- (void)setContentSize:(CGSize)contentSize
+{
+ // Do Nothing
+}
+
+- (void)_handleFirstScreenTime
+{
+ // Do Nothing, firstScreenTime is set by cellDidRendered:
+}
+
+- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated
+{
+ CGPoint contentOffset = _tableView.contentOffset;
+ CGFloat contentOffsetY = 0;
+
+ WXComponent *cellComponent = component;
+ CGRect cellRect;
+ while (cellComponent) {
+ if ([cellComponent isKindOfClass:[WXCellComponent class]]) {
+ NSIndexPath *toIndexPath = [self indexPathForCell:(WXCellComponent*)cellComponent sections:_completedSections];
+ cellRect = [_tableView rectForRowAtIndexPath:toIndexPath];
+ break;
+ }
+ if ([cellComponent isKindOfClass:[WXHeaderComponent class]]) {
+ NSUInteger toIndex = [self indexForHeader:(WXHeaderComponent *)cellComponent sections:_completedSections];
+ cellRect = [_tableView rectForSection:toIndex];
+ break;
+ }
+ contentOffsetY += cellComponent.calculatedFrame.origin.y;
+ cellComponent = cellComponent.supercomponent;
+ }
+
+ contentOffsetY += cellRect.origin.y;
+ contentOffsetY += offset * self.weexInstance.pixelScaleFactor;
+
+ if (_tableView.contentSize.height >= _tableView.frame.size.height && contentOffsetY > _tableView.contentSize.height - _tableView.frame.size.height) {
+ contentOffset.y = _tableView.contentSize.height - _tableView.frame.size.height;
+ } else {
+ contentOffset.y = contentOffsetY;
+ }
+
+ [_tableView setContentOffset:contentOffset animated:animated];
+}
+
+
+#pragma mark - Inheritance
+
+- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ if ([subcomponent isKindOfClass:[WXCellComponent class]]) {
+ ((WXCellComponent *)subcomponent).delegate = self;
+ } else if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
+ ((WXHeaderComponent *)subcomponent).delegate = self;
+ } else if (![subcomponent isKindOfClass:[WXRefreshComponent class]]
+ && ![subcomponent isKindOfClass:[WXLoadingComponent class]]
+ && subcomponent->_positionType != WXPositionTypeFixed) {
+ WXLogError(@"list only support cell/header/refresh/loading/fixed-component as child.");
+ return;
+ }
+
+ [super _insertSubcomponent:subcomponent atIndex:index];
+
+ if (![subcomponent isKindOfClass:[WXHeaderComponent class]]
+ && ![subcomponent isKindOfClass:[WXCellComponent class]]) {
+ // Don't insert section if subcomponent is not header or cell
+ return;
+ }
+
+ NSIndexPath *indexPath = [self indexPathForSubIndex:index];
+
+ if ([subcomponent isKindOfClass:[WXHeaderComponent class]] || _sections.count <= indexPath.section) {
+ // conditions to insert section: insert a header or insert first cell of table view
+ // this will be updated by recycler's update controller in the future
+ WXSectionComponent *insertSection = [WXSectionComponent new];
+ BOOL keepScrollPosition = NO;
+ if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
+ WXHeaderComponent *header = (WXHeaderComponent*)subcomponent;
+ insertSection.header = header;
+ keepScrollPosition = header.keepScrollPosition;
+ }
+
+ NSUInteger insertIndex = indexPath.section;
+ WXSectionComponent *reloadSection;
+ if (insertIndex > 0 && insertIndex <= _sections.count
+ && [subcomponent isKindOfClass:[WXHeaderComponent class]]) {
+ // insert a header in the middle, one section may divide into two
+ // so the original section need to be reloaded
+ NSIndexPath *indexPathBeforeHeader = [self indexPathForSubIndex:index - 1];
+ if (_sections[insertIndex - 1].rows.count != 0 && indexPathBeforeHeader.row < _sections[insertIndex - 1].rows.count - 1) {
+ reloadSection = _sections[insertIndex - 1];
+ NSArray *rowsToSeparate = reloadSection.rows;
+ insertSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(indexPathBeforeHeader.row + 1, rowsToSeparate.count - indexPathBeforeHeader.row - 1)] mutableCopy];
+ reloadSection.rows = [[rowsToSeparate subarrayWithRange:NSMakeRange(0, indexPathBeforeHeader.row + 1)] mutableCopy];
+ }
+ }
+
+ [_sections insertObject:insertSection atIndex:insertIndex];
+ WXSectionComponent *completedInsertSection = [insertSection copy];
+ WXSectionComponent *completedReloadSection;
+ if (reloadSection) {
+ completedReloadSection = [reloadSection copy];
+ }
+
+ [self.weexInstance.componentManager _addUITask:^{
+ WXLogDebug(@"Insert section:%ld", (unsigned long)insertIndex);
+
+ [UIView performWithoutAnimation:^{
+
+ @try {
+ [_tableView beginUpdates];
+
+ [_completedSections insertObject:completedInsertSection atIndex:insertIndex];
+ if (completedReloadSection) {
+ WXLogDebug(@"Reload section:%lu", (unsigned long)(insertIndex - 1));
+ _completedSections[insertIndex - 1] = completedReloadSection;
+ }
+
+ [self _insertTableViewSectionAtIndex:insertIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone];
+
+ if (completedReloadSection) {
+ [_tableView reloadSections:[NSIndexSet indexSetWithIndex:insertIndex - 1] withRowAnimation:UITableViewRowAnimationNone];
+ }
+
+ [_tableView endUpdates];
+ } @catch (NSException *exception) {
+ WXLogError(@"list insert component occurs exception %@", exception);
+ } @finally {
+ // nothing
+ }
+
+ }];
+
+ }];
+
+ }
+}
+
+- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ //Here will not insert cell or header's view again
+ if (![subcomponent isKindOfClass:[WXCellComponent class]]
+ && ![subcomponent isKindOfClass:[WXHeaderComponent class]]) {
+ [super insertSubview:subcomponent atIndex:index];
+ }
+}
+
+#pragma mark - WXHeaderRenderDelegate
+
+- (float)headerWidthForLayout:(WXHeaderComponent *)cell
+{
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex]){
+ return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
+ }
+//#else
+ else
+ {
+ return self.flexScrollerCSSNode->getStyleWidth();
+ }
+//#endif
+}
+
+- (void)headerDidLayout:(WXHeaderComponent *)header
+{
+ [self.weexInstance.componentManager _addUITask:^{
+ // trigger section header update
+ [UIView performWithoutAnimation:^{
+ [_tableView beginUpdates];
+
+ NSUInteger reloadIndex = [self indexForHeader:header sections:_completedSections];
+ [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone];
+
+ [_tableView endUpdates];
+ }];
+ }];
+}
+
+- (void)headerDidRemove:(WXHeaderComponent *)header
+{
+ NSUInteger headerIndex = [self indexForHeader:header sections:_sections];
+ // this will be updated by recycler's update controller in the future
+ WXSectionComponent *headerSection = _sections[headerIndex];
+ WXSectionComponent *reloadSection;
+ NSUInteger reloadIndex = -1;
+ BOOL isDeleteSection = NO;
+ if (headerIndex == 0 && headerSection.rows.count > 0) {
+ // delete a header in the first section and the section still has cells
+ // reload the first section
+ reloadIndex = 0;
+ reloadSection = _sections[reloadIndex];
+ _sections[reloadIndex].header = nil;
+ } else if (headerIndex > 0 && headerSection.rows.count > 0) {
+ // delete a header in the middle, two sections merge into one
+ // so one section need to be deleted and the other should be relo
+ isDeleteSection = YES;
+ reloadIndex = headerIndex - 1;
+ reloadSection = _sections[reloadIndex];
+ reloadSection.rows = [[reloadSection.rows arrayByAddingObjectsFromArray:headerSection.rows] mutableCopy];
+ [_sections removeObjectAtIndex:headerIndex];
+ } else {
+ // delete a header with no cell in that section
+ // just delete the section
+ isDeleteSection = YES;
+ [_sections removeObjectAtIndex:headerIndex];
+ }
+
+ WXSectionComponent *completedReloadSection;
+ if (reloadSection) {
+ completedReloadSection = [reloadSection copy];
+ }
+ BOOL keepScrollPosition = header.keepScrollPosition;
+
+ [self.weexInstance.componentManager _addUITask:^{
+ if (isDeleteSection) {
+ WXLogDebug(@"delete section:%lu", (unsigned long)headerIndex);
+ [_completedSections removeObjectAtIndex:headerIndex];
+ }
+
+ if (reloadIndex == 0 && !isDeleteSection) {
+ _completedSections[reloadIndex].header = nil;
+ }
+
+ if (completedReloadSection) {
+ WXLogDebug(@"Reload section:%lu", (unsigned long)reloadIndex);
+ _completedSections[reloadIndex] = completedReloadSection;
+ }
+
+ [UIView performWithoutAnimation:^{
+ [_tableView beginUpdates];
+ if (isDeleteSection) {
+ [self _deleteTableViewSectionAtIndex:headerIndex keepScrollPosition:keepScrollPosition animation:UITableViewRowAnimationNone];
+ }
+
+ if (completedReloadSection) {
+ [_tableView reloadSections:[NSIndexSet indexSetWithIndex:reloadIndex] withRowAnimation:UITableViewRowAnimationNone];
+ }
+
+ [_tableView endUpdates];
+ }];
+
+ }];
+}
+
+#pragma mark - WXCellRenderDelegate
+
+- (float)containerWidthForLayout:(WXCellComponent *)cell
+{
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
+ }
+//#else
+ else{
+ return self.flexScrollerCSSNode->getStyleWidth();
+ }
+//#endif
+}
+
+- (void)cellDidRemove:(WXCellComponent *)cell
+{
+ WXAssertComponentThread();
+
+ NSIndexPath *indexPath = [self indexPathForCell:cell sections:_sections];
+ if(!indexPath){
+ //protect when cell not exist in sections
+ return;
+ }
+ [self removeCellForIndexPath:indexPath withSections:_sections];
+
+ [self.weexInstance.componentManager _addUITask:^{
+ [self removeCellForIndexPath:indexPath withSections:_completedSections];
+
+ WXLogDebug(@"Delete cell:%@ at indexPath:%@", cell.ref, indexPath);
+ if (cell.deleteAnimation == UITableViewRowAnimationNone) {
+ [UIView performWithoutAnimation:^{
+ [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone];
+ }];
+ } else {
+ [self _deleteTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.deleteAnimation];
+ }
+ }];
+}
+
+- (void)cellDidLayout:(WXCellComponent *)cell
+{
+ WXAssertComponentThread() ;
+
+ NSUInteger index = [self.subcomponents indexOfObject:cell];
+ NSIndexPath *indexPath = [self indexPathForSubIndex:index];
+
+ NSInteger sectionNum = indexPath.section;
+ if (sectionNum >= [_sections count] || sectionNum < 0) {
+ // try to protect sectionNum out of range.
+ return;
+ }
+ NSInteger row = indexPath.row;
+ NSMutableArray *sections = _sections;
+ WXSectionComponent *section = sections[sectionNum];
+ WXAssert(section, @"no section found for section number:%ld", sectionNum);
+ NSMutableArray *completedSections;
+ BOOL isReload = [section.rows containsObject:cell];
+ if (!isReload && row > [section.rows count]) {
+ // protect crash when row out of bounds
+ return ;
+ }
+ if (!isReload) {
+ [section.rows insertObject:cell atIndex:row];
+ // deep copy
+ completedSections = [[NSMutableArray alloc] initWithArray:sections copyItems:YES];;
+ }
+
+ [self.weexInstance.componentManager _addUITask:^{
+ if (!isReload) {
+ WXLogDebug(@"Insert cell:%@ at indexPath:%@", cell.ref, indexPath);
+ _completedSections = completedSections;
+ if (cell.insertAnimation == UITableViewRowAnimationNone) {
+ [UIView performWithoutAnimation:^{
+ [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:UITableViewRowAnimationNone];
+ }];
+ } else {
+ [self _insertTableViewCellAtIndexPath:indexPath keepScrollPosition:cell.keepScrollPosition animation:cell.insertAnimation];
+ }
+ } else {
+ WXLogInfo(@"Reload cell:%@ at indexPath:%@", cell.ref, indexPath);
+ [UIView performWithoutAnimation:^{
+ [_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
+ [self handleAppear];
+ }];
+ }
+ }];
+}
+
+- (void)cellDidRendered:(WXCellComponent *)cell
+{
+ WXAssertMainThread();
+
+ if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance) && !self.weexInstance.onRenderProgress) {
+ // improve performance
+ return;
+ }
+
+ NSIndexPath *indexPath = [self indexPathForCell:cell sections:_completedSections];
+ if (!indexPath || indexPath.section >= [_tableView numberOfSections] ||
+ indexPath.row < 0 || indexPath.row >= [_tableView numberOfRowsInSection:indexPath.section]) {
+ WXLogWarning(@"Rendered cell:%@ out of range, sections:%@", cell, _completedSections);
+ return;
+ }
+
+ CGRect cellRect = [_tableView rectForRowAtIndexPath:indexPath];
+ if (cellRect.origin.y + cellRect.size.height >= _tableView.frame.size.height) {
+ WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance);
+ }
+
+ if (self.weexInstance.onRenderProgress) {
+ CGRect renderRect = [_tableView convertRect:cellRect toView:self.weexInstance.rootView];
+ self.weexInstance.onRenderProgress(renderRect);
+ }
+}
+
+- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index
+{
+ WXAssertComponentThread();
+
+ NSIndexPath *fromIndexPath = [self indexPathForCell:cell sections:_sections];
+ NSIndexPath *toIndexPath = [self indexPathForSubIndex:index];
+ if (toIndexPath.row > [_sections[toIndexPath.section].rows count] || toIndexPath.row < 0) {
+ WXLogError(@"toIndexPath %@ is out of range as the current is %lu",toIndexPath ,(unsigned long)[_sections[toIndexPath.section].rows count]);
+ return;
+ }
+ [self removeCellForIndexPath:fromIndexPath withSections:_sections];
+ [self insertCell:cell forIndexPath:toIndexPath withSections:_sections];
+
+ [self.weexInstance.componentManager _addUITask:^{
+ if (_reloadInterval > 0) {
+ // use [UITableView reloadData] to do batch updates, will move to recycler's update controller
+ __weak typeof(self) weakSelf = self;
+ if (!_updates) {
+ _updates = [NSMutableArray array];
+ }
+ [_updates addObject:^{
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ [strongSelf removeCellForIndexPath:fromIndexPath withSections:strongSelf->_completedSections];
+ [strongSelf insertCell:cell forIndexPath:toIndexPath withSections:strongSelf->_completedSections];
+ }];
+
+ [self checkReloadData];
+ } else {
+ [self removeCellForIndexPath:fromIndexPath withSections:_completedSections];
+ [self insertCell:cell forIndexPath:toIndexPath withSections:_completedSections];
+ [UIView performWithoutAnimation:^{
+ @try {
+ [_tableView beginUpdates];
+ [_tableView moveRowAtIndexPath:fromIndexPath toIndexPath:toIndexPath];
+ [self handleAppear];
+ [_tableView endUpdates];
+ }@catch(NSException * exception){
+ WXLogDebug(@"move cell exception: %@", [exception description]);
+ }@finally {
+ // do nothing
+ }
+ }];
+ }
+ }];
+}
+
+- (void)checkReloadData
+{
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_reloadInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ if (_isUpdating || _updates.count == 0) {
+ return ;
+ }
+
+ _isUpdating = YES;
+ NSArray *updates = [_updates copy];
+ [_updates removeAllObjects];
+ for (void(^update)(void) in updates) {
+ update();
+ }
+ [_tableView reloadData];
+ _isUpdating = NO;
+
+ [self checkReloadData];
+ });
+}
+
+- (void)addStickyComponent:(WXComponent *)sticky
+{
+
+}
+
+- (void)removeStickyComponent:(WXComponent *)sticky
+{
+
+}
+#pragma mark - TableView delegate
+
+- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
+{
+
+}
+
+- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath);
+ NSArray *visibleIndexPaths = [tableView indexPathsForVisibleRows];
+ if (![visibleIndexPaths containsObject:indexPath]) {
+ if (cell.contentView.subviews.count > 0) {
+ UIView *wxCellView = [cell.contentView.subviews firstObject];
+ // Must invoke synchronously otherwise it will remove the view just added.
+ WXCellComponent *cellComponent = (WXCellComponent *)wxCellView.wx_component;
+ if (cellComponent.isRecycle) {
+ [wxCellView.wx_component _unloadViewWithReusing:YES];
+ }
+ }
+ }
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXCellComponent *cell = [self cellForIndexPath:indexPath];
+ return cell.calculatedFrame.size.height;
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
+{
+ WXHeaderComponent *header = ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).header;
+ if (header) {
+ return header.calculatedFrame.size.height;
+ } else {
+ return 0.0;
+ }
+}
+
+- (void)scrollViewDidScroll:(UIScrollView *)scrollView
+{
+ [super scrollViewDidScroll:scrollView];
+ if ([[_tableView indexPathsForVisibleRows] count] > 0) {
+ NSIndexPath *topCellPath = [[_tableView indexPathsForVisibleRows] objectAtIndex:0];
+ if (self.currentTopVisibleSection != topCellPath.section) {
+ if (self.currentTopVisibleSection) {
+ WXSectionComponent *removeSection = [_sections wx_safeObjectAtIndex:self.currentTopVisibleSection];
+ if (removeSection.header && [removeSection.header.events containsObject:@"unsticky"]) {
+ [removeSection.header fireEvent:@"unsticky" params:nil];
+ }
+ }
+ self.currentTopVisibleSection = topCellPath.section;
+ WXSectionComponent *showSection = [_sections wx_safeObjectAtIndex:topCellPath.section];
+ if (showSection.header && [showSection.header.events containsObject:@"sticky"]) {
+ [showSection.header fireEvent:@"sticky" params:nil];
+ }
+ }
+ }
+}
+
+- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
+{
+ WXHeaderComponent *header = ((WXSectionComponent *)_completedSections[section]).header;
+ WXLogDebug(@"header view for section %ld:%@", (long)section, header.view);
+ return header.view;
+}
+
+#pragma mark - TableView Data Source
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+ return _completedSections.count;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+ return ((WXSectionComponent *)[_completedSections wx_safeObjectAtIndex:section]).rows.count;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXLogDebug(@"Getting cell at indexPath:%@", indexPath);
+ static NSString *reuseIdentifier = @"WXTableViewCell";
+
+ UITableViewCell *cellView = [_tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
+ if (!cellView) {
+ cellView = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
+ cellView.backgroundColor = [UIColor clearColor];
+ }
+
+ WXCellComponent *cell = [self cellForIndexPath:indexPath];
+
+ if (cell.zIndex) {
+ cellView.layer.zPosition = [WXConvert CGFloat:cell.zIndex];
+ }
+
+ if (!cell) {
+ return cellView;
+ }
+
+ if (cell.view.superview == cellView.contentView) {
+ return cellView;
+ }
+
+ for (UIView *view in cellView.contentView.subviews) {
+ [view removeFromSuperview];
+ }
+
+ [cellView.contentView addSubview:cell.view];
+
+ [cellView setAccessibilityIdentifier:cell.view.accessibilityIdentifier];
+
+ WXLogDebug(@"Created cell:%@ view:%@ cellView:%@ at indexPath:%@", cell.ref, cell.view, cellView, indexPath);
+ return cellView;
+}
+
+#pragma mark - Load More Event
+
+- (void)setLoadmoreretry:(NSUInteger)loadmoreretry
+{
+ if (loadmoreretry != self.loadmoreretry) {
+ _previousLoadMoreRowNumber = 0;
+ }
+
+ [super setLoadmoreretry:loadmoreretry];
+}
+
+- (void)loadMore
+{
+ [super loadMore];
+
+ _previousLoadMoreRowNumber = [self totalNumberOfRows];
+}
+
+- (BOOL)isNeedLoadMore
+{
+ BOOL superNeedLoadMore = [super isNeedLoadMore];
+ return superNeedLoadMore && _previousLoadMoreRowNumber != [self totalNumberOfRows];
+}
+
+- (NSUInteger)totalNumberOfRows
+{
+ NSUInteger rowNumber = 0;
+ NSUInteger sectionCount = [_tableView numberOfSections];
+ for (int section = 0; section < sectionCount; section ++) {
+ rowNumber += [_tableView numberOfRowsInSection:section];
+ }
+
+ return rowNumber;
+}
+
+- (void)resetLoadmore{
+ [super resetLoadmore];
+ _previousLoadMoreRowNumber=0;
+}
+
+#pragma mark Private
+
+- (WXCellComponent *)cellForIndexPath:(NSIndexPath *)indexPath
+{
+ WXSectionComponent *section = [_completedSections wx_safeObjectAtIndex:indexPath.section];
+ if (!section) {
+ WXLogError(@"No section found for num:%ld, completed sections:%ld", (long)indexPath.section, (unsigned long)_completedSections.count);
+ return nil;
+ }
+
+ WXCellComponent *cell = [section.rows wx_safeObjectAtIndex:indexPath.row];
+ if (!cell) {
+ WXLogError(@"No cell found for num:%ld, completed rows:%ld", (long)indexPath.row, (unsigned long)section.rows.count);
+ return nil;
+ }
+
+ return cell;
+}
+
+- (void)insertCell:(WXCellComponent *)cell forIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections
+{
+ WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section];
+ if (indexPath.row > [section.rows count] || indexPath.row < 0) {
+ WXLogError(@"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
+ return;
+ }
+ WXAssert(section, @"inserting cell at indexPath:%@ section has not been inserted to list before, sections:%@", indexPath, sections);
+ WXAssert(indexPath.row <= section.rows.count, @"inserting cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
+ [section.rows insertObject:cell atIndex:indexPath.row];
+}
+
+- (void)removeCellForIndexPath:(NSIndexPath *)indexPath withSections:(NSMutableArray *)sections
+{
+ WXSectionComponent *section = [sections wx_safeObjectAtIndex:indexPath.section];
+ if (0 == [section.rows count]) {
+ return;
+ }
+ WXAssert(section, @"Removing cell at indexPath:%@ has not been inserted to cell list before, sections:%@", indexPath, sections);
+ WXAssert(indexPath.row < section.rows.count, @"Removing cell at indexPath:%@ outof range, sections:%@", indexPath, sections);
+ [section.rows removeObjectAtIndex:indexPath.row];
+}
+
+- (NSIndexPath *)indexPathForCell:(WXCellComponent *)cell sections:(NSMutableArray<WXSectionComponent *> *)sections
+{
+ __block NSIndexPath *indexPath;
+ [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull sectionStop) {
+ [section.rows enumerateObjectsUsingBlock:^(WXCellComponent * _Nonnull row, NSUInteger rowIndex, BOOL * _Nonnull stop) {
+ if (row == cell) {
+ indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
+ *stop = YES;
+ *sectionStop = YES;
+ }
+ }];
+ }];
+
+ return indexPath;
+}
+
+- (NSUInteger)indexForHeader:(WXHeaderComponent *)header sections:(NSMutableArray<WXSectionComponent *> *)sections
+{
+ __block NSUInteger index;
+ [sections enumerateObjectsUsingBlock:^(WXSectionComponent * _Nonnull section, NSUInteger sectionIndex, BOOL * _Nonnull stop) {
+ if (section.header == header) {
+ index = sectionIndex;
+ *stop = YES;
+ }
+ }];
+
+ return index;
+}
+
+- (NSIndexPath *)indexPathForSubIndex:(NSUInteger)index
+{
+ NSInteger section = 0;
+ NSInteger row = -1;
+ WXComponent *firstComponent;
+ for (int i = 0; i <= index; i++) {
+ WXComponent* component = [self.subcomponents wx_safeObjectAtIndex:i];
+ if (!component) {
+ continue;
+ }
+ if (([component isKindOfClass:[WXHeaderComponent class]]
+ || [component isKindOfClass:[WXCellComponent class]])
+ && !firstComponent) {
+ firstComponent = component;
+ }
+
+ if (component != firstComponent && [component isKindOfClass:[WXHeaderComponent class]]) {
+ section ++;
+ row = -1;
+ }
+
+ if ([component isKindOfClass:[WXCellComponent class]]) {
+ row ++;
+ }
+ }
+
+ return [NSIndexPath indexPathForRow:row inSection:section];
+}
+
+- (void)_performUpdates:(void(^)(void))updates withKeepScrollPosition:(BOOL)keepScrollPosition adjustmentBlock:(CGFloat(^)(NSIndexPath *topVisibleCell))adjustmentBlock
+{
+ CGFloat adjustment = 0;
+
+ // keep the scroll position when inserting or deleting sections/rows by adjusting the content offset
+ if (keepScrollPosition) {
+ NSIndexPath *top = _tableView.indexPathsForVisibleRows.firstObject;
+ adjustment = adjustmentBlock(top);
+ }
+
+ updates();
+
+ if (keepScrollPosition) {
+ CGPoint afterContentOffset = _tableView.contentOffset;
+ CGPoint newContentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + adjustment);
+ _tableView.contentOffset = newContentOffset;
+ }
+
+ [self handleAppear];
+}
+
+- (void)_insertTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
+{
+ [self _performUpdates:^{
+ [_tableView insertSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation];
+ } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
+ if (section <= top.section) {
+ return [self tableView:_tableView heightForHeaderInSection:section];
+ } else {
+ return 0.0;
+ }
+ }];
+}
+
+- (void)_deleteTableViewSectionAtIndex:(NSUInteger)section keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
+{
+ [self _performUpdates:^{
+ [_tableView deleteSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:animation];
+ } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
+ if (section <= top.section) {
+ return [self tableView:_tableView heightForHeaderInSection:section];
+ } else {
+ return 0.0;
+ }
+ }];
+}
+
+- (void)_insertTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
+{
+ [self _performUpdates:^{
+ if ([_updataType isEqual: @"reload"]) {
+ [_tableView reloadData];
+ } else {
+ [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation];
+ }
+ } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
+ if (([indexPath compare:top] <= 0) || [_updataType isEqual: @"reload"]) {
+ return [self tableView:_tableView heightForRowAtIndexPath:indexPath];
+ } else {
+ return 0.0;
+ }
+ }];
+}
+
+- (void)_deleteTableViewCellAtIndexPath:(NSIndexPath *)indexPath keepScrollPosition:(BOOL)keepScrollPosition animation:(UITableViewRowAnimation)animation
+{
+ if (!indexPath) {
+ return ;
+ }
+ [self _performUpdates:^{
+ [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:animation];
+ } withKeepScrollPosition:keepScrollPosition adjustmentBlock:^CGFloat(NSIndexPath *top) {
+ if ([indexPath compare:top] <= 0) {
+ return [self tableView:_tableView heightForRowAtIndexPath:indexPath];
+ } else {
+ return 0.0;
+ }
+ }];
+}
+
+- (void)fixFlicker
+{
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ // FIXME:(ง •̀_•́)ง┻━┻ Stupid scoll view, always reset content offset to zero by calling _adjustContentOffsetIfNecessary after insert cells.
+ // So if you pull down list while list is rendering, the list will be flickering.
+ // Demo:
+ // Have to hook _adjustContentOffsetIfNecessary here.
+ // Any other more elegant way?
+ NSString *a = @"ntOffsetIfNe";
+ NSString *b = @"adjustConte";
+
+ NSString *originSelector = [NSString stringWithFormat:@"_%@%@cessary", b, a];
+ [[self class] weex_swizzle:[WXTableView class] Method:NSSelectorFromString(originSelector) withMethod:@selector(fixedFlickerSelector)];
+ });
+}
+
+- (void)fixedFlickerSelector
+{
+ // DO NOT delete this method.
+}
+
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m
deleted file mode 100644
index d57fa9a..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.m
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXLoadingComponent.h"
-#import "WXScrollerComponent.h"
-#import "WXLoadingIndicator.h"
-#import "WXComponent_internal.h"
-#import "WXLog.h"
-
-@interface WXLoadingComponent()
-
-@property (nonatomic) BOOL initFinished;
-@property (nonatomic) BOOL loadingEvent;
-@property (nonatomic) BOOL displayState;
-
-@property (nonatomic, weak) WXLoadingIndicator *indicator;
-
-@end
-
-@implementation WXLoadingComponent
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
- self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
- if (self) {
- if (attributes[@"display"]) {
- if ([attributes[@"display"] isEqualToString:@"show"]) {
- _displayState = YES;
- } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
- _displayState = NO;
- } else {
- WXLogError(@"");
- }
- }
- self.cssNode->style.position_type = CSS_POSITION_ABSOLUTE;
- }
- return self;
-}
-
-- (void)viewWillUnload
-{
- _displayState = NO;
- _loadingEvent = NO;
- _initFinished = NO;
-}
-
--(void)updateAttributes:(NSDictionary *)attributes
-{
- if (attributes[@"display"]) {
- if ([attributes[@"display"] isEqualToString:@"show"]) {
- _displayState = YES;
- } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
- _displayState = NO;
- } else {
- WXLogError(@"");
- }
- [self setDisplay];
- }
-}
-
-- (void)viewDidLoad
-{
- _initFinished = YES;
-
- if (!_displayState) {
- [_indicator.view setHidden:YES];
- }
-}
-
-- (void)addEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"loading"]) {
- _loadingEvent = YES;
- }
-}
-
-- (void)removeEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"loading"]) {
- _loadingEvent = NO;
- }
-}
-
-- (void)loading
-{
- if (!_loadingEvent || _displayState)
- return;
-
- [self fireEvent:@"loading" params:nil];
-}
-
-- (void)setDisplay
-{
- id<WXScrollerProtocol> scrollerProtocol = [self ancestorScroller];
- if (scrollerProtocol == nil || !_initFinished)
- return;
- WXComponent *scroller = (WXComponent*)scrollerProtocol;
- CGPoint contentOffset = [scrollerProtocol contentOffset];
- if (_displayState) {
- contentOffset.y = [scrollerProtocol contentSize].height - scroller.calculatedFrame.size.height + self.calculatedFrame.size.height;
- [_indicator start];
- } else {
- contentOffset.y = contentOffset.y - self.calculatedFrame.size.height;
- [_indicator stop];
- }
- [scrollerProtocol setContentOffset:contentOffset animated:YES];
-}
-
-- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- if (subcomponent) {
- [super _insertSubcomponent:subcomponent atIndex:index];
- if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) {
- _indicator = (WXLoadingIndicator*)subcomponent;
- }
- }
-}
-
-- (BOOL)displayState
-{
- return _displayState;
-}
-
-- (void)resizeFrame
-{
- CGRect rect = self.calculatedFrame;
-
- id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
- if (scrollerProtocol) {
- rect.origin.y = [scrollerProtocol contentSize].height;
- }
-
- [self.view setFrame:rect];
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm
new file mode 100644
index 0000000..bb5024f
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXLoadingComponent.mm
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXLoadingComponent.h"
+#import "WXScrollerComponent.h"
+#import "WXLoadingIndicator.h"
+#import "WXComponent_internal.h"
+#import "WXLog.h"
+#import "WXComponent+Layout.h"
+
+@interface WXLoadingComponent()
+
+@property (nonatomic) BOOL initFinished;
+@property (nonatomic) BOOL loadingEvent;
+@property (nonatomic) BOOL displayState;
+
+@property (nonatomic, weak) WXLoadingIndicator *indicator;
+
+@end
+
+@implementation WXLoadingComponent
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+ self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
+ if (self) {
+ if (attributes[@"display"]) {
+ if ([attributes[@"display"] isEqualToString:@"show"]) {
+ _displayState = YES;
+ } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
+ _displayState = NO;
+ } else {
+ WXLogError(@"");
+ }
+ }
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ self.cssNode->style.position_type = CSS_POSITION_ABSOLUTE;
+ }
+//#else
+ else
+ {
+ self.flexCssNode->setStylePositionType(WeexCore::kAbsolute);
+ }
+
+//#endif
+ }
+ return self;
+}
+
+- (void)viewWillUnload
+{
+ _displayState = NO;
+ _loadingEvent = NO;
+ _initFinished = NO;
+}
+
+-(void)updateAttributes:(NSDictionary *)attributes
+{
+ if (attributes[@"display"]) {
+ if ([attributes[@"display"] isEqualToString:@"show"]) {
+ _displayState = YES;
+ } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
+ _displayState = NO;
+ } else {
+ WXLogError(@"");
+ }
+ [self setDisplay];
+ }
+}
+
+- (void)viewDidLoad
+{
+ _initFinished = YES;
+
+ if (!_displayState) {
+ [_indicator.view setHidden:YES];
+ }
+}
+
+- (void)addEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"loading"]) {
+ _loadingEvent = YES;
+ }
+}
+
+- (void)removeEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"loading"]) {
+ _loadingEvent = NO;
+ }
+}
+
+- (void)loading
+{
+ if (!_loadingEvent || _displayState)
+ return;
+
+ [self fireEvent:@"loading" params:nil];
+}
+
+- (void)setDisplay
+{
+ id<WXScrollerProtocol> scrollerProtocol = [self ancestorScroller];
+ if (scrollerProtocol == nil || !_initFinished)
+ return;
+ WXComponent *scroller = (WXComponent*)scrollerProtocol;
+ CGPoint contentOffset = [scrollerProtocol contentOffset];
+ if (_displayState) {
+ contentOffset.y = [scrollerProtocol contentSize].height - scroller.calculatedFrame.size.height + self.calculatedFrame.size.height;
+ [_indicator start];
+ } else {
+ contentOffset.y = contentOffset.y - self.calculatedFrame.size.height;
+ [_indicator stop];
+ }
+ [scrollerProtocol setContentOffset:contentOffset animated:YES];
+}
+
+- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ if (subcomponent) {
+ [super _insertSubcomponent:subcomponent atIndex:index];
+ if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) {
+ _indicator = (WXLoadingIndicator*)subcomponent;
+ }
+ }
+}
+
+- (BOOL)displayState
+{
+ return _displayState;
+}
+
+- (void)resizeFrame
+{
+ CGRect rect = self.calculatedFrame;
+
+ id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
+ if (scrollerProtocol) {
+ rect.origin.y = [scrollerProtocol contentSize].height;
+ }
+
+ [self.view setFrame:rect];
+}
+
+@end
[07/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm
new file mode 100644
index 0000000..bb263c8
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXTextAreaComponent.h"
+#import "WXUtility.h"
+#import "WXComponent+Layout.h"
+#import "WXComponent_internal.h"
+#import "WXComponent+Layout.h"
+
+#define CorrectX 4 //textview fill text 4 pixel from left. so placeholderlabel have 4 pixel too
+#define CorrectY 8 // textview fill text 8 pixel from top
+typedef UITextView WXTextAreaView;
+
+@interface WXTextAreaComponent()
+
+@property (nonatomic, strong) WXTextAreaView *textView;
+@property (nonatomic) NSUInteger rows;
+
+@end
+
+@implementation WXTextAreaComponent {
+ UIEdgeInsets _border;
+ UIEdgeInsets _padding;
+}
+
+-(void)viewDidLoad
+{
+ _padding = UIEdgeInsetsZero;
+ _border = UIEdgeInsetsZero;
+ if (self.placeholderString) {
+ self.placeHolderLabel = [[UILabel alloc] init];
+ self.placeHolderLabel.numberOfLines = 0;
+ [_textView addSubview:self.placeHolderLabel];
+ }
+ // default placeholder hide from voice over
+ self.placeHolderLabel.isAccessibilityElement = NO;
+ _textView.isAccessibilityElement = YES;
+ _textView.delegate = self;
+ [_textView setNeedsDisplay];
+ [_textView setClipsToBounds:YES];
+ [super viewDidLoad];
+}
+
+- (void)viewWillUnload
+{
+ _textView = nil;
+}
+
+- (UIView *)loadView
+{
+ _textView = [[WXTextAreaView alloc] init];
+ return _textView;
+}
+
+#pragma mark measure frame
+- (CGSize (^)(CGSize))measureBlock
+{
+ __weak typeof(self) weakSelf = self;
+ return ^CGSize (CGSize constrainedSize) {
+
+ CGSize computedSize = [[[NSString alloc] init]sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:weakSelf.textView.font.pointSize]}];
+ computedSize.height = _rows? computedSize.height *weakSelf.rows + (CorrectY + CorrectY/2):0;
+//#ifndef USE_FLEX
+
+ if (![WXComponent isUseFlex])
+ {
+ if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) {
+ computedSize.width = MAX(computedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) {
+ computedSize.width = MIN(computedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_HEIGHT])) {
+ computedSize.height = MAX(computedSize.height, weakSelf.cssNode->style.minDimensions[CSS_HEIGHT]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT])) {
+ computedSize.height = MIN(computedSize.height, weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT]);
+ }
+ }
+
+//#else
+
+ else
+ {
+ if (!isnan(weakSelf.flexCssNode->getMinWidth())) {
+ computedSize.width = MAX(computedSize.width, weakSelf.flexCssNode->getMinWidth());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMaxWidth())) {
+ computedSize.width = MIN(computedSize.width, weakSelf.flexCssNode->getMaxWidth());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMinHeight())) {
+ computedSize.height = MAX(computedSize.height, weakSelf.flexCssNode->getMinHeight());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMaxHeight())) {
+ computedSize.height = MIN(computedSize.height, weakSelf.flexCssNode->getMaxHeight());
+ }
+ }
+
+//#endif
+ return (CGSize) {
+ WXCeilPixelValue(computedSize.width),
+ WXCeilPixelValue(computedSize.height)
+ };
+ };
+}
+
+#pragma mark -Overwrite method
+-(NSString *)text
+{
+ return _textView.text;
+}
+
+- (void)setText:(NSString *)text
+{
+ _textView.text = text;
+ if ([text length] >0) {
+ self.placeHolderLabel.text = @"";
+ }
+}
+
+-(void)setTextColor:(UIColor *)color
+{
+ [_textView setTextColor:color];
+}
+
+-(void)setTextAlignment:(NSTextAlignment)textAlignForStyle
+{
+ [_textView setTextAlignment:textAlignForStyle];
+}
+
+-(void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
+{
+ [_textView setUserInteractionEnabled:userInteractionEnabled];
+}
+
+-(void)setEnabled:(BOOL)enabled
+{
+ _textView.editable = enabled;
+ _textView.selectable = enabled;
+}
+
+-(void)setReturnKeyType:(UIReturnKeyType)returnKeyType
+{
+ [_textView setReturnKeyType:returnKeyType];
+}
+
+-(void)setInputAccessoryView:(UIView *)inputAccessoryView
+{
+ [_textView setInputAccessoryView:inputAccessoryView];
+}
+
+-(void)setEditSelectionRange:(NSInteger)selectionStart selectionEnd:(NSInteger)selectionEnd
+{
+ [self.textView becomeFirstResponder];
+ UITextPosition *startPos = [self.textView positionFromPosition:self.textView.beginningOfDocument offset:selectionStart];
+ UITextPosition *endPos = [self.textView positionFromPosition:self.textView.beginningOfDocument offset:selectionEnd];
+ UITextRange *textRange = [self.textView textRangeFromPosition:startPos
+ toPosition:endPos];
+ self.textView.selectedTextRange = textRange;
+}
+
+-(NSDictionary *)getEditSelectionRange
+{
+ NSInteger selectionStart = [self.textView offsetFromPosition:self.textView.beginningOfDocument toPosition:self.textView.selectedTextRange.start];
+ NSInteger selectionEnd = [self.textView offsetFromPosition:self.textView.beginningOfDocument toPosition:self.textView.selectedTextRange.end];
+ NSDictionary *res = @{@"selectionStart":@(selectionStart),@"selectionEnd":@(selectionEnd)};
+ return res;
+}
+
+-(void)setKeyboardType:(UIKeyboardType)keyboardType
+{
+ [_textView setKeyboardType:keyboardType];
+}
+
+-(void)setSecureTextEntry:(BOOL)secureTextEntry
+{
+ [_textView setSecureTextEntry:secureTextEntry];
+}
+
+-(void)setEditPadding:(UIEdgeInsets)padding
+{
+ _padding = padding;
+ [self _updateTextContentInset];
+}
+
+-(void)setEditBorder:(UIEdgeInsets)border
+{
+ _border = border;
+ [self _updateTextContentInset];
+}
+
+-(void)setAttributedPlaceholder:(NSMutableAttributedString *)attributedString font:(UIFont *)font
+{
+ if (self.placeholderColor) {
+ [attributedString addAttribute:NSForegroundColorAttributeName value:self.placeholderColor range:NSMakeRange(0, self.placeholderString.length)];
+ [attributedString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, self.placeholderString.length)];
+ }
+ self.placeHolderLabel.backgroundColor = [UIColor clearColor];
+ CGRect expectedLabelSize = [attributedString boundingRectWithSize:(CGSize){self.view.frame.size.width, CGFLOAT_MAX}
+ options:NSStringDrawingUsesLineFragmentOrigin
+ context:nil];
+
+ self.placeHolderLabel.clipsToBounds = NO;
+ CGRect newFrame = self.placeHolderLabel.frame;
+ newFrame.size.height = ceil(expectedLabelSize.size.height);
+ newFrame.size.width = _textView.frame.size.width- CorrectX*2;
+ newFrame.origin.x = CorrectX + _padding.left + _border.left; // the cursor origin.x
+ self.placeHolderLabel.frame = newFrame;
+ self.placeHolderLabel.attributedText = attributedString;
+}
+
+-(void)setFont:(UIFont *)font
+{
+ [_textView setFont:font];
+}
+
+-(void)setRows:(NSUInteger)rows
+{
+ _rows = rows;
+ [self setNeedsLayout];
+}
+
+#pragma mark -Private Method
+- (void)_updateTextContentInset
+{
+ [_textView setTextContainerInset:UIEdgeInsetsMake(_padding.top + _border.top,
+ _padding.left + _border.left,
+ _padding.bottom + _border.bottom,
+ _border.right + _border.right)];
+
+ //when textview update, placeHolderLabel update too
+ CGRect newFrame = self.placeHolderLabel.frame;
+ newFrame.size.width = self.textView.frame.size.width - (_padding.left + _border.left) -CorrectX*2;
+ newFrame.origin.x = CorrectX + _padding.left + _border.left; // the cursor origin.x
+ newFrame.origin.y = _padding.top + _border.top;
+ self.placeHolderLabel.frame = newFrame;
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m
deleted file mode 100644
index 2a948e2..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m
+++ /dev/null
@@ -1,1076 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXTextComponent.h"
-#import "WXSDKInstance_private.h"
-#import "WXComponent_internal.h"
-#import "WXLayer.h"
-#import "WXUtility.h"
-#import "WXConvert.h"
-#import "WXRuleManager.h"
-#import "WXDefine.h"
-#import "WXView.h"
-#import <pthread/pthread.h>
-#import <CoreText/CoreText.h>
-
-// WXText is a non-public is not permitted
-@interface WXTextView : WXView
-@property (nonatomic, strong) NSTextStorage *textStorage;
-@end
-
-@implementation WXTextView
-
-- (instancetype)initWithFrame:(CGRect)frame
-{
- if ((self = [super initWithFrame:frame])) {
- self.accessibilityTraits |= UIAccessibilityTraitStaticText;
-
- self.opaque = NO;
- self.contentMode = UIViewContentModeRedraw;
- self.textStorage = [NSTextStorage new];
- }
- return self;
-}
-
-+ (Class)layerClass
-{
- return [WXLayer class];
-}
-
-- (void)copy:(id)sender
-{
- [[UIPasteboard generalPasteboard] setString:((WXTextComponent*)self.wx_component).text];
-}
-
-- (void)setTextStorage:(NSTextStorage *)textStorage
-{
- if (_textStorage != textStorage) {
- _textStorage = textStorage;
- [self.wx_component setNeedsDisplay];
- }
-}
-
-- (BOOL)canBecomeFirstResponder
-{
- return YES;
-}
-
-- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
-{
- if (action == @selector(copy:)) {
- return [[self.wx_component valueForKey:@"_enableCopy"] boolValue];
- }
- return [super canPerformAction:action withSender:sender];
-}
-
-- (NSString *)description
-{
- NSString *superDescription = super.description;
- NSRange semicolonRange = [superDescription rangeOfString:@";"];
- NSString * content = _textStorage.string;
- if ([(WXTextComponent*)self.wx_component useCoreText]) {
- content = ((WXTextComponent*)self.wx_component).text;
- }
- NSString *replacement = [NSString stringWithFormat:@"; text: %@; frame:%f,%f,%f,%f", content, self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height];
- return [superDescription stringByReplacingCharactersInRange:semicolonRange withString:replacement];
-}
-
-- (NSString *)accessibilityValue
-{
- if (self.wx_component && self.wx_component->_ariaLabel) {
- return [super accessibilityValue];
- }
- if (![(WXTextComponent*)self.wx_component useCoreText]) {
- return _textStorage.string;
- }
- return ((WXTextComponent*)self.wx_component).text;
-}
-
-- (NSString *)accessibilityLabel
-{
- if (self.wx_component) {
- if (self.wx_component->_ariaLabel) {
- return self.wx_component->_ariaLabel;
- }
- }
- return [super accessibilityLabel];
-}
-
-@end
-
-static BOOL textRenderUsingCoreText = YES;
-
-NSString *const WXTextTruncationToken = @"\u2026";
-CGFloat WXTextDefaultLineThroughWidth = 1.2;
-
-@interface WXTextComponent()
-@property (nonatomic, strong) NSString *useCoreTextAttr;
-@end
-
-@implementation WXTextComponent
-{
- UIEdgeInsets _border;
- UIEdgeInsets _padding;
- NSTextStorage *_textStorage;
- CGFloat _textStorageWidth;
-
- UIColor *_color;
- NSString *_fontFamily;
- CGFloat _fontSize;
- CGFloat _fontWeight;
- WXTextStyle _fontStyle;
- NSUInteger _lines;
- NSTextAlignment _textAlign;
- NSString *_direction;
- WXTextDecoration _textDecoration;
- NSString *_textOverflow;
- CGFloat _lineHeight;
- CGFloat _letterSpacing;
- BOOL _truncationLine; // support trunk tail
-
- NSAttributedString * _ctAttributedString;
- NSString *_wordWrap;
-
- pthread_mutex_t _ctAttributedStringMutex;
- pthread_mutexattr_t _propertMutexAttr;
- BOOL _observerIconfont;
- BOOL _enableCopy;
-}
-
-+ (void)setRenderUsingCoreText:(BOOL)usingCoreText
-{
- textRenderUsingCoreText = usingCoreText;
-}
-
-+ (BOOL)textRenderUsingCoreText
-{
- return textRenderUsingCoreText;
-}
-
-- (instancetype)initWithRef:(NSString *)ref
- type:(NSString *)type
- styles:(NSDictionary *)styles
- attributes:(NSDictionary *)attributes
- events:(NSArray *)events
- weexInstance:(WXSDKInstance *)weexInstance
-{
- self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
- if (self) {
- // just for coretext and textkit render replacement
- pthread_mutexattr_init(&(_propertMutexAttr));
- pthread_mutexattr_settype(&(_propertMutexAttr), PTHREAD_MUTEX_RECURSIVE);
- pthread_mutex_init(&(_ctAttributedStringMutex), &(_propertMutexAttr));
-
- if ([attributes objectForKey:@"coretext"]) {
- _useCoreTextAttr = [WXConvert NSString:attributes[@"coretext"]];
- } else {
- _useCoreTextAttr = nil;
- }
-
- [self fillCSSStyles:styles];
- [self fillAttributes:attributes];
- }
-
- return self;
-}
-
-- (BOOL)useCoreText
-{
- if ([_useCoreTextAttr isEqualToString:@"true"]) {
- return YES;
- }
- if ([_useCoreTextAttr isEqualToString:@"false"]) {
- return NO;
- }
-
- if ([WXTextComponent textRenderUsingCoreText]) {
- return YES;
- }
- return NO;
-}
-
-- (void)dealloc
-{
- if (_fontFamily && _observerIconfont) {
- [[NSNotificationCenter defaultCenter] removeObserver:self name:WX_ICONFONT_DOWNLOAD_NOTIFICATION object:nil];
- }
- pthread_mutex_destroy(&_ctAttributedStringMutex);
- pthread_mutexattr_destroy(&_propertMutexAttr);
-}
-
-#define WX_STYLE_FILL_TEXT(key, prop, type, needLayout)\
-do {\
- id value = styles[@#key];\
- if (value) {\
- _##prop = [WXConvert type:value];\
- [self setNeedsRepaint];\
- if (needLayout) {\
- [self setNeedsLayout];\
- }\
- }\
-} while(0);
-
-#define WX_STYLE_FILL_TEXT_WITH_DEFAULT_VALUE(key, prop, type, defaultValue,needLayout)\
-do {\
- id value = styles[@#key];\
- if (value) {\
- if([WXUtility isBlankString:value]){\
- _##prop = defaultValue;\
- }else {\
- _##prop = [WXConvert type:value];\
- }\
- [self setNeedsRepaint];\
- if (needLayout) {\
- [self setNeedsLayout];\
- }\
- }\
-} while(0);
-
-
-#define WX_STYLE_FILL_TEXT_PIXEL(key, prop, needLayout)\
-do {\
- id value = styles[@#key];\
- if (value) {\
- _##prop = [WXConvert WXPixelType:value scaleFactor:self.weexInstance.pixelScaleFactor];\
- [self setNeedsRepaint];\
- if (needLayout) {\
- [self setNeedsLayout];\
- }\
-}\
-} while(0);
-
-- (void)fillCSSStyles:(NSDictionary *)styles
-{
- WX_STYLE_FILL_TEXT_WITH_DEFAULT_VALUE(color, color, UIColor, [UIColor blackColor], NO)
- WX_STYLE_FILL_TEXT(fontFamily, fontFamily, NSString, YES)
- WX_STYLE_FILL_TEXT_PIXEL(fontSize, fontSize, YES)
- WX_STYLE_FILL_TEXT(fontWeight, fontWeight, WXTextWeight, YES)
- WX_STYLE_FILL_TEXT(fontStyle, fontStyle, WXTextStyle, YES)
- WX_STYLE_FILL_TEXT(lines, lines, NSUInteger, YES)
- WX_STYLE_FILL_TEXT(textAlign, textAlign, NSTextAlignment, NO)
- WX_STYLE_FILL_TEXT(textDecoration, textDecoration, WXTextDecoration, YES)
- WX_STYLE_FILL_TEXT(textOverflow, textOverflow, NSString, NO)
- WX_STYLE_FILL_TEXT_PIXEL(lineHeight, lineHeight, YES)
- WX_STYLE_FILL_TEXT_PIXEL(letterSpacing, letterSpacing, YES)
- WX_STYLE_FILL_TEXT(wordWrap, wordWrap, NSString, YES);
- WX_STYLE_FILL_TEXT(direction, direction, NSString, YES)
- if (_fontFamily && !_observerIconfont) {
- // notification received when custom icon font file download finish
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(repaintText:) name:WX_ICONFONT_DOWNLOAD_NOTIFICATION object:nil];
- _observerIconfont = YES;
- }
- UIEdgeInsets padding = {
- WXFloorPixelValue(self.cssNode->style.padding[CSS_TOP] + self.cssNode->style.border[CSS_TOP]),
- WXFloorPixelValue(self.cssNode->style.padding[CSS_LEFT] + self.cssNode->style.border[CSS_LEFT]),
- WXFloorPixelValue(self.cssNode->style.padding[CSS_BOTTOM] + self.cssNode->style.border[CSS_BOTTOM]),
- WXFloorPixelValue(self.cssNode->style.padding[CSS_RIGHT] + self.cssNode->style.border[CSS_RIGHT])
- };
-
- if (!UIEdgeInsetsEqualToEdgeInsets(padding, _padding)) {
- _padding = padding;
- [self setNeedsRepaint];
- }
-}
-
-- (void)fillAttributes:(NSDictionary *)attributes
-{
- id text = [WXConvert NSString:attributes[@"value"]];
- if (text && ![self.text isEqualToString:text]) {
- self.text = text;
- [self setNeedsRepaint];
- [self setNeedsLayout];
- }
- if (attributes[@"enableCopy"]) {
- _enableCopy = [WXConvert BOOL:attributes[@"enableCopy"]];
- }
-}
-
-- (void)setNeedsRepaint
-{
- _textStorage = nil;
-
- pthread_mutex_lock(&(_ctAttributedStringMutex));
- _ctAttributedString = nil;
- pthread_mutex_unlock(&(_ctAttributedStringMutex));
-
-}
-
-#pragma mark - Subclass
-
-- (void)setNeedsLayout
-{
- [super setNeedsLayout];
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
- BOOL useCoreText = NO;
- if ([self.view.wx_component isKindOfClass:NSClassFromString(@"WXTextComponent")] && [self.view.wx_component respondsToSelector:@selector(useCoreText)]) {
- useCoreText = [(WXTextComponent*)self.view.wx_component useCoreText];
- }
- if (!useCoreText) {
- ((WXTextView *)self.view).textStorage = _textStorage;
- }
- if (_enableCopy) {
- UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(displayMenuController:)];
- [self.view addGestureRecognizer:longPress];
- }
- self.view.isAccessibilityElement = YES;
-
- [self setNeedsDisplay];
-}
-
-- (void)displayMenuController:(id)sender
-{
- if ([self.view becomeFirstResponder] && ((UILongPressGestureRecognizer*)sender).state == UIGestureRecognizerStateBegan) {
- UIMenuController *theMenu = [UIMenuController sharedMenuController];
- CGSize size = [self ctAttributedString].size;
- CGRect selectionRect = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, size.width, size.height);
- [theMenu setTargetRect:selectionRect inView:self.view.superview];
- [theMenu setMenuVisible:YES animated:YES];
- }
-}
-
-- (UIView *)loadView
-{
- return [[WXTextView alloc] init];
-}
-
-- (BOOL)needsDrawRect
-{
- return YES;
-}
-
-- (UIImage *)drawRect:(CGRect)rect;
-{
- CGContextRef context = UIGraphicsGetCurrentContext();
- if (_isCompositingChild) {
- [self drawTextWithContext:context bounds:rect padding:_padding view:nil];
- } else {
- WXTextView *textView = (WXTextView *)_view;
- [self drawTextWithContext:context bounds:rect padding:_padding view:textView];
- }
-
- return nil;
-}
-
-- (CGSize (^)(CGSize))measureBlock
-{
- __weak typeof(self) weakSelf = self;
- return ^CGSize (CGSize constrainedSize) {
- CGSize computedSize = CGSizeZero;
- NSTextStorage *textStorage = nil;
-
- //TODO:more elegant way to use max and min constrained size
- if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) {
- constrainedSize.width = MAX(constrainedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]);
- }
-
- if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) {
- constrainedSize.width = MIN(constrainedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]);
- }
-
- if (![self useCoreText]) {
- textStorage = [weakSelf textStorageWithWidth:constrainedSize.width];
- NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
- NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
- computedSize = [layoutManager usedRectForTextContainer:textContainer].size;
- } else {
- computedSize = [weakSelf calculateTextHeightWithWidth:constrainedSize.width];
- }
-
- if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_HEIGHT])) {
- computedSize.height = MAX(computedSize.height, weakSelf.cssNode->style.minDimensions[CSS_HEIGHT]);
- }
-
- if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT])) {
- computedSize.height = MIN(computedSize.height, weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT]);
- }
- if (textStorage && [WXUtility isBlankString:textStorage.string]) {
- // if the text value is empty or nil, then set the height is 0.
- computedSize.height = 0;
- }
-
- return (CGSize) {
- WXCeilPixelValue(computedSize.width),
- WXCeilPixelValue(computedSize.height)
- };
- };
-}
-
-#pragma mark Text Building
-
-- (NSAttributedString *)ctAttributedString
-{
- if (!self.text) {
- return nil;
- }
- NSAttributedString * attributedString = nil;
- pthread_mutex_lock(&(_ctAttributedStringMutex));
- if (!_ctAttributedString) {
- _ctAttributedString = [self buildCTAttributeString];
- WXPerformBlockOnComponentThread(^{
- [self.weexInstance.componentManager startComponentTasks];
- });
- }
- attributedString = [_ctAttributedString copy];
- pthread_mutex_unlock(&(_ctAttributedStringMutex));
- return attributedString;
-}
-
-- (void)repaintText:(NSNotification *)notification
-{
- if (![_fontFamily isEqualToString:notification.userInfo[@"fontFamily"]]) {
- return;
- }
- [self setNeedsRepaint];
- WXPerformBlockOnComponentThread(^{
- [self.weexInstance.componentManager startComponentTasks];
- WXPerformBlockOnMainThread(^{
- [self setNeedsLayout];
- [self setNeedsDisplay];
- });
- });
-}
-
-- (NSMutableAttributedString *)buildCTAttributeString
-{
- NSString * string = self.text;
- if (![string isKindOfClass:[NSString class]]) {
- WXLogError(@"text %@ is invalid", self.text);
- string = @"";
- }
- NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: string];
- if (_color) {
- [attributedString addAttribute:NSForegroundColorAttributeName value:_color range:NSMakeRange(0, string.length)];
- }
-
- // set font
- UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor useCoreText:[self useCoreText]];
- CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)font.fontName,
- font.pointSize,
- NULL);
- if (ctFont) {
- [attributedString addAttribute:(id)kCTFontAttributeName value:(__bridge id)(ctFont) range:NSMakeRange(0, string.length)];
- CFRelease(ctFont);
- }
-
- if(_textDecoration == WXTextDecorationUnderline){
- [attributedString addAttribute:(id)kCTUnderlineStyleAttributeName value:@(kCTUnderlinePatternSolid | kCTUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
- } else if(_textDecoration == WXTextDecorationLineThrough){
- [attributedString addAttribute:NSStrikethroughStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
- }
-
- NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
-
- // handle text direction style, default ltr
- if (_cssNode->layout.direction == CSS_DIRECTION_RTL) {
- if (0 == _textAlign) {
- //force text right-align if don't specified any align.
- _textAlign = NSTextAlignmentRight;
- }
- paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft;
- } else {
- //if you specify NSWritingDirectionNaturalDirection, the receiver resolves the writing
- //directionto eitherNSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft,
- //depending on the direction for the user’s language preference setting.
- paragraphStyle.baseWritingDirection = NSWritingDirectionNatural;
- }
-
- if (_textAlign) {
- paragraphStyle.alignment = _textAlign;
- }
-
- if ([[_wordWrap lowercaseString] isEqualToString:@"break-word"]) {
- paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
- } else if ([[_wordWrap lowercaseString] isEqualToString:@"normal"]){
- paragraphStyle.lineBreakMode = NSLineBreakByClipping;
- } else {
- // set default lineBreakMode
- paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
- }
- _truncationLine = NO;
- if (_textOverflow && [_textOverflow length] > 0) {
- if (_lines && [_textOverflow isEqualToString:@"ellipsis"])
- _truncationLine = YES;
- }
-
- if (_lineHeight) {
- paragraphStyle.maximumLineHeight = _lineHeight;
- paragraphStyle.minimumLineHeight = _lineHeight;
- }
- if (_lineHeight || _textAlign || [_textOverflow length] > 0) {
- [attributedString addAttribute:NSParagraphStyleAttributeName
- value:paragraphStyle
- range:(NSRange){0, attributedString.length}];
- }
-
- if (_letterSpacing) {
- [attributedString addAttribute:NSKernAttributeName value:@(_letterSpacing) range:(NSRange){0, attributedString.length}];
- }
-
- if ([self adjustLineHeight]) {
- if (_lineHeight > font.lineHeight) {
- [attributedString addAttribute:NSBaselineOffsetAttributeName
- value:@((_lineHeight - font.lineHeight)/ 2)
- range:(NSRange){0, attributedString.length}];
- }
- }
-
- return attributedString;
-}
-
-- (NSAttributedString *)buildAttributeString
-{
- NSString *string = self.text ?: @"";
-
- NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
-
- // set textColor
- if(_color) {
- [attributedString addAttribute:NSForegroundColorAttributeName value:_color range:NSMakeRange(0, string.length)];
- }
-
- // set font
- UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor];
- if (font) {
- [attributedString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, string.length)];
- }
-
- if(_textDecoration == WXTextDecorationUnderline){
- [attributedString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
- } else if(_textDecoration == WXTextDecorationLineThrough){
- [attributedString addAttribute:NSStrikethroughStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
- }
-
- NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
-
- // handle text direction style, default ltr
- if (_cssNode->layout.direction == CSS_DIRECTION_RTL) {
- if (0 == _textAlign) {
- //force text right-align if don't specified any align.
- _textAlign = NSTextAlignmentRight;
- }
- paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft;
- } else {
- //if you specify NSWritingDirectionNaturalDirection, the receiver resolves the writing
- //directionto eitherNSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft,
- //depending on the direction for the user’s language preference setting.
- paragraphStyle.baseWritingDirection = NSWritingDirectionNatural;
- }
-
- if (_textAlign) {
- paragraphStyle.alignment = _textAlign;
- }
-
- if (_lineHeight) {
- paragraphStyle.maximumLineHeight = _lineHeight;
- paragraphStyle.minimumLineHeight = _lineHeight;
- }
-
- if (_lineHeight || _textAlign) {
- [attributedString addAttribute:NSParagraphStyleAttributeName
- value:paragraphStyle
- range:(NSRange){0, attributedString.length}];
- }
- if ([self adjustLineHeight]) {
- if (_lineHeight > font.lineHeight) {
- [attributedString addAttribute:NSBaselineOffsetAttributeName
- value:@((_lineHeight - font.lineHeight)/ 2)
- range:(NSRange){0, attributedString.length}];
- }
- }
-
- return attributedString;
-}
-
-- (BOOL)adjustLineHeight
-{
- if (WX_SYS_VERSION_LESS_THAN(@"10.0")) {
- return true;
- }
- return ![self useCoreText];
-}
-
-- (NSTextStorage *)textStorageWithWidth:(CGFloat)width
-{
- if (_textStorage && width == _textStorageWidth) {
- return _textStorage;
- }
-
- NSLayoutManager *layoutManager = [NSLayoutManager new];
-
- // build AttributeString
- NSAttributedString *attributedString = [self buildAttributeString];
-
- NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
- [textStorage addLayoutManager:layoutManager];
-
- NSTextContainer *textContainer = [NSTextContainer new];
- textContainer.lineFragmentPadding = 0.0;
-
- if ([[_wordWrap lowercaseString] isEqualToString:@"break-word"]) {
- textContainer.lineBreakMode = NSLineBreakByWordWrapping;
- } else if ([[_wordWrap lowercaseString] isEqualToString:@"normal"]){
- textContainer.lineBreakMode = NSLineBreakByClipping;
- } else {
- // set default lineBreakMode
- textContainer.lineBreakMode = NSLineBreakByCharWrapping;
- }
-
- if (_textOverflow && [_textOverflow length] > 0) {
- if ([_textOverflow isEqualToString:@"ellipsis"])
- textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
- }
- textContainer.maximumNumberOfLines = _lines > 0 ? _lines : 0;
- textContainer.size = (CGSize){isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX};
-
- [layoutManager addTextContainer:textContainer];
- [layoutManager ensureLayoutForTextContainer:textContainer];
-
- _textStorageWidth = width;
- _textStorage = textStorage;
-
- return textStorage;
-}
-
-- (void)syncTextStorageForView
-{
- CGFloat width = self.calculatedFrame.size.width - (_padding.left + _padding.right);
- NSTextStorage *textStorage = nil;
- if (![self useCoreText]) {
- textStorage = [self textStorageWithWidth:width];
- }
- [self.weexInstance.componentManager _addUITask:^{
- if ([self isViewLoaded]) {
- if (![self useCoreText]) {
- ((WXTextView *)self.view).textStorage = textStorage;
- }
- [self readyToRender]; // notify super component
- [self setNeedsDisplay];
- }
- }];
-}
-
-- (void)_frameDidCalculated:(BOOL)isChanged
-{
- [super _frameDidCalculated:isChanged];
- [self syncTextStorageForView];
-}
-
-- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles
-{
- [super _updateStylesOnComponentThread:styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:isUpdateStyles];
- NSMutableDictionary * newStyles = [styles mutableCopy];
- for (NSString * key in [resetStyles copy]) {
- [newStyles setObject:@"" forKey:key];
- }
- [self fillCSSStyles:newStyles];
-
- [self syncTextStorageForView];
-}
-
-- (void)_updateAttributesOnComponentThread:(NSDictionary *)attributes
-{
- [super _updateAttributesOnComponentThread:attributes];
-
- [self fillAttributes:attributes];
-
- [self syncTextStorageForView];
-}
-
-- (void)drawTextWithContext:(CGContextRef)context bounds:(CGRect)bounds padding:(UIEdgeInsets)padding view:(WXTextView *)view
-{
- if (bounds.size.width <= 0 || bounds.size.height <= 0) {
- return;
- }
-
- if ([self _needsDrawBorder]) {
- [self _drawBorderWithContext:context size:bounds.size];
- } else {
- WXPerformBlockOnMainThread(^{
- [self _resetNativeBorderRadius];
- });
- }
- if (![self useCoreText]) {
- NSLayoutManager *layoutManager = _textStorage.layoutManagers.firstObject;
- NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
-
- CGRect textFrame = UIEdgeInsetsInsetRect(bounds, padding);
- NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
-
- [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin];
- [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin];
- } else {
- CGRect textFrame = UIEdgeInsetsInsetRect(bounds, padding);
- // sufficient height for text to draw, or frame lines will be empty
- textFrame.size.height = bounds.size.height * 2;
- CGContextSaveGState(context);
- //flip the coordinate system
- CGContextSetTextMatrix(context, CGAffineTransformIdentity);
- CGContextTranslateCTM(context, 0, textFrame.size.height);
- CGContextScaleCTM(context, 1.0, -1.0);
-
- NSAttributedString * attributedStringCopy = [self ctAttributedString];
- //add path
- CGPathRef cgPath = NULL;
- cgPath = CGPathCreateWithRect(textFrame, NULL);
- CTFrameRef _coreTextFrameRef = NULL;
- if (_coreTextFrameRef) {
- CFRelease(_coreTextFrameRef);
- _coreTextFrameRef = NULL;
- }
- if(!attributedStringCopy) {
- return;
- }
- CTFramesetterRef ctframesetterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attributedStringCopy));
- _coreTextFrameRef = CTFramesetterCreateFrame(ctframesetterRef, CFRangeMake(0, attributedStringCopy.length), cgPath, NULL);
- CFArrayRef ctLines = NULL;
- if (NULL == _coreTextFrameRef) {
- // try to protect crash from frame is NULL
- return;
- }
- CFRelease(ctframesetterRef);
- ctframesetterRef = NULL;
- ctLines = CTFrameGetLines(_coreTextFrameRef);
- CFIndex lineCount = CFArrayGetCount(ctLines);
- NSMutableArray * mutableLines = [NSMutableArray new];
- CGPoint lineOrigins[lineCount];
- NSUInteger rowCount = 0;
- BOOL needTruncation = NO;
- CTLineRef ctTruncatedLine = NULL;
- CTFrameGetLineOrigins(_coreTextFrameRef, CFRangeMake(0, 0), lineOrigins);
- for (CFIndex lineIndex = 0;(!_lines || _lines > lineIndex) && lineIndex < lineCount; lineIndex ++) {
- CTLineRef lineRef = NULL;
- lineRef = CFArrayGetValueAtIndex(ctLines, lineIndex);
- if (!lineRef) {
- break;
- }
- CGPoint lineOrigin = lineOrigins[lineIndex];
- lineOrigin.x += padding.left;
- lineOrigin.y -= padding.top;
- CFArrayRef runs = CTLineGetGlyphRuns(lineRef);
- [mutableLines addObject:(__bridge id _Nonnull)(lineRef)];
- // lineIndex base 0
- rowCount = lineIndex + 1;
- if (_lines > 0 && _truncationLine) {
- if (_truncationLine && rowCount > _lines) {
- needTruncation = YES;
- do {
- NSUInteger lastRow = [mutableLines count];
- if (lastRow < rowCount) {
- break;
- }
- [mutableLines removeLastObject];
- } while (1);
-
- }
- }
- if (_lines > 0 && _truncationLine) {
- if (rowCount >= _lines &&!needTruncation && (CTLineGetStringRange(lineRef).length + CTLineGetStringRange(lineRef).location) < attributedStringCopy.length) {
- needTruncation = YES;
- }
- }
-
- if (needTruncation) {
- CGContextSetTextPosition(context, lineOrigin.x, lineOrigin.y);
- ctTruncatedLine = [self buildTruncatedLineWithRuns:runs lines:mutableLines path:cgPath];
- if (ctTruncatedLine) {
- CFArrayRef truncatedRuns = CTLineGetGlyphRuns(ctTruncatedLine);
- [self drawTextWithRuns:truncatedRuns context:context lineOrigin:lineOrigin];
- CFRelease(ctTruncatedLine);
- ctTruncatedLine = NULL;
- continue;
- }
- }else {
- [self drawTextWithRuns:runs context:context lineOrigin:lineOrigin];
- }
- }
-
- [mutableLines removeAllObjects];
- CGPathRelease(cgPath);
- CFRelease(_coreTextFrameRef);
- _coreTextFrameRef = NULL;
- cgPath = NULL;
- CGContextRestoreGState(context);
- }
-}
-
-- (void)drawTextWithRuns:(CFArrayRef)runs context:(CGContextRef)context lineOrigin:(CGPoint)lineOrigin
-{
- for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runs); runIndex ++) {
- CTRunRef run = NULL;
- run = CFArrayGetValueAtIndex(runs, runIndex);
- CFDictionaryRef attr = NULL;
- attr = CTRunGetAttributes(run);
- if (0 == runIndex) {
- NSNumber *baselineOffset = (NSNumber*)CFDictionaryGetValue(attr, NSBaselineOffsetAttributeName);
- if (baselineOffset) {
- lineOrigin.y += [baselineOffset doubleValue];
- }
- }
- CGContextSetTextPosition(context, lineOrigin.x, lineOrigin.y);
- CTRunDraw(run, context, CFRangeMake(0, 0));
- CFIndex glyphCount = CTRunGetGlyphCount(run);
- if (glyphCount <= 0) continue;
-
- NSUnderlineStyle strikethrough = (NSUnderlineStyle)CFDictionaryGetValue(attr, NSStrikethroughStyleAttributeName);
-
- if (strikethrough) {
- // draw strikethrough
- [self drawLineThroughWithRun:runs context:context index:runIndex origin:lineOrigin];
- }
- }
-}
-
-- (CTLineRef)buildTruncatedLineWithRuns:(CFArrayRef)runs lines:(NSMutableArray*)mutableLines path:(CGPathRef)cgPath
-{
- NSAttributedString * truncationToken = nil;
- CTLineRef ctTruncatedLine = NULL;
- CTLineRef lastLine = (__bridge CTLineRef)(mutableLines.lastObject);
-
- CFArrayRef lastLineRuns = CTLineGetGlyphRuns(lastLine);
- NSUInteger lastLineRunCount = CFArrayGetCount(lastLineRuns);
-
- CTLineRef truncationTokenLine = NULL;
- NSMutableDictionary *attrs = nil;
- if (lastLineRunCount > 0) {
- CTRunRef run = CFArrayGetValueAtIndex(runs, lastLineRunCount - 1);
- attrs = (id)CTRunGetAttributes(run);
- attrs = attrs ? attrs.mutableCopy : [NSMutableDictionary new];
- CTFontRef font = (__bridge CTFontRef)(attrs[(id)kCTFontAttributeName]);
- CGFloat fontSize = font ? CTFontGetSize(font):32 * self.weexInstance.pixelScaleFactor;
- UIFont * uiFont = [UIFont systemFontOfSize:fontSize];
- if (uiFont) {
- font = CTFontCreateWithName((__bridge CFStringRef)uiFont.fontName, uiFont.pointSize, NULL);
- }
- if (font) {
- attrs[(id)kCTFontAttributeName] = (__bridge id)(font);
- uiFont = nil;
- CFRelease(font);
- }
- CGColorRef color = (__bridge CGColorRef)(attrs[(id)kCTForegroundColorAttributeName]);
- if (color && CFGetTypeID(color) == CGColorGetTypeID() && CGColorGetAlpha(color) == 0) {
- [attrs removeObjectForKey:(id)kCTForegroundColorAttributeName];
- }
-
- attrs = attrs?:[NSMutableDictionary new];
- truncationToken = [[NSAttributedString alloc] initWithString:WXTextTruncationToken attributes:attrs];
- truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)truncationToken);
- }
-
- if (truncationTokenLine) {
- // default truncationType is kCTLineTruncationEnd
- CTLineTruncationType truncationType = kCTLineTruncationEnd;
- NSAttributedString *attributedString = [self ctAttributedString];
- NSAttributedString * lastLineText = nil;
- NSRange lastLineTextRange = WXNSRangeFromCFRange(CTLineGetStringRange(lastLine));
- NSRange attributeStringRange = NSMakeRange(0, attributedString.string.length);
- NSRange interSectionRange = NSIntersectionRange(lastLineTextRange, attributeStringRange);
- if (!NSEqualRanges(interSectionRange, lastLineTextRange)) {
- // out of bounds
- lastLineTextRange = interSectionRange;
- }
- lastLineText = [attributedString attributedSubstringFromRange: lastLineTextRange];
- if (!lastLineText) {
- lastLineText = attributedString;
- }
- NSMutableAttributedString *mutableLastLineText = lastLineText.mutableCopy;
- [mutableLastLineText appendAttributedString:truncationToken];
- CTLineRef ctLastLineExtend = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)[mutableLastLineText copy]);
- if (ctLastLineExtend) {
- CGRect cgPathRect = CGRectZero;
- CGFloat truncatedWidth = 0;
- if (CGPathIsRect(cgPath, &cgPathRect)) {
- truncatedWidth = cgPathRect.size.width;
- }
- ctTruncatedLine = CTLineCreateTruncatedLine(ctLastLineExtend, truncatedWidth, truncationType, truncationTokenLine);
- CFRelease(ctLastLineExtend);
- ctLastLineExtend = NULL;
- CFRelease(truncationTokenLine);
- truncationTokenLine = NULL;
- }
- }
-
- return ctTruncatedLine;
-}
-
-- (void)drawLineThroughWithRun:(CFArrayRef)runs context:(CGContextRef)context index:(CFIndex)runIndex origin:(CGPoint)lineOrigin
-{
- CFRetain(runs);
- CGContextRetain(context);
-
- CGContextSaveGState(context);
- CGFloat xHeight = 0, underLinePosition = 0, lineThickness = 0;
- CTRunRef run = CFArrayGetValueAtIndex(runs, runIndex);
- WXTextGetRunsMaxMetric(runs, &xHeight, &underLinePosition, &lineThickness);
-
- CGPoint strikethroughStart;
- strikethroughStart.x = lineOrigin.x - underLinePosition;
- strikethroughStart.y = lineOrigin.y + xHeight/2;
- CGPoint runPosition = CGPointZero;
- CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition);
- strikethroughStart.x = lineOrigin.x + runPosition.x;
- CGContextSetLineWidth(context, WXTextDefaultLineThroughWidth);
- double length = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL);
- CGContextMoveToPoint(context, strikethroughStart.x, strikethroughStart.y);
- CGContextAddLineToPoint(context, strikethroughStart.x + length, strikethroughStart.y);
- CGContextStrokePath(context);
-
- CGContextRestoreGState(context);
- CFRelease(runs);
- CGContextRelease(context);
-}
-
-- (CGSize)calculateTextHeightWithWidth:(CGFloat)aWidth
-{
- CGFloat totalHeight = 0;
- CGSize suggestSize = CGSizeZero;
- NSAttributedString * attributedStringCpy = [self ctAttributedString];
- if (!attributedStringCpy) {
- return CGSizeZero;
- }
- if (isnan(aWidth)) {
- aWidth = CGFLOAT_MAX;
- }
- aWidth = [attributedStringCpy boundingRectWithSize:CGSizeMake(aWidth, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil].size.width;
- CTFramesetterRef ctframesetterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attributedStringCpy));
- suggestSize = CTFramesetterSuggestFrameSizeWithConstraints(ctframesetterRef, CFRangeMake(0, 0), NULL, CGSizeMake(aWidth, MAXFLOAT), NULL);
-
- CGMutablePathRef path = NULL;
- path = CGPathCreateMutable();
- // sufficient height to draw text
- CGPathAddRect(path, NULL, CGRectMake(0, 0, aWidth, suggestSize.height * 10));
-
- CTFrameRef frameRef = NULL;
- frameRef = CTFramesetterCreateFrame(ctframesetterRef, CFRangeMake(0, attributedStringCpy.length), path, NULL);
- CGPathRelease(path);
-
- CFArrayRef lines = NULL;
- if (NULL == frameRef) {
- //try to protect unexpected crash.
- return suggestSize;
- }
- CFRelease(ctframesetterRef);
- ctframesetterRef = NULL;
- lines = CTFrameGetLines(frameRef);
- CFIndex lineCount = CFArrayGetCount(lines);
- CGFloat ascent = 0;
- CGFloat descent = 0;
- CGFloat leading = 0;
-
- // height = ascent + descent + lineCount*leading
- // ignore linespaing
- NSUInteger actualLineCount = 0;
- for (CFIndex lineIndex = 0; (!_lines|| lineIndex < _lines) && lineIndex < lineCount; lineIndex ++)
- {
- CTLineRef lineRef = NULL;
- lineRef = CFArrayGetValueAtIndex(lines, lineIndex);
- CTLineGetTypographicBounds(lineRef, &ascent, &descent, &leading);
- totalHeight += ascent + descent;
- actualLineCount ++;
- }
-
- totalHeight = totalHeight + actualLineCount * leading;
- CFRelease(frameRef);
- frameRef = NULL;
-
- if (WX_SYS_VERSION_LESS_THAN(@"10.0")) {
- // there is something wrong with coreText drawing text height, trying to fix this with more efficent way.
- if(actualLineCount && actualLineCount < lineCount) {
- suggestSize.height = suggestSize.height * actualLineCount / lineCount;
- }
- return CGSizeMake(aWidth, suggestSize.height);
- }
- return CGSizeMake(aWidth, totalHeight);
-}
-
-static void WXTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat *underlinePosition, CGFloat *lineThickness)
-{
- CFRetain(runs);
- CGFloat maxXHeight = 0;
- CGFloat maxUnderlinePos = 0;
- CGFloat maxLineThickness = 0;
- for (NSUInteger index = 0, runsCount = CFArrayGetCount(runs); index < runsCount; index ++) {
- CTRunRef run = CFArrayGetValueAtIndex(runs, index);
- CFDictionaryRef attrs = CTRunGetAttributes(run);
- if (attrs) {
- CTFontRef font = CFDictionaryGetValue(attrs, kCTFontAttributeName);
- if (font) {
- CGFloat xHeight = CTFontGetXHeight(font);
- if (xHeight > maxXHeight) {
- maxXHeight = xHeight;
- }
-
- CGFloat underlinePos = CTFontGetUnderlinePosition(font);
- if (underlinePos < maxUnderlinePos) {
- maxUnderlinePos = underlinePos;
- }
-
- CGFloat lineThickness = CTFontGetUnderlineThickness(font);
- if (lineThickness > maxLineThickness) {
- maxLineThickness = lineThickness;
- }
- }
- }
- }
-
- if (xHeight) {
- *xHeight = maxXHeight;
- }
-
- if (underlinePosition) {
- *underlinePosition = maxUnderlinePos;
- }
-
- if (lineThickness) {
- *lineThickness = maxLineThickness;
- }
-
- CFRelease(runs);
-}
-
-NS_INLINE NSRange WXNSRangeFromCFRange(CFRange range) {
- return NSMakeRange(range.location, range.length);
-}
-
-#ifdef UITEST
-- (NSString *)description
-{
- return super.description;
-}
-#endif
-
-- (void)_resetCSSNodeStyles:(NSArray *)styles
-{
- [super _resetCSSNodeStyles:styles];
- if ([styles containsObject:@"color"]) {
- _color = [UIColor blackColor];
- [self setNeedsRepaint];
- }
- if ([styles containsObject:@"fontSize"]) {
- _fontSize = WX_TEXT_FONT_SIZE;
- [self setNeedsRepaint];
- [self setNeedsLayout];
- }
-}
-
-@end
-
[10/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.m
deleted file mode 100644
index 585c9f3..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.m
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXRefreshComponent.h"
-#import "WXScrollerComponent.h"
-#import "WXLoadingIndicator.h"
-#import "WXComponent_internal.h"
-#import "WXLog.h"
-
-@interface WXRefreshComponent()
-
-@property (nonatomic) BOOL displayState;
-@property (nonatomic) BOOL initFinished;
-@property (nonatomic) BOOL refreshEvent;
-@property (nonatomic) BOOL pullingdownEvent;
-
-@property (nonatomic, weak) WXLoadingIndicator *indicator;
-
-@end
-
-@implementation WXRefreshComponent
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
- self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
- if (self) {
- _refreshEvent = NO;
- _pullingdownEvent = NO;
- if (attributes[@"display"]) {
- if ([attributes[@"display"] isEqualToString:@"show"]) {
- _displayState = YES;
- } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
- _displayState = NO;
- } else {
- WXLogError(@"");
- }
- }
- self.cssNode->style.position_type = CSS_POSITION_ABSOLUTE;
- }
- return self;
-}
-
-- (void)viewDidLoad
-{
- _initFinished = YES;
-
- if (!_displayState) {
- [_indicator.view setHidden:YES];
- }
-}
-
-- (void)layoutDidFinish
-{
- [self.view setFrame: (CGRect) {
- .size = self.calculatedFrame.size,
- .origin.x = self.calculatedFrame.origin.x,
- .origin.y = self.view.frame.origin.y - CGRectGetHeight(self.calculatedFrame)
- }];
-}
-
-- (void)viewWillUnload
-{
- _displayState = NO;
- _refreshEvent = NO;
- _initFinished = NO;
-}
-
-- (void)refresh
-{
- if (!_refreshEvent || _displayState) {
- return;
- }
- [self fireEvent:@"refresh" params:nil];
-}
-
-- (void)pullingdown:(NSDictionary*)param
-{
- if (!_pullingdownEvent) {
- return ;
- }
-
- [self fireEvent:@"pullingdown" params:param];
-}
-
-- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- if (subcomponent) {
- [super _insertSubcomponent:subcomponent atIndex:index];
- if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) {
- _indicator = (WXLoadingIndicator*)subcomponent;
- }
- }
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- if (attributes[@"display"]) {
- if ([attributes[@"display"] isEqualToString:@"show"]) {
- _displayState = YES;
- } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
- _displayState = NO;
- } else {
- WXLogError(@"");
- }
- [self setDisplay];
- }
-}
-
-- (void)addEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"refresh"]) {
- _refreshEvent = YES;
- }
- if ([eventName isEqualToString:@"pullingdown"]) {
- _pullingdownEvent = YES;
- }
-}
-
-- (void)removeEvent:(NSString *)evetName
-{
- if ([evetName isEqualToString:@"refresh"]) {
- _refreshEvent = NO;
- }
- if ([evetName isEqualToString:@"pullingdown"]) {
- _pullingdownEvent = NO;
- }
-}
-
-- (void)setDisplay
-{
- id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
- if (scrollerProtocol == nil || !_initFinished)
- return;
-
- if ([scrollerProtocol respondsToSelector:@selector(refreshType)] &&
- [[scrollerProtocol refreshType] isEqualToString:@"refreshForAppear"]) {
- UIEdgeInsets inset = [scrollerProtocol contentInset];
- if (_displayState) {
- inset.top = self.calculatedFrame.size.height;
- if ([_indicator.view isHidden]) {
- [_indicator.view setHidden:NO];
- }
- [_indicator start];
- } else {
- inset.top = 0;
- [_indicator stop];
- }
- [scrollerProtocol setContentInset:inset];
- } else {
- CGPoint offset = [scrollerProtocol contentOffset];
- if (_displayState) {
- offset.y = -self.calculatedFrame.size.height;
- if ([_indicator.view isHidden]) {
- [_indicator.view setHidden:NO];
- }
- [_indicator start];
- } else {
- offset.y = 0;
- [_indicator stop];
- }
- [scrollerProtocol setContentOffset:offset animated:YES];
- }
-
-}
-
-- (BOOL)displayState
-{
- return _displayState;
-}
-
-- (void)setIndicatorHidden:(BOOL)hidden {
- [_indicator.view setHidden:hidden];
-
- id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
- if (scrollerProtocol == nil || !_initFinished)
- return;
- if ([scrollerProtocol respondsToSelector:@selector(refreshType)] &&
- [[scrollerProtocol refreshType] isEqualToString:@"refreshForAppear"]) {
- UIEdgeInsets inset = [scrollerProtocol contentInset];
- if (!hidden) {
- inset.top = self.calculatedFrame.size.height;
- [_indicator start];
- } else {
- inset.top = 0;
- [_indicator stop];
- }
- [scrollerProtocol setContentInset:inset];
- }
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.mm
new file mode 100644
index 0000000..f940e3a
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.mm
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXRefreshComponent.h"
+#import "WXScrollerComponent.h"
+#import "WXLoadingIndicator.h"
+#import "WXComponent_internal.h"
+#import "WXLog.h"
+#import "WXComponent+Layout.h"
+
+@interface WXRefreshComponent()
+
+@property (nonatomic) BOOL displayState;
+@property (nonatomic) BOOL initFinished;
+@property (nonatomic) BOOL refreshEvent;
+@property (nonatomic) BOOL pullingdownEvent;
+
+@property (nonatomic, weak) WXLoadingIndicator *indicator;
+
+@end
+
+@implementation WXRefreshComponent
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+ self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
+ if (self) {
+ _refreshEvent = NO;
+ _pullingdownEvent = NO;
+ if (attributes[@"display"]) {
+ if ([attributes[@"display"] isEqualToString:@"show"]) {
+ _displayState = YES;
+ } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
+ _displayState = NO;
+ } else {
+ WXLogError(@"");
+ }
+ }
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ self.cssNode->style.position_type = CSS_POSITION_ABSOLUTE;
+ }
+//#else
+ else
+ {
+ self.flexCssNode->setStylePositionType(WeexCore::kAbsolute);
+ }
+
+//#endif
+ }
+ return self;
+}
+
+- (void)viewDidLoad
+{
+ _initFinished = YES;
+
+ if (!_displayState) {
+ [_indicator.view setHidden:YES];
+ }
+}
+
+- (void)layoutDidFinish
+{
+ [self.view setFrame: (CGRect) {
+ .size = self.calculatedFrame.size,
+ .origin.x = self.calculatedFrame.origin.x,
+ .origin.y = self.view.frame.origin.y - CGRectGetHeight(self.calculatedFrame)
+ }];
+}
+
+- (void)viewWillUnload
+{
+ _displayState = NO;
+ _refreshEvent = NO;
+ _initFinished = NO;
+}
+
+- (void)refresh
+{
+ if (!_refreshEvent || _displayState) {
+ return;
+ }
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> refreshComponent : refresh ref:%@",self.ref);
+#endif
+ [self fireEvent:@"refresh" params:nil];
+}
+
+- (void)pullingdown:(NSDictionary*)param
+{
+ if (!_pullingdownEvent) {
+ return ;
+ }
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> refreshComponent : pullingdown ,ref:%@",self.ref);
+#endif
+
+ [self fireEvent:@"pullingdown" params:param];
+}
+
+- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ if (subcomponent) {
+ [super _insertSubcomponent:subcomponent atIndex:index];
+ if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) {
+ _indicator = (WXLoadingIndicator*)subcomponent;
+ }
+ }
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ if (attributes[@"display"]) {
+ if ([attributes[@"display"] isEqualToString:@"show"]) {
+ _displayState = YES;
+ } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
+ _displayState = NO;
+ } else {
+ WXLogError(@"");
+ }
+ [self setDisplay];
+ }
+}
+
+- (void)addEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"refresh"]) {
+ _refreshEvent = YES;
+ }
+ if ([eventName isEqualToString:@"pullingdown"]) {
+ _pullingdownEvent = YES;
+ }
+}
+
+- (void)removeEvent:(NSString *)evetName
+{
+ if ([evetName isEqualToString:@"refresh"]) {
+ _refreshEvent = NO;
+ }
+ if ([evetName isEqualToString:@"pullingdown"]) {
+ _pullingdownEvent = NO;
+ }
+}
+
+- (void)setDisplay
+{
+ id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
+ if (scrollerProtocol == nil || !_initFinished)
+ return;
+
+ if ([scrollerProtocol respondsToSelector:@selector(refreshType)] &&
+ [[scrollerProtocol refreshType] isEqualToString:@"refreshForAppear"]) {
+ UIEdgeInsets inset = [scrollerProtocol contentInset];
+ if (_displayState) {
+ inset.top = self.calculatedFrame.size.height;
+ if ([_indicator.view isHidden]) {
+ [_indicator.view setHidden:NO];
+ }
+ [_indicator start];
+ } else {
+ inset.top = 0;
+ [_indicator stop];
+ }
+ [scrollerProtocol setContentInset:inset];
+ } else {
+ CGPoint offset = [scrollerProtocol contentOffset];
+ if (_displayState) {
+ offset.y = -self.calculatedFrame.size.height;
+ if ([_indicator.view isHidden]) {
+ [_indicator.view setHidden:NO];
+ }
+ [_indicator start];
+ } else {
+ offset.y = 0;
+ [_indicator stop];
+ }
+ [scrollerProtocol setContentOffset:offset animated:YES];
+ }
+
+}
+
+- (BOOL)displayState
+{
+ return _displayState;
+}
+
+- (void)setIndicatorHidden:(BOOL)hidden {
+ [_indicator.view setHidden:hidden];
+
+ id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
+ if (scrollerProtocol == nil || !_initFinished)
+ return;
+ if ([scrollerProtocol respondsToSelector:@selector(refreshType)] &&
+ [[scrollerProtocol refreshType] isEqualToString:@"refreshForAppear"]) {
+ UIEdgeInsets inset = [scrollerProtocol contentInset];
+ if (!hidden) {
+ inset.top = self.calculatedFrame.size.height;
+ [_indicator start];
+ } else {
+ inset.top = 0;
+ [_indicator stop];
+ }
+ [scrollerProtocol setContentInset:inset];
+ }
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h
index db0c155..8f2b587 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h
@@ -28,8 +28,6 @@
@property (nonatomic, assign) CGSize contentSize;
-@property (nonatomic, readonly, assign) css_node_t *scrollerCSSNode;
-
- (NSUInteger)childrenCountForScrollerLayout;
- (void)handleAppear;
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.m
deleted file mode 100644
index 5f197d1..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.m
+++ /dev/null
@@ -1,884 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXScrollerComponent.h"
-#import "WXComponent_internal.h"
-#import "WXComponent.h"
-#import "WXDefine.h"
-#import "WXConvert.h"
-#import "WXSDKInstance.h"
-#import "WXUtility.h"
-#import "WXLoadingComponent.h"
-#import "WXRefreshComponent.h"
-#import "WXConfigCenterProtocol.h"
-#import "WXSDKEngine.h"
-#import "WXComponent+Events.h"
-
-@interface WXScrollerComponentView:UIScrollView
-@end
-
-@implementation WXScrollerComponentView
-- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
-{
- if ([(id <WXScrollerProtocol>) self.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) {
- return [(id <WXScrollerProtocol>) self.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
- }
- else{
- return YES;
- }
-}
-@end
-
-@interface WXScrollToTarget : NSObject
-
-@property (nonatomic, weak) WXComponent *target;
-@property (nonatomic, assign) BOOL hasAppear;
-
-@end
-
-@implementation WXScrollToTarget
-
-@end
-
-
-@interface WXScrollerComponent()
-
-@property (nonatomic, strong) NSMutableArray * stickyArray;
-@property (nonatomic, strong) NSMutableArray * listenerArray;
-@property (nonatomic, weak) WXRefreshComponent *refreshComponent;
-@property (nonatomic, weak) WXLoadingComponent *loadingComponent;
-
-@end
-
-@implementation WXScrollerComponent
-{
- CGSize _contentSize;
- BOOL _listenLoadMore;
- BOOL _scrollEvent;
- BOOL _scrollStartEvent;
- BOOL _scrollEndEvent;
- BOOL _isScrolling;
- CGFloat _loadMoreOffset;
- CGFloat _previousLoadMoreContentHeight;
- CGFloat _offsetAccuracy;
- CGPoint _lastContentOffset;
- CGPoint _lastScrollEventFiredOffset;
- BOOL _scrollable;
- NSString * _alwaysScrollableVertical;
- NSString * _alwaysScrollableHorizontal;
- BOOL _bounces;
-
- // refreshForAppear: load more when refresh component begin appear(if scroll is dragging or decelerating, should delay)
- // refreshForWholeVisible: load more until the whole refresh component visible
- NSString *_refreshType;
-
- // vertical & horizontal
- WXScrollDirection _scrollDirection;
- // left & right & up & down
- NSString *_direction;
- BOOL _showScrollBar;
- BOOL _pagingEnabled;
-
- BOOL _shouldNotifiAppearDescendantView;
- BOOL _shouldRemoveScrollerListener;
-
- css_node_t *_scrollerCSSNode;
-
- NSHashTable* _delegates;
-}
-
-WX_EXPORT_METHOD(@selector(resetLoadmore))
-
-- (void)resetLoadmore
-{
- _previousLoadMoreContentHeight=0;
-}
-
-- (css_node_t *)scrollerCSSNode
-{
- return _scrollerCSSNode;
-}
-
-- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- [super _insertSubcomponent:subcomponent atIndex:index];
-
- if ([subcomponent isKindOfClass:[WXRefreshComponent class]]) {
- _refreshComponent = (WXRefreshComponent*)subcomponent;
- }
- else if ([subcomponent isKindOfClass:[WXLoadingComponent class]]) {
- _loadingComponent = (WXLoadingComponent*)subcomponent;
- }
-}
-
--(instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
- self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
- if (self) {
-
- _stickyArray = [NSMutableArray array];
- _listenerArray = [NSMutableArray array];
- _scrollEvent = NO;
- _scrollStartEvent = NO;
- _scrollEndEvent = NO;
- _lastScrollEventFiredOffset = CGPointMake(0, 0);
- _scrollDirection = attributes[@"scrollDirection"] ? [WXConvert WXScrollDirection:attributes[@"scrollDirection"]] : WXScrollDirectionVertical;
- _showScrollBar = attributes[@"showScrollbar"] ? [WXConvert BOOL:attributes[@"showScrollbar"]] : YES;
-
- if (attributes[@"alwaysScrollableVertical"]) {
- _alwaysScrollableVertical = [WXConvert NSString:attributes[@"alwaysScrollableVertical"]];
- }
- if (attributes[@"alwaysScrollableHorizontal"]) {
- _alwaysScrollableHorizontal = [WXConvert NSString:attributes[@"alwaysScrollableHorizontal"]];
- }
- _bounces = attributes[@"bounce"]?[WXConvert BOOL:attributes[@"bounce"]]:YES;
- _refreshType = [WXConvert NSString:attributes[@"refreshType"]]?:@"refreshForWholeVisible";
- _pagingEnabled = attributes[@"pagingEnabled"] ? [WXConvert BOOL:attributes[@"pagingEnabled"]] : NO;
- _loadMoreOffset = attributes[@"loadmoreoffset"] ? [WXConvert WXPixelType:attributes[@"loadmoreoffset"] scaleFactor:self.weexInstance.pixelScaleFactor] : 0;
- _loadmoreretry = attributes[@"loadmoreretry"] ? [WXConvert NSUInteger:attributes[@"loadmoreretry"]] : 0;
- _listenLoadMore = [events containsObject:@"loadmore"];
- _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
- _offsetAccuracy = attributes[@"offsetAccuracy"] ? [WXConvert WXPixelType:attributes[@"offsetAccuracy"] scaleFactor:self.weexInstance.pixelScaleFactor] : 0;
- _scrollerCSSNode = new_css_node();
-
- // let scroller fill the rest space if it is a child component and has no fixed height & width
- if (((_scrollDirection == WXScrollDirectionVertical &&
- isUndefined(self.cssNode->style.dimensions[CSS_HEIGHT])) ||
- (_scrollDirection == WXScrollDirectionHorizontal &&
- isUndefined(self.cssNode->style.dimensions[CSS_WIDTH]))) &&
- self.cssNode->style.flex <= 0.0) {
- self.cssNode->style.flex = 1.0;
- }
- id configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)];
- if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
- BOOL shouldNotifiAppearDescendantView = [[configCenter configForKey:@"iOS_weex_ext_config.shouldNotifiAppearDescendantView" defaultValue:@(YES) isDefault:NULL] boolValue];
- _shouldNotifiAppearDescendantView = shouldNotifiAppearDescendantView;
- BOOL shouldRemoveScrollerListener = [[configCenter configForKey:@"iOS_weex_ext_config.shouldRemoveScrollerListener" defaultValue:@(YES) isDefault:NULL] boolValue];
- _shouldRemoveScrollerListener = shouldRemoveScrollerListener;
-
- }
- }
-
- return self;
-}
-
-- (UIView *)loadView
-{
- return [[WXScrollerComponentView alloc] init];
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
- [self setContentSize:_contentSize];
- WXScrollerComponentView* scrollView = (WXScrollerComponentView *)self.view;
- scrollView.delegate = self;
- scrollView.exclusiveTouch = YES;
- scrollView.autoresizesSubviews = NO;
- scrollView.clipsToBounds = YES;
- scrollView.showsVerticalScrollIndicator = _showScrollBar;
- scrollView.showsHorizontalScrollIndicator = _showScrollBar;
- scrollView.scrollEnabled = _scrollable;
- scrollView.pagingEnabled = _pagingEnabled;
-
- if (scrollView.bounces != _bounces) {
- scrollView.bounces = _bounces;
- }
-
- if (_alwaysScrollableHorizontal) {
- scrollView.alwaysBounceHorizontal = [WXConvert BOOL:_alwaysScrollableHorizontal];
- }
- if (_alwaysScrollableVertical) {
- scrollView.alwaysBounceVertical = [WXConvert BOOL:_alwaysScrollableVertical];
- }
-
- if (@available(iOS 11.0, *)) {
- if ([scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
- scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
- }
- } else {
- // Fallback on earlier versions
- }
-
- if (self.ancestorScroller) {
- scrollView.scrollsToTop = NO;
- } else {
- scrollView.scrollsToTop = YES;
- }
-}
-
-- (void)layoutDidFinish
-{
- if ([self isViewLoaded]) {
- [self setContentSize:_contentSize];
- [self adjustSticky];
- [self handleAppear];
- }
-
- [_loadingComponent resizeFrame];
-}
-
-- (void)viewWillUnload
-{
- ((UIScrollView *)_view).delegate = nil;
-}
-
-- (void)dealloc
-{
- ((UIScrollView *)_view).delegate = nil;
- [self.stickyArray removeAllObjects];
- [self.listenerArray removeAllObjects];
-
- free(_scrollerCSSNode);
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- if (attributes[@"showScrollbar"]) {
- _showScrollBar = [WXConvert BOOL:attributes[@"showScrollbar"]];
- ((UIScrollView *)self.view).showsHorizontalScrollIndicator = _showScrollBar;
- ((UIScrollView *)self.view).showsVerticalScrollIndicator = _showScrollBar;
- }
-
- if (attributes[@"pagingEnabled"]) {
- _pagingEnabled = [WXConvert BOOL:attributes[@"pagingEnabled"]];
- ((UIScrollView *)self.view).pagingEnabled = _pagingEnabled;
- }
-
- if (attributes[@"loadmoreoffset"]) {
- _loadMoreOffset = [WXConvert WXPixelType:attributes[@"loadmoreoffset"] scaleFactor:self.weexInstance.pixelScaleFactor];
- }
- if (attributes[@"bounce"]) {
- _bounces = [WXConvert BOOL:attributes[@"bounce"]];
- ((UIScrollView *)self.view).bounces = _bounces;
- }
-
- if (attributes[@"loadmoreretry"]) {
- NSUInteger loadmoreretry = [WXConvert NSUInteger:attributes[@"loadmoreretry"]];
- if (loadmoreretry != _loadmoreretry) {
- _previousLoadMoreContentHeight = 0;
- }
- self.loadmoreretry = loadmoreretry;
- }
- if (attributes[@"scrollable"]) {
- _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
- ((UIScrollView *)self.view).scrollEnabled = _scrollable;
- }
- if (attributes[@"alwaysScrollableHorizontal"]) {
- _alwaysScrollableHorizontal = [WXConvert NSString:attributes[@"alwaysScrollableHorizontal"]];
- ((UIScrollView*)self.view).alwaysBounceHorizontal = [WXConvert BOOL:_alwaysScrollableHorizontal];
- }
-
- if (attributes[@"alwaysScrollableVertical"]) {
- _alwaysScrollableVertical = [WXConvert NSString:attributes[@"alwaysScrollableVertical"]];
- ((UIScrollView*)self.view).alwaysBounceVertical = [WXConvert BOOL:_alwaysScrollableVertical];
- }
-
- if (attributes[@"refreshType"]) {
- _refreshType = [WXConvert NSString:attributes[@"refreshType"]];
- }
-
- if (attributes[@"offsetAccuracy"]) {
- _offsetAccuracy = [WXConvert WXPixelType:attributes[@"offsetAccuracy"] scaleFactor:self.weexInstance.pixelScaleFactor];
- }
-
- if (attributes[@"scrollDirection"]) {
- _scrollDirection = attributes[@"scrollDirection"] ? [WXConvert WXScrollDirection:attributes[@"scrollDirection"]] : WXScrollDirectionVertical;
- }
-}
-
-- (void)addEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"loadmore"]) {
- _listenLoadMore = YES;
- }
- if ([eventName isEqualToString:@"scroll"]) {
- _scrollEvent = YES;
- }
- if ([eventName isEqualToString:@"scrollstart"]) {
- _scrollStartEvent = YES;
- }
- if ([eventName isEqualToString:@"scrollend"]) {
- _scrollEndEvent = YES;
- }
-}
-
-- (void)removeEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"loadmore"]) {
- _listenLoadMore = NO;
- }
- if ([eventName isEqualToString:@"scroll"]) {
- _scrollEvent = NO;
- }
- if ([eventName isEqualToString:@"scrollstart"]) {
- _scrollStartEvent = NO;
- }
- if ([eventName isEqualToString:@"scrollend"]) {
- _scrollEndEvent = NO;
- }
-}
-
-#pragma mark WXScrollerProtocol
-
-- (void)addStickyComponent:(WXComponent *)sticky
-{
- if(![self.stickyArray containsObject:sticky]) {
- [self.stickyArray addObject:sticky];
- [self adjustSticky];
- }
-}
-
-- (void)removeStickyComponent:(WXComponent *)sticky
-{
- if([self.stickyArray containsObject:sticky]) {
- [self.stickyArray removeObject:sticky];
- [self adjustSticky];
- }
-}
-
-- (void)adjustSticky
-{
- if (![self isViewLoaded]) {
- return;
- }
- CGFloat scrollOffsetY = ((UIScrollView *)self.view).contentOffset.y;
- for(WXComponent *component in self.stickyArray) {
- if (isnan(component->_absolutePosition.x) && isnan(component->_absolutePosition.y)) {
- component->_absolutePosition = [component.supercomponent.view convertPoint:component.view.frame.origin toView:self.view];
- }
- CGPoint relativePosition = component->_absolutePosition;
- if (isnan(relativePosition.y)) {
- continue;
- }
-
- WXComponent *supercomponent = component.supercomponent;
- if(supercomponent != self && component.view.superview != self.view) {
- [component.view removeFromSuperview];
- [self.view addSubview:component.view];
- } else {
- [self.view bringSubviewToFront:component.view];
- }
-
- CGFloat relativeY = relativePosition.y;
- BOOL needSticky = NO;
-
- if (scrollOffsetY >= relativeY) {
- needSticky = YES;
- } else {
- // important: reset views' frame
- component.view.frame = CGRectMake(relativePosition.x, relativePosition.y, component.calculatedFrame.size.width, component.calculatedFrame.size.height);
- }
-
- if (!needSticky) {
- continue;
- }
-
- // The minimum Y sticky view can reach is its original position
- CGFloat minY = relativeY;
- CGPoint superRelativePosition = supercomponent == self ? CGPointZero : [supercomponent.supercomponent.view convertPoint:supercomponent.view.frame.origin toView:self.view];
- CGFloat maxY = superRelativePosition.y + supercomponent.calculatedFrame.size.height - component.calculatedFrame.size.height;
-
- CGFloat stickyY = scrollOffsetY;
- if (stickyY < minY) {
- stickyY = minY;
- } else if (stickyY > maxY && ![supercomponent conformsToProtocol:@protocol(WXScrollerProtocol)]) {
- // Sticky component can not go beyond its parent's bounds when its parent is not scroller;
- stickyY = maxY;
- }
-
- UIView *stickyView = component.view;
- CGPoint origin = stickyView.frame.origin;
- origin.y = stickyY;
- stickyView.frame = (CGRect){origin,stickyView.frame.size};
- }
-}
-
-- (void)addScrollToListener:(WXComponent *)target
-{
- BOOL has = NO;
- NSMutableArray *listenerArray = [self.listenerArray copy];
- for (WXScrollToTarget *targetData in listenerArray) {
- if (targetData.target == target) {
- has = YES;
- break;
- }
- }
- if (!has) {
- WXScrollToTarget *scrollTarget = [[WXScrollToTarget alloc] init];
- scrollTarget.target = target;
- scrollTarget.hasAppear = NO;
- [self.listenerArray addObject:scrollTarget];
- }
-}
-
-- (void)removeScrollToListener:(WXComponent *)target
-{
- if (_shouldRemoveScrollerListener) {
- WXScrollToTarget *targetData = nil;
- NSMutableArray *listenerArray = [self.listenerArray copy];
- for (WXScrollToTarget *targetDataTemp in listenerArray) {
- if (targetDataTemp.target == target) {
- targetData = targetDataTemp;
- break;
- }
- }
- if(targetData) {
- [self.listenerArray removeObject:targetData];
- }
- }
-}
-
-- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated
-{
- UIScrollView *scrollView = (UIScrollView *)self.view;
-
- CGPoint contentOffset = scrollView.contentOffset;
- CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
-
- if (_scrollDirection == WXScrollDirectionHorizontal) {
- CGFloat contentOffetX = [component.supercomponent.view convertPoint:component.view.frame.origin toView:self.view].x;
- contentOffetX += offset * scaleFactor;
-
- if (scrollView.contentSize.width >= scrollView.frame.size.width && contentOffetX > scrollView.contentSize.width - scrollView.frame.size.width) {
- contentOffset.x = scrollView.contentSize.width - scrollView.frame.size.width;
- } else {
- contentOffset.x = contentOffetX;
- }
- } else {
- CGFloat contentOffetY = [component.supercomponent.view convertPoint:component.view.frame.origin toView:self.view].y;
- contentOffetY += offset * scaleFactor;
-
- if (scrollView.contentSize.height >= scrollView.frame.size.height && contentOffetY > scrollView.contentSize.height - scrollView.frame.size.height) {
- contentOffset.y = scrollView.contentSize.height - scrollView.frame.size.height;
- } else {
- contentOffset.y = contentOffetY;
- }
- }
-
- [scrollView setContentOffset:contentOffset animated:animated];
-}
-
-- (BOOL)isNeedLoadMore
-{
- if (WXScrollDirectionVertical == _scrollDirection) {
- if (_loadMoreOffset >= 0.0 && ((UIScrollView *)self.view).contentOffset.y >= 0) {
- return _previousLoadMoreContentHeight != ((UIScrollView *)self.view).contentSize.height && ((UIScrollView *)self.view).contentSize.height - ((UIScrollView *)self.view).contentOffset.y - self.view.frame.size.height <= _loadMoreOffset;
- }
- } else if (WXScrollDirectionHorizontal == _scrollDirection) {
- if (_loadMoreOffset >= 0.0 && ((UIScrollView *)self.view).contentOffset.x >= 0) {
- return _previousLoadMoreContentHeight != ((UIScrollView *)self.view).contentSize.width && ((UIScrollView *)self.view).contentSize.width - ((UIScrollView *)self.view).contentOffset.x - self.view.frame.size.width <= _loadMoreOffset;
- }
- }
-
- return NO;
-}
-
-- (void)loadMore
-{
- [self fireEvent:@"loadmore" params:nil];
-
- if (WXScrollDirectionVertical == _scrollDirection) {
- _previousLoadMoreContentHeight = ((UIScrollView *)self.view).contentSize.height;
- } else if (WXScrollDirectionHorizontal == _scrollDirection) {
- _previousLoadMoreContentHeight = ((UIScrollView *)self.view).contentSize.width;
- }
-}
-
-- (CGPoint)contentOffset
-{
- CGPoint rtv = CGPointZero;
- UIScrollView *scrollView = (UIScrollView *)self.view;
- if (scrollView) {
- rtv = scrollView.contentOffset;
- }
- return rtv;
-}
-
-- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
-{
- UIScrollView *scrollView = (UIScrollView *)self.view;
- [scrollView setContentOffset:contentOffset animated:animated];
-}
-
-- (CGSize)contentSize
-{
- return ((UIScrollView *)self.view).contentSize;
-}
-
-- (void)setContentSize:(CGSize)size
-{
- UIScrollView *scrollView = (UIScrollView *)self.view;
- scrollView.contentSize = size;
-}
-
-- (UIEdgeInsets)contentInset
-{
- UIEdgeInsets rtv = UIEdgeInsetsZero;
- UIScrollView *scrollView = (UIScrollView *)self.view;
- if (scrollView) {
- rtv = scrollView.contentInset;
- }
- return rtv;
-}
-
-- (void)setContentInset:(UIEdgeInsets)contentInset
-{
- UIScrollView *scrollView = (UIScrollView *)self.view;
- [scrollView setContentInset:contentInset];
-}
-
-- (void)addScrollDelegate:(id<UIScrollViewDelegate>)delegate
-{
- if (delegate) {
- if (!_delegates) {
- _delegates = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
- }
- [_delegates addObject:delegate];
- }
-}
-
-- (void)removeScrollDelegate:(id<UIScrollViewDelegate>)delegate
-{
- if (delegate) {
- [_delegates removeObject:delegate];
- }
-}
-
-- (WXScrollDirection)scrollDirection
-{
- return _scrollDirection;
-}
-
-- (NSString*)refreshType
-{
- return _refreshType;
-}
-- (BOOL)requestGestureShouldStopPropagation:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
-{
- return [self gestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
-}
-
-#pragma mark UIScrollViewDelegate
-- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
-{
- if ([_refreshType isEqualToString:@"refreshForAppear"] && _refreshComponent) {
- [_refreshComponent setIndicatorHidden:NO];
- }
-
- if (_scrollStartEvent) {
- CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
- NSDictionary *contentSizeData = @{@"width":[NSNumber numberWithFloat:scrollView.contentSize.width / scaleFactor],@"height":[NSNumber numberWithFloat:scrollView.contentSize.height / scaleFactor]};
- NSDictionary *contentOffsetData = @{@"x":[NSNumber numberWithFloat:-scrollView.contentOffset.x / scaleFactor],@"y":[NSNumber numberWithFloat:-scrollView.contentOffset.y / scaleFactor]};
- [self fireEvent:@"scrollstart" params:@{@"contentSize":contentSizeData,@"contentOffset":contentOffsetData} domChanges:nil];
- }
-}
-
-- (void)scrollViewDidScroll:(UIScrollView *)scrollView
-{
- //apply block which are registered
- WXSDKInstance *instance = self.weexInstance;
- if ([self.ref isEqualToString:WX_SDK_ROOT_REF] &&
- [self isKindOfClass:[WXScrollerComponent class]]) {
- if (instance.onScroll) {
- instance.onScroll(scrollView.contentOffset);
- }
- }
-
- if (_lastContentOffset.x > scrollView.contentOffset.x) {
- _direction = @"right";
- } else if (_lastContentOffset.x < scrollView.contentOffset.x) {
- _direction = @"left";
- if (WXScrollDirectionHorizontal == _scrollDirection) {
- [self handleLoadMore];
- }
- } else if(_lastContentOffset.y > scrollView.contentOffset.y) {
- _direction = @"down";
- } else if(_lastContentOffset.y < scrollView.contentOffset.y) {
- _direction = @"up";
- if (WXScrollDirectionVertical == _scrollDirection) {
- [self handleLoadMore];
- }
- }
-
- CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
- [_refreshComponent pullingdown:@{
- REFRESH_DISTANCE_Y: @(fabs((scrollView.contentOffset.y - _lastContentOffset.y)/scaleFactor)),
- REFRESH_VIEWHEIGHT: @(_refreshComponent.view.frame.size.height/scaleFactor),
- REFRESH_PULLINGDISTANCE: @(scrollView.contentOffset.y/scaleFactor),
- @"type":@"pullingdown"
- }];
- _lastContentOffset = scrollView.contentOffset;
- // check sticky
- [self adjustSticky];
- [self handleAppear];
-
- if (self.onScroll) {
- self.onScroll(scrollView);
- }
- if (_scrollEvent) {
- NSDictionary *contentSizeData = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithFloat:scrollView.contentSize.width / scaleFactor],@"width",[NSNumber numberWithFloat:scrollView.contentSize.height / scaleFactor],@"height", nil];
- //contentOffset values are replaced by (-contentOffset.x,-contentOffset.y) ,in order to be consistent with Android client.
- NSDictionary *contentOffsetData = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithFloat:-scrollView.contentOffset.x / scaleFactor],@"x",[NSNumber numberWithFloat:-scrollView.contentOffset.y / scaleFactor],@"y", nil];
- CGFloat distance = 0;
- if (_scrollDirection == WXScrollDirectionHorizontal) {
- distance = scrollView.contentOffset.x - _lastScrollEventFiredOffset.x;
- } else {
- distance = scrollView.contentOffset.y - _lastScrollEventFiredOffset.y;
- }
- if (fabs(distance) >= _offsetAccuracy) {
- [self fireEvent:@"scroll" params:@{@"contentSize":contentSizeData,@"contentOffset":contentOffsetData} domChanges:nil];
- _lastScrollEventFiredOffset = scrollView.contentOffset;
- }
- }
- NSHashTable *delegates = [_delegates copy];
- for (id<UIScrollViewDelegate> delegate in delegates) {
- if ([delegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
- [delegate scrollViewDidScroll:scrollView];
- }
- }
-}
-
-- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
-{
- UIEdgeInsets inset = [scrollView contentInset];
-
-// currently only set contentInset when loading
-// if ([_refreshComponent displayState]) {
-// inset.top = _refreshComponent.view.frame.size.height;
-// }
-// else {
-// inset.top = 0;
-// }
-
- if ([_loadingComponent displayState]) {
- inset.bottom = _loadingComponent.view.frame.size.height;
- } else {
- inset.bottom = 0;
- }
- [scrollView setContentInset:inset];
-}
-
-- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
-{
- if (_scrollEndEvent) {
- if (!_isScrolling) {
- CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
- NSDictionary *contentSizeData = @{@"width":[NSNumber numberWithFloat:scrollView.contentSize.width / scaleFactor],@"height":[NSNumber numberWithFloat:scrollView.contentSize.height / scaleFactor]};
- NSDictionary *contentOffsetData = @{@"x":[NSNumber numberWithFloat:-scrollView.contentOffset.x / scaleFactor],@"y":[NSNumber numberWithFloat:-scrollView.contentOffset.y / scaleFactor]};
- [self fireEvent:@"scrollend" params:@{@"contentSize":contentSizeData,@"contentOffset":contentOffsetData} domChanges:nil];
- }
- }
-}
-
-- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
-{
- if ([_refreshType isEqualToString:@"refreshForAppear"]) {
- if(targetContentOffset == nil)
- return;
- CGPoint offset = *targetContentOffset;
- if(velocity.y <= 0) {
- // drop down
- if( offset.y <= _refreshComponent.calculatedFrame.size.height ) {
- [self loadMoreIfNeed];
- }
- } else if (velocity.y > 0) {
- // drop up
- }
- }
-}
-
-- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
-{
- [_loadingComponent.view setHidden:NO];
- [_refreshComponent.view setHidden:NO];
-
- //refresh
- if ([_refreshType isEqualToString:@"refreshForWholeVisible"]) {
- if (_refreshComponent && scrollView.contentOffset.y < 0 && scrollView.contentOffset.y + _refreshComponent.calculatedFrame.size.height < _refreshComponent.calculatedFrame.origin.y) {
- [_refreshComponent refresh];
- }
- }
-
- //loading
- if (_loadingComponent && scrollView.contentOffset.y > 0 &&
- scrollView.contentOffset.y + scrollView.frame.size.height > _loadingComponent.view.frame.origin.y + _loadingComponent.calculatedFrame.size.height) {
- [_loadingComponent loading];
- }
- if (!decelerate) {
- _isScrolling = NO;
- [self performSelector:@selector(scrollViewDidEndDecelerating:) withObject:nil afterDelay:0.1];
- }
-}
-
-- (void)loadMoreIfNeed
-{
- WXScrollerComponentView* scrollView = (WXScrollerComponentView *)self.view;
- if (scrollView.isDragging || scrollView.isTracking || scrollView.isDecelerating) {
- [self performSelector:@selector(loadMoreIfNeed) withObject:nil afterDelay:0.1];
- return;
- }
- [_refreshComponent refresh];
-}
-
-- (void)handleAppear
-{
- if (![self isViewLoaded]) {
- return;
- }
- UIScrollView *scrollView = (UIScrollView *)self.view;
- CGFloat vx = scrollView.contentInset.left + scrollView.contentOffset.x;
- CGFloat vy = scrollView.contentInset.top + scrollView.contentOffset.y;
- CGFloat vw = scrollView.frame.size.width - scrollView.contentInset.left - scrollView.contentInset.right;
- CGFloat vh = scrollView.frame.size.height - scrollView.contentInset.top - scrollView.contentInset.bottom;
- CGRect scrollRect = CGRectMake(vx, vy, vw, vh);;
-
- // notify action for appear
- NSArray *listenerArrayCopy = [self.listenerArray copy];
- for(WXScrollToTarget *target in listenerArrayCopy){
- if (_shouldNotifiAppearDescendantView) {
- // if target component is descendant of scrollerview, it should notify the appear event handler, or here will skip this appear calculation.
- if ([target.target isViewLoaded] && [target.target.view isDescendantOfView:self.view]) {
- [self scrollToTarget:target scrollRect:scrollRect];
- }
- } else {
- [self scrollToTarget:target scrollRect:scrollRect];
- }
- }
-}
-
-- (CGPoint)absolutePositionForComponent:(WXComponent *)component
-{
- return [component->_view.superview convertPoint:component->_view.frame.origin toView:_view];
-}
-
-#pragma mark Private Methods
-
-- (void)scrollToTarget:(WXScrollToTarget *)target scrollRect:(CGRect)rect
-{
- WXComponent *component = target.target;
- if (![component isViewLoaded]) {
- return;
- }
-
- CGFloat ctop;
- if (component && component->_view && component->_view.superview) {
- ctop = [self absolutePositionForComponent:component].y;
- } else {
- ctop = 0.0;
- }
- CGFloat cbottom = ctop + CGRectGetHeight(component.calculatedFrame);
- CGFloat cleft;
- if (component && component->_view && component->_view.superview) {
- cleft = [self absolutePositionForComponent:component].x;
- } else {
- cleft = 0.0;
- }
- CGFloat cright = cleft + CGRectGetWidth(component.calculatedFrame);
-
- CGFloat vtop = CGRectGetMinY(rect), vbottom = CGRectGetMaxY(rect), vleft = CGRectGetMinX(rect), vright = CGRectGetMaxX(rect);
- if(cbottom > vtop && ctop <= vbottom && cleft <= vright && cright > vleft){
- if(!target.hasAppear && component){
- target.hasAppear = YES;
- if (component->_appearEvent) {
-// NSLog(@"appear:%@, %.2f", component, ctop);
- [component fireEvent:@"appear" params:_direction ? @{@"direction":_direction} : nil];
- }
- }
- } else {
- if(target.hasAppear && component){
- target.hasAppear = NO;
- if(component->_disappearEvent){
-// NSLog(@"disappear:%@", component);
- [component fireEvent:@"disappear" params:_direction ? @{@"direction":_direction} : nil];
- }
- }
- }
-}
-
-- (void)handleLoadMore
-{
- if (_listenLoadMore && [self isNeedLoadMore]) {
- [self loadMore];
- }
-}
-
-#pragma mark Layout
-
-- (NSUInteger)_childrenCountForLayout;
-{
- return 0;
-}
-
-- (NSUInteger)childrenCountForScrollerLayout
-{
- return [super _childrenCountForLayout];
-}
-
-- (void)_calculateFrameWithSuperAbsolutePosition:(CGPoint)superAbsolutePosition
- gatherDirtyComponents:(NSMutableSet<WXComponent *> *)dirtyComponents
-{
- /**
- * Pretty hacky way
- * layout from root to scroller to get scroller's frame,
- * layout from children to scroller to get scroller's contentSize
- */
- if ([self needsLayout]) {
- memcpy(_scrollerCSSNode, self.cssNode, sizeof(css_node_t));
- _scrollerCSSNode->children_count = (int)[self childrenCountForScrollerLayout];
-
- _scrollerCSSNode->style.position[CSS_LEFT] = 0;
- _scrollerCSSNode->style.position[CSS_TOP] = 0;
-
- if (_scrollDirection == WXScrollDirectionVertical) {
- _scrollerCSSNode->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN;
- _scrollerCSSNode->style.dimensions[CSS_WIDTH] = _cssNode->layout.dimensions[CSS_WIDTH];
- _scrollerCSSNode->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
- } else {
- _scrollerCSSNode->style.flex_direction = CSS_FLEX_DIRECTION_ROW;
- _scrollerCSSNode->style.dimensions[CSS_HEIGHT] = _cssNode->layout.dimensions[CSS_HEIGHT];
- _scrollerCSSNode->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
- }
-
- _scrollerCSSNode->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
- _scrollerCSSNode->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
-
- layoutNode(_scrollerCSSNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
- if ([WXLog logLevel] >= WXLogLevelDebug) {
- print_css_node(_scrollerCSSNode, CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN);
- }
- CGSize size = {
- WXRoundPixelValue(_scrollerCSSNode->layout.dimensions[CSS_WIDTH]),
- WXRoundPixelValue(_scrollerCSSNode->layout.dimensions[CSS_HEIGHT])
- };
-
- if (!CGSizeEqualToSize(size, _contentSize)) {
- // content size
- _contentSize = size;
- [dirtyComponents addObject:self];
- }
-
- _scrollerCSSNode->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
- _scrollerCSSNode->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
- }
-
- [super _calculateFrameWithSuperAbsolutePosition:superAbsolutePosition gatherDirtyComponents:dirtyComponents];
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
new file mode 100644
index 0000000..215d9ff
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
@@ -0,0 +1,951 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXScrollerComponent.h"
+#import "WXComponent_internal.h"
+#import "WXComponent.h"
+#import "WXDefine.h"
+#import "WXConvert.h"
+#import "WXSDKInstance.h"
+#import "WXUtility.h"
+#import "WXLoadingComponent.h"
+#import "WXRefreshComponent.h"
+#import "WXConfigCenterProtocol.h"
+#import "WXSDKEngine.h"
+#import "WXComponent+Events.h"
+#import "WXScrollerComponent+Layout.h"
+#import "WXCoreLayout.h"
+
+@interface WXScrollerComponentView:UIScrollView
+@end
+
+@implementation WXScrollerComponentView
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
+{
+ if ([(id <WXScrollerProtocol>) self.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) {
+ return [(id <WXScrollerProtocol>) self.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
+ }
+ else{
+ return YES;
+ }
+}
+@end
+
+@interface WXScrollToTarget : NSObject
+
+@property (nonatomic, weak) WXComponent *target;
+@property (nonatomic, assign) BOOL hasAppear;
+
+@end
+
+@implementation WXScrollToTarget
+
+@end
+
+
+@interface WXScrollerComponent()
+
+@property (nonatomic, strong) NSMutableArray * stickyArray;
+@property (nonatomic, strong) NSMutableArray * listenerArray;
+@property (nonatomic, weak) WXRefreshComponent *refreshComponent;
+@property (nonatomic, weak) WXLoadingComponent *loadingComponent;
+
+@end
+
+@implementation WXScrollerComponent
+{
+ CGSize _contentSize;
+ BOOL _listenLoadMore;
+ BOOL _scrollEvent;
+ BOOL _scrollStartEvent;
+ BOOL _scrollEndEvent;
+ BOOL _isScrolling;
+ CGFloat _loadMoreOffset;
+ CGFloat _previousLoadMoreContentHeight;
+ CGFloat _offsetAccuracy;
+ CGPoint _lastContentOffset;
+ CGPoint _lastScrollEventFiredOffset;
+ BOOL _scrollable;
+ NSString * _alwaysScrollableVertical;
+ NSString * _alwaysScrollableHorizontal;
+ BOOL _bounces;
+
+ // refreshForAppear: load more when refresh component begin appear(if scroll is dragging or decelerating, should delay)
+ // refreshForWholeVisible: load more until the whole refresh component visible
+ NSString *_refreshType;
+
+ // vertical & horizontal
+ WXScrollDirection _scrollDirection;
+ // left & right & up & down
+ NSString *_direction;
+ BOOL _showScrollBar;
+ BOOL _pagingEnabled;
+
+ BOOL _shouldNotifiAppearDescendantView;
+ BOOL _shouldRemoveScrollerListener;
+
+ //css_node_t *_scrollerCSSNode;
+
+ NSHashTable* _delegates;
+}
+
+WX_EXPORT_METHOD(@selector(resetLoadmore))
+
+- (void)resetLoadmore
+{
+ _previousLoadMoreContentHeight=0;
+}
+
+- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ [super _insertSubcomponent:subcomponent atIndex:index];
+
+ if ([subcomponent isKindOfClass:[WXRefreshComponent class]]) {
+ _refreshComponent = (WXRefreshComponent*)subcomponent;
+ }
+ else if ([subcomponent isKindOfClass:[WXLoadingComponent class]]) {
+ _loadingComponent = (WXLoadingComponent*)subcomponent;
+ }
+}
+
+-(instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+ self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
+ if (self) {
+
+ _stickyArray = [NSMutableArray array];
+ _listenerArray = [NSMutableArray array];
+ _scrollEvent = NO;
+ _scrollStartEvent = NO;
+ _scrollEndEvent = NO;
+ _lastScrollEventFiredOffset = CGPointMake(0, 0);
+ _scrollDirection = attributes[@"scrollDirection"] ? [WXConvert WXScrollDirection:attributes[@"scrollDirection"]] : WXScrollDirectionVertical;
+ _showScrollBar = attributes[@"showScrollbar"] ? [WXConvert BOOL:attributes[@"showScrollbar"]] : YES;
+
+ if (attributes[@"alwaysScrollableVertical"]) {
+ _alwaysScrollableVertical = [WXConvert NSString:attributes[@"alwaysScrollableVertical"]];
+ }
+ if (attributes[@"alwaysScrollableHorizontal"]) {
+ _alwaysScrollableHorizontal = [WXConvert NSString:attributes[@"alwaysScrollableHorizontal"]];
+ }
+ _bounces = attributes[@"bounce"]?[WXConvert BOOL:attributes[@"bounce"]]:YES;
+ _refreshType = [WXConvert NSString:attributes[@"refreshType"]]?:@"refreshForWholeVisible";
+ _pagingEnabled = attributes[@"pagingEnabled"] ? [WXConvert BOOL:attributes[@"pagingEnabled"]] : NO;
+ _loadMoreOffset = attributes[@"loadmoreoffset"] ? [WXConvert WXPixelType:attributes[@"loadmoreoffset"] scaleFactor:self.weexInstance.pixelScaleFactor] : 0;
+ _loadmoreretry = attributes[@"loadmoreretry"] ? [WXConvert NSUInteger:attributes[@"loadmoreretry"]] : 0;
+ _listenLoadMore = [events containsObject:@"loadmore"];
+ _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
+ _offsetAccuracy = attributes[@"offsetAccuracy"] ? [WXConvert WXPixelType:attributes[@"offsetAccuracy"] scaleFactor:self.weexInstance.pixelScaleFactor] : 0;
+
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ _scrollerCSSNode = new_css_node();
+
+ // let scroller fill the rest space if it is a child component and has no fixed height & width
+ if (((_scrollDirection == WXScrollDirectionVertical &&
+ isUndefined(self.cssNode->style.dimensions[CSS_HEIGHT])) ||
+ (_scrollDirection == WXScrollDirectionHorizontal &&
+ isUndefined(self.cssNode->style.dimensions[CSS_WIDTH]))) &&
+ self.cssNode->style.flex <= 0.0) {
+ self.cssNode->style.flex = 1.0;
+ }
+ }
+//#else
+ else
+ {
+ _flexScrollerCSSNode = new WeexCore::WXCoreLayoutNode();
+ // let scroller fill the rest space if it is a child component and has no fixed height & width
+ if (((_scrollDirection == WXScrollDirectionVertical &&
+ flexIsUndefined(self.flexCssNode->getStyleHeight())) ||
+ (_scrollDirection == WXScrollDirectionHorizontal &&
+ flexIsUndefined(self.flexCssNode->getStyleWidth()))) &&
+ self.flexCssNode->getFlex() <= 0.0) {
+ self.flexCssNode->setFlex(1.0);
+ }
+ }
+//#endif
+ id configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)];
+ if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
+ BOOL shouldNotifiAppearDescendantView = [[configCenter configForKey:@"iOS_weex_ext_config.shouldNotifiAppearDescendantView" defaultValue:@(YES) isDefault:NULL] boolValue];
+ _shouldNotifiAppearDescendantView = shouldNotifiAppearDescendantView;
+ BOOL shouldRemoveScrollerListener = [[configCenter configForKey:@"iOS_weex_ext_config.shouldRemoveScrollerListener" defaultValue:@(YES) isDefault:NULL] boolValue];
+ _shouldRemoveScrollerListener = shouldRemoveScrollerListener;
+
+ }
+ }
+
+ return self;
+}
+
+- (UIView *)loadView
+{
+ return [[WXScrollerComponentView alloc] init];
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+ [self setContentSize:_contentSize];
+ WXScrollerComponentView* scrollView = (WXScrollerComponentView *)self.view;
+ scrollView.delegate = self;
+ scrollView.exclusiveTouch = YES;
+ scrollView.autoresizesSubviews = NO;
+ scrollView.clipsToBounds = YES;
+ scrollView.showsVerticalScrollIndicator = _showScrollBar;
+ scrollView.showsHorizontalScrollIndicator = _showScrollBar;
+ scrollView.scrollEnabled = _scrollable;
+ scrollView.pagingEnabled = _pagingEnabled;
+
+ if (scrollView.bounces != _bounces) {
+ scrollView.bounces = _bounces;
+ }
+
+ if (_alwaysScrollableHorizontal) {
+ scrollView.alwaysBounceHorizontal = [WXConvert BOOL:_alwaysScrollableHorizontal];
+ }
+ if (_alwaysScrollableVertical) {
+ scrollView.alwaysBounceVertical = [WXConvert BOOL:_alwaysScrollableVertical];
+ }
+
+ if (@available(iOS 11.0, *)) {
+ if ([scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
+ scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
+ }
+ } else {
+ // Fallback on earlier versions
+ }
+
+ if (self.ancestorScroller) {
+ scrollView.scrollsToTop = NO;
+ } else {
+ scrollView.scrollsToTop = YES;
+ }
+}
+
+- (void)layoutDidFinish
+{
+
+
+ if ([self isViewLoaded]) {
+ [self setContentSize:_contentSize];
+ [self adjustSticky];
+ [self handleAppear];
+ }
+
+ [_loadingComponent resizeFrame];
+}
+
+- (void)viewWillUnload
+{
+ ((UIScrollView *)_view).delegate = nil;
+}
+
+- (void)dealloc
+{
+ ((UIScrollView *)_view).delegate = nil;
+ [self.stickyArray removeAllObjects];
+ [self.listenerArray removeAllObjects];
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ free(_scrollerCSSNode);
+ }
+//#else
+ else
+ {
+ if(_flexScrollerCSSNode){
+ delete _flexScrollerCSSNode;
+
+ //WeexCore::WXCoreLayoutNode::freeNodeTree(_flexScrollerCSSNode);
+
+ _flexScrollerCSSNode=nullptr;
+ }
+ }
+//#endif
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ if (attributes[@"showScrollbar"]) {
+ _showScrollBar = [WXConvert BOOL:attributes[@"showScrollbar"]];
+ ((UIScrollView *)self.view).showsHorizontalScrollIndicator = _showScrollBar;
+ ((UIScrollView *)self.view).showsVerticalScrollIndicator = _showScrollBar;
+ }
+
+ if (attributes[@"pagingEnabled"]) {
+ _pagingEnabled = [WXConvert BOOL:attributes[@"pagingEnabled"]];
+ ((UIScrollView *)self.view).pagingEnabled = _pagingEnabled;
+ }
+
+ if (attributes[@"loadmoreoffset"]) {
+ _loadMoreOffset = [WXConvert WXPixelType:attributes[@"loadmoreoffset"] scaleFactor:self.weexInstance.pixelScaleFactor];
+ }
+ if (attributes[@"bounce"]) {
+ _bounces = [WXConvert BOOL:attributes[@"bounce"]];
+ ((UIScrollView *)self.view).bounces = _bounces;
+ }
+
+ if (attributes[@"loadmoreretry"]) {
+ NSUInteger loadmoreretry = [WXConvert NSUInteger:attributes[@"loadmoreretry"]];
+ if (loadmoreretry != _loadmoreretry) {
+ _previousLoadMoreContentHeight = 0;
+ }
+ self.loadmoreretry = loadmoreretry;
+ }
+ if (attributes[@"scrollable"]) {
+ _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
+ ((UIScrollView *)self.view).scrollEnabled = _scrollable;
+ }
+ if (attributes[@"alwaysScrollableHorizontal"]) {
+ _alwaysScrollableHorizontal = [WXConvert NSString:attributes[@"alwaysScrollableHorizontal"]];
+ ((UIScrollView*)self.view).alwaysBounceHorizontal = [WXConvert BOOL:_alwaysScrollableHorizontal];
+ }
+
+ if (attributes[@"alwaysScrollableVertical"]) {
+ _alwaysScrollableVertical = [WXConvert NSString:attributes[@"alwaysScrollableVertical"]];
+ ((UIScrollView*)self.view).alwaysBounceVertical = [WXConvert BOOL:_alwaysScrollableVertical];
+ }
+
+ if (attributes[@"refreshType"]) {
+ _refreshType = [WXConvert NSString:attributes[@"refreshType"]];
+ }
+
+ if (attributes[@"offsetAccuracy"]) {
+ _offsetAccuracy = [WXConvert WXPixelType:attributes[@"offsetAccuracy"] scaleFactor:self.weexInstance.pixelScaleFactor];
+ }
+
+ if (attributes[@"scrollDirection"]) {
+ _scrollDirection = attributes[@"scrollDirection"] ? [WXConvert WXScrollDirection:attributes[@"scrollDirection"]] : WXScrollDirectionVertical;
+ }
+}
+
+- (void)addEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"loadmore"]) {
+ _listenLoadMore = YES;
+ }
+ if ([eventName isEqualToString:@"scroll"]) {
+ _scrollEvent = YES;
+ }
+ if ([eventName isEqualToString:@"scrollstart"]) {
+ _scrollStartEvent = YES;
+ }
+ if ([eventName isEqualToString:@"scrollend"]) {
+ _scrollEndEvent = YES;
+ }
+}
+
+- (void)removeEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"loadmore"]) {
+ _listenLoadMore = NO;
+ }
+ if ([eventName isEqualToString:@"scroll"]) {
+ _scrollEvent = NO;
+ }
+ if ([eventName isEqualToString:@"scrollstart"]) {
+ _scrollStartEvent = NO;
+ }
+ if ([eventName isEqualToString:@"scrollend"]) {
+ _scrollEndEvent = NO;
+ }
+}
+
+#pragma mark WXScrollerProtocol
+
+- (void)addStickyComponent:(WXComponent *)sticky
+{
+ if(![self.stickyArray containsObject:sticky]) {
+ [self.stickyArray addObject:sticky];
+ [self adjustSticky];
+ }
+}
+
+- (void)removeStickyComponent:(WXComponent *)sticky
+{
+ if([self.stickyArray containsObject:sticky]) {
+ [self.stickyArray removeObject:sticky];
+ [self adjustSticky];
+ }
+}
+
+- (void)adjustSticky
+{
+ if (![self isViewLoaded]) {
+ return;
+ }
+ CGFloat scrollOffsetY = ((UIScrollView *)self.view).contentOffset.y;
+ for(WXComponent *component in self.stickyArray) {
+ if (isnan(component->_absolutePosition.x) && isnan(component->_absolutePosition.y)) {
+ component->_absolutePosition = [component.supercomponent.view convertPoint:component.view.frame.origin toView:self.view];
+ }
+ CGPoint relativePosition = component->_absolutePosition;
+ if (isnan(relativePosition.y)) {
+ continue;
+ }
+
+ WXComponent *supercomponent = component.supercomponent;
+ if(supercomponent != self && component.view.superview != self.view) {
+ [component.view removeFromSuperview];
+ [self.view addSubview:component.view];
+ } else {
+ [self.view bringSubviewToFront:component.view];
+ }
+
+ CGFloat relativeY = relativePosition.y;
+ BOOL needSticky = NO;
+
+ if (scrollOffsetY >= relativeY) {
+ needSticky = YES;
+ } else {
+ // important: reset views' frame
+ component.view.frame = CGRectMake(relativePosition.x, relativePosition.y, component.calculatedFrame.size.width, component.calculatedFrame.size.height);
+ }
+
+ if (!needSticky) {
+ continue;
+ }
+
+ // The minimum Y sticky view can reach is its original position
+ CGFloat minY = relativeY;
+ CGPoint superRelativePosition = supercomponent == self ? CGPointZero : [supercomponent.supercomponent.view convertPoint:supercomponent.view.frame.origin toView:self.view];
+ CGFloat maxY = superRelativePosition.y + supercomponent.calculatedFrame.size.height - component.calculatedFrame.size.height;
+
+ CGFloat stickyY = scrollOffsetY;
+ if (stickyY < minY) {
+ stickyY = minY;
+ } else if (stickyY > maxY && ![supercomponent conformsToProtocol:@protocol(WXScrollerProtocol)]) {
+ // Sticky component can not go beyond its parent's bounds when its parent is not scroller;
+ stickyY = maxY;
+ }
+
+ UIView *stickyView = component.view;
+ CGPoint origin = stickyView.frame.origin;
+ origin.y = stickyY;
+ stickyView.frame = (CGRect){origin,stickyView.frame.size};
+ }
+}
+
+- (void)addScrollToListener:(WXComponent *)target
+{
+ BOOL has = NO;
+ NSMutableArray *listenerArray = [self.listenerArray copy];
+ for (WXScrollToTarget *targetData in listenerArray) {
+ if (targetData.target == target) {
+ has = YES;
+ break;
+ }
+ }
+ if (!has) {
+ WXScrollToTarget *scrollTarget = [[WXScrollToTarget alloc] init];
+ scrollTarget.target = target;
+ scrollTarget.hasAppear = NO;
+ [self.listenerArray addObject:scrollTarget];
+ }
+}
+
+- (void)removeScrollToListener:(WXComponent *)target
+{
+ if (_shouldRemoveScrollerListener) {
+ WXScrollToTarget *targetData = nil;
+ NSMutableArray *listenerArray = [self.listenerArray copy];
+ for (WXScrollToTarget *targetDataTemp in listenerArray) {
+ if (targetDataTemp.target == target) {
+ targetData = targetDataTemp;
+ break;
+ }
+ }
+ if(targetData) {
+ [self.listenerArray removeObject:targetData];
+ }
+ }
+}
+
+- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated
+{
+ UIScrollView *scrollView = (UIScrollView *)self.view;
+
+ CGPoint contentOffset = scrollView.contentOffset;
+ CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
+
+ if (_scrollDirection == WXScrollDirectionHorizontal) {
+ CGFloat contentOffetX = [component.supercomponent.view convertPoint:component.view.frame.origin toView:self.view].x;
+ contentOffetX += offset * scaleFactor;
+
+ if (scrollView.contentSize.width >= scrollView.frame.size.width && contentOffetX > scrollView.contentSize.width - scrollView.frame.size.width) {
+ contentOffset.x = scrollView.contentSize.width - scrollView.frame.size.width;
+ } else {
+ contentOffset.x = contentOffetX;
+ }
+ } else {
+ CGFloat contentOffetY = [component.supercomponent.view convertPoint:component.view.frame.origin toView:self.view].y;
+ contentOffetY += offset * scaleFactor;
+
+ if (scrollView.contentSize.height >= scrollView.frame.size.height && contentOffetY > scrollView.contentSize.height - scrollView.frame.size.height) {
+ contentOffset.y = scrollView.contentSize.height - scrollView.frame.size.height;
+ } else {
+ contentOffset.y = contentOffetY;
+ }
+ }
+
+ [scrollView setContentOffset:contentOffset animated:animated];
+}
+
+- (BOOL)isNeedLoadMore
+{
+ if (WXScrollDirectionVertical == _scrollDirection) {
+ if (_loadMoreOffset >= 0.0 && ((UIScrollView *)self.view).contentOffset.y >= 0) {
+ return _previousLoadMoreContentHeight != ((UIScrollView *)self.view).contentSize.height && ((UIScrollView *)self.view).contentSize.height - ((UIScrollView *)self.view).contentOffset.y - self.view.frame.size.height <= _loadMoreOffset;
+ }
+ } else if (WXScrollDirectionHorizontal == _scrollDirection) {
+ if (_loadMoreOffset >= 0.0 && ((UIScrollView *)self.view).contentOffset.x >= 0) {
+ return _previousLoadMoreContentHeight != ((UIScrollView *)self.view).contentSize.width && ((UIScrollView *)self.view).contentSize.width - ((UIScrollView *)self.view).contentOffset.x - self.view.frame.size.width <= _loadMoreOffset;
+ }
+ }
+
+ return NO;
+}
+
+- (void)loadMore
+{
+ [self fireEvent:@"loadmore" params:nil];
+
+ if (WXScrollDirectionVertical == _scrollDirection) {
+ _previousLoadMoreContentHeight = ((UIScrollView *)self.view).contentSize.height;
+ } else if (WXScrollDirectionHorizontal == _scrollDirection) {
+ _previousLoadMoreContentHeight = ((UIScrollView *)self.view).contentSize.width;
+ }
+}
+
+- (CGPoint)contentOffset
+{
+ CGPoint rtv = CGPointZero;
+ UIScrollView *scrollView = (UIScrollView *)self.view;
+ if (scrollView) {
+ rtv = scrollView.contentOffset;
+ }
+ return rtv;
+}
+
+- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
+{
+ UIScrollView *scrollView = (UIScrollView *)self.view;
+ [scrollView setContentOffset:contentOffset animated:animated];
+}
+
+- (CGSize)contentSize
+{
+ return ((UIScrollView *)self.view).contentSize;
+}
+
+- (void)setContentSize:(CGSize)size
+{
+ UIScrollView *scrollView = (UIScrollView *)self.view;
+ scrollView.contentSize = size;
+}
+
+- (UIEdgeInsets)contentInset
+{
+ UIEdgeInsets rtv = UIEdgeInsetsZero;
+ UIScrollView *scrollView = (UIScrollView *)self.view;
+ if (scrollView) {
+ rtv = scrollView.contentInset;
+ }
+ return rtv;
+}
+
+- (void)setContentInset:(UIEdgeInsets)contentInset
+{
+ UIScrollView *scrollView = (UIScrollView *)self.view;
+ [scrollView setContentInset:contentInset];
+}
+
+- (void)addScrollDelegate:(id<UIScrollViewDelegate>)delegate
+{
+ if (delegate) {
+ if (!_delegates) {
+ _delegates = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
+ }
+ [_delegates addObject:delegate];
+ }
+}
+
+- (void)removeScrollDelegate:(id<UIScrollViewDelegate>)delegate
+{
+ if (delegate) {
+ [_delegates removeObject:delegate];
+ }
+}
+
+- (WXScrollDirection)scrollDirection
+{
+ return _scrollDirection;
+}
+
+- (NSString*)refreshType
+{
+ return _refreshType;
+}
+- (BOOL)requestGestureShouldStopPropagation:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
+{
+ return [self gestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
+}
+
+#pragma mark UIScrollViewDelegate
+- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
+{
+ if ([_refreshType isEqualToString:@"refreshForAppear"] && _refreshComponent) {
+ [_refreshComponent setIndicatorHidden:NO];
+ }
+
+ if (_scrollStartEvent) {
+ CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
+ NSDictionary *contentSizeData = @{@"width":[NSNumber numberWithFloat:scrollView.contentSize.width / scaleFactor],@"height":[NSNumber numberWithFloat:scrollView.contentSize.height / scaleFactor]};
+ NSDictionary *contentOffsetData = @{@"x":[NSNumber numberWithFloat:-scrollView.contentOffset.x / scaleFactor],@"y":[NSNumber numberWithFloat:-scrollView.contentOffset.y / scaleFactor]};
+ [self fireEvent:@"scrollstart" params:@{@"contentSize":contentSizeData,@"contentOffset":contentOffsetData} domChanges:nil];
+ }
+}
+
+- (void)scrollViewDidScroll:(UIScrollView *)scrollView
+{
+ //apply block which are registered
+ WXSDKInstance *instance = self.weexInstance;
+ if ([self.ref isEqualToString:WX_SDK_ROOT_REF] &&
+ [self isKindOfClass:[WXScrollerComponent class]]) {
+ if (instance.onScroll) {
+ instance.onScroll(scrollView.contentOffset);
+ }
+ }
+
+ if (_lastContentOffset.x > scrollView.contentOffset.x) {
+ _direction = @"right";
+ } else if (_lastContentOffset.x < scrollView.contentOffset.x) {
+ _direction = @"left";
+ if (WXScrollDirectionHorizontal == _scrollDirection) {
+ [self handleLoadMore];
+ }
+ } else if(_lastContentOffset.y > scrollView.contentOffset.y) {
+ _direction = @"down";
+ } else if(_lastContentOffset.y < scrollView.contentOffset.y) {
+ _direction = @"up";
+ if (WXScrollDirectionVertical == _scrollDirection) {
+ [self handleLoadMore];
+ }
+ }
+
+ CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
+ [_refreshComponent pullingdown:@{
+ REFRESH_DISTANCE_Y: @(fabs((scrollView.contentOffset.y - _lastContentOffset.y)/scaleFactor)),
+ REFRESH_VIEWHEIGHT: @(_refreshComponent.view.frame.size.height/scaleFactor),
+ REFRESH_PULLINGDISTANCE: @(scrollView.contentOffset.y/scaleFactor),
+ @"type":@"pullingdown"
+ }];
+ _lastContentOffset = scrollView.contentOffset;
+ // check sticky
+ [self adjustSticky];
+ [self handleAppear];
+
+ if (self.onScroll) {
+ self.onScroll(scrollView);
+ }
+ if (_scrollEvent) {
+ NSDictionary *contentSizeData = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithFloat:scrollView.contentSize.width / scaleFactor],@"width",[NSNumber numberWithFloat:scrollView.contentSize.height / scaleFactor],@"height", nil];
+ //contentOffset values are replaced by (-contentOffset.x,-contentOffset.y) ,in order to be consistent with Android client.
+ NSDictionary *contentOffsetData = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithFloat:-scrollView.contentOffset.x / scaleFactor],@"x",[NSNumber numberWithFloat:-scrollView.contentOffset.y / scaleFactor],@"y", nil];
+ CGFloat distance = 0;
+ if (_scrollDirection == WXScrollDirectionHorizontal) {
+ distance = scrollView.contentOffset.x - _lastScrollEventFiredOffset.x;
+ } else {
+ distance = scrollView.contentOffset.y - _lastScrollEventFiredOffset.y;
+ }
+ if (fabs(distance) >= _offsetAccuracy) {
+ [self fireEvent:@"scroll" params:@{@"contentSize":contentSizeData,@"contentOffset":contentOffsetData} domChanges:nil];
+ _lastScrollEventFiredOffset = scrollView.contentOffset;
+ }
+ }
+ NSHashTable *delegates = [_delegates copy];
+ for (id<UIScrollViewDelegate> delegate in delegates) {
+ if ([delegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
+ [delegate scrollViewDidScroll:scrollView];
+ }
+ }
+}
+
+- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
+{
+ UIEdgeInsets inset = [scrollView contentInset];
+
+// currently only set contentInset when loading
+// if ([_refreshComponent displayState]) {
+// inset.top = _refreshComponent.view.frame.size.height;
+// }
+// else {
+// inset.top = 0;
+// }
+
+ if ([_loadingComponent displayState]) {
+ inset.bottom = _loadingComponent.view.frame.size.height;
+ } else {
+ inset.bottom = 0;
+ }
+ [scrollView setContentInset:inset];
+}
+
+- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
+{
+ if (_scrollEndEvent) {
+ if (!_isScrolling) {
+ CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
+ NSDictionary *contentSizeData = @{@"width":[NSNumber numberWithFloat:scrollView.contentSize.width / scaleFactor],@"height":[NSNumber numberWithFloat:scrollView.contentSize.height / scaleFactor]};
+ NSDictionary *contentOffsetData = @{@"x":[NSNumber numberWithFloat:-scrollView.contentOffset.x / scaleFactor],@"y":[NSNumber numberWithFloat:-scrollView.contentOffset.y / scaleFactor]};
+ [self fireEvent:@"scrollend" params:@{@"contentSize":contentSizeData,@"contentOffset":contentOffsetData} domChanges:nil];
+ }
+ }
+}
+
+- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
+{
+ if ([_refreshType isEqualToString:@"refreshForAppear"]) {
+ if(targetContentOffset == nil)
+ return;
+ CGPoint offset = *targetContentOffset;
+ if(velocity.y <= 0) {
+ // drop down
+ if( offset.y <= _refreshComponent.calculatedFrame.size.height ) {
+ [self loadMoreIfNeed];
+ }
+ } else if (velocity.y > 0) {
+ // drop up
+ }
+ }
+}
+
+- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
+{
+ [_loadingComponent.view setHidden:NO];
+ [_refreshComponent.view setHidden:NO];
+
+ //refresh
+ if ([_refreshType isEqualToString:@"refreshForWholeVisible"]) {
+ if (_refreshComponent && scrollView.contentOffset.y < 0 && scrollView.contentOffset.y + _refreshComponent.calculatedFrame.size.height < _refreshComponent.calculatedFrame.origin.y) {
+ [_refreshComponent refresh];
+ }
+ }
+
+ //loading
+ if (_loadingComponent && scrollView.contentOffset.y > 0 &&
+ scrollView.contentOffset.y + scrollView.frame.size.height > _loadingComponent.view.frame.origin.y + _loadingComponent.calculatedFrame.size.height) {
+ [_loadingComponent loading];
+ }
+ if (!decelerate) {
+ _isScrolling = NO;
+ [self performSelector:@selector(scrollViewDidEndDecelerating:) withObject:nil afterDelay:0.1];
+ }
+}
+
+- (void)loadMoreIfNeed
+{
+ WXScrollerComponentView* scrollView = (WXScrollerComponentView *)self.view;
+ if (scrollView.isDragging || scrollView.isTracking || scrollView.isDecelerating) {
+ [self performSelector:@selector(loadMoreIfNeed) withObject:nil afterDelay:0.1];
+ return;
+ }
+ [_refreshComponent refresh];
+}
+
+- (void)handleAppear
+{
+ if (![self isViewLoaded]) {
+ return;
+ }
+ UIScrollView *scrollView = (UIScrollView *)self.view;
+ CGFloat vx = scrollView.contentInset.left + scrollView.contentOffset.x;
+ CGFloat vy = scrollView.contentInset.top + scrollView.contentOffset.y;
+ CGFloat vw = scrollView.frame.size.width - scrollView.contentInset.left - scrollView.contentInset.right;
+ CGFloat vh = scrollView.frame.size.height - scrollView.contentInset.top - scrollView.contentInset.bottom;
+ CGRect scrollRect = CGRectMake(vx, vy, vw, vh);;
+
+ // notify action for appear
+ NSArray *listenerArrayCopy = [self.listenerArray copy];
+ for(WXScrollToTarget *target in listenerArrayCopy){
+ if (_shouldNotifiAppearDescendantView) {
+ // if target component is descendant of scrollerview, it should notify the appear event handler, or here will skip this appear calculation.
+ if ([target.target isViewLoaded] && [target.target.view isDescendantOfView:self.view]) {
+ [self scrollToTarget:target scrollRect:scrollRect];
+ }
+ } else {
+ [self scrollToTarget:target scrollRect:scrollRect];
+ }
+ }
+}
+
+- (CGPoint)absolutePositionForComponent:(WXComponent *)component
+{
+ return [component->_view.superview convertPoint:component->_view.frame.origin toView:_view];
+}
+
+#pragma mark Private Methods
+
+- (void)scrollToTarget:(WXScrollToTarget *)target scrollRect:(CGRect)rect
+{
+ WXComponent *component = target.target;
+ if (![component isViewLoaded]) {
+ return;
+ }
+
+ CGFloat ctop;
+ if (component && component->_view && component->_view.superview) {
+ ctop = [self absolutePositionForComponent:component].y;
+ } else {
+ ctop = 0.0;
+ }
+ CGFloat cbottom = ctop + CGRectGetHeight(component.calculatedFrame);
+ CGFloat cleft;
+ if (component && component->_view && component->_view.superview) {
+ cleft = [self absolutePositionForComponent:component].x;
+ } else {
+ cleft = 0.0;
+ }
+ CGFloat cright = cleft + CGRectGetWidth(component.calculatedFrame);
+
+ CGFloat vtop = CGRectGetMinY(rect), vbottom = CGRectGetMaxY(rect), vleft = CGRectGetMinX(rect), vright = CGRectGetMaxX(rect);
+ if(cbottom > vtop && ctop <= vbottom && cleft <= vright && cright > vleft){
+ if(!target.hasAppear && component){
+ target.hasAppear = YES;
+ if (component->_appearEvent) {
+// NSLog(@"appear:%@, %.2f", component, ctop);
+ [component fireEvent:@"appear" params:_direction ? @{@"direction":_direction} : nil];
+ }
+ }
+ } else {
+ if(target.hasAppear && component){
+ target.hasAppear = NO;
+ if(component->_disappearEvent){
+// NSLog(@"disappear:%@", component);
+ [component fireEvent:@"disappear" params:_direction ? @{@"direction":_direction} : nil];
+ }
+ }
+ }
+}
+
+- (void)handleLoadMore
+{
+ if (_listenLoadMore && [self isNeedLoadMore]) {
+ [self loadMore];
+ }
+}
+
+#pragma mark Layout
+
+- (NSUInteger)_childrenCountForLayout;
+{
+ return 0;
+}
+
+- (NSUInteger)childrenCountForScrollerLayout
+{
+ return [super _childrenCountForLayout];
+}
+
+- (void)_calculateFrameWithSuperAbsolutePosition:(CGPoint)superAbsolutePosition
+ gatherDirtyComponents:(NSMutableSet<WXComponent *> *)dirtyComponents
+{
+ /**
+ * Pretty hacky way
+ * layout from root to scroller to get scroller's frame,
+ * layout from children to scroller to get scroller's contentSize
+ */
+ if ([self needsLayout]) {
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ memcpy(_scrollerCSSNode, self.cssNode, sizeof(css_node_t));
+ _scrollerCSSNode->children_count = (int)[self childrenCountForScrollerLayout];
+
+ _scrollerCSSNode->style.position[CSS_LEFT] = 0;
+ _scrollerCSSNode->style.position[CSS_TOP] = 0;
+
+ if (_scrollDirection == WXScrollDirectionVertical) {
+ _scrollerCSSNode->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN;
+ _scrollerCSSNode->style.dimensions[CSS_WIDTH] = _cssNode->layout.dimensions[CSS_WIDTH];
+ _scrollerCSSNode->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
+ } else {
+ _scrollerCSSNode->style.flex_direction = CSS_FLEX_DIRECTION_ROW;
+ _scrollerCSSNode->style.dimensions[CSS_HEIGHT] = _cssNode->layout.dimensions[CSS_HEIGHT];
+ _scrollerCSSNode->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
+ }
+
+ _scrollerCSSNode->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
+ _scrollerCSSNode->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
+
+ layoutNode(_scrollerCSSNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
+ if ([WXLog logLevel] >= WXLogLevelDebug) {
+ print_css_node(_scrollerCSSNode, (css_print_options_t)(CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN));
+ }
+ CGSize size = {
+ WXRoundPixelValue(_scrollerCSSNode->layout.dimensions[CSS_WIDTH]),
+ WXRoundPixelValue(_scrollerCSSNode->layout.dimensions[CSS_HEIGHT])
+ };
+
+ if (!CGSizeEqualToSize(size, _contentSize)) {
+ _contentSize = size;
+ [dirtyComponents addObject:self];
+ }
+
+ _scrollerCSSNode->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
+ _scrollerCSSNode->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
+ }
+
+//#else
+ else
+ {
+ _flexScrollerCSSNode->copyStyle(_flexCssNode);
+ _flexScrollerCSSNode->copyMeasureFunc(_flexCssNode);
+
+ if (_scrollDirection == WXScrollDirectionVertical) {
+ _flexScrollerCSSNode->setFlexDirection(WeexCore::kFlexDirectionColumn,NO);
+ _flexScrollerCSSNode->setStyleWidth(self.flexCssNode->getLayoutWidth(),NO);
+ _flexScrollerCSSNode->setStyleHeight(FlexUndefined);
+ } else {
+ _flexScrollerCSSNode->setFlexDirection(WeexCore::kFlexDirectionRow,NO);
+ _flexScrollerCSSNode->setStyleHeight(self.flexCssNode->getLayoutHeight());
+ _flexScrollerCSSNode->setStyleWidth(FlexUndefined,NO);
+ }
+ _flexScrollerCSSNode->markDirty();
+ std::pair<float, float> renderPageSize;
+ renderPageSize.first = self.weexInstance.frame.size.width;
+ renderPageSize.second = self.weexInstance.frame.size.height;
+ _flexScrollerCSSNode->calculateLayout(renderPageSize);
+ CGSize size = {
+ WXRoundPixelValue(_flexScrollerCSSNode->getLayoutWidth()),
+ WXRoundPixelValue(_flexScrollerCSSNode->getLayoutHeight())
+ };
+
+ if (!CGSizeEqualToSize(size, _contentSize)) {
+ // content size
+ _contentSize = size;
+ [dirtyComponents addObject:self];
+ }
+ }
+//#endif
+ }
+
+ [super _calculateFrameWithSuperAbsolutePosition:superAbsolutePosition gatherDirtyComponents:dirtyComponents];
+}
+
+@end
[16/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
[WEEX-311] [iOS] use new layoutEngin to replace yoga
* [iOS] support new flex-layout engine
* [iOS] update layout engin
* [iOS] fix recycleList copy node error
* [iOS] rm useless code
* [iOS] rtl old layout engin
* [iOS] rm uselse code
* [iOS] format log
* [iOS] rm useless code
close #1135
Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/b77b4259
Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/b77b4259
Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/b77b4259
Branch: refs/heads/master
Commit: b77b42599e763b2e25a63d6a9c576eeb388494dd
Parents: bf14bcd
Author: MrRaindrop <te...@gmail.com>
Authored: Tue Oct 31 22:15:05 2017 +0800
Committer: acton393 <zh...@gmail.com>
Committed: Thu Apr 26 20:38:59 2018 +0800
----------------------------------------------------------------------
WeexSDK.podspec | 2 +-
ios/playground/WeexDemo/WXExtModule.m | 7 +
ios/sdk/WeexSDK.xcodeproj/project.pbxproj | 327 ++--
.../WeexSDK/Sources/Bridge/WXBridgeContext.m | 38 +
.../Component/RecycleList/WXCellSlotComponent.m | 94 -
.../RecycleList/WXCellSlotComponent.mm | 117 ++
.../RecycleList/WXRecycleListComponent.m | 618 ------
.../RecycleList/WXRecycleListComponent.mm | 619 ++++++
.../Component/Recycler/WXRecyclerComponent.m | 738 -------
.../Component/Recycler/WXRecyclerComponent.mm | 768 ++++++++
.../WeexSDK/Sources/Component/WXCellComponent.m | 145 --
.../Sources/Component/WXCellComponent.mm | 174 ++
.../Sources/Component/WXComponent_internal.h | 11 +-
.../Sources/Component/WXCycleSliderComponent.m | 681 -------
.../Sources/Component/WXCycleSliderComponent.mm | 692 +++++++
.../WeexSDK/Sources/Component/WXEditComponent.m | 918 ---------
.../Sources/Component/WXEditComponent.mm | 971 ++++++++++
.../Sources/Component/WXHeaderComponent.m | 87 -
.../Sources/Component/WXHeaderComponent.mm | 110 ++
.../WeexSDK/Sources/Component/WXListComponent.m | 975 ----------
.../Sources/Component/WXListComponent.mm | 997 ++++++++++
.../Sources/Component/WXLoadingComponent.m | 152 --
.../Sources/Component/WXLoadingComponent.mm | 164 ++
.../Sources/Component/WXRefreshComponent.m | 207 --
.../Sources/Component/WXRefreshComponent.mm | 224 +++
.../Sources/Component/WXScrollerComponent.h | 2 -
.../Sources/Component/WXScrollerComponent.m | 884 ---------
.../Sources/Component/WXScrollerComponent.mm | 951 +++++++++
.../Component/WXSliderNeighborComponent.m | 1803 -----------------
.../Component/WXSliderNeighborComponent.mm | 1812 ++++++++++++++++++
.../Sources/Component/WXSwitchComponent.m | 151 --
.../Sources/Component/WXSwitchComponent.mm | 162 ++
.../Sources/Component/WXTextAreaComponent.m | 233 ---
.../Sources/Component/WXTextAreaComponent.mm | 261 +++
.../WeexSDK/Sources/Component/WXTextComponent.m | 1076 -----------
.../Sources/Component/WXTextComponent.mm | 1170 +++++++++++
ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m | 2 +
.../WeexSDK/Sources/Layout/WXComponent+Layout.h | 60 +-
.../Sources/Layout/WXComponent+Layout.mm | 963 ++++++++++
ios/sdk/WeexSDK/Sources/Layout/WXCoreFlexEnum.h | 107 ++
ios/sdk/WeexSDK/Sources/Layout/WXCoreLayout.cpp | 1038 ++++++++++
ios/sdk/WeexSDK/Sources/Layout/WXCoreLayout.h | 1073 +++++++++++
ios/sdk/WeexSDK/Sources/Layout/WXCoreStyle.cpp | 247 +++
ios/sdk/WeexSDK/Sources/Layout/WXCoreStyle.h | 285 +++
ios/sdk/WeexSDK/Sources/Layout/WXLayoutDefine.h | 6 +
.../Sources/Layout/WXScrollerComponent+Layout.h | 42 +
.../Layout/WXScrollerComponent+Layout.mm | 53 +
.../Sources/Manager/WXComponentManager.m | 958 ---------
.../Sources/Manager/WXComponentManager.mm | 1158 +++++++++++
ios/sdk/WeexSDK/Sources/Model/WXComponent.h | 13 +-
ios/sdk/WeexSDK/Sources/Model/WXComponent.m | 842 --------
ios/sdk/WeexSDK/Sources/Model/WXComponent.mm | 953 +++++++++
ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m | 16 +-
ios/sdk/WeexSDK/Sources/Module/WXTransition.m | 519 -----
ios/sdk/WeexSDK/Sources/Module/WXTransition.mm | 519 +++++
ios/sdk/WeexSDK/Sources/Utility/WXConvert.m | 23 +
ios/sdk/WeexSDK/Sources/Utility/WXUtility.h | 17 +-
.../Sources/View/WXComponent+ViewManagement.m | 346 ----
.../Sources/View/WXComponent+ViewManagement.mm | 347 ++++
ios/sdk/WeexSDK/Sources/View/WXRootView.m | 2 +-
ios/sdk/WeexSDK/Sources/WeexSDK.h | 4 +
61 files changed, 16358 insertions(+), 11576 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/WeexSDK.podspec
----------------------------------------------------------------------
diff --git a/WeexSDK.podspec b/WeexSDK.podspec
index 21f80ee..53f0a17 100644
--- a/WeexSDK.podspec
+++ b/WeexSDK.podspec
@@ -27,7 +27,7 @@ Pod::Spec.new do |s|
s.platform = :ios
s.ios.deployment_target = '8.0'
s.source = { :path => '.' }
- s.source_files = 'ios/sdk/WeexSDK/Sources/**/*.{h,m,mm,c}'
+ s.source_files = 'ios/sdk/WeexSDK/Sources/**/*.{h,m,mm,c,cpp}'
s.resources = 'pre-build/*.js','ios/sdk/WeexSDK/Resources/wx_load_error@3x.png'
s.user_target_xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => "'$(PODS_ROOT)/WeexSDK'" }
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/playground/WeexDemo/WXExtModule.m
----------------------------------------------------------------------
diff --git a/ios/playground/WeexDemo/WXExtModule.m b/ios/playground/WeexDemo/WXExtModule.m
index fe034e7..16b2e0a 100644
--- a/ios/playground/WeexDemo/WXExtModule.m
+++ b/ios/playground/WeexDemo/WXExtModule.m
@@ -32,8 +32,15 @@ WX_EXPORT_METHOD(@selector(generateCover:))
setenv("GCOV_PREFIX", [documentsDirectory cStringUsingEncoding:NSUTF8StringEncoding], 1);
setenv("GCOV_PREFIX_STRIP", "6", 1);
#endif
+
+#if defined __cplusplus
+ extern "C" {
+#endif
extern void __gcov_flush(void);
__gcov_flush();
+#if defined __cplusplus
+ };
+#endif
if (callback) {
NSDictionary * result = @{@"ok": @true};
[15/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
index a4c0db2..e017293 100644
--- a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
+++ b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
@@ -11,6 +11,11 @@
170B4664208733AF00562666 /* WXAnalyzerCenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C74F092072145000AB4CAB /* WXAnalyzerCenter.h */; settings = {ATTRIBUTES = (Public, ); }; };
170B4665208733BF00562666 /* WXAnalyzerCenter+Transfer.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C74F082072145000AB4CAB /* WXAnalyzerCenter+Transfer.h */; };
170B4668208733E500562666 /* WXAnalyzerCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C74F0A2072145100AB4CAB /* WXAnalyzerCenter.m */; };
+ 176BE43C209172330086B6AF /* WXComponent+Layout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 176BE43B209172330086B6AF /* WXComponent+Layout.mm */; };
+ 176BE43D209172A30086B6AF /* WXScrollerComponent+Layout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 17C7B4FF20452E1800A2296A /* WXScrollerComponent+Layout.mm */; };
+ 176BE43E209172E20086B6AF /* WXScrollerComponent+Layout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 17C7B4FF20452E1800A2296A /* WXScrollerComponent+Layout.mm */; };
+ 178EDF0D204FEC1300917F6B /* WXScrollerComponent+Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C7B50020452E1800A2296A /* WXScrollerComponent+Layout.h */; };
+ 178EDF0F204FF11700917F6B /* WXCoreLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C7B50420452E1800A2296A /* WXCoreLayout.h */; settings = {ATTRIBUTES = (Public, ); }; };
17B122212090AA9300387E33 /* WXSDKInstance_performance.m in Sources */ = {isa = PBXBuildFile; fileRef = 17B1221F2090AA9300387E33 /* WXSDKInstance_performance.m */; };
17B122222090AA9300387E33 /* WXSDKInstance_performance.h in Headers */ = {isa = PBXBuildFile; fileRef = 17B122202090AA9300387E33 /* WXSDKInstance_performance.h */; };
17B122252090AAB000387E33 /* WXSDKError.h in Headers */ = {isa = PBXBuildFile; fileRef = 17B122232090AAB000387E33 /* WXSDKError.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -20,6 +25,17 @@
17C74F0C2072145100AB4CAB /* WXAnalyzerCenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C74F092072145000AB4CAB /* WXAnalyzerCenter.h */; settings = {ATTRIBUTES = (Public, ); }; };
17C74F0D2072145100AB4CAB /* WXAnalyzerCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C74F0A2072145100AB4CAB /* WXAnalyzerCenter.m */; };
17C74F0F2072147B00AB4CAB /* WXAnalyzerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C74F0E2072147A00AB4CAB /* WXAnalyzerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 17C7B50620452E1800A2296A /* WXCoreLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C7B4FE20452E1700A2296A /* WXCoreLayout.cpp */; };
+ 17C7B50820452E1800A2296A /* WXScrollerComponent+Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C7B50020452E1800A2296A /* WXScrollerComponent+Layout.h */; };
+ 17C7B50920452E1800A2296A /* WXCoreStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C7B50120452E1800A2296A /* WXCoreStyle.cpp */; };
+ 17C7B50A20452E1800A2296A /* WXCoreFlexEnum.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C7B50220452E1800A2296A /* WXCoreFlexEnum.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 17C7B50B20452E1800A2296A /* WXCoreStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C7B50320452E1800A2296A /* WXCoreStyle.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 17E5ACD82091F02D00EE81F1 /* WXCoreLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C7B50420452E1800A2296A /* WXCoreLayout.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 17E5ACD92091F03700EE81F1 /* WXCoreStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C7B50320452E1800A2296A /* WXCoreStyle.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 17E5ACDA2091F05000EE81F1 /* WXCoreFlexEnum.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C7B50220452E1800A2296A /* WXCoreFlexEnum.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 17E5ACDB2091F05700EE81F1 /* WXComponent+Layout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 176BE43B209172330086B6AF /* WXComponent+Layout.mm */; };
+ 17E5ACDC2091F05E00EE81F1 /* WXCoreLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C7B4FE20452E1700A2296A /* WXCoreLayout.cpp */; };
+ 17E5ACDD2091F06300EE81F1 /* WXCoreStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C7B50120452E1800A2296A /* WXCoreStyle.cpp */; };
17F2D6E72087227300084378 /* WXAnalyzerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C74F0E2072147A00AB4CAB /* WXAnalyzerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
1C1A2BED1D91172800539AA1 /* WXConvertTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C1A2BEC1D91172800539AA1 /* WXConvertTests.m */; };
1D3000F11D40B9AC004F3B4F /* WXClipboardModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D3000EF1D40B9AB004F3B4F /* WXClipboardModule.h */; };
@@ -30,13 +46,13 @@
2A42AF8A1C23B33E00818EA6 /* WeexSDK_MTL.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A42AF891C23B33E00818EA6 /* WeexSDK_MTL.m */; };
2A4445BF1CA8FD56009E7C6D /* WXTextComponentProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A4445BE1CA8FD56009E7C6D /* WXTextComponentProtocol.h */; };
2A60CE9C1C91733E00857B9F /* WXSwitchComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A60CE9A1C91733E00857B9F /* WXSwitchComponent.h */; };
- 2A60CE9D1C91733E00857B9F /* WXSwitchComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A60CE9B1C91733E00857B9F /* WXSwitchComponent.m */; };
+ 2A60CE9D1C91733E00857B9F /* WXSwitchComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A60CE9B1C91733E00857B9F /* WXSwitchComponent.mm */; };
2A837AB21CD9DE9200AEDF03 /* WXLoadingComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A837AAC1CD9DE9200AEDF03 /* WXLoadingComponent.h */; };
- 2A837AB31CD9DE9200AEDF03 /* WXLoadingComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A837AAD1CD9DE9200AEDF03 /* WXLoadingComponent.m */; };
+ 2A837AB31CD9DE9200AEDF03 /* WXLoadingComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A837AAD1CD9DE9200AEDF03 /* WXLoadingComponent.mm */; };
2A837AB41CD9DE9200AEDF03 /* WXLoadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A837AAE1CD9DE9200AEDF03 /* WXLoadingIndicator.h */; };
2A837AB51CD9DE9200AEDF03 /* WXLoadingIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A837AAF1CD9DE9200AEDF03 /* WXLoadingIndicator.m */; };
2A837AB61CD9DE9200AEDF03 /* WXRefreshComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A837AB01CD9DE9200AEDF03 /* WXRefreshComponent.h */; };
- 2A837AB71CD9DE9200AEDF03 /* WXRefreshComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A837AB11CD9DE9200AEDF03 /* WXRefreshComponent.m */; };
+ 2A837AB71CD9DE9200AEDF03 /* WXRefreshComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A837AB11CD9DE9200AEDF03 /* WXRefreshComponent.mm */; };
2A8E658A1C7C7AA20025C7B7 /* WXVideoComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A8E65881C7C7AA20025C7B7 /* WXVideoComponent.h */; };
2A8E658B1C7C7AA20025C7B7 /* WXVideoComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A8E65891C7C7AA20025C7B7 /* WXVideoComponent.m */; };
2A919DA61E321F1F006EB6B5 /* WXBridgeMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A919DA41E321F1F006EB6B5 /* WXBridgeMethod.h */; };
@@ -49,11 +65,9 @@
2AFEB17B1C747139000507FA /* WXInstanceWrap.h in Headers */ = {isa = PBXBuildFile; fileRef = 2AFEB1791C747139000507FA /* WXInstanceWrap.h */; };
2AFEB17C1C747139000507FA /* WXInstanceWrap.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AFEB17A1C747139000507FA /* WXInstanceWrap.m */; };
333D9A271F41507A007CED39 /* WXTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = 333D9A251F41507A007CED39 /* WXTransition.h */; };
- 333D9A281F41507A007CED39 /* WXTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = 333D9A251F41507A007CED39 /* WXTransition.h */; };
- 333D9A291F41507A007CED39 /* WXTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 333D9A261F41507A007CED39 /* WXTransition.m */; };
- 333D9A2A1F41507A007CED39 /* WXTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 333D9A261F41507A007CED39 /* WXTransition.m */; };
+ 333D9A2A1F41507A007CED39 /* WXTransition.mm in Sources */ = {isa = PBXBuildFile; fileRef = 333D9A261F41507A007CED39 /* WXTransition.mm */; };
37B51EE41E97804D0040A743 /* WXCycleSliderComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 37B51EE21E97804D0040A743 /* WXCycleSliderComponent.h */; };
- 37B51EE51E97804D0040A743 /* WXCycleSliderComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B51EE31E97804D0040A743 /* WXCycleSliderComponent.m */; };
+ 37B51EE51E97804D0040A743 /* WXCycleSliderComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37B51EE31E97804D0040A743 /* WXCycleSliderComponent.mm */; };
591324A31D49B7F1004E89ED /* WXTimerModuleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 591324A21D49B7F1004E89ED /* WXTimerModuleTests.m */; };
591DD3311D23AD5800BE8709 /* WXErrorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 591DD32F1D23AD5800BE8709 /* WXErrorView.m */; };
591DD3321D23AD5800BE8709 /* WXErrorView.h in Headers */ = {isa = PBXBuildFile; fileRef = 591DD3301D23AD5800BE8709 /* WXErrorView.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -101,7 +115,7 @@
740938F91D3D0E0300DBB801 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 740938F81D3D0E0300DBB801 /* MediaPlayer.framework */; };
740938FB1D3D0E1700DBB801 /* AVKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 740938FA1D3D0E1700DBB801 /* AVKit.framework */; };
7410811F1CED585A001BC6E5 /* WXComponentManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 7410811D1CED585A001BC6E5 /* WXComponentManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 741081201CED585A001BC6E5 /* WXComponentManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7410811E1CED585A001BC6E5 /* WXComponentManager.m */; };
+ 741081201CED585A001BC6E5 /* WXComponentManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7410811E1CED585A001BC6E5 /* WXComponentManager.mm */; };
741081231CED6756001BC6E5 /* WXComponentFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 741081211CED6756001BC6E5 /* WXComponentFactory.h */; };
741081241CED6756001BC6E5 /* WXComponentFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 741081221CED6756001BC6E5 /* WXComponentFactory.m */; };
741081261CEDB4EC001BC6E5 /* WXComponent_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 741081251CEDB4EC001BC6E5 /* WXComponent_internal.h */; };
@@ -127,18 +141,17 @@
743933B51C7ED9AA00773BB7 /* WXSimulatorShortcutManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 743933B31C7ED9AA00773BB7 /* WXSimulatorShortcutManager.m */; };
744BEA551D05178F00452B5D /* WXComponent+Display.h in Headers */ = {isa = PBXBuildFile; fileRef = 744BEA531D05178F00452B5D /* WXComponent+Display.h */; };
744BEA561D05178F00452B5D /* WXComponent+Display.m in Sources */ = {isa = PBXBuildFile; fileRef = 744BEA541D05178F00452B5D /* WXComponent+Display.m */; };
- 744BEA591D0520F300452B5D /* WXComponent+Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = 744BEA571D0520F300452B5D /* WXComponent+Layout.h */; };
- 744BEA5A1D0520F300452B5D /* WXComponent+Layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 744BEA581D0520F300452B5D /* WXComponent+Layout.m */; };
+ 744BEA591D0520F300452B5D /* WXComponent+Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = 744BEA571D0520F300452B5D /* WXComponent+Layout.h */; settings = {ATTRIBUTES = (Public, ); }; };
744D610C1E49978200B624B3 /* WXHeaderComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 744D610A1E49978200B624B3 /* WXHeaderComponent.h */; };
- 744D610D1E49978200B624B3 /* WXHeaderComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 744D610B1E49978200B624B3 /* WXHeaderComponent.m */; };
+ 744D610D1E49978200B624B3 /* WXHeaderComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 744D610B1E49978200B624B3 /* WXHeaderComponent.mm */; };
744D61101E49979000B624B3 /* WXFooterComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 744D610E1E49979000B624B3 /* WXFooterComponent.h */; };
744D61111E49979000B624B3 /* WXFooterComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 744D610F1E49979000B624B3 /* WXFooterComponent.m */; };
744D61141E4AF23E00B624B3 /* WXDiffUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 744D61121E4AF23E00B624B3 /* WXDiffUtil.h */; };
744D61151E4AF23E00B624B3 /* WXDiffUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 744D61131E4AF23E00B624B3 /* WXDiffUtil.m */; };
745B2D681E5A8E1E0092D38A /* WXMultiColumnLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 745B2D5E1E5A8E1E0092D38A /* WXMultiColumnLayout.h */; };
745B2D691E5A8E1E0092D38A /* WXMultiColumnLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D5F1E5A8E1E0092D38A /* WXMultiColumnLayout.m */; };
- 745B2D6A1E5A8E1E0092D38A /* WXRecyclerComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 745B2D601E5A8E1E0092D38A /* WXRecyclerComponent.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 745B2D6B1E5A8E1E0092D38A /* WXRecyclerComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.m */; };
+ 745B2D6A1E5A8E1E0092D38A /* WXRecyclerComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 745B2D601E5A8E1E0092D38A /* WXRecyclerComponent.h */; };
+ 745B2D6B1E5A8E1E0092D38A /* WXRecyclerComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.mm */; };
745B2D6C1E5A8E1E0092D38A /* WXRecyclerDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 745B2D621E5A8E1E0092D38A /* WXRecyclerDataController.h */; };
745B2D6D1E5A8E1E0092D38A /* WXRecyclerDataController.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D631E5A8E1E0092D38A /* WXRecyclerDataController.m */; };
745B2D6E1E5A8E1E0092D38A /* WXRecyclerUpdateController.h in Headers */ = {isa = PBXBuildFile; fileRef = 745B2D641E5A8E1E0092D38A /* WXRecyclerUpdateController.h */; };
@@ -160,9 +173,9 @@
7469869F1C4E2C000054A57E /* NSArray+Weex.h in Headers */ = {isa = PBXBuildFile; fileRef = 7469869D1C4E2C000054A57E /* NSArray+Weex.h */; };
746986A01C4E2C010054A57E /* NSArray+Weex.m in Sources */ = {isa = PBXBuildFile; fileRef = 7469869E1C4E2C000054A57E /* NSArray+Weex.m */; };
746B923B1F46BE36009AE86B /* WXCellSlotComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 746B92391F46BE36009AE86B /* WXCellSlotComponent.h */; };
- 746B923C1F46BE36009AE86B /* WXCellSlotComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 746B923A1F46BE36009AE86B /* WXCellSlotComponent.m */; };
+ 746B923C1F46BE36009AE86B /* WXCellSlotComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 746B923A1F46BE36009AE86B /* WXCellSlotComponent.mm */; };
747A787C1D1BAAC900DED9D0 /* WXComponent+ViewManagement.h in Headers */ = {isa = PBXBuildFile; fileRef = 747A787A1D1BAAC900DED9D0 /* WXComponent+ViewManagement.h */; };
- 747A787D1D1BAAC900DED9D0 /* WXComponent+ViewManagement.m in Sources */ = {isa = PBXBuildFile; fileRef = 747A787B1D1BAAC900DED9D0 /* WXComponent+ViewManagement.m */; };
+ 747A787D1D1BAAC900DED9D0 /* WXComponent+ViewManagement.mm in Sources */ = {isa = PBXBuildFile; fileRef = 747A787B1D1BAAC900DED9D0 /* WXComponent+ViewManagement.mm */; };
747DF6821E31AEE4005C53A8 /* WXLength.h in Headers */ = {isa = PBXBuildFile; fileRef = 747DF6801E31AEE4005C53A8 /* WXLength.h */; };
747DF6831E31AEE4005C53A8 /* WXLength.m in Sources */ = {isa = PBXBuildFile; fileRef = 747DF6811E31AEE4005C53A8 /* WXLength.m */; };
74862F791E02B88D00B7A041 /* JSValue+Weex.h in Headers */ = {isa = PBXBuildFile; fileRef = 74862F771E02B88D00B7A041 /* JSValue+Weex.h */; };
@@ -193,7 +206,7 @@
74AD99851D5B0E59008F0336 /* WXPolyfillSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 74AD99831D5B0E59008F0336 /* WXPolyfillSet.m */; };
74B232D21D2A2BA4006322EA /* WXLayoutDefine.h in Headers */ = {isa = PBXBuildFile; fileRef = 74B232D11D2A2BA4006322EA /* WXLayoutDefine.h */; settings = {ATTRIBUTES = (Public, ); }; };
74B81AE31F73C3E300D3A61D /* WXRecycleListComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CFDD371F45939C007A1A66 /* WXRecycleListComponent.h */; };
- 74B81AE41F73C3E500D3A61D /* WXRecycleListComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 74CFDD381F45939C007A1A66 /* WXRecycleListComponent.m */; };
+ 74B81AE41F73C3E500D3A61D /* WXRecycleListComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 74CFDD381F45939C007A1A66 /* WXRecycleListComponent.mm */; };
74B81AE51F73C3E900D3A61D /* WXRecycleListDataManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CFDD3B1F459400007A1A66 /* WXRecycleListDataManager.h */; };
74B81AE61F73C3E900D3A61D /* WXRecycleListDataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 74CFDD3C1F459400007A1A66 /* WXRecycleListDataManager.m */; };
74B81AE71F73C3E900D3A61D /* WXRecycleListTemplateManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CFDD3F1F45941E007A1A66 /* WXRecycleListTemplateManager.h */; };
@@ -203,7 +216,7 @@
74B81AEB1F73C3E900D3A61D /* WXRecycleListLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 74BA4AB11F70F4B600AC29BF /* WXRecycleListLayout.h */; };
74B81AEC1F73C3E900D3A61D /* WXRecycleListLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 74BA4AB21F70F4B600AC29BF /* WXRecycleListLayout.m */; };
74B81AED1F73C3E900D3A61D /* WXCellSlotComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 746B92391F46BE36009AE86B /* WXCellSlotComponent.h */; };
- 74B81AEE1F73C3E900D3A61D /* WXCellSlotComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 746B923A1F46BE36009AE86B /* WXCellSlotComponent.m */; };
+ 74B81AEE1F73C3E900D3A61D /* WXCellSlotComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 746B923A1F46BE36009AE86B /* WXCellSlotComponent.mm */; };
74B81AEF1F73C3E900D3A61D /* WXComponent+DataBinding.h in Headers */ = {isa = PBXBuildFile; fileRef = 7423EB4F1F4ADE30001662D1 /* WXComponent+DataBinding.h */; };
74B81AF01F73C3E900D3A61D /* WXComponent+DataBinding.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7423EB501F4ADE30001662D1 /* WXComponent+DataBinding.mm */; };
74B81AF11F73C3E900D3A61D /* WXJSASTParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 74BF19F61F5139BB00AEE3D7 /* WXJSASTParser.h */; };
@@ -220,11 +233,11 @@
74C896401D2AC2210043B82A /* WeexSDKTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 74C8963F1D2AC2210043B82A /* WeexSDKTests.m */; };
74C896421D2AC2210043B82A /* WeexSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77D160FD1C02DBE70010B15B /* WeexSDK.framework */; };
74CC7A1C1C2BC5F800829368 /* WXCellComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CC7A1A1C2BC5F800829368 /* WXCellComponent.h */; };
- 74CC7A1D1C2BC5F800829368 /* WXCellComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 74CC7A1B1C2BC5F800829368 /* WXCellComponent.m */; };
+ 74CC7A1D1C2BC5F800829368 /* WXCellComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 74CC7A1B1C2BC5F800829368 /* WXCellComponent.mm */; };
74CC7A201C2BF9DC00829368 /* WXListComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CC7A1E1C2BF9DC00829368 /* WXListComponent.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 74CC7A211C2BF9DC00829368 /* WXListComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 74CC7A1F1C2BF9DC00829368 /* WXListComponent.m */; };
+ 74CC7A211C2BF9DC00829368 /* WXListComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 74CC7A1F1C2BF9DC00829368 /* WXListComponent.mm */; };
74CFDD391F45939C007A1A66 /* WXRecycleListComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CFDD371F45939C007A1A66 /* WXRecycleListComponent.h */; };
- 74CFDD3A1F45939C007A1A66 /* WXRecycleListComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 74CFDD381F45939C007A1A66 /* WXRecycleListComponent.m */; };
+ 74CFDD3A1F45939C007A1A66 /* WXRecycleListComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 74CFDD381F45939C007A1A66 /* WXRecycleListComponent.mm */; };
74CFDD3D1F459400007A1A66 /* WXRecycleListDataManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CFDD3B1F459400007A1A66 /* WXRecycleListDataManager.h */; };
74CFDD3E1F459400007A1A66 /* WXRecycleListDataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 74CFDD3C1F459400007A1A66 /* WXRecycleListDataManager.m */; };
74CFDD411F45941E007A1A66 /* WXRecycleListTemplateManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CFDD3F1F45941E007A1A66 /* WXRecycleListTemplateManager.h */; };
@@ -250,7 +263,7 @@
77D161281C02DE1A0010B15B /* WXSDKManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 77D161261C02DE1A0010B15B /* WXSDKManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
77D161291C02DE1A0010B15B /* WXSDKManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 77D161271C02DE1A0010B15B /* WXSDKManager.m */; };
77D161301C02DE4E0010B15B /* WXComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 77D1612E1C02DE4E0010B15B /* WXComponent.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 77D161311C02DE4E0010B15B /* WXComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 77D1612F1C02DE4E0010B15B /* WXComponent.m */; };
+ 77D161311C02DE4E0010B15B /* WXComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 77D1612F1C02DE4E0010B15B /* WXComponent.mm */; };
77D161381C02DE940010B15B /* WXBridgeManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 77D161361C02DE940010B15B /* WXBridgeManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
77D161391C02DE940010B15B /* WXBridgeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 77D161371C02DE940010B15B /* WXBridgeManager.m */; };
77D1613C1C02DEA60010B15B /* WXJSCoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 77D1613A1C02DEA60010B15B /* WXJSCoreBridge.h */; };
@@ -271,13 +284,14 @@
77E65A111C155EA8008B8775 /* WXImageComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 77E65A0F1C155EA8008B8775 /* WXImageComponent.h */; };
77E65A121C155EA8008B8775 /* WXImageComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A101C155EA8008B8775 /* WXImageComponent.m */; };
77E65A151C155EB5008B8775 /* WXTextComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 77E65A131C155EB5008B8775 /* WXTextComponent.h */; };
- 77E65A161C155EB5008B8775 /* WXTextComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A141C155EB5008B8775 /* WXTextComponent.m */; };
+ 77E65A161C155EB5008B8775 /* WXTextComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A141C155EB5008B8775 /* WXTextComponent.mm */; };
77E65A191C155F25008B8775 /* WXScrollerComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 77E65A171C155F25008B8775 /* WXScrollerComponent.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 77E65A1A1C155F25008B8775 /* WXScrollerComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A181C155F25008B8775 /* WXScrollerComponent.m */; };
+ 77E65A1A1C155F25008B8775 /* WXScrollerComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A181C155F25008B8775 /* WXScrollerComponent.mm */; };
841CD1031F9739890081196D /* WXExceptionUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 841CD1021F9739890081196D /* WXExceptionUtils.m */; };
841CD1051F974DFA0081196D /* WXExceptionUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 841CD1041F97399C0081196D /* WXExceptionUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
841CD1061F974DFA0081196D /* WXExceptionUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 841CD1041F97399C0081196D /* WXExceptionUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
841CD1071F974E000081196D /* WXExceptionUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 841CD1021F9739890081196D /* WXExceptionUtils.m */; };
+ 847E1DE41F9A1BCF00A48124 /* WXCoreStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 847E1DE01F9A1BCF00A48124 /* WXCoreStyle.h */; };
9B9E74791FA2DB5800DAAEA9 /* WXTestBridgeMethodDummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B9E74781FA2DB5800DAAEA9 /* WXTestBridgeMethodDummy.m */; };
BA5F00F11FC5AFFE00F76B5C /* WXLocaleModule.h in Headers */ = {isa = PBXBuildFile; fileRef = BA5F00EF1FC5AFFE00F76B5C /* WXLocaleModule.h */; };
BA5F00F21FC5AFFE00F76B5C /* WXLocaleModule.m in Sources */ = {isa = PBXBuildFile; fileRef = BA5F00F01FC5AFFE00F76B5C /* WXLocaleModule.m */; };
@@ -298,7 +312,7 @@
C47B78CF1F2998EE001D3B0C /* WXExtendCallNativeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C47B78CD1F2998EE001D3B0C /* WXExtendCallNativeManager.m */; };
C49642EC1F73E6DF0092CC5A /* WXWebSocketHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = C4F012761E1502A6003378D0 /* WXWebSocketHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
C4B3D6D41E6954300013F38D /* WXEditComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = C4B3D6D21E6954300013F38D /* WXEditComponent.h */; };
- C4B3D6D51E6954300013F38D /* WXEditComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = C4B3D6D31E6954300013F38D /* WXEditComponent.m */; };
+ C4B3D6D51E6954300013F38D /* WXEditComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = C4B3D6D31E6954300013F38D /* WXEditComponent.mm */; };
C4B834271DE69B09007AD27E /* WXPickerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = C4B834251DE69B09007AD27E /* WXPickerModule.m */; };
C4B834281DE69B09007AD27E /* WXPickerModule.h in Headers */ = {isa = PBXBuildFile; fileRef = C4B834261DE69B09007AD27E /* WXPickerModule.h */; };
C4C30DE81E1B833D00786B6C /* WXComponent+PseudoClassManagement.m in Sources */ = {isa = PBXBuildFile; fileRef = C4C30DE61E1B833D00786B6C /* WXComponent+PseudoClassManagement.m */; };
@@ -328,7 +342,7 @@
D362F9501C83EDA20003F546 /* WXWebViewModule.m in Sources */ = {isa = PBXBuildFile; fileRef = D362F94E1C83EDA20003F546 /* WXWebViewModule.m */; };
D3FC0DF71C508B2A002B9E31 /* WXTimerModule.h in Headers */ = {isa = PBXBuildFile; fileRef = D3FC0DF51C508B2A002B9E31 /* WXTimerModule.h */; };
D3FC0DF81C508B2A002B9E31 /* WXTimerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = D3FC0DF61C508B2A002B9E31 /* WXTimerModule.m */; };
- DC03ADB91D508719003F76E7 /* WXTextAreaComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = DC03ADB71D508719003F76E7 /* WXTextAreaComponent.m */; };
+ DC03ADB91D508719003F76E7 /* WXTextAreaComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = DC03ADB71D508719003F76E7 /* WXTextAreaComponent.mm */; };
DC03ADBA1D508719003F76E7 /* WXTextAreaComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = DC03ADB81D508719003F76E7 /* WXTextAreaComponent.h */; };
DC15A3DB2010BC93009C8977 /* weex-main-jsfm.js in Resources */ = {isa = PBXBuildFile; fileRef = DC15A3D92010BC93009C8977 /* weex-main-jsfm.js */; };
DC15A3DC2010BC93009C8977 /* weex-rax-api.js in Resources */ = {isa = PBXBuildFile; fileRef = DC15A3DA2010BC93009C8977 /* weex-rax-api.js */; };
@@ -342,7 +356,6 @@
DC9F46871D61BA8C00A88239 /* wx_load_error@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 59AC02501D2A7E6E00355112 /* wx_load_error@3x.png */; };
DCA0EF641D6EED6F00CB18B9 /* WXGlobalEventModule.h in Headers */ = {isa = PBXBuildFile; fileRef = DCA0EF621D6EED6F00CB18B9 /* WXGlobalEventModule.h */; };
DCA0EF651D6EED6F00CB18B9 /* WXGlobalEventModule.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA0EF631D6EED6F00CB18B9 /* WXGlobalEventModule.m */; };
- DCA4452D1EFA55B300D0CFA8 /* WXComponent+Layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 744BEA581D0520F300452B5D /* WXComponent+Layout.m */; };
DCA4452F1EFA55B300D0CFA8 /* WXResourceLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 742AD7391DF98C8B007DC46C /* WXResourceLoader.m */; };
DCA445301EFA55B300D0CFA8 /* WXComponent+Events.m in Sources */ = {isa = PBXBuildFile; fileRef = 7408C48D1CFB345D000BCCD0 /* WXComponent+Events.m */; };
DCA445311EFA55B300D0CFA8 /* WXComponent+BoxShadow.m in Sources */ = {isa = PBXBuildFile; fileRef = C4E375351E5FCBD3009B2D9C /* WXComponent+BoxShadow.m */; };
@@ -356,39 +369,39 @@
DCA445391EFA55B300D0CFA8 /* WXComponent+PseudoClassManagement.m in Sources */ = {isa = PBXBuildFile; fileRef = C4C30DE61E1B833D00786B6C /* WXComponent+PseudoClassManagement.m */; };
DCA4453A1EFA55B300D0CFA8 /* WXView.m in Sources */ = {isa = PBXBuildFile; fileRef = 745ED2D71C5F2C7E002DB5A8 /* WXView.m */; };
DCA4453B1EFA55B300D0CFA8 /* WXErrorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 591DD32F1D23AD5800BE8709 /* WXErrorView.m */; };
- DCA4453C1EFA55B300D0CFA8 /* WXComponent+ViewManagement.m in Sources */ = {isa = PBXBuildFile; fileRef = 747A787B1D1BAAC900DED9D0 /* WXComponent+ViewManagement.m */; };
+ DCA4453C1EFA55B300D0CFA8 /* WXComponent+ViewManagement.mm in Sources */ = {isa = PBXBuildFile; fileRef = 747A787B1D1BAAC900DED9D0 /* WXComponent+ViewManagement.mm */; };
DCA4453D1EFA55B300D0CFA8 /* WXRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 74B8BEFD1DC47B72004A6027 /* WXRootView.m */; };
DCA4453E1EFA55B300D0CFA8 /* WXBaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 59A5962C1CB632050012CD52 /* WXBaseViewController.m */; };
DCA4453F1EFA55B300D0CFA8 /* WXRootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 59A5962E1CB632050012CD52 /* WXRootViewController.m */; };
- DCA445401EFA55B300D0CFA8 /* WXEditComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = C4B3D6D31E6954300013F38D /* WXEditComponent.m */; };
+ DCA445401EFA55B300D0CFA8 /* WXEditComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = C4B3D6D31E6954300013F38D /* WXEditComponent.mm */; };
DCA445411EFA55B300D0CFA8 /* WXMultiColumnLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D5F1E5A8E1E0092D38A /* WXMultiColumnLayout.m */; };
- DCA445421EFA55B300D0CFA8 /* WXRecyclerComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.m */; };
+ DCA445421EFA55B300D0CFA8 /* WXRecyclerComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.mm */; };
DCA445431EFA55B300D0CFA8 /* WXRecyclerDataController.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D631E5A8E1E0092D38A /* WXRecyclerDataController.m */; };
DCA445441EFA55B300D0CFA8 /* WXRecyclerUpdateController.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D651E5A8E1E0092D38A /* WXRecyclerUpdateController.m */; };
DCA445451EFA55B300D0CFA8 /* WXSectionDataController.m in Sources */ = {isa = PBXBuildFile; fileRef = 745B2D671E5A8E1E0092D38A /* WXSectionDataController.m */; };
- DCA445461EFA55B300D0CFA8 /* WXLoadingComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A837AAD1CD9DE9200AEDF03 /* WXLoadingComponent.m */; };
- DCA445471EFA55B300D0CFA8 /* WXSliderNeighborComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC77C111D770AE300CE7288 /* WXSliderNeighborComponent.m */; };
+ DCA445461EFA55B300D0CFA8 /* WXLoadingComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A837AAD1CD9DE9200AEDF03 /* WXLoadingComponent.mm */; };
+ DCA445471EFA55B300D0CFA8 /* WXSliderNeighborComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = DCC77C111D770AE300CE7288 /* WXSliderNeighborComponent.mm */; };
DCA445481EFA55B300D0CFA8 /* WXLoadingIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A837AAF1CD9DE9200AEDF03 /* WXLoadingIndicator.m */; };
- DCA445491EFA55B300D0CFA8 /* WXRefreshComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A837AB11CD9DE9200AEDF03 /* WXRefreshComponent.m */; };
+ DCA445491EFA55B300D0CFA8 /* WXRefreshComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A837AB11CD9DE9200AEDF03 /* WXRefreshComponent.mm */; };
DCA4454A1EFA55B300D0CFA8 /* WXEmbedComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 59CE27E71CC387DB000BE37A /* WXEmbedComponent.m */; };
DCA4454B1EFA55B300D0CFA8 /* WXVideoComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A8E65891C7C7AA20025C7B7 /* WXVideoComponent.m */; };
- DCA4454C1EFA55B300D0CFA8 /* WXComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 77D1612F1C02DE4E0010B15B /* WXComponent.m */; };
+ DCA4454C1EFA55B300D0CFA8 /* WXComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 77D1612F1C02DE4E0010B15B /* WXComponent.mm */; };
DCA4454D1EFA55B300D0CFA8 /* WXDivComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A0C1C155E99008B8775 /* WXDivComponent.m */; };
DCA4454E1EFA55B300D0CFA8 /* WXImageComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A101C155EA8008B8775 /* WXImageComponent.m */; };
- DCA4454F1EFA55B300D0CFA8 /* WXTextComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A141C155EB5008B8775 /* WXTextComponent.m */; };
- DCA445501EFA55B300D0CFA8 /* WXScrollerComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A181C155F25008B8775 /* WXScrollerComponent.m */; };
- DCA445511EFA55B300D0CFA8 /* WXCycleSliderComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B51EE31E97804D0040A743 /* WXCycleSliderComponent.m */; };
- DCA445531EFA55B300D0CFA8 /* WXCellComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 74CC7A1B1C2BC5F800829368 /* WXCellComponent.m */; };
- DCA445541EFA55B300D0CFA8 /* WXListComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 74CC7A1F1C2BF9DC00829368 /* WXListComponent.m */; };
+ DCA4454F1EFA55B300D0CFA8 /* WXTextComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A141C155EB5008B8775 /* WXTextComponent.mm */; };
+ DCA445501EFA55B300D0CFA8 /* WXScrollerComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 77E65A181C155F25008B8775 /* WXScrollerComponent.mm */; };
+ DCA445511EFA55B300D0CFA8 /* WXCycleSliderComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37B51EE31E97804D0040A743 /* WXCycleSliderComponent.mm */; };
+ DCA445531EFA55B300D0CFA8 /* WXCellComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 74CC7A1B1C2BC5F800829368 /* WXCellComponent.mm */; };
+ DCA445541EFA55B300D0CFA8 /* WXListComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 74CC7A1F1C2BF9DC00829368 /* WXListComponent.mm */; };
DCA445551EFA55B300D0CFA8 /* WXIndicatorComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AC750231C7565690041D390 /* WXIndicatorComponent.m */; };
DCA445561EFA55B300D0CFA8 /* WXTextInputComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A1F57B61C75C6A600B58017 /* WXTextInputComponent.m */; };
- DCA445571EFA55B300D0CFA8 /* WXTextAreaComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = DC03ADB71D508719003F76E7 /* WXTextAreaComponent.m */; };
+ DCA445571EFA55B300D0CFA8 /* WXTextAreaComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = DC03ADB71D508719003F76E7 /* WXTextAreaComponent.mm */; };
DCA445581EFA55B300D0CFA8 /* WXTransform.m in Sources */ = {isa = PBXBuildFile; fileRef = D317338B1C57257000BB7539 /* WXTransform.m */; };
DCA445591EFA55B300D0CFA8 /* WXWebComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = D312CE3A1C730DEB00046D68 /* WXWebComponent.m */; };
- DCA4455A1EFA55B300D0CFA8 /* WXSwitchComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A60CE9B1C91733E00857B9F /* WXSwitchComponent.m */; };
+ DCA4455A1EFA55B300D0CFA8 /* WXSwitchComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2A60CE9B1C91733E00857B9F /* WXSwitchComponent.mm */; };
DCA4455B1EFA55B300D0CFA8 /* WXAComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AE5B7511CAB7DBD0082FDDB /* WXAComponent.m */; };
DCA4455C1EFA55B300D0CFA8 /* WXCanvasComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = D33451071D3E19480083598A /* WXCanvasComponent.m */; };
- DCA4455D1EFA55B300D0CFA8 /* WXHeaderComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 744D610B1E49978200B624B3 /* WXHeaderComponent.m */; };
+ DCA4455D1EFA55B300D0CFA8 /* WXHeaderComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 744D610B1E49978200B624B3 /* WXHeaderComponent.mm */; };
DCA4455E1EFA55B300D0CFA8 /* WXFooterComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 744D610F1E49979000B624B3 /* WXFooterComponent.m */; };
DCA4455F1EFA55B300D0CFA8 /* WXNavigationDefaultImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 59A583051CF5B2FD0081FD3E /* WXNavigationDefaultImpl.m */; };
DCA445601EFA55B300D0CFA8 /* WXURLRewriteDefaultImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 74EF31AC1DE58BE200667A07 /* WXURLRewriteDefaultImpl.m */; };
@@ -439,7 +452,7 @@
DCA445901EFA55B300D0CFA8 /* WXBridgeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 77D161371C02DE940010B15B /* WXBridgeManager.m */; };
DCA445911EFA55B300D0CFA8 /* WXModuleFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E659F01C0C3612008B8775 /* WXModuleFactory.m */; };
DCA445921EFA55B300D0CFA8 /* WXHandlerFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 74A4BA9D1CB3C0A100195969 /* WXHandlerFactory.m */; };
- DCA445931EFA55B300D0CFA8 /* WXComponentManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7410811E1CED585A001BC6E5 /* WXComponentManager.m */; };
+ DCA445931EFA55B300D0CFA8 /* WXComponentManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7410811E1CED585A001BC6E5 /* WXComponentManager.mm */; };
DCA445941EFA55B300D0CFA8 /* WXComponentFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 741081221CED6756001BC6E5 /* WXComponentFactory.m */; };
DCA445951EFA55B300D0CFA8 /* WXRuleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DCAB35FD1D658EB700C0EA70 /* WXRuleManager.m */; };
DCA445961EFA55B300D0CFA8 /* WXMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 749DC27A1D40827B009E1C91 /* WXMonitor.m */; };
@@ -583,7 +596,7 @@
DCA446291EFA688B00D0CFA8 /* WeexSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = DCA446261EFA5DAF00D0CFA8 /* WeexSDK.h */; settings = {ATTRIBUTES = (Public, ); }; };
DCAB35FE1D658EB700C0EA70 /* WXRuleManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DCAB35FC1D658EB700C0EA70 /* WXRuleManager.h */; };
DCAB35FF1D658EB700C0EA70 /* WXRuleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DCAB35FD1D658EB700C0EA70 /* WXRuleManager.m */; };
- DCC77C131D770AE300CE7288 /* WXSliderNeighborComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC77C111D770AE300CE7288 /* WXSliderNeighborComponent.m */; };
+ DCC77C131D770AE300CE7288 /* WXSliderNeighborComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = DCC77C111D770AE300CE7288 /* WXSliderNeighborComponent.mm */; };
DCC77C141D770AE300CE7288 /* WXSliderNeighborComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = DCC77C121D770AE300CE7288 /* WXSliderNeighborComponent.h */; };
DCD8D0F32073392A0002C420 /* weex-polyfill.js in Resources */ = {isa = PBXBuildFile; fileRef = DCD8D0F22073392A0002C420 /* weex-polyfill.js */; };
DCD8D0F42073392A0002C420 /* weex-polyfill.js in Resources */ = {isa = PBXBuildFile; fileRef = DCD8D0F22073392A0002C420 /* weex-polyfill.js */; };
@@ -633,6 +646,7 @@
/* Begin PBXFileReference section */
042013AC1E66CD6A001FC79C /* WXValidateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXValidateProtocol.h; sourceTree = "<group>"; };
+ 176BE43B209172330086B6AF /* WXComponent+Layout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "WXComponent+Layout.mm"; path = "Layout/WXComponent+Layout.mm"; sourceTree = "<group>"; };
17B1221F2090AA9300387E33 /* WXSDKInstance_performance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXSDKInstance_performance.m; sourceTree = "<group>"; };
17B122202090AA9300387E33 /* WXSDKInstance_performance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXSDKInstance_performance.h; sourceTree = "<group>"; };
17B122232090AAB000387E33 /* WXSDKError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXSDKError.h; sourceTree = "<group>"; };
@@ -641,6 +655,13 @@
17C74F092072145000AB4CAB /* WXAnalyzerCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXAnalyzerCenter.h; sourceTree = "<group>"; };
17C74F0A2072145100AB4CAB /* WXAnalyzerCenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXAnalyzerCenter.m; sourceTree = "<group>"; };
17C74F0E2072147A00AB4CAB /* WXAnalyzerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXAnalyzerProtocol.h; sourceTree = "<group>"; };
+ 17C7B4FE20452E1700A2296A /* WXCoreLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WXCoreLayout.cpp; path = Layout/WXCoreLayout.cpp; sourceTree = "<group>"; };
+ 17C7B4FF20452E1800A2296A /* WXScrollerComponent+Layout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "WXScrollerComponent+Layout.mm"; path = "Layout/WXScrollerComponent+Layout.mm"; sourceTree = "<group>"; };
+ 17C7B50020452E1800A2296A /* WXScrollerComponent+Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "WXScrollerComponent+Layout.h"; path = "Layout/WXScrollerComponent+Layout.h"; sourceTree = "<group>"; };
+ 17C7B50120452E1800A2296A /* WXCoreStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WXCoreStyle.cpp; path = Layout/WXCoreStyle.cpp; sourceTree = "<group>"; };
+ 17C7B50220452E1800A2296A /* WXCoreFlexEnum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXCoreFlexEnum.h; path = Layout/WXCoreFlexEnum.h; sourceTree = "<group>"; };
+ 17C7B50320452E1800A2296A /* WXCoreStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXCoreStyle.h; path = Layout/WXCoreStyle.h; sourceTree = "<group>"; };
+ 17C7B50420452E1800A2296A /* WXCoreLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXCoreLayout.h; path = Layout/WXCoreLayout.h; sourceTree = "<group>"; };
1C1A2BEC1D91172800539AA1 /* WXConvertTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXConvertTests.m; sourceTree = "<group>"; };
1D3000EF1D40B9AB004F3B4F /* WXClipboardModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXClipboardModule.h; sourceTree = "<group>"; };
1D3000F01D40B9AB004F3B4F /* WXClipboardModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXClipboardModule.m; sourceTree = "<group>"; };
@@ -651,13 +672,13 @@
2A42AF891C23B33E00818EA6 /* WeexSDK_MTL.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WeexSDK_MTL.m; sourceTree = "<group>"; };
2A4445BE1CA8FD56009E7C6D /* WXTextComponentProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXTextComponentProtocol.h; sourceTree = "<group>"; };
2A60CE9A1C91733E00857B9F /* WXSwitchComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXSwitchComponent.h; sourceTree = "<group>"; };
- 2A60CE9B1C91733E00857B9F /* WXSwitchComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXSwitchComponent.m; sourceTree = "<group>"; };
+ 2A60CE9B1C91733E00857B9F /* WXSwitchComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXSwitchComponent.mm; sourceTree = "<group>"; };
2A837AAC1CD9DE9200AEDF03 /* WXLoadingComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXLoadingComponent.h; sourceTree = "<group>"; };
- 2A837AAD1CD9DE9200AEDF03 /* WXLoadingComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXLoadingComponent.m; sourceTree = "<group>"; };
+ 2A837AAD1CD9DE9200AEDF03 /* WXLoadingComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXLoadingComponent.mm; sourceTree = "<group>"; };
2A837AAE1CD9DE9200AEDF03 /* WXLoadingIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXLoadingIndicator.h; sourceTree = "<group>"; };
2A837AAF1CD9DE9200AEDF03 /* WXLoadingIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXLoadingIndicator.m; sourceTree = "<group>"; };
2A837AB01CD9DE9200AEDF03 /* WXRefreshComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXRefreshComponent.h; sourceTree = "<group>"; };
- 2A837AB11CD9DE9200AEDF03 /* WXRefreshComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXRefreshComponent.m; sourceTree = "<group>"; };
+ 2A837AB11CD9DE9200AEDF03 /* WXRefreshComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXRefreshComponent.mm; sourceTree = "<group>"; };
2A8E65881C7C7AA20025C7B7 /* WXVideoComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXVideoComponent.h; sourceTree = "<group>"; };
2A8E65891C7C7AA20025C7B7 /* WXVideoComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXVideoComponent.m; sourceTree = "<group>"; };
2A919DA41E321F1F006EB6B5 /* WXBridgeMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXBridgeMethod.h; sourceTree = "<group>"; };
@@ -670,9 +691,9 @@
2AFEB1791C747139000507FA /* WXInstanceWrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXInstanceWrap.h; sourceTree = "<group>"; };
2AFEB17A1C747139000507FA /* WXInstanceWrap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXInstanceWrap.m; sourceTree = "<group>"; };
333D9A251F41507A007CED39 /* WXTransition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXTransition.h; sourceTree = "<group>"; };
- 333D9A261F41507A007CED39 /* WXTransition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXTransition.m; sourceTree = "<group>"; };
+ 333D9A261F41507A007CED39 /* WXTransition.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXTransition.mm; sourceTree = "<group>"; };
37B51EE21E97804D0040A743 /* WXCycleSliderComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXCycleSliderComponent.h; sourceTree = "<group>"; };
- 37B51EE31E97804D0040A743 /* WXCycleSliderComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXCycleSliderComponent.m; sourceTree = "<group>"; };
+ 37B51EE31E97804D0040A743 /* WXCycleSliderComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXCycleSliderComponent.mm; sourceTree = "<group>"; };
591324A21D49B7F1004E89ED /* WXTimerModuleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXTimerModuleTests.m; sourceTree = "<group>"; };
591DD32F1D23AD5800BE8709 /* WXErrorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXErrorView.m; sourceTree = "<group>"; };
591DD3301D23AD5800BE8709 /* WXErrorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXErrorView.h; sourceTree = "<group>"; };
@@ -707,6 +728,7 @@
59CE27E71CC387DB000BE37A /* WXEmbedComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXEmbedComponent.m; sourceTree = "<group>"; };
59D3CA3E1CF9ED57008835DC /* Layout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Layout.c; path = Layout/Layout.c; sourceTree = "<group>"; };
59D3CA3F1CF9ED57008835DC /* Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Layout.h; path = Layout/Layout.h; sourceTree = "<group>"; };
+ 59D3CA461CFC3CC0008835DC /* WXSliderComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXSliderComponent.mm; sourceTree = "<group>"; };
59D3CA481CFC3CE1008835DC /* NSTimer+Weex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTimer+Weex.h"; sourceTree = "<group>"; };
59D3CA491CFC3CE1008835DC /* NSTimer+Weex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+Weex.m"; sourceTree = "<group>"; };
740451E81E14BB26004157CB /* WXServiceFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXServiceFactory.h; sourceTree = "<group>"; };
@@ -720,7 +742,7 @@
740938F81D3D0E0300DBB801 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
740938FA1D3D0E1700DBB801 /* AVKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVKit.framework; path = System/Library/Frameworks/AVKit.framework; sourceTree = SDKROOT; };
7410811D1CED585A001BC6E5 /* WXComponentManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXComponentManager.h; sourceTree = "<group>"; };
- 7410811E1CED585A001BC6E5 /* WXComponentManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXComponentManager.m; sourceTree = "<group>"; };
+ 7410811E1CED585A001BC6E5 /* WXComponentManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXComponentManager.mm; sourceTree = "<group>"; };
741081211CED6756001BC6E5 /* WXComponentFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXComponentFactory.h; sourceTree = "<group>"; };
741081221CED6756001BC6E5 /* WXComponentFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXComponentFactory.m; sourceTree = "<group>"; };
741081251CEDB4EC001BC6E5 /* WXComponent_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXComponent_internal.h; sourceTree = "<group>"; };
@@ -747,9 +769,8 @@
744BEA531D05178F00452B5D /* WXComponent+Display.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WXComponent+Display.h"; sourceTree = "<group>"; };
744BEA541D05178F00452B5D /* WXComponent+Display.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WXComponent+Display.m"; sourceTree = "<group>"; };
744BEA571D0520F300452B5D /* WXComponent+Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "WXComponent+Layout.h"; path = "Layout/WXComponent+Layout.h"; sourceTree = "<group>"; };
- 744BEA581D0520F300452B5D /* WXComponent+Layout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "WXComponent+Layout.m"; path = "Layout/WXComponent+Layout.m"; sourceTree = "<group>"; };
744D610A1E49978200B624B3 /* WXHeaderComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXHeaderComponent.h; sourceTree = "<group>"; };
- 744D610B1E49978200B624B3 /* WXHeaderComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXHeaderComponent.m; sourceTree = "<group>"; };
+ 744D610B1E49978200B624B3 /* WXHeaderComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXHeaderComponent.mm; sourceTree = "<group>"; };
744D610E1E49979000B624B3 /* WXFooterComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXFooterComponent.h; sourceTree = "<group>"; };
744D610F1E49979000B624B3 /* WXFooterComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXFooterComponent.m; sourceTree = "<group>"; };
744D61121E4AF23E00B624B3 /* WXDiffUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXDiffUtil.h; sourceTree = "<group>"; };
@@ -757,7 +778,7 @@
745B2D5E1E5A8E1E0092D38A /* WXMultiColumnLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXMultiColumnLayout.h; path = WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.h; sourceTree = SOURCE_ROOT; };
745B2D5F1E5A8E1E0092D38A /* WXMultiColumnLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WXMultiColumnLayout.m; path = WeexSDK/Sources/Component/Recycler/WXMultiColumnLayout.m; sourceTree = SOURCE_ROOT; };
745B2D601E5A8E1E0092D38A /* WXRecyclerComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXRecyclerComponent.h; path = WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.h; sourceTree = SOURCE_ROOT; };
- 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WXRecyclerComponent.m; path = WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m; sourceTree = SOURCE_ROOT; };
+ 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WXRecyclerComponent.mm; path = WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.mm; sourceTree = SOURCE_ROOT; };
745B2D621E5A8E1E0092D38A /* WXRecyclerDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXRecyclerDataController.h; path = WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.h; sourceTree = SOURCE_ROOT; };
745B2D631E5A8E1E0092D38A /* WXRecyclerDataController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WXRecyclerDataController.m; path = WeexSDK/Sources/Component/Recycler/WXRecyclerDataController.m; sourceTree = SOURCE_ROOT; };
745B2D641E5A8E1E0092D38A /* WXRecyclerUpdateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXRecyclerUpdateController.h; path = WeexSDK/Sources/Component/Recycler/WXRecyclerUpdateController.h; sourceTree = SOURCE_ROOT; };
@@ -780,9 +801,9 @@
7469869D1C4E2C000054A57E /* NSArray+Weex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Weex.h"; sourceTree = "<group>"; };
7469869E1C4E2C000054A57E /* NSArray+Weex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Weex.m"; sourceTree = "<group>"; };
746B92391F46BE36009AE86B /* WXCellSlotComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WXCellSlotComponent.h; sourceTree = "<group>"; };
- 746B923A1F46BE36009AE86B /* WXCellSlotComponent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WXCellSlotComponent.m; sourceTree = "<group>"; };
+ 746B923A1F46BE36009AE86B /* WXCellSlotComponent.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = WXCellSlotComponent.mm; sourceTree = "<group>"; };
747A787A1D1BAAC900DED9D0 /* WXComponent+ViewManagement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WXComponent+ViewManagement.h"; sourceTree = "<group>"; };
- 747A787B1D1BAAC900DED9D0 /* WXComponent+ViewManagement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WXComponent+ViewManagement.m"; sourceTree = "<group>"; };
+ 747A787B1D1BAAC900DED9D0 /* WXComponent+ViewManagement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "WXComponent+ViewManagement.mm"; sourceTree = "<group>"; };
747DF6801E31AEE4005C53A8 /* WXLength.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXLength.h; sourceTree = "<group>"; };
747DF6811E31AEE4005C53A8 /* WXLength.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXLength.m; sourceTree = "<group>"; };
74862F771E02B88D00B7A041 /* JSValue+Weex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JSValue+Weex.h"; sourceTree = "<group>"; };
@@ -826,11 +847,11 @@
74C8963F1D2AC2210043B82A /* WeexSDKTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WeexSDKTests.m; sourceTree = "<group>"; };
74C896411D2AC2210043B82A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
74CC7A1A1C2BC5F800829368 /* WXCellComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXCellComponent.h; sourceTree = "<group>"; };
- 74CC7A1B1C2BC5F800829368 /* WXCellComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXCellComponent.m; sourceTree = "<group>"; };
+ 74CC7A1B1C2BC5F800829368 /* WXCellComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXCellComponent.mm; sourceTree = "<group>"; };
74CC7A1E1C2BF9DC00829368 /* WXListComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXListComponent.h; sourceTree = "<group>"; };
- 74CC7A1F1C2BF9DC00829368 /* WXListComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXListComponent.m; sourceTree = "<group>"; };
+ 74CC7A1F1C2BF9DC00829368 /* WXListComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXListComponent.mm; sourceTree = "<group>"; };
74CFDD371F45939C007A1A66 /* WXRecycleListComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WXRecycleListComponent.h; sourceTree = "<group>"; };
- 74CFDD381F45939C007A1A66 /* WXRecycleListComponent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WXRecycleListComponent.m; sourceTree = "<group>"; };
+ 74CFDD381F45939C007A1A66 /* WXRecycleListComponent.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = WXRecycleListComponent.mm; sourceTree = "<group>"; };
74CFDD3B1F459400007A1A66 /* WXRecycleListDataManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WXRecycleListDataManager.h; sourceTree = "<group>"; };
74CFDD3C1F459400007A1A66 /* WXRecycleListDataManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WXRecycleListDataManager.m; sourceTree = "<group>"; };
74CFDD3F1F45941E007A1A66 /* WXRecycleListTemplateManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WXRecycleListTemplateManager.h; sourceTree = "<group>"; };
@@ -858,7 +879,7 @@
77D161261C02DE1A0010B15B /* WXSDKManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXSDKManager.h; sourceTree = "<group>"; };
77D161271C02DE1A0010B15B /* WXSDKManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXSDKManager.m; sourceTree = "<group>"; };
77D1612E1C02DE4E0010B15B /* WXComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WXComponent.h; path = ../Model/WXComponent.h; sourceTree = "<group>"; };
- 77D1612F1C02DE4E0010B15B /* WXComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WXComponent.m; path = ../Model/WXComponent.m; sourceTree = "<group>"; };
+ 77D1612F1C02DE4E0010B15B /* WXComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WXComponent.mm; path = ../Model/WXComponent.mm; sourceTree = "<group>"; };
77D161361C02DE940010B15B /* WXBridgeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXBridgeManager.h; sourceTree = "<group>"; };
77D161371C02DE940010B15B /* WXBridgeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXBridgeManager.m; sourceTree = "<group>"; };
77D1613A1C02DEA60010B15B /* WXJSCoreBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXJSCoreBridge.h; sourceTree = "<group>"; };
@@ -879,11 +900,13 @@
77E65A0F1C155EA8008B8775 /* WXImageComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXImageComponent.h; sourceTree = "<group>"; };
77E65A101C155EA8008B8775 /* WXImageComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXImageComponent.m; sourceTree = "<group>"; };
77E65A131C155EB5008B8775 /* WXTextComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXTextComponent.h; sourceTree = "<group>"; };
- 77E65A141C155EB5008B8775 /* WXTextComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXTextComponent.m; sourceTree = "<group>"; };
+ 77E65A141C155EB5008B8775 /* WXTextComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXTextComponent.mm; sourceTree = "<group>"; };
77E65A171C155F25008B8775 /* WXScrollerComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXScrollerComponent.h; sourceTree = "<group>"; };
- 77E65A181C155F25008B8775 /* WXScrollerComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXScrollerComponent.m; sourceTree = "<group>"; };
+ 77E65A181C155F25008B8775 /* WXScrollerComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXScrollerComponent.mm; sourceTree = "<group>"; };
841CD1021F9739890081196D /* WXExceptionUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXExceptionUtils.m; sourceTree = "<group>"; };
841CD1041F97399C0081196D /* WXExceptionUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WXExceptionUtils.h; sourceTree = "<group>"; };
+ 8431F8DD1F9EFCC100C76E1E /* WXScrollerComponent+Layout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "WXScrollerComponent+Layout.mm"; path = "Layout/WXScrollerComponent+Layout.mm"; sourceTree = "<group>"; };
+ 847E1DE01F9A1BCF00A48124 /* WXCoreStyle.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = WXCoreStyle.h; path = Layout/WXCoreStyle.h; sourceTree = "<group>"; };
9B9E74771FA2DB5800DAAEA9 /* WXTestBridgeMethodDummy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXTestBridgeMethodDummy.h; sourceTree = "<group>"; };
9B9E74781FA2DB5800DAAEA9 /* WXTestBridgeMethodDummy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXTestBridgeMethodDummy.m; sourceTree = "<group>"; };
BA5F00EF1FC5AFFE00F76B5C /* WXLocaleModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WXLocaleModule.h; sourceTree = "<group>"; };
@@ -898,7 +921,7 @@
C47B78CC1F2998EE001D3B0C /* WXExtendCallNativeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXExtendCallNativeManager.h; sourceTree = "<group>"; };
C47B78CD1F2998EE001D3B0C /* WXExtendCallNativeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXExtendCallNativeManager.m; sourceTree = "<group>"; };
C4B3D6D21E6954300013F38D /* WXEditComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXEditComponent.h; sourceTree = "<group>"; };
- C4B3D6D31E6954300013F38D /* WXEditComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXEditComponent.m; sourceTree = "<group>"; };
+ C4B3D6D31E6954300013F38D /* WXEditComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXEditComponent.mm; sourceTree = "<group>"; };
C4B834251DE69B09007AD27E /* WXPickerModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXPickerModule.m; sourceTree = "<group>"; };
C4B834261DE69B09007AD27E /* WXPickerModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXPickerModule.h; sourceTree = "<group>"; };
C4C30DE61E1B833D00786B6C /* WXComponent+PseudoClassManagement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WXComponent+PseudoClassManagement.m"; sourceTree = "<group>"; };
@@ -929,7 +952,7 @@
D3FC0DF51C508B2A002B9E31 /* WXTimerModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXTimerModule.h; sourceTree = "<group>"; };
D3FC0DF61C508B2A002B9E31 /* WXTimerModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXTimerModule.m; sourceTree = "<group>"; };
DAB176F008F516E4F9391C61 /* libPods-WeexSDK.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WeexSDK.a"; sourceTree = BUILT_PRODUCTS_DIR; };
- DC03ADB71D508719003F76E7 /* WXTextAreaComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXTextAreaComponent.m; sourceTree = "<group>"; };
+ DC03ADB71D508719003F76E7 /* WXTextAreaComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXTextAreaComponent.mm; sourceTree = "<group>"; };
DC03ADB81D508719003F76E7 /* WXTextAreaComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXTextAreaComponent.h; sourceTree = "<group>"; };
DC15A3D92010BC93009C8977 /* weex-main-jsfm.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = "weex-main-jsfm.js"; path = "../../../../pre-build/weex-main-jsfm.js"; sourceTree = "<group>"; };
DC15A3DA2010BC93009C8977 /* weex-rax-api.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = "weex-rax-api.js"; path = "../../../../pre-build/weex-rax-api.js"; sourceTree = "<group>"; };
@@ -946,7 +969,7 @@
DCA446261EFA5DAF00D0CFA8 /* WeexSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WeexSDK.h; sourceTree = "<group>"; };
DCAB35FC1D658EB700C0EA70 /* WXRuleManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXRuleManager.h; sourceTree = "<group>"; };
DCAB35FD1D658EB700C0EA70 /* WXRuleManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXRuleManager.m; sourceTree = "<group>"; };
- DCC77C111D770AE300CE7288 /* WXSliderNeighborComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXSliderNeighborComponent.m; sourceTree = "<group>"; };
+ DCC77C111D770AE300CE7288 /* WXSliderNeighborComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WXSliderNeighborComponent.mm; sourceTree = "<group>"; };
DCC77C121D770AE300CE7288 /* WXSliderNeighborComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXSliderNeighborComponent.h; sourceTree = "<group>"; };
DCD8D0F22073392A0002C420 /* weex-polyfill.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = "weex-polyfill.js"; path = "../../../../pre-build/weex-polyfill.js"; sourceTree = "<group>"; };
DCDFED001E68238F00C228D7 /* WXJSExceptionProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXJSExceptionProtocol.h; sourceTree = "<group>"; };
@@ -1003,6 +1026,16 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 17B12228209170CF00387E33 /* Recovered References */ = {
+ isa = PBXGroup;
+ children = (
+ 8431F8DD1F9EFCC100C76E1E /* WXScrollerComponent+Layout.mm */,
+ 59D3CA461CFC3CC0008835DC /* WXSliderComponent.mm */,
+ 847E1DE01F9A1BCF00A48124 /* WXCoreStyle.h */,
+ );
+ name = "Recovered References";
+ sourceTree = "<group>";
+ };
2A42AF861C23B33E00818EA6 /* WeexSDK_MTL */ = {
isa = PBXGroup;
children = (
@@ -1015,10 +1048,17 @@
2AF626C61C191E2200E71A38 /* Layout */ = {
isa = PBXGroup;
children = (
+ 176BE43B209172330086B6AF /* WXComponent+Layout.mm */,
+ 17C7B50220452E1800A2296A /* WXCoreFlexEnum.h */,
+ 17C7B4FE20452E1700A2296A /* WXCoreLayout.cpp */,
+ 17C7B50420452E1800A2296A /* WXCoreLayout.h */,
+ 17C7B50120452E1800A2296A /* WXCoreStyle.cpp */,
+ 17C7B50320452E1800A2296A /* WXCoreStyle.h */,
+ 17C7B50020452E1800A2296A /* WXScrollerComponent+Layout.h */,
+ 17C7B4FF20452E1800A2296A /* WXScrollerComponent+Layout.mm */,
59D3CA3E1CF9ED57008835DC /* Layout.c */,
59D3CA3F1CF9ED57008835DC /* Layout.h */,
744BEA571D0520F300452B5D /* WXComponent+Layout.h */,
- 744BEA581D0520F300452B5D /* WXComponent+Layout.m */,
74B232D11D2A2BA4006322EA /* WXLayoutDefine.h */,
);
name = Layout;
@@ -1076,7 +1116,7 @@
591DD3301D23AD5800BE8709 /* WXErrorView.h */,
591DD32F1D23AD5800BE8709 /* WXErrorView.m */,
747A787A1D1BAAC900DED9D0 /* WXComponent+ViewManagement.h */,
- 747A787B1D1BAAC900DED9D0 /* WXComponent+ViewManagement.m */,
+ 747A787B1D1BAAC900DED9D0 /* WXComponent+ViewManagement.mm */,
74B8BEFC1DC47B72004A6027 /* WXRootView.h */,
74B8BEFD1DC47B72004A6027 /* WXRootView.m */,
);
@@ -1177,7 +1217,7 @@
isa = PBXGroup;
children = (
74CFDD371F45939C007A1A66 /* WXRecycleListComponent.h */,
- 74CFDD381F45939C007A1A66 /* WXRecycleListComponent.m */,
+ 74CFDD381F45939C007A1A66 /* WXRecycleListComponent.mm */,
74CFDD3B1F459400007A1A66 /* WXRecycleListDataManager.h */,
74CFDD3C1F459400007A1A66 /* WXRecycleListDataManager.m */,
74CFDD3F1F45941E007A1A66 /* WXRecycleListTemplateManager.h */,
@@ -1187,7 +1227,7 @@
74BA4AB11F70F4B600AC29BF /* WXRecycleListLayout.h */,
74BA4AB21F70F4B600AC29BF /* WXRecycleListLayout.m */,
746B92391F46BE36009AE86B /* WXCellSlotComponent.h */,
- 746B923A1F46BE36009AE86B /* WXCellSlotComponent.m */,
+ 746B923A1F46BE36009AE86B /* WXCellSlotComponent.mm */,
7423EB4F1F4ADE30001662D1 /* WXComponent+DataBinding.h */,
7423EB501F4ADE30001662D1 /* WXComponent+DataBinding.mm */,
74BF19F61F5139BB00AEE3D7 /* WXJSASTParser.h */,
@@ -1204,7 +1244,7 @@
745B2D5E1E5A8E1E0092D38A /* WXMultiColumnLayout.h */,
745B2D5F1E5A8E1E0092D38A /* WXMultiColumnLayout.m */,
745B2D601E5A8E1E0092D38A /* WXRecyclerComponent.h */,
- 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.m */,
+ 745B2D611E5A8E1E0092D38A /* WXRecyclerComponent.mm */,
745B2D621E5A8E1E0092D38A /* WXRecyclerDataController.h */,
745B2D631E5A8E1E0092D38A /* WXRecyclerDataController.m */,
745B2D641E5A8E1E0092D38A /* WXRecyclerUpdateController.h */,
@@ -1243,6 +1283,7 @@
DCA445261EFA555400D0CFA8 /* WeexSDK-Dynamic */,
77D160FE1C02DBE70010B15B /* Products */,
A5818E244F9E235722E3B938 /* Frameworks */,
+ 17B12228209170CF00387E33 /* Recovered References */,
);
sourceTree = "<group>";
};
@@ -1351,7 +1392,7 @@
74A4BA9C1CB3C0A100195969 /* WXHandlerFactory.h */,
74A4BA9D1CB3C0A100195969 /* WXHandlerFactory.m */,
7410811D1CED585A001BC6E5 /* WXComponentManager.h */,
- 7410811E1CED585A001BC6E5 /* WXComponentManager.m */,
+ 7410811E1CED585A001BC6E5 /* WXComponentManager.mm */,
741081211CED6756001BC6E5 /* WXComponentFactory.h */,
741081221CED6756001BC6E5 /* WXComponentFactory.m */,
DCAB35FC1D658EB700C0EA70 /* WXRuleManager.h */,
@@ -1461,7 +1502,7 @@
DCE2CF981F46D4220021BDC4 /* WXVoiceOverModule.m */,
DCE2CF991F46D4220021BDC4 /* WXVoiceOverModule.h */,
333D9A251F41507A007CED39 /* WXTransition.h */,
- 333D9A261F41507A007CED39 /* WXTransition.m */,
+ 333D9A261F41507A007CED39 /* WXTransition.mm */,
C43C03E41EC8ACA40044C7FF /* WXPrerenderManager.h */,
C43C03E51EC8ACA40044C7FF /* WXPrerenderManager.m */,
C4F012801E1502E9003378D0 /* WXWebSocketModule.h */,
@@ -1502,56 +1543,56 @@
isa = PBXGroup;
children = (
C4B3D6D21E6954300013F38D /* WXEditComponent.h */,
- C4B3D6D31E6954300013F38D /* WXEditComponent.m */,
+ C4B3D6D31E6954300013F38D /* WXEditComponent.mm */,
74CFDD361F45937D007A1A66 /* RecycleList */,
74D8DB401E4825920078B667 /* Recycler */,
2A837AAC1CD9DE9200AEDF03 /* WXLoadingComponent.h */,
- 2A837AAD1CD9DE9200AEDF03 /* WXLoadingComponent.m */,
+ 2A837AAD1CD9DE9200AEDF03 /* WXLoadingComponent.mm */,
2A837AAE1CD9DE9200AEDF03 /* WXLoadingIndicator.h */,
- DCC77C111D770AE300CE7288 /* WXSliderNeighborComponent.m */,
+ DCC77C111D770AE300CE7288 /* WXSliderNeighborComponent.mm */,
DCC77C121D770AE300CE7288 /* WXSliderNeighborComponent.h */,
2A837AAF1CD9DE9200AEDF03 /* WXLoadingIndicator.m */,
2A837AB01CD9DE9200AEDF03 /* WXRefreshComponent.h */,
- 2A837AB11CD9DE9200AEDF03 /* WXRefreshComponent.m */,
+ 2A837AB11CD9DE9200AEDF03 /* WXRefreshComponent.mm */,
59CE27E61CC387DB000BE37A /* WXEmbedComponent.h */,
59CE27E71CC387DB000BE37A /* WXEmbedComponent.m */,
2A8E65881C7C7AA20025C7B7 /* WXVideoComponent.h */,
2A8E65891C7C7AA20025C7B7 /* WXVideoComponent.m */,
77D1612E1C02DE4E0010B15B /* WXComponent.h */,
- 77D1612F1C02DE4E0010B15B /* WXComponent.m */,
+ 77D1612F1C02DE4E0010B15B /* WXComponent.mm */,
77E65A0B1C155E99008B8775 /* WXDivComponent.h */,
77E65A0C1C155E99008B8775 /* WXDivComponent.m */,
77E65A0F1C155EA8008B8775 /* WXImageComponent.h */,
77E65A101C155EA8008B8775 /* WXImageComponent.m */,
77E65A131C155EB5008B8775 /* WXTextComponent.h */,
- 77E65A141C155EB5008B8775 /* WXTextComponent.m */,
+ 77E65A141C155EB5008B8775 /* WXTextComponent.mm */,
77E65A171C155F25008B8775 /* WXScrollerComponent.h */,
- 77E65A181C155F25008B8775 /* WXScrollerComponent.m */,
+ 77E65A181C155F25008B8775 /* WXScrollerComponent.mm */,
37B51EE21E97804D0040A743 /* WXCycleSliderComponent.h */,
- 37B51EE31E97804D0040A743 /* WXCycleSliderComponent.m */,
- 74CC7A1B1C2BC5F800829368 /* WXCellComponent.m */,
+ 37B51EE31E97804D0040A743 /* WXCycleSliderComponent.mm */,
+ 74CC7A1B1C2BC5F800829368 /* WXCellComponent.mm */,
74CC7A1E1C2BF9DC00829368 /* WXListComponent.h */,
- 74CC7A1F1C2BF9DC00829368 /* WXListComponent.m */,
+ 74CC7A1F1C2BF9DC00829368 /* WXListComponent.mm */,
2AC750221C7565690041D390 /* WXIndicatorComponent.h */,
2AC750231C7565690041D390 /* WXIndicatorComponent.m */,
74CC7A1A1C2BC5F800829368 /* WXCellComponent.h */,
2A1F57B51C75C6A600B58017 /* WXTextInputComponent.h */,
2A1F57B61C75C6A600B58017 /* WXTextInputComponent.m */,
DC03ADB81D508719003F76E7 /* WXTextAreaComponent.h */,
- DC03ADB71D508719003F76E7 /* WXTextAreaComponent.m */,
+ DC03ADB71D508719003F76E7 /* WXTextAreaComponent.mm */,
D317338A1C57257000BB7539 /* WXTransform.h */,
D317338B1C57257000BB7539 /* WXTransform.m */,
D312CE391C730DEB00046D68 /* WXWebComponent.h */,
D312CE3A1C730DEB00046D68 /* WXWebComponent.m */,
2A60CE9A1C91733E00857B9F /* WXSwitchComponent.h */,
- 2A60CE9B1C91733E00857B9F /* WXSwitchComponent.m */,
+ 2A60CE9B1C91733E00857B9F /* WXSwitchComponent.mm */,
2AE5B7501CAB7DBD0082FDDB /* WXAComponent.h */,
2AE5B7511CAB7DBD0082FDDB /* WXAComponent.m */,
741081251CEDB4EC001BC6E5 /* WXComponent_internal.h */,
D33451061D3E19480083598A /* WXCanvasComponent.h */,
D33451071D3E19480083598A /* WXCanvasComponent.m */,
744D610A1E49978200B624B3 /* WXHeaderComponent.h */,
- 744D610B1E49978200B624B3 /* WXHeaderComponent.m */,
+ 744D610B1E49978200B624B3 /* WXHeaderComponent.mm */,
744D610E1E49979000B624B3 /* WXFooterComponent.h */,
744D610F1E49979000B624B3 /* WXFooterComponent.m */,
);
@@ -1662,6 +1703,7 @@
74BB5FB91DFEE81A004FC3DF /* WXMetaModule.h in Headers */,
DCA0EF641D6EED6F00CB18B9 /* WXGlobalEventModule.h in Headers */,
2A837AB21CD9DE9200AEDF03 /* WXLoadingComponent.h in Headers */,
+ 17E5ACD82091F02D00EE81F1 /* WXCoreLayout.h in Headers */,
DCA446271EFA5DAF00D0CFA8 /* WeexSDK.h in Headers */,
841CD1051F974DFA0081196D /* WXExceptionUtils.h in Headers */,
745B2D6A1E5A8E1E0092D38A /* WXRecyclerComponent.h in Headers */,
@@ -1686,6 +1728,7 @@
741DFE021DDD7D18009B020F /* WXRoundedRect.h in Headers */,
17C74F0F2072147B00AB4CAB /* WXAnalyzerProtocol.h in Headers */,
7423899B1C3174EB00D748CA /* WXWeakObjectWrapper.h in Headers */,
+ 17C7B50A20452E1800A2296A /* WXCoreFlexEnum.h in Headers */,
74BF19F81F5139BB00AEE3D7 /* WXJSASTParser.h in Headers */,
59A596191CB630E50012CD52 /* WXNavigationProtocol.h in Headers */,
59A5962F1CB632050012CD52 /* WXBaseViewController.h in Headers */,
@@ -1718,9 +1761,11 @@
77E659F11C0C3612008B8775 /* WXModuleFactory.h in Headers */,
77D161431C02DEE40010B15B /* WXBridgeProtocol.h in Headers */,
59A582D41CF481110081FD3E /* WXAppMonitorProtocol.h in Headers */,
+ 847E1DE41F9A1BCF00A48124 /* WXCoreStyle.h in Headers */,
2A837AB61CD9DE9200AEDF03 /* WXRefreshComponent.h in Headers */,
C43C03E81EC8ACA40044C7FF /* WXPrerenderManager.h in Headers */,
17C74F0C2072145100AB4CAB /* WXAnalyzerCenter.h in Headers */,
+ 17C7B50B20452E1800A2296A /* WXCoreStyle.h in Headers */,
74B232D21D2A2BA4006322EA /* WXLayoutDefine.h in Headers */,
C4B834281DE69B09007AD27E /* WXPickerModule.h in Headers */,
59A596311CB632050012CD52 /* WXRootViewController.h in Headers */,
@@ -1731,6 +1776,7 @@
74CC7A1C1C2BC5F800829368 /* WXCellComponent.h in Headers */,
74896F301D1AC79400D1D593 /* NSObject+WXSwizzle.h in Headers */,
DCF343671E49CAEE00A2FB34 /* WXJSExceptionInfo.h in Headers */,
+ 17C7B50820452E1800A2296A /* WXScrollerComponent+Layout.h in Headers */,
C4F012821E1502E9003378D0 /* WXWebSocketModule.h in Headers */,
74EF31AA1DE58AE600667A07 /* WXURLRewriteProtocol.h in Headers */,
59A596241CB6311F0012CD52 /* WXStorageModule.h in Headers */,
@@ -1781,10 +1827,10 @@
170B4665208733BF00562666 /* WXAnalyzerCenter+Transfer.h in Headers */,
DCA445A01EFA56F400D0CFA8 /* WXType.h in Headers */,
DCA445B21EFA576D00D0CFA8 /* WXListComponent.h in Headers */,
- 333D9A281F41507A007CED39 /* WXTransition.h in Headers */,
DCA445AD1EFA575100D0CFA8 /* WXNavigationProtocol.h in Headers */,
DCA445B01EFA576200D0CFA8 /* WXModalUIModule.h in Headers */,
DCA445A61EFA571E00D0CFA8 /* WXSDKEngine.h in Headers */,
+ 17E5ACDA2091F05000EE81F1 /* WXCoreFlexEnum.h in Headers */,
DCA445AA1EFA573900D0CFA8 /* WXResourceRequest.h in Headers */,
DCA445C61EFA57EE00D0CFA8 /* NSObject+WXSwizzle.h in Headers */,
74B81AEB1F73C3E900D3A61D /* WXRecycleListLayout.h in Headers */,
@@ -1827,6 +1873,7 @@
DCA445DB1EFA59AA00D0CFA8 /* WXRecyclerComponent.h in Headers */,
DCA445CD1EFA592E00D0CFA8 /* WXComponent+Events.h in Headers */,
DCA445EC1EFA5A0E00D0CFA8 /* WXTextAreaComponent.h in Headers */,
+ 17E5ACD92091F03700EE81F1 /* WXCoreStyle.h in Headers */,
DCA445D81EFA599400D0CFA8 /* WXRootView.h in Headers */,
DCA446131EFA5A8C00D0CFA8 /* WXCallJSMethod.h in Headers */,
BA5F00F31FC6834900F76B5C /* WXLocaleModule.h in Headers */,
@@ -1880,6 +1927,8 @@
DCA445E61EFA59E500D0CFA8 /* WXImageComponent.h in Headers */,
DCA4461B1EFA5AA200D0CFA8 /* WXDatePickerManager.h in Headers */,
DCA445D71EFA598D00D0CFA8 /* WXComponent+ViewManagement.h in Headers */,
+ 178EDF0D204FEC1300917F6B /* WXScrollerComponent+Layout.h in Headers */,
+ DCA445DB1EFA59AA00D0CFA8 /* WXRecyclerComponent.h in Headers */,
DCA445D31EFA594A00D0CFA8 /* WXRoundedRect.h in Headers */,
DCA445EA1EFA5A0300D0CFA8 /* WXCellComponent.h in Headers */,
DCA446201EFA5AB800D0CFA8 /* WXComponent+Navigation.h in Headers */,
@@ -1911,6 +1960,7 @@
DCA4460C1EFA5A7600D0CFA8 /* WXThreadSafeMutableDictionary.h in Headers */,
DCA445CE1EFA593500D0CFA8 /* WXComponent+BoxShadow.h in Headers */,
74B81AF11F73C3E900D3A61D /* WXJSASTParser.h in Headers */,
+ 178EDF0F204FF11700917F6B /* WXCoreLayout.h in Headers */,
DCA4461C1EFA5AA600D0CFA8 /* WXModuleFactory.h in Headers */,
DCA445D91EFA59A100D0CFA8 /* WXEditComponent.h in Headers */,
DCA445FB1EFA5A3C00D0CFA8 /* WXStorageModule.h in Headers */,
@@ -2165,6 +2215,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 176BE43E209172E20086B6AF /* WXScrollerComponent+Layout.mm in Sources */,
77D161291C02DE1A0010B15B /* WXSDKManager.m in Sources */,
7461F8911CFB373100F62D44 /* WXDisplayQueue.m in Sources */,
74896F311D1AC79400D1D593 /* NSObject+WXSwizzle.m in Sources */,
@@ -2175,14 +2226,14 @@
747DF6831E31AEE4005C53A8 /* WXLength.m in Sources */,
77E65A0E1C155E99008B8775 /* WXDivComponent.m in Sources */,
ED053502207F4DEB007B4568 /* JSContext+Weex.m in Sources */,
- 2A60CE9D1C91733E00857B9F /* WXSwitchComponent.m in Sources */,
+ 2A60CE9D1C91733E00857B9F /* WXSwitchComponent.mm in Sources */,
744D61111E49979000B624B3 /* WXFooterComponent.m in Sources */,
745B2D6F1E5A8E1E0092D38A /* WXRecyclerUpdateController.m in Sources */,
- 745B2D6B1E5A8E1E0092D38A /* WXRecyclerComponent.m in Sources */,
- 2A837AB71CD9DE9200AEDF03 /* WXRefreshComponent.m in Sources */,
+ 745B2D6B1E5A8E1E0092D38A /* WXRecyclerComponent.mm in Sources */,
+ 2A837AB71CD9DE9200AEDF03 /* WXRefreshComponent.mm in Sources */,
74A4BA9B1CB3BAA100195969 /* WXThreadSafeMutableDictionary.m in Sources */,
- 77E65A1A1C155F25008B8775 /* WXScrollerComponent.m in Sources */,
- 747A787D1D1BAAC900DED9D0 /* WXComponent+ViewManagement.m in Sources */,
+ 77E65A1A1C155F25008B8775 /* WXScrollerComponent.mm in Sources */,
+ 747A787D1D1BAAC900DED9D0 /* WXComponent+ViewManagement.mm in Sources */,
C4E375371E5FCBD3009B2D9C /* WXComponent+BoxShadow.m in Sources */,
C43C03E91EC8ACA40044C7FF /* WXPrerenderManager.m in Sources */,
2A837AB51CD9DE9200AEDF03 /* WXLoadingIndicator.m in Sources */,
@@ -2190,7 +2241,7 @@
59D3CA401CF9ED57008835DC /* Layout.c in Sources */,
DCF087621DCAE161005CD6EB /* WXInvocationConfig.m in Sources */,
C47B78CF1F2998EE001D3B0C /* WXExtendCallNativeManager.m in Sources */,
- 77D161311C02DE4E0010B15B /* WXComponent.m in Sources */,
+ 77D161311C02DE4E0010B15B /* WXComponent.mm in Sources */,
74CFDD461F459443007A1A66 /* WXRecycleListUpdateManager.m in Sources */,
74862F7A1E02B88D00B7A041 /* JSValue+Weex.m in Sources */,
740451EB1E14BB26004157CB /* WXServiceFactory.m in Sources */,
@@ -2200,12 +2251,12 @@
59A5961D1CB630F10012CD52 /* WXComponent+Navigation.m in Sources */,
77D161631C02ED790010B15B /* WXLog.m in Sources */,
17C74F0D2072145100AB4CAB /* WXAnalyzerCenter.m in Sources */,
- 744BEA5A1D0520F300452B5D /* WXComponent+Layout.m in Sources */,
59A582FD1CF5B17B0081FD3E /* WXBridgeContext.m in Sources */,
+ 17C7B50620452E1800A2296A /* WXCoreLayout.cpp in Sources */,
743933B51C7ED9AA00773BB7 /* WXSimulatorShortcutManager.m in Sources */,
BA5F00F21FC5AFFE00F76B5C /* WXLocaleModule.m in Sources */,
74BB5FBA1DFEE81A004FC3DF /* WXMetaModule.m in Sources */,
- 741081201CED585A001BC6E5 /* WXComponentManager.m in Sources */,
+ 741081201CED585A001BC6E5 /* WXComponentManager.mm in Sources */,
1D3000F21D40B9AC004F3B4F /* WXClipboardModule.m in Sources */,
741DFE071DDD9B30009B020F /* UIBezierPath+Weex.m in Sources */,
D312CE3C1C730DEB00046D68 /* WXWebComponent.m in Sources */,
@@ -2217,15 +2268,15 @@
591DD3311D23AD5800BE8709 /* WXErrorView.m in Sources */,
59D3CA4B1CFC3CE1008835DC /* NSTimer+Weex.m in Sources */,
59A596321CB632050012CD52 /* WXRootViewController.m in Sources */,
- DCC77C131D770AE300CE7288 /* WXSliderNeighborComponent.m in Sources */,
+ DCC77C131D770AE300CE7288 /* WXSliderNeighborComponent.mm in Sources */,
2A8E658B1C7C7AA20025C7B7 /* WXVideoComponent.m in Sources */,
74862F7E1E03A0F300B7A041 /* WXModuleMethod.m in Sources */,
742AD7341DF98C45007DC46C /* WXResourceResponse.m in Sources */,
- 77E65A161C155EB5008B8775 /* WXTextComponent.m in Sources */,
+ 77E65A161C155EB5008B8775 /* WXTextComponent.mm in Sources */,
C4D872261E5DDF7500E39BC1 /* WXBoxShadow.m in Sources */,
746319031C60AFC100EFEBD4 /* WXThreadSafeCounter.m in Sources */,
74A4BAA71CB4F98300195969 /* WXStreamModule.m in Sources */,
- 744D610D1E49978200B624B3 /* WXHeaderComponent.m in Sources */,
+ 744D610D1E49978200B624B3 /* WXHeaderComponent.mm in Sources */,
77E659F21C0C3612008B8775 /* WXModuleFactory.m in Sources */,
DCF343681E49CAEE00A2FB34 /* WXJSExceptionInfo.m in Sources */,
59CE27E91CC387DB000BE37A /* WXEmbedComponent.m in Sources */,
@@ -2238,8 +2289,9 @@
744D61151E4AF23E00B624B3 /* WXDiffUtil.m in Sources */,
74EF31AE1DE58BE200667A07 /* WXURLRewriteDefaultImpl.m in Sources */,
17B122262090AAB000387E33 /* WXSDKError.m in Sources */,
- C4B3D6D51E6954300013F38D /* WXEditComponent.m in Sources */,
+ C4B3D6D51E6954300013F38D /* WXEditComponent.mm in Sources */,
C4C30DE81E1B833D00786B6C /* WXComponent+PseudoClassManagement.m in Sources */,
+ 17C7B50920452E1800A2296A /* WXCoreStyle.cpp in Sources */,
74915F481C8EB02B00BEBCC0 /* WXAssert.m in Sources */,
59A596251CB6311F0012CD52 /* WXStorageModule.m in Sources */,
2AFEB17C1C747139000507FA /* WXInstanceWrap.m in Sources */,
@@ -2251,17 +2303,17 @@
745B2D711E5A8E1E0092D38A /* WXSectionDataController.m in Sources */,
DCE2CF9A1F46D4220021BDC4 /* WXVoiceOverModule.m in Sources */,
2A1F57B81C75C6A600B58017 /* WXTextInputComponent.m in Sources */,
- 74CC7A1D1C2BC5F800829368 /* WXCellComponent.m in Sources */,
+ 74CC7A1D1C2BC5F800829368 /* WXCellComponent.mm in Sources */,
74862F821E03A24500B7A041 /* WXComponentMethod.m in Sources */,
77E65A121C155EA8008B8775 /* WXImageComponent.m in Sources */,
74CFDD3E1F459400007A1A66 /* WXRecycleListDataManager.m in Sources */,
- 2A837AB31CD9DE9200AEDF03 /* WXLoadingComponent.m in Sources */,
+ 2A837AB31CD9DE9200AEDF03 /* WXLoadingComponent.mm in Sources */,
2AE5B7531CAB7DBD0082FDDB /* WXAComponent.m in Sources */,
- 333D9A291F41507A007CED39 /* WXTransition.m in Sources */,
- 74CFDD3A1F45939C007A1A66 /* WXRecycleListComponent.m in Sources */,
+ 176BE43C209172330086B6AF /* WXComponent+Layout.mm in Sources */,
+ 74CFDD3A1F45939C007A1A66 /* WXRecycleListComponent.mm in Sources */,
741DFE031DDD7D18009B020F /* WXRoundedRect.mm in Sources */,
59A596301CB632050012CD52 /* WXBaseViewController.m in Sources */,
- 74CC7A211C2BF9DC00829368 /* WXListComponent.m in Sources */,
+ 74CC7A211C2BF9DC00829368 /* WXListComponent.mm in Sources */,
7423899C1C3174EB00D748CA /* WXWeakObjectWrapper.m in Sources */,
744BEA561D05178F00452B5D /* WXComponent+Display.m in Sources */,
7408C48F1CFB
<TRUNCATED>
[08/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.mm
new file mode 100644
index 0000000..86124ff
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXSliderNeighborComponent.mm
@@ -0,0 +1,1812 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXSliderNeighborComponent.h"
+#import "WXConvert.h"
+#import "WXUtility.h"
+#import "WXComponent_internal.h"
+#import "WXIndicatorComponent.h"
+#import "WXSDKInstance.h"
+#import "NSTimer+Weex.h"
+#import "WXComponent+Layout.h"
+
+#define MAX_VISIBLE_ITEMS 30
+#define MIN_TOGGLE_DURATION 0.2
+#define MAX_TOGGLE_DURATION 0.4
+#define SCROLL_DURATION 0.4
+#define INSERT_DURATION 0.4
+#define DECELERATE_THRESHOLD 0.1
+#define SCROLL_SPEED_THRESHOLD 2.0
+#define SCROLL_DISTANCE_THRESHOLD 0.1
+#define DECELERATION_MULTIPLIER 30.0
+#define FLOAT_ERROR_MARGIN 0.000001
+
+@protocol WXSliderNeighborDelegate, WXSliderNeighborDataSource;
+
+@interface WXSliderNeighborView: UIView
+@property (nonatomic, strong) WXIndicatorView *indicator;
+@property (nonatomic, weak) id<WXSliderNeighborDelegate> delegate;
+@property (nonatomic, weak) id<WXSliderNeighborDataSource> dataSource;
+@property (nonatomic, assign) CGFloat perspective;
+@property (nonatomic, readonly, getter = isDragging) BOOL dragging;
+@property (nonatomic, readonly, getter = isScrolling) BOOL scrolling;
+@property (nonatomic, assign) CGFloat autoscroll;
+@property (nonatomic, assign) CGFloat scrollOffset;
+@property (nonatomic, assign) CGFloat previousTranslation;
+@property (nonatomic, assign, getter = isVertical) BOOL vertical;
+@property (nonatomic, assign) CGFloat decelerationRate;
+@property (nonatomic, assign, getter = isScrollEnabled) BOOL scrollEnabled;
+@property (nonatomic, assign, getter = isPagingEnabled) BOOL pagingEnabled;
+@property (nonatomic, assign) BOOL bounces;
+@property (nonatomic, assign) BOOL didDrag;
+@property (nonatomic, readonly, getter = isDecelerating) BOOL decelerating;
+@property (nonatomic, strong, readonly) UIView *contentView;
+@property (nonatomic, strong) NSMutableDictionary *itemViews;
+@property (nonatomic, readonly) NSInteger numberOfVisibleItems;
+@property (nonatomic, readonly) NSInteger numberOfItems;
+@property (nonatomic, readonly) NSInteger numberOfPlaceholders;
+@property (nonatomic, strong) NSMutableSet *itemViewPool;
+@property (nonatomic, strong) NSMutableSet *placeholderViewPool;
+@property (nonatomic, assign) NSTimeInterval startTime;
+@property (nonatomic, assign) NSTimeInterval lastTime;
+@property (nonatomic, assign) CGFloat startVelocity;
+@property (nonatomic, assign) CGFloat offsetMultiplier;
+@property (nonatomic, assign) CGFloat startOffset;
+@property (nonatomic, assign) CGFloat endOffset;
+@property (nonatomic, assign) CGSize contentOffset;
+@property (nonatomic, assign) CGSize viewpointOffset;
+@property (nonatomic, assign) CGFloat scrollSpeed;
+@property (nonatomic, assign) CGFloat bounceDistance;
+@property (nonatomic, assign) BOOL stopAtItemBoundary;
+@property (nonatomic, assign) BOOL scrollToItemBoundary;
+@property (nonatomic, assign) BOOL centerItemWhenSelected;
+@property (nonatomic, assign) NSTimeInterval scrollDuration;
+@property (nonatomic, readonly, getter = isWrapEnabled) BOOL wrapEnabled;
+@property (nonatomic, strong) NSTimer *timer;
+@property (nonatomic, assign) NSInteger numberOfPlaceholdersToShow;
+@property (nonatomic, assign) CGFloat previousScrollOffset;
+@property (nonatomic, assign) NSTimeInterval toggleTime;
+@property (nonatomic, readonly) CGFloat toggle;
+@property (nonatomic, assign) NSInteger previousItemIndex;
+@property (nonatomic, readonly) CGFloat itemWidth;
+@property (nonatomic, assign) BOOL inited;
+@end
+
+@implementation WXSliderNeighborView
+
+- (instancetype)init {
+
+ self = [super init];
+ if (!self) {
+ }
+ _decelerationRate = 0.01;
+ _scrollEnabled = YES;
+ _bounces = YES;
+ _offsetMultiplier = 1.0;
+ _perspective = -1.0/500.0;
+ _contentOffset = CGSizeZero;
+ _viewpointOffset = CGSizeZero;
+ _scrollSpeed = 1.0;
+ _bounceDistance = 1.0;
+ _stopAtItemBoundary = YES;
+ _scrollToItemBoundary = YES;
+ _centerItemWhenSelected = YES;
+ _inited = NO;
+
+ _contentView = [[UIView alloc] initWithFrame:self.bounds];
+
+ //pan gesture recognizer
+ self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didPan:)];
+ panGesture.delegate = (id <UIGestureRecognizerDelegate>)self;
+ [_contentView addGestureRecognizer:panGesture];
+
+ //tap gesture recognizer
+ UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap:)];
+ tapGesture.delegate = (id <UIGestureRecognizerDelegate>)self;
+ [_contentView addGestureRecognizer:tapGesture];
+
+ self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction;
+ self.isAccessibilityElement = YES;
+
+ [self addSubview:_contentView];
+
+ if (_dataSource) {
+ [self reloadData];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [self stopAnimation];
+}
+
+- (void)accessibilityIncrement
+{
+ [self setCurrentItemIndex: [self lastItemIndex]];
+}
+
+- (void)accessibilityDecrement
+{
+ [self setCurrentItemIndex:[self nextItemIndex]];
+}
+
+- (void)setDataSource:(id<WXSliderNeighborDataSource>)dataSource
+{
+ if (_dataSource != dataSource) {
+ _dataSource = dataSource;
+ if (_dataSource) {
+ [self reloadData];
+ }
+ }
+}
+
+- (void)setDelegate:(id<WXSliderNeighborDelegate>)delegate
+{
+ if (_delegate != delegate) {
+ _delegate = delegate;
+ if (_delegate && _dataSource) {
+ [self setNeedsLayout];
+ }
+ }
+}
+
+- (void)setVertical:(BOOL)vertical
+{
+ if (_vertical != vertical)
+ {
+ _vertical = vertical;
+ [self layOutItemViews];
+ }
+}
+
+#pragma clang diagnostic push
+#pragma GCC diagnostic ignored "-Wundeclared-selector"
+- (void)setCurrentItemIndex:(NSInteger)currentItemIndex
+{
+ if ([self currentItemIndex] == currentItemIndex) return;
+
+ [self setScrollOffset:currentItemIndex];
+ [self.indicator setCurrentPoint:currentItemIndex];
+
+ if (self.delegate && [self.delegate respondsToSelector:@selector(sliderView:didScrollToItemAtIndex:)]) {
+ [self.delegate sliderNeighbor:self didScrollToItemAtIndex:currentItemIndex];
+ }
+ [self scroll2ItemViewAtIndex:currentItemIndex animated:YES];
+
+}
+#pragma clang diagnostic pop
+
+- (void)updateItemWidth
+{
+ _itemWidth = [_delegate sliderNeighborItemWidth:self] ?: _itemWidth;
+ if (_numberOfItems > 0) {
+ if ([_itemViews count] == 0) {
+ [self loadViewAtIndex:0];
+ }
+ } else if (_numberOfPlaceholders > 0) {
+ if ([_itemViews count] == 0) {
+ [self loadViewAtIndex:-1];
+ }
+ }
+}
+
+- (NSInteger)circularCarouselItemCount
+{
+ NSInteger count = 0;
+ CGFloat spacing = [self valueForOption:WXSliderNeighborOptionSpacing withDefault:1.0];
+ CGFloat width = _vertical ? self.bounds.size.height: self.bounds.size.width;
+ count = MIN(MAX_VISIBLE_ITEMS, MAX(12, ceil(width / (spacing * _itemWidth)) * M_PI));
+ count = MIN(_numberOfItems + _numberOfPlaceholdersToShow, count);
+ return [self valueForOption:WXSliderNeighborOptionCount withDefault:count];
+}
+
+- (void)updateNumberOfVisibleItems
+{
+ //get number of visible items
+ //based on count value
+ CGFloat spacing = [self valueForOption:WXSliderNeighborOptionSpacing withDefault:1.0];
+ CGFloat width = _vertical ? self.bounds.size.height: self.bounds.size.width;
+ CGFloat itemWidth = _itemWidth * spacing;
+ _numberOfVisibleItems = ceil(width / itemWidth) + 2;
+ _numberOfVisibleItems = MIN(MAX_VISIBLE_ITEMS, _numberOfVisibleItems);
+ _numberOfVisibleItems = [self valueForOption:WXSliderNeighborOptionVisibleItems withDefault:_numberOfVisibleItems];
+ _numberOfVisibleItems = MAX(0, MIN(_numberOfVisibleItems, _numberOfItems + _numberOfPlaceholdersToShow));
+
+}
+
+- (CGFloat)offsetForItemAtIndex:(NSInteger)index
+{
+ //calculate relative position
+ CGFloat offset = index - _scrollOffset;
+ if (_wrapEnabled) {
+ if (offset > _numberOfItems/2.0) {
+ offset -= _numberOfItems;
+ } else if (offset < -_numberOfItems/2.0) {
+ offset += _numberOfItems;
+ }
+ }
+
+ return offset;
+}
+
+- (CGFloat)alphaForItemWithOffset:(CGFloat)offset
+{
+ CGFloat fadeMin = -INFINITY;
+ CGFloat fadeMax = INFINITY;
+ CGFloat fadeRange = 1.0;
+ CGFloat fadeMinAlpha = 0.0;
+ fadeMin = [self valueForOption:WXSliderNeighborOptionFadeMin withDefault:fadeMin];
+ fadeMax = [self valueForOption:WXSliderNeighborOptionFadeMax withDefault:fadeMax];
+ fadeRange = [self valueForOption:WXSliderNeighborOptionFadeRange withDefault:fadeRange];
+ fadeMinAlpha = [self valueForOption:WXSliderNeighborOptionFadeMinAlpha withDefault:fadeMinAlpha];
+
+ CGFloat factor = 0.0;
+ if (offset > fadeMax) {
+ factor = offset - fadeMax;
+ } else if (offset < fadeMin) {
+ factor = fadeMin - offset;
+ }
+ return 1.0 - MIN(factor, fadeRange) / fadeRange * (1.0 - fadeMinAlpha);
+}
+
+
+- (UIView *)containView:(UIView *)view
+{
+ //set item width
+ if (!_itemWidth) {
+ _itemWidth = _vertical? view.frame.size.height: view.frame.size.width;
+ }
+
+ //set container frame
+ CGRect frame = view.frame;
+ frame.size.width = _vertical? frame.size.width: _itemWidth;
+ frame.size.height = _vertical? _itemWidth: frame.size.height;
+ UIView *containerView = [[UIView alloc] initWithFrame:frame];
+
+ //set view frame
+ frame = view.frame;
+ frame.origin.x = (containerView.bounds.size.width - frame.size.width) / 2.0;
+ frame.origin.y = (containerView.bounds.size.height - frame.size.height) / 2.0;
+ view.frame = frame;
+ [containerView addSubview:view];
+ containerView.layer.opacity = 0;
+ containerView.transform = CGAffineTransformMakeScale(0.8, 1.0);
+ return containerView;
+}
+
+- (void)transformItemViews
+{
+ for (NSNumber *number in _itemViews)
+ {
+ NSInteger index = [number integerValue];
+ UIView *view = _itemViews[number];
+ [self transformItemView:view atIndex:index];
+ }
+}
+
+- (CATransform3D)transformForItemViewWithOffset:(CGFloat)offset
+{
+ //set up base transform
+ CATransform3D transform = CATransform3DIdentity;
+ transform.m34 = _perspective;
+ transform = CATransform3DTranslate(transform, -_viewpointOffset.width, -_viewpointOffset.height, 0.0);
+
+ //perform transforms
+ CGFloat spacing = [self valueForOption:WXSliderNeighborOptionSpacing withDefault:1.0];
+ if (_vertical) {
+ return CATransform3DTranslate(transform, 0.0, offset * _itemWidth * spacing, 0.0);
+ } else {
+ return CATransform3DTranslate(transform, offset * _itemWidth * spacing, 0.0, 0.0);
+ }
+}
+
+- (void)transformItemView:(UIView *)view atIndex:(NSInteger)index
+{
+ //calculate offset
+ CGFloat offset = [self offsetForItemAtIndex:index];
+
+ //update alpha
+ view.superview.layer.opacity = [self alphaForItemWithOffset:offset];
+
+ //center view
+ view.superview.center = CGPointMake(self.bounds.size.width/2.0 + _contentOffset.width,
+ self.bounds.size.height/2.0 + _contentOffset.height);
+
+ //enable/disable interaction
+ view.superview.userInteractionEnabled = (!_centerItemWhenSelected || index == self.currentItemIndex);
+ view.superview.layer.rasterizationScale = [UIScreen mainScreen].scale;
+
+ [view layoutIfNeeded];
+
+ CGFloat clampedOffset = MAX(-1.0, MIN(1.0, offset));
+ if (_decelerating || (_scrolling && !_dragging && !_didDrag) || (_autoscroll && !_dragging) ||
+ (!_wrapEnabled && (_scrollOffset < 0 || _scrollOffset >= _numberOfItems - 1))) {
+ if (offset > 0) {
+ _toggle = (offset <= 0.5)? -clampedOffset: (1.0 - clampedOffset);
+ } else {
+ _toggle = (offset > -0.5)? -clampedOffset: (- 1.0 - clampedOffset);
+ }
+ }
+
+ //calculate transform
+ CATransform3D transform = [self transformForItemViewWithOffset:offset];
+
+ //transform view
+ view.superview.layer.transform = transform;
+
+ //backface culling
+ BOOL showBackfaces = view.layer.doubleSided;
+ if (showBackfaces) {
+ showBackfaces = YES;
+ }
+ showBackfaces = !![self valueForOption:WXSliderNeighborOptionShowBackfaces withDefault:showBackfaces];
+
+ view.superview.hidden = !(showBackfaces ?: (transform.m33 > 0.0));
+}
+
+NSComparisonResult sliderNeighorCompareViewDepth(UIView *view1, UIView *view2, WXSliderNeighborView *self)
+{
+ //compare depths
+ CATransform3D t1 = view1.superview.layer.transform;
+ CATransform3D t2 = view2.superview.layer.transform;
+ CGFloat z1 = t1.m13 + t1.m23 + t1.m33 + t1.m43;
+ CGFloat z2 = t2.m13 + t2.m23 + t2.m33 + t2.m43;
+ CGFloat difference = z1 - z2;
+
+ //if depths are equal, compare distance from current view
+ if (difference == 0.0) {
+ CATransform3D t3 = [self currentItemView].superview.layer.transform;
+ if (self.vertical) {
+ CGFloat y1 = t1.m12 + t1.m22 + t1.m32 + t1.m42;
+ CGFloat y2 = t2.m12 + t2.m22 + t2.m32 + t2.m42;
+ CGFloat y3 = t3.m12 + t3.m22 + t3.m32 + t3.m42;
+ difference = fabs(y2 - y3) - fabs(y1 - y3);
+ } else {
+ CGFloat x1 = t1.m11 + t1.m21 + t1.m31 + t1.m41;
+ CGFloat x2 = t2.m11 + t2.m21 + t2.m31 + t2.m41;
+ CGFloat x3 = t3.m11 + t3.m21 + t3.m31 + t3.m41;
+ difference = fabs(x2 - x3) - fabs(x1 - x3);
+ }
+ }
+ return (difference < 0.0)? NSOrderedAscending: NSOrderedDescending;
+}
+
+- (void)depthSortViews
+{
+ for (UIView *view in [[_itemViews allValues] sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))sliderNeighorCompareViewDepth context:(__bridge void *)self])
+ {
+ [_contentView bringSubviewToFront:(UIView *__nonnull)view.superview];
+ }
+}
+
+- (void)layOutItemViews
+{
+ //bail out if not set up yet
+ if (!_dataSource || !_contentView) {
+ return;
+ }
+
+ //update wrap
+ _wrapEnabled = YES;
+ _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
+
+ //no placeholders on wrapped carousels
+ _numberOfPlaceholdersToShow = _wrapEnabled? 0: _numberOfPlaceholders;
+
+ //set item width
+ [self updateItemWidth];
+
+ //update number of visible items
+ [self updateNumberOfVisibleItems];
+
+ //prevent false index changed event
+ _previousScrollOffset = self.scrollOffset;
+
+ //update offset multiplier
+ _offsetMultiplier = 1.0;
+ _offsetMultiplier = [self valueForOption:WXSliderNeighborOptionOffsetMultiplier withDefault:_offsetMultiplier];
+
+ //align
+ if (!_scrolling && !_decelerating && !_autoscroll) {
+ if (_scrollToItemBoundary && self.currentItemIndex != -1) {
+ [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
+ } else {
+ _scrollOffset = [self clampedOffset:_scrollOffset];
+ }
+ }
+ //update views
+ if (_inited) {
+ [self didScroll];
+ }
+
+ _inited = YES;
+}
+
+- (CGFloat)valueForOption:(WXSliderNeighborOption)option withDefault:(CGFloat)value
+{
+ if (_delegate && [_delegate respondsToSelector:@selector(sliderNeighbor:valueForOption:withDefault:)]) {
+ return [_delegate sliderNeighbor:self valueForOption:option withDefault:value];
+ }
+ return value;
+}
+
+#pragma mark -
+#pragma mark Gestures and taps
+
+- (NSInteger)viewOrSuperviewIndex:(UIView *)view
+{
+ if (view == nil || view == _contentView) {
+ return NSNotFound;
+ }
+ NSInteger index = [self indexOfItemView:view];
+ if (index == NSNotFound) {
+ return [self viewOrSuperviewIndex:view.superview];
+ }
+ return index;
+}
+
+- (BOOL)viewOrSuperview:(UIView *)view implementsSelector:(SEL)selector
+{
+ if (!view || view == self.contentView) {
+ return NO;
+ }
+
+ Class viewClass = [view class];
+ while (viewClass && viewClass != [UIView class])
+ {
+ unsigned int numberOfMethods;
+ Method *methods = class_copyMethodList(viewClass, &numberOfMethods);
+ for (unsigned int i = 0; i < numberOfMethods; i++)
+ {
+ if (method_getName(methods[i]) == selector) {
+ free(methods);
+ return YES;
+ }
+ }
+ if (methods) free(methods);
+ viewClass = [viewClass superclass];
+ }
+
+ return [self viewOrSuperview:view.superview implementsSelector:selector];
+}
+
+- (id)viewOrSuperview:(UIView *)view ofClass:(Class)swizzClass
+{
+ if (!view || view == self.contentView) {
+ return nil;
+ } else if ([view isKindOfClass:swizzClass]) {
+ return view;
+ }
+ return [self viewOrSuperview:view.superview ofClass:swizzClass];
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch
+{
+ if (_scrollEnabled) {
+ _dragging = NO;
+ _scrolling = NO;
+ _decelerating = NO;
+ }
+
+ if ([gesture isKindOfClass:[UITapGestureRecognizer class]]) {
+ //handle tap
+ NSInteger index = [self viewOrSuperviewIndex:touch.view];
+ if (index == NSNotFound && _centerItemWhenSelected) {
+ //view is a container view
+ index = [self viewOrSuperviewIndex:[touch.view.subviews lastObject]];
+ }
+ if (index != NSNotFound) {
+ if ([self viewOrSuperview:touch.view implementsSelector:@selector(touchesBegan:withEvent:)]) {
+ return NO;
+ }
+ }
+ } else if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
+ if (!_scrollEnabled) {
+ return NO;
+ } else if ([self viewOrSuperview:touch.view implementsSelector:@selector(touchesMoved:withEvent:)]) {
+ UIScrollView *scrollView = [self viewOrSuperview:touch.view ofClass:[UIScrollView class]];
+ if (scrollView) {
+ return !scrollView.scrollEnabled ||
+ (self.vertical && scrollView.contentSize.height <= scrollView.frame.size.height) ||
+ (!self.vertical && scrollView.contentSize.width <= scrollView.frame.size.width);
+ }
+ if ([self viewOrSuperview:touch.view ofClass:[UIButton class]] ||
+ [self viewOrSuperview:touch.view ofClass:[UIBarButtonItem class]]) {
+ return YES;
+ }
+ return NO;
+ }
+ }
+ return YES;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
+ //if the view which the otherGestureRecognizer works on is a scrollview and also it is scrollEnabled vertically ,at this time,we should not block the guesture from being recognized by the otherGestureRecognize
+ if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
+ if ([otherGestureRecognizer.view isKindOfClass:[UIScrollView class]]) {
+ UIScrollView* scrollview = (UIScrollView *)otherGestureRecognizer.view;
+ if (scrollview.scrollEnabled) {
+ UIPanGestureRecognizer* panRcgn= (UIPanGestureRecognizer *)gestureRecognizer;
+ //check offset for confirming vertival movement
+ if (fabs([panRcgn translationInView:panRcgn.view].y) > fabs([panRcgn translationInView:panRcgn.view].x)*16) {
+ return YES;
+ }
+ }
+ }
+ }
+ return NO;
+}
+
+
+- (void)didPan:(UIPanGestureRecognizer *)panGesture
+{
+ if (_scrollEnabled && _numberOfItems) {
+ switch (panGesture.state)
+ {
+ case UIGestureRecognizerStateBegan:
+ {
+ _dragging = YES;
+ _scrolling = NO;
+ _decelerating = NO;
+ _previousTranslation = _vertical? [panGesture translationInView:self].y: [panGesture translationInView:self].x;
+
+ [_delegate sliderNeighborWillBeginDragging:self];
+ break;
+ }
+ case UIGestureRecognizerStateEnded:
+ case UIGestureRecognizerStateCancelled:
+ case UIGestureRecognizerStateFailed:
+ {
+ _dragging = NO;
+ _didDrag = YES;
+ if ([self shouldDecelerate]) {
+ _didDrag = NO;
+ [self startDecelerating];
+ }
+
+ [self pushAnimationState:YES];
+ [_delegate sliderNeighborDidEndDragging:self willDecelerate:_decelerating];
+ [self popAnimationState];
+
+ if (!_decelerating) {
+ if ((_scrollToItemBoundary || fabs(_scrollOffset - [self clampedOffset:_scrollOffset]) > FLOAT_ERROR_MARGIN) && !_autoscroll) {
+ if (fabs(_scrollOffset - self.currentItemIndex) < FLOAT_ERROR_MARGIN) {
+ //call scroll to trigger events for legacy support reasons
+ //even though technically we don't need to scroll at all
+ [self scroll2ItemViewAtIndex:self.currentItemIndex duration:0.01];
+ } else {
+ [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
+ }
+ } else {
+ [self depthSortViews];
+ }
+ } else {
+ [self pushAnimationState:YES];
+ [_delegate sliderNeighborWillBeginDecelerating:self];
+ [self popAnimationState];
+ }
+ break;
+ }
+ case UIGestureRecognizerStateChanged:
+ {
+ CGFloat translation = _vertical? [panGesture translationInView:self].y: [panGesture translationInView:self].x;
+ CGFloat velocity = _vertical? [panGesture velocityInView:self].y: [panGesture velocityInView:self].x;
+
+ CGFloat factor = 1.0;
+ if (!_wrapEnabled && _bounces) {
+ factor = 1.0 - MIN(fabs(_scrollOffset - [self clampedOffset:_scrollOffset]),
+ _bounceDistance) / _bounceDistance;
+ }
+
+ _startVelocity = -velocity * factor * _scrollSpeed / _itemWidth;
+ _scrollOffset -= (translation - _previousTranslation) * factor * _offsetMultiplier / _itemWidth;
+ _previousTranslation = translation;
+ [self didScroll];
+ break;
+ }
+ case UIGestureRecognizerStatePossible:
+ {
+ //do nothing
+ break;
+ }
+ }
+ }
+}
+
+- (void)didTap:(UITapGestureRecognizer *)tapGesture
+{
+ //check for tapped view
+ NSInteger index = [self indexOfItemView:[self itemViewAtPoint:[tapGesture locationInView:_contentView]]];
+ if (index != NSNotFound) {
+ if (!_delegate || [_delegate sliderNeighbor:self shouldSelectItemAtIndex:index]) {
+ if ((index != self.currentItemIndex && _centerItemWhenSelected) ||
+ (index == self.currentItemIndex && _scrollToItemBoundary)) {
+ [self scroll2ItemViewAtIndex:index animated:YES];
+ }
+ [_delegate sliderNeighbor:self didSelectItemAtIndex:index];
+ } else if (_scrollEnabled && _scrollToItemBoundary && _autoscroll) {
+ [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
+ }
+ } else {
+ [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
+ }
+}
+
+- (void)pushAnimationState:(BOOL)enabled
+{
+ [CATransaction begin];
+ [CATransaction setDisableActions:!enabled];
+}
+
+- (void)popAnimationState
+{
+ [CATransaction commit];
+}
+
+- (void)reloadData
+{
+ //remove old views
+ for (UIView *view in [_itemViews allValues])
+ {
+ [view.superview removeFromSuperview];
+ }
+
+ //bail out if not set up yet
+ if (!_dataSource || !_contentView)
+ {
+ return;
+ }
+
+ //get number of items and placeholders
+ _numberOfVisibleItems = 0;
+ _numberOfItems = [_dataSource numberOfItemsInSliderNeighbor:self];
+ if ([_dataSource respondsToSelector:@selector(numberOfPlaceholdersInsliderNeighbor:)]) {
+ _numberOfPlaceholders = [_dataSource numberOfPlaceholdersInsliderNeighbor:self];
+ }
+
+ //reset view pools
+ self.itemViews = [NSMutableDictionary dictionary];
+ self.itemViewPool = [NSMutableSet set];
+ self.placeholderViewPool = [NSMutableSet setWithCapacity:_numberOfPlaceholders];
+
+ //layout views
+ [self setNeedsLayout];
+
+ //fix scroll offset
+ if (_numberOfItems > 0 && _scrollOffset < 0.0)
+ {
+ [self scroll2ItemViewAtIndex:0 animated:(_numberOfPlaceholders > 0)];
+ }
+}
+
+#pragma mark scrolling
+
+- (NSInteger)currentItemIndex
+{
+ return [self clampedIndex:round(_scrollOffset)];
+}
+
+- (NSInteger)nextItemIndex
+{
+ return ([self currentItemIndex]+1)%_numberOfItems;
+}
+
+- (NSInteger)lastItemIndex
+{
+ if ([self currentItemIndex] == 0) {
+ return _numberOfItems - 1;
+ }
+ return ((NSInteger)round(abs((int)[self currentItemIndex] - 1))) % _numberOfItems;
+}
+
+- (NSInteger)minScrollDistanceFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex
+{
+ NSInteger directDistance = toIndex - fromIndex;
+ if (_wrapEnabled)
+ {
+ NSInteger wrappedDistance = MIN(toIndex, fromIndex) + _numberOfItems - MAX(toIndex, fromIndex);
+ if (fromIndex < toIndex)
+ {
+ wrappedDistance = -wrappedDistance;
+ }
+ return (ABS(directDistance) <= ABS(wrappedDistance))? directDistance: wrappedDistance;
+ }
+ return directDistance;
+}
+
+- (NSInteger)clampedIndex:(NSInteger)index
+{
+ if (_numberOfItems == 0)
+ {
+ return -1;
+ } else if (_wrapEnabled) {
+ return index - floor((CGFloat)index / (CGFloat)_numberOfItems) * _numberOfItems;
+ } else {
+ return MIN(MAX(0, index), MAX(0, _numberOfItems - 1));
+ }
+}
+
+- (CGFloat)clampedOffset:(CGFloat)offset
+{
+ if (_numberOfItems == 0) {
+ return -1.0;
+ } else if (_wrapEnabled) {
+ return offset - floor(offset / (CGFloat)_numberOfItems) * _numberOfItems;
+ } else {
+ return MIN(MAX(0.0, offset), MAX(0.0, (CGFloat)_numberOfItems - 1.0));
+ }
+}
+
+- (CGFloat)minScrollDistanceFromOffset:(CGFloat)fromOffset toOffset:(CGFloat)toOffset
+{
+ CGFloat directDistance = toOffset - fromOffset;
+ if (_wrapEnabled) {
+ CGFloat wrappedDistance = MIN(toOffset, fromOffset) + _numberOfItems - MAX(toOffset, fromOffset);
+ if (fromOffset < toOffset) {
+ wrappedDistance = -wrappedDistance;
+ }
+ return (fabs(directDistance) <= fabs(wrappedDistance))? directDistance: wrappedDistance;
+ }
+ return directDistance;
+}
+
+- (void)scrollByOffset:(CGFloat)offset duration:(NSTimeInterval)duration
+{
+ if (duration > 0.0) {
+ _decelerating = NO;
+ _scrolling = YES;
+ _startTime = CACurrentMediaTime();
+ _startOffset = _scrollOffset;
+ _scrollDuration = duration;
+ _endOffset = _startOffset + offset;
+ if (!_wrapEnabled) {
+ _endOffset = [self clampedOffset:_endOffset];
+ }
+ [_delegate sliderNeighborWillBeginScrollingAnimation:self];
+ [self startAnimation];
+ } else {
+ self.scrollOffset += offset;
+ }
+}
+
+- (void)scroll2Offset:(CGFloat)offset duration:(NSTimeInterval)duration
+{
+ [self scrollByOffset:[self minScrollDistanceFromOffset:_scrollOffset toOffset:offset] duration:duration];
+ if (_delegate && [_delegate respondsToSelector:@selector(sliderNeighbor:didScrollToItemAtIndex:)]) {
+ [_delegate sliderNeighbor:self didScrollToItemAtIndex:offset];
+ }
+}
+
+- (void)scroll2ItemViewAtIndex:(NSInteger)index duration:(NSTimeInterval)duration
+{
+ [self scroll2Offset:index duration:duration];
+}
+
+- (void)scroll2ItemViewAtIndex:(NSInteger)index animated:(BOOL)animated
+{
+ [self scroll2ItemViewAtIndex:index duration:animated? 0.6: 0];
+}
+
+- (void)scrollByNumberOfItems:(NSInteger)itemCount duration:(NSTimeInterval)duration
+{
+ if (duration > 0.0) {
+ CGFloat offset = 0.0;
+ if (itemCount > 0) {
+ offset = (floor(_scrollOffset) + itemCount) - _scrollOffset;
+ } else if (itemCount < 0) {
+ offset = (ceil(_scrollOffset) + itemCount) - _scrollOffset;
+ } else {
+ offset = round(_scrollOffset) - _scrollOffset;
+ }
+ [self scrollByOffset:offset duration:duration];
+ } else {
+ self.scrollOffset = [self clampedIndex:_previousItemIndex + itemCount];
+ }
+}
+
+- (void)removeItemAtIndex:(NSInteger)index animated:(BOOL)animated
+{
+ index = [self clampedIndex:index];
+ UIView *itemView = [self itemViewAtIndex:index];
+
+ if (animated) {
+ [UIView beginAnimations:nil context:nil];
+ [UIView setAnimationDuration:0.1];
+ [UIView setAnimationDelegate:itemView.superview];
+ [UIView setAnimationDidStopSelector:@selector(removeFromSuperview)];
+ [self performSelector:@selector(queueItemView:) withObject:itemView afterDelay:0.1];
+ itemView.superview.layer.opacity = 0.0;
+ [UIView commitAnimations];
+
+ [UIView beginAnimations:nil context:nil];
+ [UIView setAnimationDelay:0.1];
+ [UIView setAnimationDuration:INSERT_DURATION];
+ [UIView setAnimationDelegate:self];
+ [UIView setAnimationDidStopSelector:@selector(depthSortViews)];
+ [self removeViewAtIndex:index];
+ _numberOfItems --;
+ _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
+ [self updateNumberOfVisibleItems];
+ _scrollOffset = self.currentItemIndex;
+ [self didScroll];
+ [UIView commitAnimations];
+ } else {
+ [self pushAnimationState:NO];
+ [self queueItemView:itemView];
+ [itemView.superview removeFromSuperview];
+ [self removeViewAtIndex:index];
+ _numberOfItems --;
+ _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
+ _scrollOffset = self.currentItemIndex;
+ [self didScroll];
+ [self depthSortViews];
+ [self popAnimationState];
+ }
+}
+
+- (void)insertItemAtIndex:(NSInteger)index animated:(BOOL)animated
+{
+ _numberOfItems ++;
+ _wrapEnabled = !![self valueForOption:WXSliderNeighborOptionWrap withDefault:_wrapEnabled];
+ [self updateNumberOfVisibleItems];
+
+ index = [self clampedIndex:index];
+ [self insertView:nil atIndex:index];
+ [self loadViewAtIndex:index];
+
+ if (fabs(_itemWidth) < FLOAT_ERROR_MARGIN) {
+ [self updateItemWidth];
+ }
+
+ if (animated) {
+ [UIView beginAnimations:nil context:nil];
+ [UIView setAnimationDuration:INSERT_DURATION];
+ [UIView setAnimationDelegate:self];
+ [UIView setAnimationDidStopSelector:@selector(didScroll)];
+ [self transformItemViews];
+ [UIView commitAnimations];
+ } else {
+ [self pushAnimationState:NO];
+ [self didScroll];
+ [self popAnimationState];
+ }
+
+ if (_scrollOffset < 0.0) {
+ [self scroll2ItemViewAtIndex:0 animated:(animated && _numberOfPlaceholders)];
+ }
+}
+
+- (void)reloadItemAtIndex:(NSInteger)index animated:(BOOL)animated
+{
+ //get container view
+ UIView *containerView = [[self itemViewAtIndex:index] superview];
+ if (containerView) {
+ if (animated) {
+ //fade transition
+ CATransition *transition = [CATransition animation];
+ transition.duration = INSERT_DURATION;
+ transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+ transition.type = kCATransitionFade;
+ [containerView.layer addAnimation:transition forKey:nil];
+ }
+
+ //reload view
+ [self loadViewAtIndex:index withContainerView:containerView];
+ }
+}
+
+#pragma mark Animation
+- (void)step
+{
+ [self pushAnimationState:NO];
+ NSTimeInterval currentTime = CACurrentMediaTime();
+ double delta = currentTime - _lastTime;
+ _lastTime = currentTime;
+
+ if (_scrolling && !_dragging) {
+ NSTimeInterval time = MIN(1.0, (currentTime - _startTime) / _scrollDuration);
+ delta = [self easeInOut:time];
+ _scrollOffset = _startOffset + (_endOffset - _startOffset) * delta;
+ [self didScroll];
+ if (time >= 1.0) {
+ _scrolling = NO;
+ [self depthSortViews];
+ [self pushAnimationState:YES];
+ [_delegate sliderNeighborDidEndScrollingAnimation:self];
+ [self popAnimationState];
+ }
+ } else if (_decelerating) {
+ CGFloat time = MIN(_scrollDuration, currentTime - _startTime);
+ CGFloat acceleration = -_startVelocity/_scrollDuration;
+ CGFloat distance = _startVelocity * time + 0.5 * acceleration * pow(time, 2.0);
+ _scrollOffset = _startOffset + distance;
+ [self didScroll];
+ if (fabs(time - _scrollDuration) < FLOAT_ERROR_MARGIN) {
+ _decelerating = NO;
+ [self pushAnimationState:YES];
+ [_delegate sliderNeighborDidEndDecelerating:self];
+ [self popAnimationState];
+ if ((_scrollToItemBoundary || fabs(_scrollOffset - [self clampedOffset:_scrollOffset]) > FLOAT_ERROR_MARGIN) && !_autoscroll) {
+ if (fabs(_scrollOffset - self.currentItemIndex) < FLOAT_ERROR_MARGIN) {
+ //call scroll to trigger events for legacy support reasons
+ //even though technically we don't need to scroll at all
+ [self scroll2ItemViewAtIndex:self.currentItemIndex duration:0.01];
+ } else {
+ [self scroll2ItemViewAtIndex:self.currentItemIndex animated:YES];
+ }
+ } else {
+ CGFloat difference = round(_scrollOffset) - _scrollOffset;
+ if (difference > 0.5) {
+ difference = difference - 1.0;
+ } else if (difference < -0.5) {
+ difference = 1.0 + difference;
+ }
+ _toggleTime = currentTime - MAX_TOGGLE_DURATION * fabs(difference);
+ _toggle = MAX(-1.0, MIN(1.0, -difference));
+ }
+ }
+ } else if (_autoscroll && !_dragging) {
+ //autoscroll goes backwards from what you'd expect, for historical reasons
+ self.scrollOffset = [self clampedOffset:_scrollOffset - delta * _autoscroll];
+ } else if (fabs(_toggle) > FLOAT_ERROR_MARGIN) {
+ NSTimeInterval toggleDuration = _startVelocity? MIN(1.0, MAX(0.0, 1.0 / fabs(_startVelocity))): 1.0;
+ toggleDuration = MIN_TOGGLE_DURATION + (MAX_TOGGLE_DURATION - MIN_TOGGLE_DURATION) * toggleDuration;
+ NSTimeInterval time = MIN(1.0, (currentTime - _toggleTime) / toggleDuration);
+ delta = [self easeInOut:time];
+ _toggle = (_toggle < 0.0)? (delta - 1.0): (1.0 - delta);
+ [self didScroll];
+ } else if (!_autoscroll) {
+ [self stopAnimation];
+ }
+
+ [self popAnimationState];
+}
+
+- (CGFloat)easeInOut:(CGFloat)time
+{
+ return (time < 0.5)? 0.5 * pow(time * 2.0, 3.0): 0.5 * pow(time * 2.0 - 2.0, 3.0) + 1.0;
+}
+
+- (CGFloat)decelerationDistance
+{
+ CGFloat acceleration = -_startVelocity * DECELERATION_MULTIPLIER * (1.0 - _decelerationRate);
+ return -pow(_startVelocity, 2.0) / (2.0 * acceleration);
+}
+
+- (void)startDecelerating
+{
+ CGFloat distance = [self decelerationDistance];
+ _startOffset = _scrollOffset;
+ _endOffset = _startOffset + distance;
+ if (_pagingEnabled) {
+ if (distance > 0.0) {
+ _endOffset = ceil(_startOffset);
+ } else {
+ _endOffset = floor(_startOffset);
+ }
+ } else if (_stopAtItemBoundary) {
+ if (distance > 0.0) {
+ _endOffset = ceil(_endOffset);
+ } else {
+ _endOffset = floor(_endOffset);
+ }
+ }
+ if (!_wrapEnabled) {
+ if (_bounces) {
+ _endOffset = MAX(-_bounceDistance, MIN(_numberOfItems - 1.0 + _bounceDistance, _endOffset));
+ } else {
+ _endOffset = [self clampedOffset:_endOffset];
+ }
+ }
+ distance = _endOffset - _startOffset;
+
+ _startTime = CACurrentMediaTime();
+ _scrollDuration = fabs(distance) / fabs(0.5 * _startVelocity);
+
+ if (distance != 0.0) {
+ _decelerating = YES;
+ [self startAnimation];
+ }
+}
+
+
+- (BOOL)shouldDecelerate
+{
+ return (fabs(_startVelocity) > SCROLL_SPEED_THRESHOLD) &&
+ (fabs([self decelerationDistance]) > DECELERATE_THRESHOLD);
+}
+
+- (BOOL)shouldScroll
+{
+ return (fabs(_startVelocity) > SCROLL_SPEED_THRESHOLD) &&
+ (fabs(_scrollOffset - self.currentItemIndex) > SCROLL_DISTANCE_THRESHOLD);
+}
+
+- (void)didScroll
+{
+ if (_wrapEnabled || !_bounces) {
+ _scrollOffset = [self clampedOffset:_scrollOffset];
+ } else {
+ CGFloat min = -_bounceDistance;
+ CGFloat max = MAX(_numberOfItems - 1, 0.0) + _bounceDistance;
+ if (_scrollOffset < min) {
+ _scrollOffset = min;
+ _startVelocity = 0.0;
+ } else if (_scrollOffset > max) {
+ _scrollOffset = max;
+ _startVelocity = 0.0;
+ }
+ }
+
+ //check if index has changed
+ NSInteger difference = [self minScrollDistanceFromIndex:self.currentItemIndex toIndex:self.previousItemIndex];
+ if (difference) {
+ _toggleTime = CACurrentMediaTime();
+ _toggle = MAX(-1, MIN(1, difference));
+
+ [self startAnimation];
+ }
+
+ [self loadUnloadViews];
+ [self transformItemViews];
+
+ //notify delegate of offset change
+ if (fabs(_scrollOffset - _previousScrollOffset) > 0.000001)
+ {
+ [self pushAnimationState:YES];
+ [_delegate sliderNeighborDidScroll:self];
+ [self popAnimationState];
+ }
+
+ //notify delegate of index change
+ if (_previousItemIndex != self.currentItemIndex)
+ {
+ [self pushAnimationState:YES];
+ if([_delegate respondsToSelector:@selector(sliderNeighborCurrentItemIndexDidChange:from:to:)]) {
+ [_delegate sliderNeighborCurrentItemIndexDidChange:self from:_previousItemIndex to:self.currentItemIndex];
+ }
+ [self popAnimationState];
+ }
+
+ //update previous index
+ _previousScrollOffset = _scrollOffset;
+ _previousItemIndex = self.currentItemIndex;
+ [self.indicator setCurrentPoint:self.currentItemIndex];
+}
+
+- (void)startAnimation
+{
+ if (!_timer) {
+ self.timer = [NSTimer timerWithTimeInterval:1.0/60.0
+ target:self
+ selector:@selector(step)
+ userInfo:nil
+ repeats:YES];
+
+ [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
+ [[NSRunLoop mainRunLoop] addTimer:_timer forMode:UITrackingRunLoopMode];
+
+ }
+}
+
+- (void)stopAnimation
+{
+ [_timer invalidate];
+ _timer = nil;
+}
+
+#pragma mark -
+#pragma mark view management
+
+- (NSArray *)indexesForVisibleItems
+{
+ return [[_itemViews allKeys] sortedArrayUsingSelector:@selector(compare:)];
+}
+
+- (NSArray *)visibleItemViews
+{
+ NSArray *indexes = [self indexesForVisibleItems];
+ return [_itemViews objectsForKeys:indexes notFoundMarker:[NSNull null]];
+}
+
+- (UIView *)itemViewAtIndex:(NSInteger)index
+{
+ return _itemViews[@(index)];
+}
+
+- (UIView *)currentItemView
+{
+ return [self itemViewAtIndex:self.currentItemIndex];
+}
+
+- (NSInteger)indexOfItemView:(UIView *)view
+{
+ NSInteger index = [[_itemViews allValues] indexOfObject:view];
+ if (index != NSNotFound) {
+ return [[_itemViews allKeys][index] integerValue];
+ }
+ return NSNotFound;
+}
+
+- (NSInteger)indexOfItemViewOrSubview:(UIView *)view
+{
+ NSInteger index = [self indexOfItemView:view];
+ if (index == NSNotFound && view != nil && view != _contentView) {
+ return [self indexOfItemViewOrSubview:view.superview];
+ }
+ return index;
+}
+
+- (UIView *)itemViewAtPoint:(CGPoint)point
+{
+ for (UIView *view in [[[_itemViews allValues] sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))sliderNeighorCompareViewDepth context:(__bridge void *)self] reverseObjectEnumerator])
+ {
+ if ([view.superview.layer hitTest:point]) {
+ return view;
+ }
+ }
+ return nil;
+}
+
+- (void)setItemView:(UIView *)view forIndex:(NSInteger)index
+{
+ _itemViews[@(index)] = view;
+}
+
+- (void)removeViewAtIndex:(NSInteger)index
+{
+ NSMutableDictionary *newItemViews = [NSMutableDictionary dictionaryWithCapacity:[_itemViews count] - 1];
+ for (NSNumber *number in [self indexesForVisibleItems])
+ {
+ NSInteger i = [number integerValue];
+ if (i < index) {
+ newItemViews[number] = _itemViews[number];
+ } else if (i > index) {
+ newItemViews[@(i - 1)] = _itemViews[number];
+ }
+ }
+ self.itemViews = newItemViews;
+ [self.indicator setPointCount:self.itemViews.count];
+}
+
+- (void)insertView:(UIView *)view atIndex:(NSInteger)index
+{
+ NSMutableDictionary *newItemViews = [NSMutableDictionary dictionaryWithCapacity:[_itemViews count] + 1];
+ for (NSNumber *number in [self indexesForVisibleItems])
+ {
+ NSInteger i = [number integerValue];
+ if (i < index) {
+ newItemViews[number] = _itemViews[number];
+ } else {
+ newItemViews[@(i + 1)] = _itemViews[number];
+ }
+ }
+ if (view) {
+ [self setItemView:view forIndex:index];
+ }
+ self.itemViews = newItemViews;
+
+ [self.indicator setPointCount:self.itemViews.count];
+}
+
+#pragma mark -
+#pragma mark View loading
+
+- (UIView *)loadViewAtIndex:(NSInteger)index withContainerView:(UIView *)containerView
+{
+ [self pushAnimationState:NO];
+
+ UIView *view = nil;
+ if (index < 0) {
+ view = [_dataSource sliderNeighbor:self placeholderViewAtIndex:(NSInteger)(ceil((CGFloat)_numberOfPlaceholdersToShow/2.0)) + index reusingView:[self dequeuePlaceholderView]];
+ } else if (index >= _numberOfItems) {
+ view = [_dataSource sliderNeighbor:self placeholderViewAtIndex:_numberOfPlaceholdersToShow/2.0 + index - _numberOfItems reusingView:[self dequeuePlaceholderView]];
+ } else {
+ view = [_dataSource sliderNeighbor:self viewForItemAtIndex:index reusingView:[self dequeueItemView]];
+ }
+
+ if (view == nil) {
+ view = [[UIView alloc] init];
+ }
+
+ [self setItemView:view forIndex:index];
+ if (containerView) {
+ //get old item view
+ UIView *oldItemView = [containerView.subviews lastObject];
+ if (index < 0 || index >= _numberOfItems) {
+ [self queuePlaceholderView:oldItemView];
+ } else {
+ [self queueItemView:oldItemView];
+ }
+
+ //set container frame
+ CGRect frame = containerView.bounds;
+ if(_vertical) {
+ frame.size.width = view.frame.size.width;
+ frame.size.height = MIN(_itemWidth, view.frame.size.height);
+ } else {
+ frame.size.width = MIN(_itemWidth, view.frame.size.width);
+ frame.size.height = view.frame.size.height;
+ }
+ containerView.bounds = frame;
+
+ //set view frame
+ frame = view.frame;
+ frame.origin.x = (containerView.bounds.size.width - frame.size.width) / 2.0;
+ frame.origin.y = (containerView.bounds.size.height - frame.size.height) / 2.0;
+ view.frame = frame;
+
+ //switch views
+ [oldItemView removeFromSuperview];
+ [containerView addSubview:view];
+ } else {
+ [_contentView addSubview:[self containView:view]];
+ }
+ view.superview.layer.opacity = 0.0;
+ [self transformItemView:view atIndex:index];
+
+ [self popAnimationState];
+
+ return view;
+}
+
+- (UIView *)loadViewAtIndex:(NSInteger)index
+{
+ return [self loadViewAtIndex:index withContainerView:nil];
+}
+
+- (void)loadUnloadViews
+{
+ //set item width
+ [self updateItemWidth];
+
+ //update number of visible items
+ [self updateNumberOfVisibleItems];
+
+ //calculate visible view indices
+ NSMutableSet *visibleIndices = [NSMutableSet setWithCapacity:_numberOfVisibleItems];
+ NSInteger min = -(NSInteger)(ceil((CGFloat)_numberOfPlaceholdersToShow/2.0));
+ NSInteger max = _numberOfItems - 1 + _numberOfPlaceholdersToShow/2;
+ NSInteger offset = self.currentItemIndex - _numberOfVisibleItems/2;
+ if (!_wrapEnabled) {
+ offset = MAX(min, MIN(max - _numberOfVisibleItems + 1, offset));
+ }
+ for (NSInteger i = 0; i < _numberOfVisibleItems; i++)
+ {
+ NSInteger index = i + offset;
+ if (_wrapEnabled) {
+ index = [self clampedIndex:index];
+ }
+ CGFloat alpha = [self alphaForItemWithOffset:[self offsetForItemAtIndex:index]];
+ if (alpha) {
+ //only add views with alpha > 0
+ [visibleIndices addObject:@(index)];
+ }
+ }
+
+ //remove offscreen views
+ for (NSNumber *number in [_itemViews allKeys])
+ {
+ if (![visibleIndices containsObject:number]) {
+ UIView *view = _itemViews[number];
+ if ([number integerValue] < 0 || [number integerValue] >= _numberOfItems) {
+ [self queuePlaceholderView:view];
+ } else {
+ [self queueItemView:view];
+ }
+ [view.superview removeFromSuperview];
+ [(NSMutableDictionary *)_itemViews removeObjectForKey:number];
+ }
+ }
+
+ //add onscreen views
+ for (NSNumber *number in visibleIndices)
+ {
+ UIView *view = _itemViews[number];
+ if (view == nil) {
+ [self loadViewAtIndex:[number integerValue]];
+ }
+ }
+}
+
+#pragma mark View queing
+
+- (void)queueItemView:(UIView *)view
+{
+ if (view) {
+ [_itemViewPool addObject:view];
+ }
+}
+
+- (void)queuePlaceholderView:(UIView *)view
+{
+ if (view) {
+ [_placeholderViewPool addObject:view];
+ }
+}
+
+- (UIView *)dequeueItemView
+{
+ UIView *view = [_itemViewPool anyObject];
+ if (view) {
+ [_itemViewPool removeObject:view];
+ }
+ return view;
+}
+
+- (UIView *)dequeuePlaceholderView
+{
+ UIView *view = [_placeholderViewPool anyObject];
+ if (view) {
+ [_placeholderViewPool removeObject:view];
+ }
+ return view;
+}
+
+- (void)setIndicator:(WXIndicatorView *)indicator
+{
+ _indicator = indicator;
+ [_indicator setPointCount:self.numberOfItems];
+ [_indicator setCurrentPoint:[self currentItemIndex]];
+}
+
+- (void)layoutSubviews
+{
+ [super layoutSubviews];
+ _contentView.frame = self.bounds;
+ [self layOutItemViews];
+}
+
+@end
+
+@implementation NSObject (WXSliderNeighborView)
+
+- (NSUInteger)numberOfPlaceholdersInSliderNeighborView:(__unused WXSliderNeighborView *)sliderNeighbor { return 0; }
+- (void)sliderNeighborWillBeginScrollingAnimation:(__unused WXSliderNeighborView *)sliderNeighbor {}
+- (void)sliderNeighborDidEndScrollingAnimation:(__unused WXSliderNeighborView *)sliderNeighbor {}
+- (void)sliderNeighborDidScroll:(__unused WXSliderNeighborView *)sliderNeighbor {}
+
+- (void)sliderNeighborCurrentItemIndexDidChange:(__unused WXSliderNeighborView *)sliderNeighbor {}
+- (void)sliderNeighborWillBeginDragging:(__unused WXSliderNeighborView *)sliderNeighbor {}
+- (void)sliderNeighborDidEndDragging:(__unused WXSliderNeighborView *)sliderNeighbor willDecelerate:(__unused BOOL)decelerate {}
+- (void)sliderNeighborWillBeginDecelerating:(__unused WXSliderNeighborView *)sliderNeighbor {}
+- (void)sliderNeighborDidEndDecelerating:(__unused WXSliderNeighborView *)sliderNeighbor {}
+
+- (BOOL)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor shouldSelectItemAtIndex:(__unused NSInteger)index { return YES; }
+- (void)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor didSelectItemAtIndex:(__unused NSInteger)index {}
+
+- (CGFloat)sliderNeighborItemWidth:(__unused WXSliderNeighborView *)sliderNeighbor { return 0; }
+- (CATransform3D)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor
+ itemTransformForOffset:(__unused CGFloat)offset
+ baseTransform:(CATransform3D)transform { return transform; }
+- (CGFloat)sliderNeighbor:(__unused WXSliderNeighborView *)sliderNeighbor
+ valueForOption:(__unused WXSliderNeighborOption)option
+ withDefault:(CGFloat)value { return value; }
+
+@end
+
+@interface WXSliderNeighborComponent () <WXIndicatorComponentDelegate>{
+ WXPixelType neighborSpace;
+ CGFloat neighborAlpha;
+ CGFloat neighborScale;
+ CGFloat currentItemScale;
+}
+
+@property (nonatomic, strong) WXSliderNeighborView *sliderView;
+@property (nonatomic, assign) BOOL autoPlay;
+@property (nonatomic, assign) NSUInteger interval;
+@property (nonatomic, assign) NSInteger index;
+@property (nonatomic, strong) NSMutableArray *items;
+@property (nonatomic, strong) NSTimer *autoTimer;
+@property (nonatomic, assign) BOOL sliderChangeEvent;
+@property (nonatomic, assign) NSInteger currentIndex;
+@property (nonatomic) CGRect itemRect;
+@property (nonatomic, assign) BOOL scrollable;
+
+@end
+
+#define DEFAULT_NEIGHBOR_ITEM_SCALE 0.8
+#define DEFAULT_CURRENT_ITEM_SCALE 0.9
+#define DEFAULT_NEIGHBOR_ALPHA 0.6
+#define DEFAULT_ANIMATION_DURATION 0.3
+#define DEFAULT_NEIGHBOR_SPACE 25
+
+
+@implementation WXSliderNeighborComponent
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance {
+ if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
+ _sliderChangeEvent = NO;
+ _interval = 3000;
+ _items = [NSMutableArray array];
+ _itemRect = CGRectNull;
+ self->neighborAlpha = DEFAULT_NEIGHBOR_ALPHA;
+ self->neighborScale = DEFAULT_NEIGHBOR_ITEM_SCALE;
+ self->currentItemScale = DEFAULT_CURRENT_ITEM_SCALE;
+ self->neighborSpace = [WXConvert WXPixelType:@(DEFAULT_NEIGHBOR_SPACE) scaleFactor:self.weexInstance.pixelScaleFactor];
+ _scrollable = YES;
+ [self setAttributes:attributes];
+
+ _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
+ }
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ self.cssNode->style.flex_direction = CSS_FLEX_DIRECTION_ROW;
+ }
+//#else
+ else
+ {
+ self.flexCssNode->setFlexDirection(WeexCore::kFlexDirectionRow,NO);
+ }
+//#endif
+ return self;
+}
+
+
+#pragma mark life circle
+
+- (UIView *)loadView
+{
+ _sliderView = [[WXSliderNeighborView alloc] init];
+ return _sliderView;
+}
+
+- (void)dealloc
+{
+ _sliderView.delegate = nil;
+ _sliderView.dataSource = nil;
+ if (_autoPlay) {
+ [self _stopAutoPlayTimer];
+ }
+ _sliderView = nil;
+ [self.items removeAllObjects];
+}
+
+- (void)viewDidUnload
+{
+ [self.items removeAllObjects];
+}
+
+- (void)viewDidLoad
+{
+ _sliderView = (WXSliderNeighborView *)self.view;
+ _sliderView.delegate = self;
+ _sliderView.dataSource = self;
+ _sliderView.contentView.clipsToBounds = YES;
+ _sliderView.scrollEnabled = _scrollable;
+ UIAccessibilityTraits traits = UIAccessibilityTraitAdjustable;
+
+ if (_autoPlay) {
+ traits |= UIAccessibilityTraitUpdatesFrequently;
+ [self _startAutoPlayTimer];
+ } else {
+ [self _stopAutoPlayTimer];
+ }
+ _sliderView.accessibilityTraits = traits;
+}
+
+- (void)layoutDidFinish {
+
+ [_sliderView setCurrentItemIndex:_index];
+}
+
+- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ if (subcomponent->_positionType == WXPositionTypeFixed) {
+ [self.weexInstance.rootView addSubview:subcomponent.view];
+ return;
+ }
+
+ // use _lazyCreateView to forbid component like cell's view creating
+ if(_lazyCreateView) {
+ subcomponent->_lazyCreateView = YES;
+ }
+
+ if (!subcomponent->_lazyCreateView || (self->_lazyCreateView && [self isViewLoaded])) {
+ UIView *view = subcomponent.view;
+
+ WXSliderNeighborView *sliderView = (WXSliderNeighborView *)self.view;
+ if ([view isKindOfClass:[WXIndicatorView class]]) {
+ ((WXIndicatorComponent *)subcomponent).delegate = self;
+ [sliderView addSubview:view];
+ return;
+ }
+
+ if(index < 0) {
+ [self.items addObject:view];
+ } else {
+ [self.items insertObject:view atIndex:index];
+ }
+
+ subcomponent.isViewFrameSyncWithCalculated = NO;
+
+ if (index == -1) {
+ [sliderView insertView:view atIndex:index];
+ } else {
+ NSInteger offset = 0;
+ for (int i = 0; i < [self.items count]; ++i) {
+ if (index == i) break;
+
+ if ([self.items[i] isKindOfClass:[WXIndicatorView class]]) {
+ offset++;
+ }
+ }
+ [sliderView insertView:view atIndex:index - offset];
+ }
+
+ [sliderView reloadData];
+ }
+}
+
+#pragma mark attributes update
+- (void)setAttributes:(NSDictionary *)attributes
+{
+ if (attributes[@"index"]) {
+ _index = [attributes[@"index"] integerValue];
+ _currentIndex = _index;
+ }
+ if (attributes[@"autoPlay"]) {
+ _autoPlay = [attributes[@"autoPlay"] boolValue];
+ if (_autoPlay) {
+ [self _startAutoPlayTimer];
+ } else {
+ [self _stopAutoPlayTimer];
+ }
+ }
+ if (attributes[@"interval"]) {
+ _interval = [attributes[@"interval"] integerValue];
+
+ [self _stopAutoPlayTimer];
+
+ if (_autoPlay) {
+ [self _startAutoPlayTimer];
+ }
+ }
+ if (attributes[@"neighborScale"]) {
+ [self setNeighborScale:attributes];
+ }
+ if (attributes[@"currentItemScale"]) {
+ [self setCurrentItemScale:attributes];
+ }
+ if (attributes[@"neighborAlpha"]) {
+ [self setNeighborAlpha:attributes];
+ }
+ if (attributes[@"neighborSpace"]) {
+ [self setNeighborSpace:attributes];
+ }
+ if (attributes[@"scrollable"]) {
+ _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
+ ((WXSliderNeighborView *)self.view).scrollEnabled = _scrollable;
+ }
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ [self setAttributes:attributes];
+ [self.sliderView setCurrentItemIndex:_index];
+ [self updateSliderPage:YES];
+}
+
+#pragma mark styles update
+- (void)updateStyles:(NSDictionary *)styles {
+
+}
+
+#pragma mark event
+- (void)addEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"change"]) {
+ _sliderChangeEvent = YES;
+ }
+}
+
+- (void)removeEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"change"]) {
+ _sliderChangeEvent = NO;
+ }
+}
+
+#pragma mark WXIndicatorComponentDelegate Methods
+
+- (void)setIndicatorView:(WXIndicatorView *)indicatorView
+{
+ NSAssert(_sliderView, @"");
+ [_sliderView setIndicator:indicatorView];
+}
+
+- (void)setNeighborSpace:(NSDictionary *)attributes{
+ if(attributes[@"neighborSpace"]) {
+ self->neighborSpace = [WXConvert WXPixelType:attributes[@"neighborSpace"] scaleFactor:self.weexInstance.pixelScaleFactor];
+ }
+}
+
+- (void)setNeighborAlpha:(NSDictionary *)attributes {
+ if (attributes[@"neighborAlpha"]) {
+ self->neighborAlpha = [WXConvert CGFloat:attributes[@"neighborAlpha"]];
+ self->neighborAlpha = self->neighborAlpha >= 0 ? self->neighborAlpha : 0;
+ self->neighborAlpha = self->neighborAlpha <= 1 ? self->neighborAlpha: 1;
+ }
+}
+
+- (void)setCurrentItemScale:(NSDictionary *)attributes {
+ if (attributes[@"currentItemScale"]) {
+ self->currentItemScale = [WXConvert CGFloat:attributes[@"currentItemScale"]];
+ self->currentItemScale = self->currentItemScale >= 0 ? self->currentItemScale : 0;
+ self->currentItemScale = self->currentItemScale <= 1 ? self->currentItemScale: 1;
+ }
+}
+
+- (void)setNeighborScale:(NSDictionary *)attributes
+{
+ if (attributes[@"neighborScale"]) {
+ self->neighborScale = [WXConvert CGFloat:attributes[@"neighborScale"]];
+ self->neighborScale = self->neighborScale >= 0? self->neighborScale : 0;
+ self->neighborScale = self->neighborScale <= 1? self->neighborScale :1;
+ }
+}
+
+#pragma mark Private Methods
+
+- (void)_startAutoPlayTimer
+{
+ if (!self.autoTimer || ![self.autoTimer isValid]) {
+ __weak __typeof__(self) weakSelf = self;
+ self.autoTimer = [NSTimer wx_scheduledTimerWithTimeInterval:_interval/1000.0f block:^() {
+ [weakSelf _autoPlayOnTimer];
+ } repeats:YES];
+ [[NSRunLoop currentRunLoop] addTimer:self.autoTimer forMode:NSRunLoopCommonModes];
+ }
+}
+
+- (void)_stopAutoPlayTimer
+{
+ if (self.autoTimer && [self.autoTimer isValid]) {
+ [self.autoTimer invalidate];
+ self.autoTimer = nil;
+ }
+}
+
+- (void)_autoPlayOnTimer
+{
+ WXSliderNeighborView *sliderNeighborView = (WXSliderNeighborView *)self.view;
+
+ int indicatorCnt = 0;
+ for (int i = 0; i < [self.items count]; ++i) {
+ if ([self.items[i] isKindOfClass:[WXIndicatorView class]]) {
+ indicatorCnt++;
+ }
+ }
+
+ self.currentIndex ++;
+ if (self.currentIndex >= self.items.count - indicatorCnt) {
+ self.currentIndex = 0;
+ }
+
+ [sliderNeighborView setCurrentItemIndex:self.currentIndex];
+}
+
+#pragma mark sliderNeighbor Delegate && dataSource
+
+- (NSInteger)numberOfItemsInSliderNeighbor:(WXSliderNeighborView *)sliderNeighbor {
+ return [self.items count];
+}
+
+- (NSInteger)numberOfPlaceholdersInsliderNeighbor:(WXSliderNeighborView *)sliderNeighbor {
+ return 2;
+}
+
+- (UIView *)sliderNeighbor:(WXSliderNeighborView *)sliderNeighbor viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view {
+
+ if (!view) {
+ if (index < [self.items count]) {
+ view = self.items[index];
+ }else {
+ return nil;
+ }
+ } else {
+ view.tag = 1;
+ }
+
+ if (CGRectIsNull(_itemRect)) {
+ _itemRect = view.frame;
+ }
+ if (index == [self.items count] - 1) {
+ [self updateSliderPage:NO];
+ }
+
+ return view;
+}
+
+- (BOOL)sliderNeighbor:(WXSliderNeighborView *)sliderNeighbor shouldSelectItemAtIndex:(NSInteger)index {
+
+ return YES;
+}
+
+- (UIView *)sliderNeighbor:(WXSliderNeighborView *)sliderNeighbor placeholderViewAtIndex:(NSInteger)index reusingView:(UIView *)view
+{
+
+ return view;
+}
+
+- (void)sliderNeighborDidEndScrollingAnimation:(WXSliderNeighborView *)sliderNeighbor
+{
+ [self updateSliderPage:YES];
+}
+
+- (void)sliderNeighbor:(WXSliderNeighborView *)sliderView didScrollToItemAtIndex:(NSInteger)index
+{
+ self.currentIndex = index;
+
+ if (_sliderChangeEvent) {
+ [self fireEvent:@"change" params:@{@"index":@(index)} domChanges:@{@"attrs": @{@"index": @(index)}}];
+ }
+}
+
+- (void)sliderNeighborWillBeginDragging:(WXSliderNeighborView *)sliderNeighbor
+{
+ [self _stopAutoPlayTimer];
+}
+
+- (void)sliderNeighborDidEndDragging:(WXSliderNeighborView *)sliderNeighbor willDecelerate:(BOOL)decelerate {
+ if (_autoPlay) {
+ [self _startAutoPlayTimer];
+ }
+}
+
+- (void)updateSliderPage:(BOOL)animate
+{
+ __block UIView * currentView = [self.sliderView itemViewAtIndex:[_sliderView currentItemIndex]];
+ __block UIView * lastView = [self.sliderView itemViewAtIndex:[_sliderView lastItemIndex]];
+ __block UIView * nextView = [self.sliderView itemViewAtIndex:[_sliderView nextItemIndex]];
+
+ __block CGAffineTransform transfrom = CGAffineTransformIdentity;
+ float duration = 0;
+ __weak typeof(self) weakSelf = self;
+ if (animate) {
+ duration = DEFAULT_ANIMATION_DURATION;
+ }
+
+ [UIView animateWithDuration:duration animations:^{
+ __strong typeof(self) strongSelf = weakSelf;
+ if (strongSelf) {
+ currentView.alpha = 1.0;
+ if (fabs(strongSelf->currentItemScale) > CGFLOAT_MIN) {
+ transfrom = CGAffineTransformConcat(transfrom,CGAffineTransformMakeScale(strongSelf->currentItemScale, strongSelf->currentItemScale));
+ }
+ currentView.transform = transfrom;
+ transfrom = CGAffineTransformIdentity;
+ if (fabs(strongSelf->neighborScale) <= CGFLOAT_MIN) {
+ strongSelf->neighborScale = DEFAULT_NEIGHBOR_ITEM_SCALE;
+ }
+
+ CGFloat tx = 0.5*_itemRect.size.width*((1-self->neighborScale)+(1-self->currentItemScale))-self->neighborSpace;
+ transfrom = CGAffineTransformConcat(transfrom, CGAffineTransformMakeScale(strongSelf->neighborScale, strongSelf->neighborScale));
+ nextView.transform = CGAffineTransformConcat(transfrom, CGAffineTransformMakeTranslation(-tx, 0));
+ lastView.transform = CGAffineTransformConcat(transfrom, CGAffineTransformMakeTranslation(tx, 0));
+ lastView.alpha = strongSelf->neighborAlpha;
+ nextView.alpha = strongSelf->neighborAlpha;
+ }
+ }];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXSwitchComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXSwitchComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXSwitchComponent.m
deleted file mode 100644
index f5f9ce2..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXSwitchComponent.m
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXSwitchComponent.h"
-#import "WXConvert.h"
-#import "WXComponent_internal.h"
-
-@interface WXSwitchView : UISwitch
-
-@end
-
-@implementation WXSwitchView
-
-@end
-
-@interface WXSwitchComponent()
-
-@property (nonatomic, strong) WXSwitchView *switchView;
-@property (nonatomic, assign) BOOL changeEvent;
-@property (nonatomic, assign) BOOL checked;
-@property (nonatomic, assign) BOOL disabled;
-
-//Background color when the switch is turned on.
-@property (nonatomic, strong) UIColor *onTintColor;
-
-
-//Color of the foreground switch grip.
-@property (nonatomic, strong) UIColor *thumbTintColor;
-
-//Border color and background color on Android when the switch is turned off
-@property (nonatomic, strong) UIColor *tintColor;
-
-@end
-
-@implementation WXSwitchComponent
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
- if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
- _checked = attributes[@"checked"] ? [WXConvert BOOL:attributes[@"checked"]] : NO;
- _disabled = attributes[@"disabled"] ? [WXConvert BOOL:attributes[@"disabled"]] : NO;
-
- if(attributes[@"onTintColor"]){
- _onTintColor = [WXConvert UIColor:attributes[@"onTintColor"]];
- }
-
- if(attributes[@"thumbTintColor"]){
- _thumbTintColor = [WXConvert UIColor:attributes[@"thumbTintColor"]];
- }
-
- if(attributes[@"tintColor"]){
- _tintColor = [WXConvert UIColor:attributes[@"tintColor"]];
- }
-
- self.cssNode->style.dimensions[CSS_WIDTH] = 51;
- self.cssNode->style.dimensions[CSS_HEIGHT] = 31;
- }
- return self;
-}
-
-- (UIView *)loadView
-{
- return [[WXSwitchView alloc] init];
-}
-
-- (void)viewDidLoad
-{
- _switchView = (WXSwitchView *)self.view;
- _switchView.isAccessibilityElement = YES;
- [_switchView setOn:_checked animated:YES];
- [_switchView setEnabled:!_disabled];
- [_switchView addTarget:self action:@selector(checkChanged) forControlEvents:UIControlEventValueChanged];
-
- if(_onTintColor){
- _switchView.onTintColor = _onTintColor;
- }
-
- if(_tintColor){
- _switchView.tintColor = _tintColor;
- }
-
- if(_thumbTintColor){
- _switchView.thumbTintColor = _thumbTintColor;
- }
-}
-
-- (void)addEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"change"]) {
- _changeEvent = YES;
- }
-}
-
-- (void)removeEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"change"]) {
- _changeEvent = NO;
- }
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- if (attributes[@"checked"]) {
- _checked = [WXConvert BOOL:attributes[@"checked"]];
- [_switchView setOn:_checked animated:YES];
- }
- else if (attributes[@"disabled"]) {
- _disabled = [WXConvert BOOL:attributes[@"disabled"]];
- [_switchView setEnabled:!_disabled];
- }
-
- if(attributes[@"onTintColor"]){
- _onTintColor = [WXConvert UIColor:attributes[@"onTintColor"]];
- _switchView.onTintColor = _onTintColor;
- }
-
- if(attributes[@"thumbTintColor"]){
- _thumbTintColor = [WXConvert UIColor:attributes[@"thumbTintColor"]];
- _switchView.thumbTintColor = _thumbTintColor;
- }
-
- if(attributes[@"tintColor"]){
- _tintColor = [WXConvert UIColor:attributes[@"tintColor"]];
- _switchView.tintColor = _tintColor;
- }
-}
-
-- (void)checkChanged
-{
- if (_changeEvent) {
- [self fireEvent:@"change" params:@{@"value":@([_switchView isOn])} domChanges:@{@"attrs": @{@"checked": @([_switchView isOn])}}];
- }
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXSwitchComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXSwitchComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXSwitchComponent.mm
new file mode 100644
index 0000000..16903a0
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXSwitchComponent.mm
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXSwitchComponent.h"
+#import "WXConvert.h"
+#import "WXComponent_internal.h"
+#import "WXComponent+Layout.h"
+
+@interface WXSwitchView : UISwitch
+
+@end
+
+@implementation WXSwitchView
+
+@end
+
+@interface WXSwitchComponent()
+
+@property (nonatomic, strong) WXSwitchView *switchView;
+@property (nonatomic, assign) BOOL changeEvent;
+@property (nonatomic, assign) BOOL checked;
+@property (nonatomic, assign) BOOL disabled;
+
+//Background color when the switch is turned on.
+@property (nonatomic, strong) UIColor *onTintColor;
+
+
+//Color of the foreground switch grip.
+@property (nonatomic, strong) UIColor *thumbTintColor;
+
+//Border color and background color on Android when the switch is turned off
+@property (nonatomic, strong) UIColor *tintColor;
+
+@end
+
+@implementation WXSwitchComponent
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+ if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
+ _checked = attributes[@"checked"] ? [WXConvert BOOL:attributes[@"checked"]] : NO;
+ _disabled = attributes[@"disabled"] ? [WXConvert BOOL:attributes[@"disabled"]] : NO;
+
+ if(attributes[@"onTintColor"]){
+ _onTintColor = [WXConvert UIColor:attributes[@"onTintColor"]];
+ }
+
+ if(attributes[@"thumbTintColor"]){
+ _thumbTintColor = [WXConvert UIColor:attributes[@"thumbTintColor"]];
+ }
+
+ if(attributes[@"tintColor"]){
+ _tintColor = [WXConvert UIColor:attributes[@"tintColor"]];
+ }
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex])
+ {
+ self.cssNode->style.dimensions[CSS_WIDTH] = 51;
+ self.cssNode->style.dimensions[CSS_HEIGHT] = 31;
+ }
+//#else
+ else
+ {
+ self.flexCssNode->setStyleWidth(51,NO);
+ self.flexCssNode->setStyleHeight(31);
+ }
+//#endif
+ }
+ return self;
+}
+
+- (UIView *)loadView
+{
+ return [[WXSwitchView alloc] init];
+}
+
+- (void)viewDidLoad
+{
+ _switchView = (WXSwitchView *)self.view;
+ _switchView.isAccessibilityElement = YES;
+ [_switchView setOn:_checked animated:YES];
+ [_switchView setEnabled:!_disabled];
+ [_switchView addTarget:self action:@selector(checkChanged) forControlEvents:UIControlEventValueChanged];
+
+ if(_onTintColor){
+ _switchView.onTintColor = _onTintColor;
+ }
+
+ if(_tintColor){
+ _switchView.tintColor = _tintColor;
+ }
+
+ if(_thumbTintColor){
+ _switchView.thumbTintColor = _thumbTintColor;
+ }
+}
+
+- (void)addEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"change"]) {
+ _changeEvent = YES;
+ }
+}
+
+- (void)removeEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"change"]) {
+ _changeEvent = NO;
+ }
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ if (attributes[@"checked"]) {
+ _checked = [WXConvert BOOL:attributes[@"checked"]];
+ [_switchView setOn:_checked animated:YES];
+ }
+ else if (attributes[@"disabled"]) {
+ _disabled = [WXConvert BOOL:attributes[@"disabled"]];
+ [_switchView setEnabled:!_disabled];
+ }
+
+ if(attributes[@"onTintColor"]){
+ _onTintColor = [WXConvert UIColor:attributes[@"onTintColor"]];
+ _switchView.onTintColor = _onTintColor;
+ }
+
+ if(attributes[@"thumbTintColor"]){
+ _thumbTintColor = [WXConvert UIColor:attributes[@"thumbTintColor"]];
+ _switchView.thumbTintColor = _thumbTintColor;
+ }
+
+ if(attributes[@"tintColor"]){
+ _tintColor = [WXConvert UIColor:attributes[@"tintColor"]];
+ _switchView.tintColor = _tintColor;
+ }
+}
+
+- (void)checkChanged
+{
+ if (_changeEvent) {
+ [self fireEvent:@"change" params:@{@"value":@([_switchView isOn])} domChanges:@{@"attrs": @{@"checked": @([_switchView isOn])}}];
+ }
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.m
deleted file mode 100644
index 24ee17f..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.m
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXTextAreaComponent.h"
-#import "WXUtility.h"
-#import "WXComponent+Layout.h"
-#import "WXComponent_internal.h"
-
-#define CorrectX 4 //textview fill text 4 pixel from left. so placeholderlabel have 4 pixel too
-#define CorrectY 8 // textview fill text 8 pixel from top
-typedef UITextView WXTextAreaView;
-
-@interface WXTextAreaComponent()
-
-@property (nonatomic, strong) WXTextAreaView *textView;
-@property (nonatomic) NSUInteger rows;
-
-@end
-
-@implementation WXTextAreaComponent {
- UIEdgeInsets _border;
- UIEdgeInsets _padding;
-}
-
--(void)viewDidLoad
-{
- _padding = UIEdgeInsetsZero;
- _border = UIEdgeInsetsZero;
- if (self.placeholderString) {
- self.placeHolderLabel = [[UILabel alloc] init];
- self.placeHolderLabel.numberOfLines = 0;
- [_textView addSubview:self.placeHolderLabel];
- }
- // default placeholder hide from voice over
- self.placeHolderLabel.isAccessibilityElement = NO;
- _textView.isAccessibilityElement = YES;
- _textView.delegate = self;
- [_textView setNeedsDisplay];
- [_textView setClipsToBounds:YES];
- [super viewDidLoad];
-}
-
-- (void)viewWillUnload
-{
- _textView = nil;
-}
-
-- (UIView *)loadView
-{
- _textView = [[WXTextAreaView alloc] init];
- return _textView;
-}
-
-#pragma mark measure frame
-- (CGSize (^)(CGSize))measureBlock
-{
- __weak typeof(self) weakSelf = self;
- return ^CGSize (CGSize constrainedSize) {
-
- CGSize computedSize = [[[NSString alloc] init]sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:weakSelf.textView.font.pointSize]}];
- computedSize.height = _rows? computedSize.height *weakSelf.rows + (CorrectY + CorrectY/2):0;
- if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) {
- computedSize.width = MAX(computedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]);
- }
-
- if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) {
- computedSize.width = MIN(computedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]);
- }
-
- if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_HEIGHT])) {
- computedSize.height = MAX(computedSize.height, weakSelf.cssNode->style.minDimensions[CSS_HEIGHT]);
- }
-
- if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT])) {
- computedSize.height = MIN(computedSize.height, weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT]);
- }
-
- return (CGSize) {
- WXCeilPixelValue(computedSize.width),
- WXCeilPixelValue(computedSize.height)
- };
- };
-}
-
-#pragma mark -Overwrite method
--(NSString *)text
-{
- return _textView.text;
-}
-
-- (void)setText:(NSString *)text
-{
- _textView.text = text;
- if ([text length] >0) {
- self.placeHolderLabel.text = @"";
- }
-}
-
--(void)setTextColor:(UIColor *)color
-{
- [_textView setTextColor:color];
-}
-
--(void)setTextAlignment:(NSTextAlignment)textAlignForStyle
-{
- [_textView setTextAlignment:textAlignForStyle];
-}
-
--(void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
-{
- [_textView setUserInteractionEnabled:userInteractionEnabled];
-}
-
--(void)setEnabled:(BOOL)enabled
-{
- _textView.editable = enabled;
- _textView.selectable = enabled;
-}
-
--(void)setReturnKeyType:(UIReturnKeyType)returnKeyType
-{
- [_textView setReturnKeyType:returnKeyType];
-}
-
--(void)setInputAccessoryView:(UIView *)inputAccessoryView
-{
- [_textView setInputAccessoryView:inputAccessoryView];
-}
-
--(void)setEditSelectionRange:(NSInteger)selectionStart selectionEnd:(NSInteger)selectionEnd
-{
- [self.textView becomeFirstResponder];
- UITextPosition *startPos = [self.textView positionFromPosition:self.textView.beginningOfDocument offset:selectionStart];
- UITextPosition *endPos = [self.textView positionFromPosition:self.textView.beginningOfDocument offset:selectionEnd];
- UITextRange *textRange = [self.textView textRangeFromPosition:startPos
- toPosition:endPos];
- self.textView.selectedTextRange = textRange;
-}
-
--(NSDictionary *)getEditSelectionRange
-{
- NSInteger selectionStart = [self.textView offsetFromPosition:self.textView.beginningOfDocument toPosition:self.textView.selectedTextRange.start];
- NSInteger selectionEnd = [self.textView offsetFromPosition:self.textView.beginningOfDocument toPosition:self.textView.selectedTextRange.end];
- NSDictionary *res = @{@"selectionStart":@(selectionStart),@"selectionEnd":@(selectionEnd)};
- return res;
-}
-
--(void)setKeyboardType:(UIKeyboardType)keyboardType
-{
- [_textView setKeyboardType:keyboardType];
-}
-
--(void)setSecureTextEntry:(BOOL)secureTextEntry
-{
- [_textView setSecureTextEntry:secureTextEntry];
-}
-
--(void)setEditPadding:(UIEdgeInsets)padding
-{
- _padding = padding;
- [self _updateTextContentInset];
-}
-
--(void)setEditBorder:(UIEdgeInsets)border
-{
- _border = border;
- [self _updateTextContentInset];
-}
-
--(void)setAttributedPlaceholder:(NSMutableAttributedString *)attributedString font:(UIFont *)font
-{
- if (self.placeholderColor) {
- [attributedString addAttribute:NSForegroundColorAttributeName value:self.placeholderColor range:NSMakeRange(0, self.placeholderString.length)];
- [attributedString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, self.placeholderString.length)];
- }
- self.placeHolderLabel.backgroundColor = [UIColor clearColor];
- CGRect expectedLabelSize = [attributedString boundingRectWithSize:(CGSize){self.view.frame.size.width, CGFLOAT_MAX}
- options:NSStringDrawingUsesLineFragmentOrigin
- context:nil];
-
- self.placeHolderLabel.clipsToBounds = NO;
- CGRect newFrame = self.placeHolderLabel.frame;
- newFrame.size.height = ceil(expectedLabelSize.size.height);
- newFrame.size.width = _textView.frame.size.width- CorrectX*2;
- newFrame.origin.x = CorrectX + _padding.left + _border.left; // the cursor origin.x
- self.placeHolderLabel.frame = newFrame;
- self.placeHolderLabel.attributedText = attributedString;
-}
-
--(void)setFont:(UIFont *)font
-{
- [_textView setFont:font];
-}
-
--(void)setRows:(NSUInteger)rows
-{
- _rows = rows;
- [self setNeedsLayout];
-}
-
-#pragma mark -Private Method
-- (void)_updateTextContentInset
-{
- [_textView setTextContainerInset:UIEdgeInsetsMake(_padding.top + _border.top,
- _padding.left + _border.left,
- _padding.bottom + _border.bottom,
- _border.right + _border.right)];
-
- //when textview update, placeHolderLabel update too
- CGRect newFrame = self.placeHolderLabel.frame;
- newFrame.size.width = self.textView.frame.size.width - (_padding.left + _border.left) -CorrectX*2;
- newFrame.origin.x = CorrectX + _padding.left + _border.left; // the cursor origin.x
- newFrame.origin.y = _padding.top + _border.top;
- self.placeHolderLabel.frame = newFrame;
-}
-
-@end
[05/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Layout/WXCoreLayout.cpp
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXCoreLayout.cpp b/ios/sdk/WeexSDK/Sources/Layout/WXCoreLayout.cpp
new file mode 100644
index 0000000..7d34d83
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXCoreLayout.cpp
@@ -0,0 +1,1038 @@
+#include "WXCoreLayout.h"
+
+using namespace WeexCore;
+
+namespace WeexCore {
+
+ /**
+ * Entry function to calculate layout
+ */
+ void WXCoreLayoutNode::calculateLayout(const std::pair<float,float> &renderPageSize) {
+ BFCs.clear();
+ initFormatingContext(BFCs);
+ auto bfcDimension = calculateBFCDimension(renderPageSize);
+ if (std::get<0>(bfcDimension) || isDirty()) {
+ mChildrenFrozen.assign(getChildCount(kNonBFC), false);
+ measure(std::get<1>(bfcDimension), std::get<2>(bfcDimension), true);
+ checkSizeConstraints(this, false);
+ }
+ layout(mCssStyle->mMargin.getMargin(kMarginLeft),
+ mCssStyle->mMargin.getMargin(kMarginTop),
+ mCssStyle->mMargin.getMargin(kMarginLeft) + getLayoutWidth(),
+ mCssStyle->mMargin.getMargin(kMarginTop) + getLayoutHeight(),
+ false, &renderPageSize);
+ for (Index i = 0; i < getChildCount(kBFC); ++i) {
+ WXCoreLayoutNode *child = getChildAt(kBFC, i);
+ child->calculateLayout(renderPageSize);
+ }
+ }
+
+ void WXCoreLayoutNode::initFormatingContext(std::vector<WXCoreLayoutNode *> &BFCs) {
+ NonBFCs.clear();
+ for(auto it = ChildListIterBegin(); it != ChildListIterEnd(); it++) {
+ WXCoreLayoutNode* child = *it;
+ if (child != nullptr) {
+ if (isBFC(child)) {
+ BFCs.push_back(child);
+ } else {
+ NonBFCs.push_back(child);
+ child->initFormatingContext(BFCs);
+ }
+ }
+ }
+ reset();
+ }
+
+ std::tuple<bool, float, float> WXCoreLayoutNode::calculateBFCDimension(const std::pair<float,float>& renderPageSize) {
+ bool sizeChanged = false;
+ float width = mCssStyle->mStyleWidth, height = mCssStyle->mStyleHeight;
+ std::pair<bool,float> ret;
+ if (isBFC(this)) {
+ ret = calculateBFCWidth(width, renderPageSize.first);
+ sizeChanged |=ret.first;
+ width = ret.second;
+
+ ret = calculateBFCHeight(height,renderPageSize.second);
+ sizeChanged |=ret.first;
+ height = ret.second;
+ }
+ return std::make_tuple(sizeChanged, width, height);
+ }
+
+ std::pair<bool,float> WXCoreLayoutNode::calculateBFCWidth(float width, const float renderPageWidth){
+ bool sizeChanged = false;
+ if (isnan(width) &&
+ mParent != nullptr &&
+ !isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft)) &&
+ !isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeRight))) {
+ float containingBlockWidth = NAN;
+ switch (mCssStyle->mPositionType) {
+ case kAbsolute:
+ containingBlockWidth = mParent->mLayoutResult->mLayoutSize.width;
+ break;
+ case kFixed:
+ if (!isnan(renderPageWidth)) {
+ containingBlockWidth = renderPageWidth;
+ }
+ break;
+ default:
+ break;
+ }
+ if (!isnan(containingBlockWidth)) {
+ width = containingBlockWidth -
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft) -
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeRight);
+ setWidthMeasureMode(kExactly);
+ sizeChanged = true;
+ }
+ }
+ return std::make_pair(sizeChanged, width);
+ }
+
+
+ std::pair<bool,float> WXCoreLayoutNode::calculateBFCHeight(float height, const float renderPageHeight){
+ bool sizeChanged = false;
+ if (isnan(mCssStyle->mStyleHeight) &&
+ mParent != nullptr &&
+ !isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeTop)) &&
+ !isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom))) {
+ float containingBlockHeight = NAN;
+ switch (mCssStyle->mPositionType) {
+ case kAbsolute:
+ containingBlockHeight = mParent->mLayoutResult->mLayoutSize.height;
+ break;
+ case kFixed:
+ if (!isnan(renderPageHeight)) {
+ containingBlockHeight = renderPageHeight;
+ }
+ break;
+ default:
+ break;
+ }
+ if (!isnan(containingBlockHeight)) {
+ height = containingBlockHeight -
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeTop) -
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom);
+ setHeightMeasureMode(kExactly);
+ sizeChanged = true;
+ }
+ }
+ return std::make_pair(sizeChanged, height);
+ }
+
+ void WXCoreLayoutNode::measure(const float width, const float height, const bool hypotheticalMeasurment){
+ if(hypotheticalMeasurment){
+ //Only BFC will enter this case.
+ hypotheticalMeasure(width, height);
+ }
+
+ if(getChildCount(kNonBFC) > 0){
+ if((isMainAxisHorizontal(this) && widthDirty) || (!isMainAxisHorizontal(this) && heightDirty)){
+ measureInternalNode(width, height, false, false);
+ }
+ determineMainSize(width, height);
+ determineCrossSize(width, height, true);
+ measureInternalNode(width, height, true, false);
+ determineCrossSize(width, height, false);
+ }
+ else {
+ if (widthDirty || heightDirty) {
+ measureLeafNode(width, height, hypotheticalMeasurment, false);
+ }
+ }
+ clearDirty();
+ }
+
+ void WXCoreLayoutNode::hypotheticalMeasure(const float width, const float height, const bool stretch){
+ if (getChildCount(kNonBFC) > 0) {
+ measureInternalNode(width, height, true, true);
+ } else {
+ measureLeafNode(width, height, true, stretch);
+ }
+
+ widthDirty = false;
+ heightDirty = false;
+ mLayoutResult->mLayoutSize.hypotheticalWidth = mLayoutResult->mLayoutSize.width;
+ mLayoutResult->mLayoutSize.hypotheticalHeight = mLayoutResult->mLayoutSize.height;
+ }
+
+ void WXCoreLayoutNode::measureLeafNode(float width, float height, const bool hypothetical, const bool stretch) {
+ if ((measureFunc != nullptr) &&
+ (widthMeasureMode == kUnspecified
+ || heightMeasureMode == kUnspecified)) {
+ float constrainsWidth = width;
+ if(widthMeasureMode == kExactly && !isnan(width)){
+ constrainsWidth -= sumPaddingBorderAlongAxis(this, true);
+ }
+ WXCoreSize dimension = measureFunc(this, constrainsWidth,
+ (stretch && !isnan(width)) ? kExactly:widthMeasureMode,
+ height, heightMeasureMode);
+ if (widthMeasureMode == kUnspecified) {
+ float actualWidth = dimension.width + sumPaddingBorderAlongAxis(this, true);
+ if (isnan(width)) {
+ width = actualWidth;
+ } else if (!stretch) {
+ width = std::min(width, actualWidth);
+ }
+ }
+ if (heightMeasureMode == kUnspecified) {
+ float actualHeight = dimension.height + sumPaddingBorderAlongAxis(this, false);
+ if (isnan(height)) {
+ height = actualHeight;
+ } else if (!stretch) {
+ height = std::min(height, actualHeight);
+ }
+ }
+ } else {
+ width = widthMeasureMode == kUnspecified ? sumPaddingBorderAlongAxis(this, true)
+ : width;
+ height = heightMeasureMode == kUnspecified ? sumPaddingBorderAlongAxis(this, false)
+ : height;
+ }
+ setMeasuredDimension(width, height);
+ }
+
+
+ /**
+ * Determine the main size by expanding the individual flexGrow attribute.
+ */
+ void WXCoreLayoutNode::determineMainSize(const float width, const float height) {
+ bool horizontal = isMainAxisHorizontal(this);
+ if((horizontal && widthMeasureMode == kExactly) || (!horizontal && heightMeasureMode == kExactly)) {
+ //The measureMode along main axis is exactly
+ float maxMainSize = horizontal ? width: height;
+ maxMainSize -= sumPaddingBorderAlongAxis(this, isMainAxisHorizontal(this));
+ Index childIndex = 0;
+ for (WXCoreFlexLine *flexLine : mFlexLines) {
+ childIndex = expandItemsInFlexLine(flexLine, maxMainSize, childIndex);
+ }
+ }
+ }
+
+
+ /**
+ * @param flexDirection the flex direction attribute
+ * @param width stylewidth by this node
+ * @param height styleheight by this node
+ * @param paddingAlongCrossAxis the padding value for this node along the cross axis
+ */
+ void WXCoreLayoutNode::determineCrossSize(const float width, const float height, const bool stretch) {
+ if (mFlexLines.size() == 1 && isCrossExactly()) {
+ bool horizontal = isMainAxisHorizontal(this);
+ float size = mFlexLines[0]->mCrossSize;
+ float paddingAlongCrossAxis = sumPaddingBorderAlongAxis(this, !horizontal);
+ if (horizontal) {
+ if (heightMeasureMode == kExactly) {
+ size = height - paddingAlongCrossAxis;
+ }
+ } else {
+ if (widthMeasureMode == kExactly) {
+ size = width - paddingAlongCrossAxis;
+ }
+ }
+ mFlexLines[0]->mCrossSize = size;
+ }
+ if (stretch) {
+ stretchViewCrossSize();
+ }
+ }
+
+
+ void WXCoreLayoutNode::measureInternalNode(const float width, const float height, const bool needMeasure,
+ const bool hypotheticalMeasurment) {
+ for (WXCoreFlexLine *flexLine : mFlexLines) {
+ if(flexLine!= nullptr) {
+ delete flexLine;
+ }
+ flexLine = nullptr;
+ }
+ mFlexLines.clear();
+ Index childCount = getChildCount(kNonBFC);
+ WXCoreFlexLine *flexLine = new WXCoreFlexLine();
+
+ for (Index i = 0; i < childCount; i++) {
+ WXCoreLayoutNode *child = getChildAt(kNonBFC, i);
+ if (child->mCssStyle->mAlignSelf == kAlignSelfStretch) {
+ flexLine->mIndicesAlignSelfStretch.push_back(i);
+ }
+ measureChild(child, flexLine->mMainSize, width, height, needMeasure, hypotheticalMeasurment);
+ checkSizeConstraints(child, hypotheticalMeasurment);
+
+ if (isWrapRequired(width, height, flexLine->mMainSize,
+ calcItemSizeAlongAxis(child, isMainAxisHorizontal(this)))) {
+ if (flexLine->mItemCount > 0) {
+ mFlexLines.push_back(flexLine);
+ }
+ flexLine = new WXCoreFlexLine();
+ flexLine->mItemCount = 1;
+ } else {
+ flexLine->mItemCount++;
+ }
+ updateCurrentFlexline(childCount, flexLine, i, child, hypotheticalMeasurment || (!hypotheticalMeasurment && !needMeasure));
+ }
+ setMeasuredDimensionForFlex(width, widthMeasureMode, height, heightMeasureMode);
+ }
+
+ void WXCoreLayoutNode::updateCurrentFlexline(const Index childCount, WXCoreFlexLine* const flexLine, const Index i,
+ const WXCoreLayoutNode* const child, const bool useHypotheticalSize){
+ flexLine->mMainSize += calcItemSizeAlongAxis(child, isMainAxisHorizontal(this), useHypotheticalSize);
+ sumFlexGrow(child, flexLine, i);
+ flexLine->mCrossSize =
+ std::max(flexLine->mCrossSize, calcItemSizeAlongAxis(child, !isMainAxisHorizontal(this), useHypotheticalSize));
+ if (i == childCount - 1 && flexLine->mItemCount != 0) {
+ mFlexLines.push_back(flexLine);
+ }
+ }
+
+ void WXCoreLayoutNode::measureChild(WXCoreLayoutNode* const child, const float currentMainSize,
+ const float parentWidth, const float parentHeight,
+ const bool needMeasure, const bool hypotheticalMeasurment) {
+ if (needMeasure && child->isDirty()) {
+ if (hypotheticalMeasurment) {
+ float childWidth = child->mCssStyle->mStyleWidth;
+ float childHeight = child->mCssStyle->mStyleHeight;
+ bool stretch = !isMainAxisHorizontal(this) &&
+ child->measureFunc != nullptr &&
+ widthMeasureMode == kExactly &&
+ isSingleFlexLine(parentHeight) &&
+ ((child->mCssStyle->mAlignSelf == kAlignSelfStretch) ||
+ (mCssStyle->mAlignItems == kAlignItemsStretch
+ && child->mCssStyle->mAlignSelf == kAlignSelfAuto));
+
+ adjustChildSize(child, currentMainSize, parentWidth,
+ parentHeight, childWidth, childHeight);
+ child->hypotheticalMeasure(childWidth, childHeight, stretch);
+ } else {
+ if(isSingleFlexLine(isMainAxisHorizontal(this) ? parentWidth : parentHeight)
+ && !isMainAxisHorizontal(this) && child->widthMeasureMode == kUnspecified){
+ child->setLayoutWidth(parentWidth - sumPaddingBorderAlongAxis(this, true)
+ -child->mCssStyle->sumMarginOfDirection(true));
+ if(child->heightMeasureMode == kUnspecified && child->widthDirty) {
+ child->mLayoutResult->mLayoutSize.height = NAN;
+ }
+ }
+ child->measure(child->mLayoutResult->mLayoutSize.width,
+ child->mLayoutResult->mLayoutSize.height, hypotheticalMeasurment);
+ }
+ }
+ }
+
+ void WXCoreLayoutNode::adjustChildSize(const WXCoreLayoutNode *child,
+ const float currentMainSize,
+ const float parentWidth,
+ const float parentHeight,
+ float &childWidth,
+ float &childHeight) const {
+ if(child->measureFunc == nullptr) {
+ if(!isnan(childWidth)){
+ childWidth = std::max(childWidth, child->sumPaddingBorderAlongAxis(child, true));
+ }
+ if(!isnan(childHeight)){
+ childHeight = std::max(childHeight, child->sumPaddingBorderAlongAxis(child, false));
+ }
+ }
+
+ if (isSingleFlexLine(isMainAxisHorizontal(this) ? parentWidth : parentHeight)) {
+ if (isMainAxisHorizontal(this)) {
+ if (!isnan(parentHeight) && isnan(child->mCssStyle->mStyleHeight)
+ && child->mCssStyle->mAlignSelf == kAlignSelfAuto
+ && mCssStyle->mAlignItems == kAlignItemsStretch) {
+ childHeight = parentHeight - sumPaddingBorderAlongAxis(this, false) -
+ child->mCssStyle->sumMarginOfDirection(false);
+ }
+ } else {
+ if (!isnan(parentWidth) && isnan(child->mCssStyle->mStyleWidth)) {
+ childWidth = parentWidth - sumPaddingBorderAlongAxis(this, true) -
+ child->mCssStyle->sumMarginOfDirection(true);
+ }
+ }
+ }
+ }
+
+ void WXCoreLayoutNode::checkSizeConstraints(WXCoreLayoutNode* const node, const bool hypotheticalMeasurment) {
+ bool widthRemeasure = false, heightRemeasure = false;
+ float nodeWidth,nodeHeight;
+ nodeWidth = node->mLayoutResult->mLayoutSize.width;
+ nodeHeight = node->mLayoutResult->mLayoutSize.height;
+
+ if (!isnan(node->mCssStyle->mMinWidth) &&
+ nodeWidth < node->mCssStyle->mMinWidth) {
+ widthRemeasure = true;
+ nodeWidth = node->mCssStyle->mMinWidth;
+ } else if (!isnan(node->mCssStyle->mMaxWidth)
+ && nodeWidth > node->mCssStyle->mMaxWidth) {
+ widthRemeasure = true;
+ nodeWidth = node->mCssStyle->mMaxWidth;
+ }
+
+ if (!isnan(node->mCssStyle->mMinHeight) &&
+ nodeHeight < node->mCssStyle->mMinHeight) {
+ heightRemeasure = true;
+ nodeHeight = node->mCssStyle->mMinHeight;
+ } else if (!isnan(node->mCssStyle->mMaxHeight) &&
+ nodeHeight > node->mCssStyle->mMaxHeight) {
+ heightRemeasure = true;
+ nodeHeight = node->mCssStyle->mMaxHeight;
+ }
+
+ node->setWidthMeasureMode(widthRemeasure ? kExactly : node->widthMeasureMode);
+ node->setHeightMeasureMode(heightRemeasure ? kExactly : node->heightMeasureMode);
+
+ if (hypotheticalMeasurment) {
+ if (widthRemeasure) {
+ node->setLayoutWidth(nodeWidth);
+ node->mLayoutResult->mLayoutSize.hypotheticalWidth = nodeWidth;
+ }
+ if (heightRemeasure) {
+ node->setLayoutHeight(nodeHeight);
+ node->mLayoutResult->mLayoutSize.hypotheticalHeight = nodeHeight;
+ }
+ } else {
+ if (widthRemeasure || heightRemeasure) {
+ node->measure(nodeWidth, nodeHeight, hypotheticalMeasurment);
+ }
+ }
+ }
+
+ Index WXCoreLayoutNode::expandItemsInFlexLine(WXCoreFlexLine* const flexLine,
+ const float maxMainSize,
+ const Index startIndex) {
+ Index childIndex = startIndex;
+ if (flexLine->mTotalFlexGrow <= 0) {
+ childIndex += flexLine->mItemCount;
+ } else {
+ bool needsReexpand = false;
+ const float unitSpace = (maxMainSize - flexLine->mMainSize + flexLine->mTotalFlexibleSize) /
+ (flexLine->mTotalFlexGrow > 1 ? flexLine->mTotalFlexGrow : 1);
+ float sizeBeforeExpand = flexLine->mMainSize;
+ flexLine->mMainSize = 0;
+
+ for (Index i = 0; i < flexLine->mItemCount; i++) {
+ WXCoreLayoutNode *child = getChildAt(kNonBFC, childIndex);
+ if (!mChildrenFrozen[childIndex]) {
+ float childSizeAlongMainAxis = unitSpace * child->mCssStyle->mFlexGrow;
+ std::pair<bool, float> limitSize = limitChildMainSize(flexLine, child,
+ childSizeAlongMainAxis, childIndex);
+ needsReexpand = limitSize.first;
+ adjustChildSize(child, limitSize.second);
+ }
+ flexLine->mMainSize += calcItemSizeAlongAxis(child, isMainAxisHorizontal(this));
+ childIndex++;
+ }
+
+ if (needsReexpand && sizeBeforeExpand != flexLine->mMainSize) {
+ // Re-invoke the method with the same startIndex to distribute the positive free space
+ // that wasn't fully distributed (because of maximum/minimum length constraint)
+ expandItemsInFlexLine(flexLine, maxMainSize, startIndex);
+ }
+ }
+ return childIndex;
+ }
+
+ void WXCoreLayoutNode::adjustChildSize(WXCoreLayoutNode* const child, const float childMainSize) {
+ if (isMainAxisHorizontal(this)) {
+ child->setWidthMeasureMode(kExactly);
+ child->setLayoutWidth(childMainSize);
+ } else {
+ child->setHeightMeasureMode(kExactly);
+ child->setLayoutHeight(childMainSize);
+ }
+ }
+
+ void WXCoreLayoutNode::stretchViewCrossSize(){
+ if (mCssStyle->mAlignItems == kAlignItemsStretch) {
+ Index viewIndex = 0;
+ for (Index i = 0; i< mFlexLines.size(); i++ ) {
+ WXCoreFlexLine *flexLine = mFlexLines.at(i);
+ for (Index j = 0; j < flexLine->mItemCount; j++, viewIndex++) {
+ WXCoreLayoutNode* child = getChildAt(kNonBFC, viewIndex);
+ if (child->mCssStyle->mAlignSelf == kAlignSelfAuto ||
+ child->mCssStyle->mAlignSelf == kAlignSelfStretch) {
+ stretchViewCrossSize(child, flexLine->mCrossSize);
+ }
+ }
+ }
+ } else {
+ for (WXCoreFlexLine *flexLine : mFlexLines) {
+ for (auto index : flexLine->mIndicesAlignSelfStretch) {
+ stretchViewCrossSize(getChildAt(kNonBFC, index), flexLine->mCrossSize);
+ }
+ }
+ }
+ }
+
+ void WXCoreLayoutNode::stretchViewCrossSize(WXCoreLayoutNode* const child, float crossSize){
+ if (isMainAxisHorizontal(this)) {
+ if (child->heightMeasureMode != kExactly) {
+ crossSize -=
+ child->mCssStyle->mMargin.getMargin(kMarginTop) +
+ child->mCssStyle->mMargin.getMargin(kMarginBottom);
+ child->setHeightMeasureMode(kExactly);
+ child->setLayoutHeight(std::max(0.f, crossSize));
+ }
+ } else {
+ if (child->widthMeasureMode != kExactly) {
+ crossSize -=
+ child->mCssStyle->mMargin.getMargin(kMarginLeft) +
+ child->mCssStyle->mMargin.getMargin(kMarginRight);
+ child->setWidthMeasureMode(kExactly);
+ child->setLayoutWidth(std::max(0.f, crossSize));
+ }
+ }
+ }
+
+ void WXCoreLayoutNode::setFrame(const float l, const float t, const float r, const float b) {
+ if (mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeLeft) != l
+ || mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeTop) != t
+ || mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeRight) != r
+ || mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeBottom) != b) {
+ setHasNewLayout(true);
+ setFrame(&mLayoutResult->mLayoutPosition, l, t, r, b);
+ }
+ }
+
+ void WXCoreLayoutNode::setFrame(WXCorePosition* position,const float l, const float t, const float r, const float b){
+ position->setPosition(kPositionEdgeLeft, l);
+ position->setPosition(kPositionEdgeTop, t);
+ position->setPosition(kPositionEdgeRight, r);
+ position->setPosition(kPositionEdgeBottom, b);
+ }
+
+ void WXCoreLayoutNode::layout(float left, float top, float right, float bottom, const bool absoluteFlexItem, const std::pair<float,float>* const renderPageSize) {
+ if(absoluteFlexItem) {
+ absoultePositon = new WXCorePosition();
+ setFrame(absoultePositon, left, top, right, bottom);
+ }
+ else{
+ switch (mCssStyle->mPositionType) {
+ case kFixed:
+ case kAbsolute:
+ calcAbsoluteOffset(left, top, right, bottom, renderPageSize);
+ break;
+ default:
+ case kRelative:
+ calcRelativeOffset(left, top, right, bottom);
+ break;
+ }
+ setFrame(left, top, right, bottom);
+ onLayout(left, top, right, bottom);
+ }
+ }
+
+ void WXCoreLayoutNode::calcRelativeOffset(float &left, float &top, float &right, float &bottom) const {
+ if (!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft))) {
+ left += mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft);
+ right += mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft);
+ } else if (!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeRight))) {
+ left -= mCssStyle->mStylePosition.getPosition(kPositionEdgeRight);
+ right -= mCssStyle->mStylePosition.getPosition(kPositionEdgeRight);
+ }
+
+ if (!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeTop))) {
+ top += mCssStyle->mStylePosition.getPosition(kPositionEdgeTop);
+ bottom += mCssStyle->mStylePosition.getPosition(kPositionEdgeTop);
+ } else if (!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom))) {
+ top -= mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom);
+ bottom -= mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom);
+ }
+ }
+
+ void WXCoreLayoutNode::calcAbsoluteOffset(float &left, float &top, float &right, float &bottom, const std::pair<float,float>* const renderPageSize){
+ WXCorePadding parentPadding;
+ WXCoreBorderWidth parentBorder;
+ WXCoreSize parentSize;
+ if (mCssStyle->mPositionType == kAbsolute && mParent != nullptr) {
+ parentPadding = mParent->mCssStyle->mPadding;
+ parentBorder = mParent->mCssStyle->mBorderWidth;
+ parentSize = mParent->mLayoutResult->mLayoutSize;
+ positionAbsoluteFlexItem(left, top, right, bottom);
+ } else if(mCssStyle->mPositionType == kFixed && renderPageSize!= nullptr){
+ parentSize.width = renderPageSize->first;
+ parentSize.height = renderPageSize->second;
+ }
+ updateLeftRightForAbsolute(left, right, parentPadding, parentBorder, parentSize);
+ updateTopBottomForAbsolute(top, bottom, parentPadding, parentBorder, parentSize);
+ }
+
+ void WXCoreLayoutNode::positionAbsoluteFlexItem(float &left, float &top, float &right, float &bottom){
+ if ((isnan(getStylePositionLeft()) && isnan(getStylePositionRight())) ||
+ (isnan(getStylePositionTop()) && isnan(getStylePositionBottom()))) {
+ WXCoreFlexLine tempLine;
+ mParent->updateFlexLineForAbsoluteItem(this, &tempLine);
+ mParent->onLayout(mParent->getLayoutPositionLeft(),
+ mParent->getLayoutPositionTop(),
+ mParent->getLayoutPositionRight(),
+ mParent->getLayoutPositionBottom(),
+ this, &tempLine);
+ if(absoultePositon != nullptr) {
+ if (isnan(getStylePositionLeft()) && isnan(getStylePositionRight())) {
+ left = absoultePositon->getPosition(kPositionEdgeLeft);
+ right = absoultePositon->getPosition(kPositionEdgeRight);
+ }
+ if (isnan(getStylePositionTop()) && isnan(getStylePositionBottom())) {
+ top = absoultePositon->getPosition(kPositionEdgeTop);
+ bottom = absoultePositon->getPosition(kPositionEdgeBottom);
+ }
+ delete absoultePositon;
+ absoultePositon = nullptr;
+ }
+ }
+ }
+
+ void WXCoreLayoutNode::updateFlexLineForAbsoluteItem(WXCoreLayoutNode *const absoluteFlexItem, WXCoreFlexLine *const flexLine){
+ flexLine->mMainSize = isMainAxisHorizontal(this) ?
+ absoluteFlexItem->getLayoutWidth() + absoluteFlexItem->getMarginLeft()
+ + absoluteFlexItem->getMarginRight() :
+ absoluteFlexItem->getLayoutHeight() + absoluteFlexItem->getMarginTop()
+ + absoluteFlexItem->getMarginBottom();
+ flexLine->mCrossSize = isMainAxisHorizontal(this) ?
+ absoluteFlexItem->getLayoutHeight() + absoluteFlexItem->getMarginTop()
+ + absoluteFlexItem->getMarginBottom() :
+ absoluteFlexItem->getLayoutWidth() + absoluteFlexItem->getMarginLeft()
+ + absoluteFlexItem->getMarginRight();
+ flexLine->mItemCount = 1;
+ }
+
+ void WXCoreLayoutNode::onLayout(const float left, const float top, const float right, const float bottom,
+ WXCoreLayoutNode *const absoulteItem, WXCoreFlexLine *const flexLine) {
+ switch (mCssStyle->mFlexDirection) {
+ case kFlexDirectionRow:
+ layoutHorizontal(false, left, top, right, bottom, absoulteItem, flexLine);
+ break;
+ case kFlexDirectionRowReverse:
+ layoutHorizontal(true, left, top, right, bottom, absoulteItem, flexLine);
+ break;
+ case kFlexDirectionColumnReverse:
+ layoutVertical(mCssStyle->mFlexWrap == kWrapReverse, true, left, top, right, bottom, absoulteItem, flexLine);
+ break;
+ case kFlexDirectionColumn:
+ default:
+ layoutVertical(mCssStyle->mFlexWrap == kWrapReverse, false, left, top, right, bottom, absoulteItem, flexLine);
+ break;
+ }
+ }
+
+ /**
+ * Sub method for {@link WXCoreLayoutNode #onLayout(int, int, int, int)} when the
+ * {@link #mFlexDirection} is either {@link WXCoreFlexDirection #WXCore_Flex_Direction_Row} or
+ * {@link WXCoreFlexDirection #WXCore_Flex_Direction_Row_REVERSE}.
+ *
+ * @param isRtl {@code true} if the horizontal layout direction is mStyleRight to mStyleLeft, {@code
+ * false} otherwise.
+ * @param left the mStyleLeft position of this View
+ * @param top the mStyleTop position of this View
+ * @param right the mStyleRight position of this View
+ * @param bottom the mStyleBottom position of this View
+ */
+ void WXCoreLayoutNode::layoutHorizontal(const bool isRtl,
+ const float left, const float top,
+ const float right, const float bottom,
+ WXCoreLayoutNode *const absoulteItem,
+ WXCoreFlexLine *const flexLine) {
+ Index currentViewIndex = 0;
+ float height = bottom - top;
+ float width = right - left;
+
+ // childBottom is used if the mFlexWrap is FLEX_WRAP_WRAP_REVERSE otherwise
+ // childTop is used to align the vertical position of the children views.
+ float childBottom = height - getPaddingBottom() - getBorderWidthBottom();
+ float childTop = getPaddingTop() + getBorderWidthTop();
+
+ // Used only for RTL layout
+ // Use float to reduce the round error that may happen in when justifyContent ==
+ // SPACE_BETWEEN or SPACE_AROUND
+ float childLeft, childRight;
+ const std::vector<WXCoreFlexLine*> &lines = (flexLine == nullptr? mFlexLines: std::vector<WXCoreFlexLine*>{flexLine});
+
+ for (WXCoreFlexLine *flexLine: lines) {
+ float spaceBetweenItem = 0.f;
+ layoutFlexlineHorizontal(width, flexLine, childLeft, childRight, spaceBetweenItem);
+ spaceBetweenItem = std::max(spaceBetweenItem, 0.f);
+
+ if(absoulteItem == nullptr) {
+ for (Index j = 0; j < flexLine->mItemCount; j++) {
+ WXCoreLayoutNode *child = getChildAt(kNonBFC, currentViewIndex);
+ if (child == nullptr) {
+ continue;
+ }
+ layoutSingleChildHorizontal(isRtl, false, childBottom, childTop,
+ flexLine, child, childLeft, childRight);
+ childLeft += child->mLayoutResult->mLayoutSize.width + spaceBetweenItem + child->getMarginRight();
+ childRight -= child->mLayoutResult->mLayoutSize.width + spaceBetweenItem + child->getMarginLeft();
+ currentViewIndex++;
+ }
+ childTop += flexLine->mCrossSize;
+ childBottom -= flexLine->mCrossSize;
+ }
+ else{
+ layoutSingleChildHorizontal(isRtl, true, childBottom, childTop,
+ flexLine, absoulteItem, childLeft, childRight);
+ }
+ }
+ }
+
+ void WXCoreLayoutNode::layoutFlexlineHorizontal(const float width,
+ const WXCoreFlexLine *const flexLine,
+ float &childLeft,
+ float &childRight,
+ float &spaceBetweenItem) const {
+ Index visibleCount, visibleItem;
+ float denominator;
+ switch (mCssStyle->mJustifyContent) {
+ case kJustifyFlexEnd:
+ childLeft = width - flexLine->mMainSize - getPaddingRight() - getBorderWidthRight();
+ childRight = width - getPaddingLeft() - getBorderWidthLeft();
+ break;
+ case kJustifyCenter:
+ childLeft = (width - flexLine->mMainSize - mCssStyle->sumPaddingBorderOfEdge(kRight)
+ + mCssStyle->sumPaddingBorderOfEdge(kLeft)) / 2;
+ childRight = childLeft + flexLine->mMainSize;
+ break;
+ case kJustifySpaceAround:
+ visibleCount = flexLine->mItemCount;
+ if (visibleCount != 0) {
+ spaceBetweenItem =
+ (width - flexLine->mMainSize - sumPaddingBorderAlongAxis(this, true)) / visibleCount;
+ }
+ childLeft = getPaddingLeft() + getBorderWidthLeft() + spaceBetweenItem / 2.f;
+ childRight = width - getPaddingRight() - getBorderWidthRight() - spaceBetweenItem / 2.f;
+ break;
+ case kJustifySpaceBetween:
+ childLeft = getPaddingLeft() + getBorderWidthLeft();
+ visibleItem = flexLine->mItemCount;
+ denominator = visibleItem != 1 ? visibleItem - 1 : 1.f;
+ spaceBetweenItem =
+ (width - flexLine->mMainSize - sumPaddingBorderAlongAxis(this, true)) / denominator;
+ childRight = width - getPaddingRight() - getBorderWidthRight();
+ break;
+ case kJustifyFlexStart:
+ default:
+ childLeft = getPaddingLeft() + getBorderWidthLeft();
+ childRight = width - getPaddingRight() - getBorderWidthRight();
+ break;
+ }
+ }
+
+ void WXCoreLayoutNode::layoutSingleChildHorizontal(const bool isRtl, const bool absoulteItem,
+ float childBottom, float childTop,
+ WXCoreFlexLine *const flexLine,
+ WXCoreLayoutNode *const child,
+ float &childLeft, float &childRight) {
+ childLeft += child->getMarginLeft();
+ childRight -= child->getMarginRight();
+ if (mCssStyle->mFlexWrap == kWrapReverse) {
+ if (isRtl) {
+ layoutSingleChildHorizontal(child,
+ flexLine,
+ mCssStyle->mFlexWrap,
+ mCssStyle->mAlignItems,
+ childRight - child->mLayoutResult->mLayoutSize.width,
+ childBottom - child->mLayoutResult->mLayoutSize.height,
+ childRight,
+ childBottom,
+ absoulteItem);
+ } else {
+ layoutSingleChildHorizontal(child,
+ flexLine,
+ mCssStyle->mFlexWrap,
+ mCssStyle->mAlignItems,
+ childLeft,
+ childBottom - child->mLayoutResult->mLayoutSize.height,
+ childLeft + child->mLayoutResult->mLayoutSize.width,
+ childBottom,
+ absoulteItem);
+ }
+ } else {
+ if (isRtl) {
+ layoutSingleChildHorizontal(child, flexLine, mCssStyle->mFlexWrap, mCssStyle->mAlignItems,
+ childRight - child->mLayoutResult->mLayoutSize.width, childTop,
+ childRight, childTop + child->mLayoutResult->mLayoutSize.height,
+ absoulteItem);
+ } else {
+ layoutSingleChildHorizontal(child, flexLine, mCssStyle->mFlexWrap, mCssStyle->mAlignItems,
+ childLeft, childTop,
+ childLeft + child->mLayoutResult->mLayoutSize.width,
+ childTop + child->mLayoutResult->mLayoutSize.height,
+ absoulteItem);
+ }
+ }
+ }
+
+ /**
+ * Place a single View when the layout direction is horizontal ({@link #mFlexDirection} is
+ * either {@link WXCoreFlexDirection #WXCore_Flex_Direction_Row} or
+ * {@link WXCoreFlexDirection #WXCore_Flex_Direction_Row_REVERSE}).
+ *
+ * @param node the View to be placed
+ * @param flexLine the {@link WXCoreFlexLine} where the View belongs to
+ * @param flexWrap the flex wrap attribute of this BasicLayoutNode
+ * @param alignItems the align items attribute of this BasicLayoutNode
+ * @param left the mStyleLeft position of the View, which the View's margin is already taken
+ * into account
+ * @param top the mStyleTop position of the flex line where the View belongs to. The actual
+ * View's mStyleTop position is shifted depending on the flexWrap and alignItems
+ * attributes
+ * @param right the mStyleRight position of the View, which the View's margin is already taken
+ * into account
+ * @param bottom the mStyleBottom position of the flex line where the View belongs to. The actual
+ * View's mStyleBottom position is shifted depending on the flexWrap and alignItems
+ * attributes
+ */
+ void WXCoreLayoutNode::layoutSingleChildHorizontal(WXCoreLayoutNode *const node, WXCoreFlexLine* const flexLine,
+ const WXCoreFlexWrap flexWrap, WXCoreAlignItems alignItems,
+ const float left, const float top, const float right, const float bottom, const bool absoluteFlexItem) {
+ if (node->mCssStyle->mAlignSelf != kAlignSelfAuto) {
+ // Expecting the values for alignItems and alignSelf match except for ALIGN_SELF_AUTO.
+ // Assigning the alignSelf value as alignItems should work.
+ alignItems = static_cast<WXCoreAlignItems>(node->mCssStyle->mAlignSelf);
+ }
+
+ float crossSize = flexLine->mCrossSize;
+
+ switch (alignItems) {
+ case kAlignItemsFlexStart:
+ case kAlignItemsStretch:
+ if (flexWrap != kWrapReverse) {
+ node->layout(left, top + node->getMarginTop(), right, bottom + node->getMarginTop(), absoluteFlexItem);
+ } else {
+ node->layout(left, top - node->getMarginBottom(), right, bottom - node->getMarginBottom(), absoluteFlexItem);
+ }
+ break;
+ case kAlignItemsFlexEnd:
+ if (flexWrap != kWrapReverse) {
+ node->layout(left,
+ top + crossSize - node->mLayoutResult->mLayoutSize.height - node->getMarginBottom(),
+ right, top + crossSize - node->getMarginBottom(), absoluteFlexItem);
+ } else {
+ // If the flexWrap == FLEX_WRAP_WRAP_REVERSE, the direction of the
+ // flexEnd is flipped (from mStyleTop to mStyleBottom).
+ node->layout(left, top - crossSize + node->mLayoutResult->mLayoutSize.height + node->getMarginTop(),
+ right, bottom - crossSize + node->mLayoutResult->mLayoutSize.height + node->getMarginTop(), absoluteFlexItem);
+ }
+ break;
+ case kAlignItemsCenter:
+ float topFromCrossAxis = (crossSize - node->mLayoutResult->mLayoutSize.height
+ + node->getMarginTop() - node->getMarginBottom()) / 2;
+ if (flexWrap != kWrapReverse) {
+ node->layout(left, top + topFromCrossAxis,
+ right, top + topFromCrossAxis + node->mLayoutResult->mLayoutSize.height, absoluteFlexItem);
+ } else {
+ node->layout(left, top - topFromCrossAxis,
+ right, top - topFromCrossAxis + node->mLayoutResult->mLayoutSize.height, absoluteFlexItem);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Sub method for {@link WXCoreLayoutNode #onLayout(int, int, int, int)} when the
+ * {@link #mFlexDirection} is either {@link WXCoreFlexDirection #WXCore_Flex_Direction_Column} or
+ * {@link WXCoreFlexDirection #WXCore_Flex_Direction_Column_Reverse}.
+ *
+ * @param isRtl {@code true} if the horizontal layout direction is mStyleRight to mStyleLeft,
+ * {@code false}
+ * otherwise
+ * @param fromBottomToTop {@code true} if the layout direction is mStyleBottom to mStyleTop, {@code false}
+ * otherwise
+ * @param left the mStyleLeft position of this View
+ * @param top the mStyleTop position of this View
+ * @param right the mStyleRight position of this View
+ * @param bottom the mStyleBottom position of this View
+ */
+ void
+ WXCoreLayoutNode::layoutVertical(const bool isRtl,
+ const bool fromBottomToTop,
+ const float left, const float top,
+ const float right, const float bottom,
+ WXCoreLayoutNode *const absoulteItem,
+ WXCoreFlexLine *const flexLine) {
+ float childLeft = getPaddingLeft() + getBorderWidthLeft();
+ Index currentViewIndex = 0;
+ float width = right - left;
+ float height = bottom - top;
+
+ // childRight is used if the mFlexWrap is FLEX_WRAP_WRAP_REVERSE otherwise
+ // childLeft is used to align the horizontal position of the children views.
+ float childRight = width - getPaddingRight() - getBorderWidthRight();
+
+ // Use float to reduce the round error that may happen in when justifyContent ==
+ // SPACE_BETWEEN or SPACE_AROUND
+ float childTop, childBottom;
+ const std::vector<WXCoreFlexLine*> &lines = (flexLine == nullptr? mFlexLines: std::vector<WXCoreFlexLine*>{flexLine});
+
+ for (WXCoreFlexLine *flexLine : lines) {
+ float spaceBetweenItem = 0.f;
+ layoutFlexlineVertical(height, flexLine, childTop, childBottom, spaceBetweenItem);
+ spaceBetweenItem = std::max(spaceBetweenItem, 0.f);
+ if(absoulteItem == nullptr) {
+ for (Index j = 0; j < flexLine->mItemCount; j++) {
+ WXCoreLayoutNode *child = getChildAt(kNonBFC, currentViewIndex);
+ if (child == nullptr) {
+ continue;
+ }
+ layoutSingleChildVertical(isRtl, fromBottomToTop, false,
+ childLeft, childRight, flexLine,
+ child, childTop, childBottom);
+ childTop += child->mLayoutResult->mLayoutSize.height + spaceBetweenItem + child->getMarginBottom();
+ childBottom -= child->mLayoutResult->mLayoutSize.height + spaceBetweenItem + child->getMarginTop();
+ currentViewIndex++;
+ }
+ childLeft += flexLine->mCrossSize;
+ childRight -= flexLine->mCrossSize;
+ }
+ else{
+ layoutSingleChildVertical(isRtl, fromBottomToTop, true,
+ childLeft, childRight, flexLine,
+ absoulteItem, childTop, childBottom);
+ }
+ }
+ }
+
+ void WXCoreLayoutNode::layoutFlexlineVertical(const float height,
+ const WXCoreFlexLine *const flexLine,
+ float &childTop,
+ float &childBottom,
+ float &spaceBetweenItem) const {
+ Index visibleCount, visibleItem;
+ float denominator;
+ switch (mCssStyle->mJustifyContent) {
+ case kJustifyFlexEnd:
+ childTop = height - flexLine->mMainSize - getPaddingBottom() - getBorderWidthBottom();
+ childBottom = height - getPaddingTop() - getBorderWidthTop();
+ break;
+ case kJustifyCenter:
+ childTop = (height - flexLine->mMainSize - mCssStyle->sumPaddingBorderOfEdge(kBottom)
+ + mCssStyle->sumPaddingBorderOfEdge(kTop)) / 2;
+ childBottom = childTop + flexLine->mMainSize;
+ break;
+ case kJustifySpaceAround:
+ visibleCount = flexLine->mItemCount;
+ if (visibleCount != 0) {
+ spaceBetweenItem = (height - flexLine->mMainSize - sumPaddingBorderAlongAxis(this, false))
+ / visibleCount;
+ }
+ childTop = getPaddingTop() + getBorderWidthTop() + spaceBetweenItem / 2;
+ childBottom = height - getPaddingBottom() - getBorderWidthBottom() - spaceBetweenItem / 2;
+ break;
+ case kJustifySpaceBetween:
+ childTop = getPaddingTop() + getBorderWidthTop();
+ visibleItem = flexLine->mItemCount;
+ denominator = visibleItem != 1 ? visibleItem - 1 : 1.f;
+ spaceBetweenItem =
+ (height - flexLine->mMainSize - sumPaddingBorderAlongAxis(this, false)) / denominator;
+ childBottom = height - getPaddingBottom() - getBorderWidthBottom();
+ break;
+ case kJustifyFlexStart:
+ default:
+ childTop = getPaddingTop() + getBorderWidthTop();
+ childBottom = height - getPaddingBottom() - getBorderWidthBottom();
+ break;
+ }
+ }
+
+ void WXCoreLayoutNode::layoutSingleChildVertical(const bool isRtl, const bool fromBottomToTop,
+ const bool absoluteFlexItem,
+ const float childLeft, const float childRight,
+ WXCoreFlexLine *const flexLine,
+ WXCoreLayoutNode *const child,
+ float &childTop, float &childBottom) {
+ childTop += child->getMarginTop();
+ childBottom -= child->getMarginBottom();
+ if (isRtl) {
+ if (fromBottomToTop) {
+ layoutSingleChildVertical(child, flexLine, true,
+ mCssStyle->mAlignItems,
+ childRight - child->mLayoutResult->mLayoutSize.width,
+ childBottom - child->mLayoutResult->mLayoutSize.height,
+ childRight, childBottom, absoluteFlexItem);
+ } else {
+ layoutSingleChildVertical(child, flexLine, true, mCssStyle->mAlignItems,
+ childRight - child->mLayoutResult->mLayoutSize.width, childTop,
+ childRight, childTop + child->mLayoutResult->mLayoutSize.height,
+ absoluteFlexItem);
+ }
+ } else {
+ if (fromBottomToTop) {
+ layoutSingleChildVertical(child, flexLine, false, mCssStyle->mAlignItems,
+ childLeft, childBottom - child->mLayoutResult->mLayoutSize.height,
+ childLeft + child->mLayoutResult->mLayoutSize.width, childBottom,
+ absoluteFlexItem);
+ } else {
+ layoutSingleChildVertical(child, flexLine, false, mCssStyle->mAlignItems,
+ childLeft, childTop,
+ childLeft + child->mLayoutResult->mLayoutSize.width,
+ childTop + child->mLayoutResult->mLayoutSize.height,
+ absoluteFlexItem);
+ }
+ }
+ }
+
+ /**
+ * Place a single View when the layout direction is vertical ({@link #mFlexDirection} is
+ * either {@link WXCoreFlexDirection #WXCore_Flex_Direction_Column} or
+ * {@link WXCoreFlexDirection #WXCore_Flex_Direction_Column_Reverse}).
+ *
+ * @param node the View to be placed
+ * @param flexLine the {@link FlexLine} where the View belongs to
+ * @param isRtl {@code true} if the layout direction is mStyleRight to mStyleLeft, {@code false}
+ * otherwise
+ * @param alignItems the align items attribute of this BasicLayoutNode
+ * @param left the mStyleLeft position of the flex line where the View belongs to. The actual
+ * View's mStyleLeft position is shifted depending on the isRtl and alignItems
+ * attributes
+ * @param top the mStyleTop position of the View, which the View's margin is already taken
+ * into account
+ * @param right the mStyleRight position of the flex line where the View belongs to. The actual
+ * View's mStyleRight position is shifted depending on the isRtl and alignItems
+ * attributes
+ * @param bottom the mStyleBottom position of the View, which the View's margin is already taken
+ * into account
+ */
+ void WXCoreLayoutNode::layoutSingleChildVertical(WXCoreLayoutNode* const node, WXCoreFlexLine* const flexLine, const bool isRtl,
+ WXCoreAlignItems alignItems, const float left, const float top, const float right,
+ const float bottom, const bool absoluteFlexItem) {
+ if (node->mCssStyle->mAlignSelf != kAlignSelfAuto) {
+ // Expecting the values for alignItems and alignSelf match except for ALIGN_SELF_AUTO.
+ // Assigning the alignSelf value as alignItems should work.
+ alignItems = static_cast<WXCoreAlignItems>(node->mCssStyle->mAlignSelf);
+ }
+
+ float crossSize = flexLine->mCrossSize;
+
+ switch (alignItems) {
+ case kAlignItemsFlexStart:
+ case kAlignItemsStretch:
+ if (!isRtl) {
+ node->layout(left + node->getMarginLeft(), top, right + node->getMarginLeft(), bottom, absoluteFlexItem);
+ } else {
+ node->layout(left - node->getMarginRight(), top, right - node->getMarginRight(), bottom, absoluteFlexItem);
+ }
+ break;
+ case kAlignItemsFlexEnd:
+ if (!isRtl) {
+ node->layout(left + crossSize - node->mLayoutResult->mLayoutSize.width - node->getMarginRight(),
+ top, right + crossSize - node->mLayoutResult->mLayoutSize.width - node->getMarginRight(),
+ bottom, absoluteFlexItem);
+ } else {
+ // If the flexWrap == FLEX_WRAP_WRAP_REVERSE, the direction of the
+ // flexEnd is flipped (from mStyleLeft to mStyleRight).
+ node->layout(left - crossSize + node->mLayoutResult->mLayoutSize.width + node->getMarginLeft(), top,
+ right - crossSize + node->mLayoutResult->mLayoutSize.width + node->getMarginLeft(),
+ bottom, absoluteFlexItem);
+ }
+ break;
+ case kAlignItemsCenter:
+ float leftFromCrossAxis = (crossSize - node->mLayoutResult->mLayoutSize.width
+ + node->getMarginLeft()
+ - node->getMarginRight()) / 2.f;
+ if (!isRtl) {
+ node->layout(left + leftFromCrossAxis, top, right + leftFromCrossAxis, bottom, absoluteFlexItem);
+ } else {
+ node->layout(left - leftFromCrossAxis, top, right - leftFromCrossAxis, bottom, absoluteFlexItem);
+ }
+ break;
+ }
+ }
+}
+
+
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Layout/WXCoreLayout.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXCoreLayout.h b/ios/sdk/WeexSDK/Sources/Layout/WXCoreLayout.h
new file mode 100644
index 0000000..498c243
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXCoreLayout.h
@@ -0,0 +1,1073 @@
+#ifdef __cplusplus
+
+#ifndef WEEXCORE_FLEXLAYOUT_WXCORELAYOUTNODE_H
+#define WEEXCORE_FLEXLAYOUT_WXCORELAYOUTNODE_H
+
+#include "WXCoreStyle.h"
+#include "WXCoreFlexEnum.h"
+#include <vector>
+#include <iostream>
+#include <string>
+#include <algorithm>
+
+namespace WeexCore {
+
+ class WXCoreLayoutNode;
+
+ class WXCoreFlexLine;
+
+ enum FormattingContext {
+ kBFC,
+ kNonBFC,
+ } ;
+
+ enum MeasureMode {
+ kUnspecified,
+ kExactly,
+ } ;
+
+ struct WXCoreSize {
+ private:
+ float hypotheticalWidth;
+ float hypotheticalHeight;
+ public:
+ friend class WXCoreLayoutNode;
+ float width;
+ float height;
+
+ WXCoreSize() : hypotheticalWidth(NAN),
+ hypotheticalHeight(NAN),
+ width(0), height(0) {}
+
+ inline void reset() {
+ hypotheticalWidth = NAN ;
+ hypotheticalHeight = NAN;
+ width = 0;
+ height = 0;
+ }
+
+ inline bool isNAN() {
+ return isnan(width) || isnan(height);
+ }
+ };
+
+ /**
+ * layout-result:layout-height、layout-width、position(left、right、top、bottom)
+ */
+ struct WXCorelayoutResult {
+ WXCoreSize mLayoutSize;
+ WXCorePosition mLayoutPosition;
+
+ inline bool isNAN() {
+ return mLayoutSize.isNAN() || mLayoutPosition.isNAN();
+ }
+
+ inline void reset() {
+ mLayoutSize.reset();
+ mLayoutPosition.reset();
+ }
+ };
+
+ typedef WXCoreSize(*WXCoreMeasureFunc)(WXCoreLayoutNode *node, float width,
+ MeasureMode widthMeasureMode,
+ float height, MeasureMode heightMeasureMode);
+
+ using Index = std::vector<WXCoreLayoutNode *>::size_type;
+
+ /**
+ * flie line
+ */
+ class WXCoreFlexLine {
+ public:
+ float mMainSize;
+
+ float mCrossSize;
+
+ Index mItemCount;
+
+ float mTotalFlexGrow;
+
+ float mTotalFlexibleSize;
+
+ /**
+ * Store the indices of the children views whose mAlignSelf property is stretch.
+ * The stored indices are the absolute indices including all children in the Flexbox,
+ * not the relative indices in this flex line.
+ */
+ std::vector<Index> mIndicesAlignSelfStretch;
+
+ WXCoreFlexLine() : mMainSize(0),
+ mCrossSize(0),
+ mItemCount(0),
+ mTotalFlexGrow(0),
+ mTotalFlexibleSize(0) {
+ }
+
+ ~WXCoreFlexLine() {
+ mMainSize = 0;
+ mCrossSize = 0;
+ mItemCount = 0;
+ mTotalFlexGrow = 0;
+ mTotalFlexibleSize = 0;
+ mIndicesAlignSelfStretch.clear();
+ }
+ };
+
+ /**
+ * Layout node
+ */
+ class WXCoreLayoutNode {
+
+ public:
+ WXCoreLayoutNode() :
+ mParent(nullptr),
+ dirty(true),
+ widthDirty{false},
+ heightDirty{false},
+ mHasNewLayout(true),
+ mIsDestroy(false),
+ measureFunc(nullptr) {
+ mCssStyle = new WXCoreCSSStyle();
+ mLayoutResult = new WXCorelayoutResult();
+ }
+
+
+ ~WXCoreLayoutNode() {
+ mIsDestroy = true;
+ mHasNewLayout = true;
+ dirty = true;
+ measureFunc = nullptr;
+ mParent = nullptr;
+ mChildList.clear();
+ BFCs.clear();
+ NonBFCs.clear();
+ mChildrenFrozen.clear();
+
+ for (WXCoreFlexLine *flexLine : mFlexLines) {
+ if (flexLine != nullptr) {
+ delete flexLine;
+ flexLine = nullptr;
+ }
+ }
+ mFlexLines.clear();
+
+ if (mCssStyle != nullptr) {
+ delete mCssStyle;
+ mCssStyle = nullptr;
+ }
+
+ if (mLayoutResult != nullptr) {
+ delete mLayoutResult;
+ mLayoutResult = nullptr;
+ }
+ }
+
+ private:
+
+ /**
+ * Holds the 'frozen' state of children during measure. If a view is frozen it will no longer
+ * expand regardless of mFlexGrow. Items are indexed by the child's
+ * reordered index.
+ */
+ std::vector<bool> mChildrenFrozen;
+
+ std::vector<WXCoreFlexLine *> mFlexLines;
+
+ std::vector<WXCoreLayoutNode *> mChildList;
+
+ std::vector<WXCoreLayoutNode *> BFCs;
+
+ std::vector<WXCoreLayoutNode *> NonBFCs;
+
+ WXCoreLayoutNode *mParent = nullptr;
+
+ WXCoreCSSStyle *mCssStyle = nullptr;
+
+ MeasureMode widthMeasureMode = kUnspecified;
+
+ MeasureMode heightMeasureMode = kUnspecified;
+
+ WXCorelayoutResult *mLayoutResult = nullptr;
+
+ WXCorePosition *absoultePositon = nullptr;
+
+ bool mHasNewLayout;
+
+ bool dirty, widthDirty, heightDirty;
+
+ bool mIsDestroy = true;
+
+ WXCoreMeasureFunc measureFunc = nullptr;
+
+ void *context;
+
+ /** ================================ Cache:Last calculate result =================================== **/
+
+ public:
+
+
+ /** ================================ Engine Entry Function =================================== **/
+
+ void calculateLayout(const std::pair<float,float>&);
+
+ /** ================================ measureFunc =================================== **/
+
+ inline void setMeasureFunc(WXCoreMeasureFunc measure) {
+ measureFunc = measure;
+ markDirty();
+ }
+
+ inline WXCoreMeasureFunc getMeasureFunc() const {
+ return measureFunc;
+ }
+
+ inline bool haveMeasureFunc() const {
+ return measureFunc != nullptr;
+ }
+
+ /** ================================ context =================================== **/
+
+
+ inline void *getContext() const {
+ return context;
+ }
+
+ inline void setContext(void * const context) {
+ this->context = context;
+ }
+
+ inline void copyStyle(WXCoreLayoutNode *srcNode) {
+ if (memcmp(mCssStyle, srcNode->mCssStyle, sizeof(WXCoreCSSStyle)) != 0) {
+ memcpy(mCssStyle, srcNode->mCssStyle, sizeof(WXCoreCSSStyle));
+ markDirty();
+ }
+ }
+
+ inline void copyMeasureFunc(WXCoreLayoutNode *srcNode) {
+ if (memcmp(&measureFunc, &srcNode->measureFunc, sizeof(WXCoreMeasureFunc)) != 0) {
+ memcpy(&measureFunc, &srcNode->measureFunc, sizeof(WXCoreMeasureFunc));
+ markDirty();
+ }
+ }
+
+ private:
+
+ /** ================================ measure =================================== **/
+
+ inline void reset() {
+ if (isDirty()) {
+ mLayoutResult->reset();
+ for (WXCoreFlexLine *flexLine : mFlexLines) {
+ if (flexLine != nullptr) {
+ delete flexLine;
+ flexLine = nullptr;
+ }
+ }
+ mFlexLines.clear();
+
+ mChildrenFrozen.assign(getChildCount(kNonBFC), false);
+ }
+ widthMeasureMode = isnan(mCssStyle->mStyleWidth) ? kUnspecified : kExactly;
+ heightMeasureMode = isnan(mCssStyle->mStyleHeight) ? kUnspecified : kExactly;
+ }
+
+ inline void setLayoutWidth(const float width) {
+ if (mLayoutResult->mLayoutSize.width != width &&
+ (!isnan(width) || !isnan(mLayoutResult->mLayoutSize.width))) {
+ mLayoutResult->mLayoutSize.width = width;
+ widthDirty = true;
+ markDirty(false);
+ }
+ }
+
+ inline void setLayoutHeight(const float height) {
+ if (mLayoutResult->mLayoutSize.height != height &&
+ (!isnan(height) || !isnan(mLayoutResult->mLayoutSize.height))) {
+ mLayoutResult->mLayoutSize.height = height;
+ heightDirty = true;
+ markDirty(false);
+ }
+ }
+
+ inline void setWidthMeasureMode(const MeasureMode measureMode) {
+ if (widthMeasureMode != measureMode) {
+ widthMeasureMode = measureMode;
+ if (getChildCount(kNonBFC) > 0) {
+ widthDirty = true;
+ }
+ }
+ }
+
+ inline void setHeightMeasureMode(const MeasureMode measureMode) {
+ if (heightMeasureMode != measureMode) {
+ heightMeasureMode = measureMode;
+ if (getChildCount(kNonBFC) > 0) {
+ heightDirty = true;
+ }
+ }
+ }
+
+ inline float firstLineCrossSize() const {
+ float sum = sumPaddingBorderAlongAxis(this, !isMainAxisHorizontal(this));
+ if (!mFlexLines.empty()) {
+ sum += mFlexLines[0]->mCrossSize;
+ }
+ return sum;
+ }
+
+ inline float getSumOfCrossSize() const {
+ float sum = sumPaddingBorderAlongAxis(this, !isMainAxisHorizontal(this));
+ for (WXCoreFlexLine *flexLine: mFlexLines) {
+ sum += flexLine->mCrossSize;
+ }
+ return sum;
+ }
+
+ inline bool isMainAxisHorizontal(const WXCoreLayoutNode* const node) const {
+ return node->mCssStyle->mFlexDirection == kFlexDirectionRow ||
+ node->mCssStyle->mFlexDirection == kFlexDirectionRowReverse;
+ }
+
+ inline bool isCrossExactly() const {
+ return isMainAxisHorizontal(this) ? heightMeasureMode == kExactly
+ : widthMeasureMode == kExactly;
+ }
+
+ inline float sumPaddingBorderAlongAxis(const WXCoreLayoutNode* const node, bool horizontal) const {
+ float paddingBorderAlongAxis;
+ if (horizontal) {
+ paddingBorderAlongAxis =
+ node->mCssStyle->mPadding.getPadding(kPaddingLeft) +
+ node->mCssStyle->mPadding.getPadding(kPaddingRight) +
+ node->mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthLeft) +
+ node->mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthRight);
+ } else {
+ paddingBorderAlongAxis =
+ node->mCssStyle->mPadding.getPadding(kPaddingTop) +
+ node->mCssStyle->mPadding.getPadding(kPaddingBottom) +
+ node->mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthTop) +
+ node->mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthBottom);
+ }
+ return paddingBorderAlongAxis;
+ }
+
+ inline bool isWrapRequired(const float &width, const float &height,
+ const float ¤tLength, const float &childLength) const {
+ float freeMainSize = calcFreeSpaceAlongMainAxis(width, height, currentLength);
+ return !isSingleFlexLine(freeMainSize) && freeMainSize < childLength;
+ }
+
+ //If width/height is NAN, ret is NAN, which property we use on purpose.
+ virtual float calcFreeSpaceAlongMainAxis(const float &width, const float &height, const float ¤tLength) const{
+ float ret;
+ if(isMainAxisHorizontal(this)){
+ ret = width - sumPaddingBorderAlongAxis(this, true) - currentLength;
+ }
+ else{
+ ret = height - sumPaddingBorderAlongAxis(this, false) - currentLength;
+ }
+ return ret;
+ }
+
+ inline bool isSingleFlexLine(const float &mainSize) const {
+ return mCssStyle->mFlexWrap == kNoWrap || isnan(mainSize);
+ }
+
+ inline void sumFlexGrow(const WXCoreLayoutNode* const child, WXCoreFlexLine* const flexLine, Index i){
+ if (child->mCssStyle->mFlexGrow > 0) {
+ flexLine->mTotalFlexGrow += child->mCssStyle->mFlexGrow;
+ mChildrenFrozen[i] = false;
+ if (isMainAxisHorizontal(this)) {
+ if (!isnan(child->mLayoutResult->mLayoutSize.hypotheticalWidth)) {
+ flexLine->mTotalFlexibleSize += child->mLayoutResult->mLayoutSize.hypotheticalWidth;
+ }
+ } else {
+ if (!isnan(child->mLayoutResult->mLayoutSize.hypotheticalHeight)) {
+ flexLine->mTotalFlexibleSize += child->mLayoutResult->mLayoutSize.hypotheticalHeight;
+ }
+ }
+ } else {
+ mChildrenFrozen[i] = true;
+ }
+ }
+
+ inline void setMeasuredDimensionForFlex(
+ const float width, const MeasureMode widthMeasureMode,
+ const float height, const MeasureMode heightMeasureMode){
+ float actualWidth, actualHeight;
+ if (isMainAxisHorizontal(this)) {
+ actualWidth = widthMeasureMode == kExactly ? width : getLargestMainSize();
+ actualHeight = heightMeasureMode == kExactly ? height : getSumOfCrossSize();
+ } else {
+ actualHeight = heightMeasureMode == kExactly ? height : getLargestMainSize();
+ actualWidth = widthMeasureMode == kExactly ? width : firstLineCrossSize();
+ }
+ setMeasuredDimension(actualWidth, actualHeight);
+ }
+
+ inline float calcItemSizeAlongAxis(const WXCoreLayoutNode* const node, const bool horizontal, const bool useHypotheticalSize = false) const {
+ float ret;
+ if (horizontal) {
+ ret = node->mCssStyle->mMargin.getMargin(kMarginLeft) +
+ node->mCssStyle->mMargin.getMargin(kMarginRight);
+ ret += useHypotheticalSize ? node->mLayoutResult->mLayoutSize.hypotheticalWidth
+ : node->mLayoutResult->mLayoutSize.width;
+ } else {
+ ret = node->mCssStyle->mMargin.getMargin(kMarginTop) +
+ node->mCssStyle->mMargin.getMargin(kMarginBottom);
+ ret += useHypotheticalSize ? node->mLayoutResult->mLayoutSize.hypotheticalHeight
+ : node->mLayoutResult->mLayoutSize.height;
+ }
+ return ret;
+ }
+
+ inline void limitMainSizeForFlexGrow(WXCoreFlexLine* const flexLine, const Index childIndex,
+ const float flexGrow) {
+ mChildrenFrozen[childIndex] = true;
+ flexLine->mTotalFlexGrow -= flexGrow;
+ }
+
+ inline void setMeasuredDimension(const float width, const float height) {
+ mLayoutResult->mLayoutSize.width = width;
+ mLayoutResult->mLayoutSize.height = height;
+ }
+
+ inline std::pair<bool, float> limitChildMainSize(WXCoreFlexLine* const flexLine, const WXCoreLayoutNode* const child,
+ float childSizeAlongMainAxis, const Index childIndex){
+ bool needsReexpand = false;
+ if (isMainAxisHorizontal(this)) {
+ if (!isnan(child->mCssStyle->mMaxWidth) &&
+ childSizeAlongMainAxis > child->mCssStyle->mMaxWidth) {
+ needsReexpand = true;
+ childSizeAlongMainAxis = child->mCssStyle->mMaxWidth;
+ } else if (!isnan(child->mCssStyle->mMinWidth) &&
+ childSizeAlongMainAxis < child->mCssStyle->mMinWidth) {
+ needsReexpand = true;
+ childSizeAlongMainAxis = child->mCssStyle->mMinWidth;
+ }
+ } else {
+ if (!isnan(child->mCssStyle->mMaxHeight) &&
+ childSizeAlongMainAxis > child->mCssStyle->mMaxHeight) {
+ needsReexpand = true;
+ childSizeAlongMainAxis = child->mCssStyle->mMaxHeight;
+ } else if (!isnan(child->mCssStyle->mMinHeight) &&
+ childSizeAlongMainAxis < child->mCssStyle->mMinHeight) {
+ needsReexpand = true;
+ childSizeAlongMainAxis = child->mCssStyle->mMinHeight;
+ }
+ }
+ limitMainSizeForFlexGrow(flexLine, childIndex, child->mCssStyle->mFlexGrow);
+ return std::make_pair(needsReexpand, childSizeAlongMainAxis);
+ }
+
+ void updateLeftRightForAbsolute(float &left, float &right,
+ const WXCorePadding &parentPadding,
+ const WXCoreBorderWidth &parentBorder,
+ const WXCoreSize &parentSize) const {
+ if (isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft))) {
+ if (isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeRight))) {
+ ;
+ } else {
+ right += parentSize.width -
+ (parentBorder.getBorderWidth(kBorderWidthRight) +
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeRight)
+ + mLayoutResult->mLayoutSize.width);
+ left += parentSize.width -
+ (parentBorder.getBorderWidth(kBorderWidthRight) +
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeRight)
+ + mLayoutResult->mLayoutSize.width);
+ }
+ } else {
+ left += parentBorder.getBorderWidth(kBorderWidthLeft) +
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft);
+ right += parentBorder.getBorderWidth(kBorderWidthLeft) +
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft);
+ }
+ }
+
+ void updateTopBottomForAbsolute(float &top, float &bottom,
+ const WXCorePadding &parentPadding,
+ const WXCoreBorderWidth &parentBorder,
+ const WXCoreSize &parentSize) const {
+ if (isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeTop))) {
+ if (isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom))) {
+ ;
+ } else {
+ top += parentSize.height -
+ (parentBorder.getBorderWidth(kBorderWidthBottom) +
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom)
+ + mLayoutResult->mLayoutSize.height);
+ bottom += parentSize.height -
+ (parentBorder.getBorderWidth(kBorderWidthBottom) +
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom)
+ + mLayoutResult->mLayoutSize.height);
+ }
+ } else {
+ top += parentBorder.getBorderWidth(kBorderWidthTop) +
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeTop);
+ bottom += parentBorder.getBorderWidth(kBorderWidthTop) +
+ mCssStyle->mStylePosition.getPosition(kPositionEdgeTop);
+ }
+ }
+
+ /** ================================ other =================================== **/
+
+ inline void clearDirty() {
+ dirty = false;
+ widthDirty = false;
+ heightDirty = false;
+ }
+
+ void
+ measure(float, float, bool);
+
+ void hypotheticalMeasure(float, float, bool = false);
+
+ void measureLeafNode(float, float, bool, bool);
+
+ void measureInternalNode(float, float, bool, bool);
+
+ void updateCurrentFlexline(Index, WXCoreFlexLine *, Index, const WXCoreLayoutNode *, bool);
+
+ void measureChild(WXCoreLayoutNode* , float, float, float, bool, bool);
+
+ void adjustChildSize(WXCoreLayoutNode *, float);
+
+ void adjustChildSize(const WXCoreLayoutNode *child,
+ const float currentMainSize,
+ const float parentWidth,
+ const float parentHeight,
+ float &childWidth,
+ float &childHeight) const;
+
+ void stretchViewCrossSize();
+
+ void stretchViewCrossSize(WXCoreLayoutNode *, float);
+
+ Index expandItemsInFlexLine(WXCoreFlexLine *, float, Index);
+
+ void checkSizeConstraints(WXCoreLayoutNode *, bool);
+
+ void
+ determineMainSize(float width, float height);
+
+ void
+ determineCrossSize(float, float, bool);
+
+ void setFrame(float, float, float, float);
+
+ void setFrame(WXCorePosition*,float, float, float, float);
+
+ /** ================================ layout =================================== **/
+
+ void layout(float left, float top, float right, float bottom, bool, const std::pair<float,float>* = nullptr);
+
+ void calcRelativeOffset(float &left, float &top, float &right, float &bottom) const ;
+
+ void calcAbsoluteOffset(float &left, float &top, float &right, float &bottom, const std::pair<float,float>* = nullptr);
+
+ void positionAbsoluteFlexItem(float &left, float &top, float &right, float &bottom);
+
+ void onLayout(float left, float top, float right, float bottom, WXCoreLayoutNode* = nullptr, WXCoreFlexLine *const flexLine = nullptr);
+
+ void layoutHorizontal(bool isRtl, float left, float top, float right, float bottom,
+ WXCoreLayoutNode*, WXCoreFlexLine *const flexLine);
+
+ void layoutFlexlineHorizontal(const float width,
+ const WXCoreFlexLine *const flexLine,
+ float &childLeft,
+ float &childRight,
+ float &spaceBetweenItem) const;
+
+ void layoutSingleChildHorizontal(WXCoreLayoutNode *node, WXCoreFlexLine *flexLine,
+ WXCoreFlexWrap flexWrap, WXCoreAlignItems alignItems,
+ float, float, float, float, bool);
+
+ void layoutSingleChildHorizontal(const bool isRtl,
+ const bool,
+ float childBottom, float childTop,
+ WXCoreFlexLine *const flexLine,
+ WXCoreLayoutNode *const child,
+ float&, float&);
+
+ void layoutVertical(bool isRtl, bool fromBottomToTop, float left, float top, float right, float bottom,
+ WXCoreLayoutNode*, WXCoreFlexLine *const flexLine);
+
+ void layoutFlexlineVertical(const float height,
+ const WXCoreFlexLine *const flexLine,
+ float &childTop,
+ float &childBottom,
+ float &spaceBetweenItem) const;
+ void layoutSingleChildVertical(WXCoreLayoutNode *node, WXCoreFlexLine *flexLine,
+ bool isRtl, WXCoreAlignItems alignItems,
+ float, float, float, float, bool);
+
+ void layoutSingleChildVertical(const bool isRtl, const bool fromBottomToTop,
+ const bool absoluteFlexItem,
+ const float childLeft, const float childRight,
+ WXCoreFlexLine *const flexLine,
+ WXCoreLayoutNode *const child,
+ float& ,float&);
+
+ void updateFlexLineForAbsoluteItem(WXCoreLayoutNode *const absoluteFlexItem, WXCoreFlexLine *const flexLine);
+
+ void initFormatingContext(std::vector<WXCoreLayoutNode *> &BFCs);
+
+ std::pair<bool,float> calculateBFCWidth(float, float);
+
+ std::pair<bool,float> calculateBFCHeight(float, float);
+
+ std::tuple<bool, float, float> calculateBFCDimension(const std::pair<float,float>&);
+
+ virtual void onLayoutBefore() {
+
+ }
+
+ virtual void onLayoutAfter(float width, float height) {
+
+ }
+
+
+ public:
+
+ /** ================================ tree =================================== **/
+
+ inline Index getChildCount(FormattingContext formattingContext) const {
+ switch (formattingContext) {
+ case kNonBFC:
+ return NonBFCs.size();
+ case kBFC:
+ return BFCs.size();
+ default:
+ return mChildList.size();
+ }
+ }
+
+ inline Index getChildCount() const {
+ return mChildList.size();
+ }
+
+ inline std::vector<WXCoreLayoutNode *>::const_iterator ChildListIterBegin() {
+ return mChildList.cbegin();
+ }
+
+ inline std::vector<WXCoreLayoutNode *>::const_iterator ChildListIterEnd() {
+ return mChildList.cend();
+ }
+
+ inline void removeChild(const WXCoreLayoutNode* const child) {
+ for (int index = 0; index < mChildList.size(); index++) {
+ if (child == mChildList[index]) {
+ mChildList.erase(mChildList.begin() + index);
+ break;
+ }
+ }
+ markDirty();
+ }
+
+ inline void addChildAt(WXCoreLayoutNode* const child, Index index) {
+ mChildList.insert(mChildList.begin() + index, child);
+ child->mParent = this;
+ markDirty();
+ }
+
+ inline WXCoreLayoutNode *getChildAt(const FormattingContext formattingContext, const Index index) const {
+ switch (formattingContext) {
+ case kNonBFC:
+ return NonBFCs[index];
+ case kBFC:
+ return BFCs[index];
+ default:
+ return mChildList[index];
+ }
+ }
+
+ inline WXCoreLayoutNode *getChildAt(const Index index) const {
+ return mChildList[index];
+ }
+
+ inline WXCoreLayoutNode *getParent() const {
+ return mParent;
+ }
+
+ inline bool isBFC(WXCoreLayoutNode* const node) const {
+ return node->mCssStyle->mPositionType == kAbsolute || node->mCssStyle->mPositionType == kFixed;
+ }
+
+ /** ================================ margin =================================== **/
+
+ inline float getMarginTop() const {
+ return mCssStyle->mMargin.getMargin(kMarginTop);
+ }
+
+ inline float getMarginBottom() const {
+ return mCssStyle->mMargin.getMargin(kMarginBottom);
+ }
+
+ inline float getMarginLeft() const {
+ return mCssStyle->mMargin.getMargin(kMarginLeft);
+ }
+
+ inline float getMarginRight() const {
+ return mCssStyle->mMargin.getMargin(kMarginRight);
+ }
+
+ inline void setMargin(const WXCoreMarginEdge &edge, const float margin) {
+ if (mCssStyle->mMargin.setMargin(edge, margin)) {
+ markDirty();
+ }
+ }
+
+ inline const WXCoreMargin &GetMargins() const {
+ return mCssStyle->mMargin;
+ }
+
+ /** ================================ padding =================================== **/
+
+ inline float getPaddingLeft() const {
+ return mCssStyle->mPadding.getPadding(kPaddingLeft);
+ }
+
+ inline float getPaddingRight() const {
+ return mCssStyle->mPadding.getPadding(kPaddingRight);
+ }
+
+ inline float getPaddingTop() const {
+ return mCssStyle->mPadding.getPadding(kPaddingTop);
+ }
+
+ inline float getPaddingBottom() const {
+ return mCssStyle->mPadding.getPadding(kPaddingBottom);
+ }
+
+ inline void setPadding(const WXCorePaddingEdge edge, const float padding) {
+ if (mCssStyle->mPadding.setPadding(edge, padding)) {
+ markDirty();
+ }
+ }
+
+ inline const WXCorePadding &GetPaddings() const {
+ return mCssStyle->mPadding;
+ }
+
+ /** ================================ border-width =================================== **/
+
+ inline float getBorderWidthLeft() const {
+ return mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthLeft);
+ }
+
+ inline float getBorderWidthRight() const {
+ return mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthRight);
+ }
+
+ inline float getBorderWidthTop() const {
+ return mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthTop);
+ }
+
+ inline float getBorderWidthBottom() const {
+ return mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthBottom);
+ }
+
+ inline void setBorderWidth(const WXCoreBorderWidthEdge edge, const float borderWidth) {
+ if (mCssStyle->mBorderWidth.setBorderWidth(edge, borderWidth)) {
+ markDirty();
+ }
+ }
+
+ inline const WXCoreBorderWidth &GetBorders() const {
+ return mCssStyle->mBorderWidth;
+ }
+
+ /** ================================ position-type =================================== **/
+
+ inline void setStylePositionType(const WXCorePositionType positionType) {
+ if (mCssStyle->mPositionType != positionType) {
+ mCssStyle->mPositionType = positionType;
+ markDirty();
+ }
+ }
+
+ inline WXCorePositionType getStypePositionType() const {
+ return mCssStyle->mPositionType;
+ }
+
+
+ /** ================================ position =================================== **/
+
+ inline float getStylePositionTop() const {
+ return mCssStyle->mStylePosition.getPosition(kPositionEdgeTop);
+ }
+
+ inline float getStylePositionBottom() const {
+ return mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom);
+ }
+
+ inline float getStylePositionLeft() const {
+ return mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft);
+ }
+
+ inline float getStylePositionRight() const {
+ return mCssStyle->mStylePosition.getPosition(kPositionEdgeRight);
+ }
+
+ inline void setStylePosition(const WXCorePositionEdge edge, const float positionRight) {
+ if (mCssStyle->mStylePosition.setPosition(edge, positionRight))
+ markDirty();
+ }
+
+
+ /** ================================ dimension =================================== **/
+
+ inline void setStyleWidthLevel(const DimensionLevel level) const {
+ if (mCssStyle->mStyleWidthLevel != level) {
+ mCssStyle->mStyleWidthLevel = level;
+ }
+ }
+
+ inline void setStyleHeightLevel(const DimensionLevel level) const {
+ if (mCssStyle->mStyleHeightLevel != level) {
+ mCssStyle->mStyleHeightLevel = level;
+ }
+ }
+
+ inline DimensionLevel getStyleHeightLevel() const {
+ return mCssStyle->mStyleHeightLevel;
+ }
+
+ inline DimensionLevel getStyleWidthLevel() const {
+ return mCssStyle->mStyleWidthLevel;
+ }
+
+ inline void setStyleWidth(const float width, const bool updating) {
+ if (mCssStyle->mStyleWidth != width) {
+ mCssStyle->mStyleWidth = width;
+ markDirty();
+ if(updating) {
+ markChildrenDirty(true);
+ }
+ }
+ }
+
+ inline void setStyleWidthToNAN() {
+ if (!isnan(mCssStyle->mStyleWidth)) {
+ mCssStyle->mStyleWidth = NAN;
+ markDirty();
+ markChildrenDirty(true);
+ }
+ }
+
+ inline float getStyleWidth() const {
+ return mCssStyle->mStyleWidth;
+ }
+
+ inline void setStyleHeight(const float height) {
+ if (mCssStyle->mStyleHeight != height) {
+ mCssStyle->mStyleHeight = height;
+ markDirty();
+ }
+ }
+
+ inline float getStyleHeight() const {
+ return mCssStyle->mStyleHeight;
+ }
+
+ inline void setMinWidth(const float minWidth, const bool updating) {
+ if (mCssStyle->mMinWidth != minWidth) {
+ mCssStyle->mMinWidth = minWidth;
+ markDirty();
+ if(updating) {
+ markChildrenDirty(true);
+ }
+ }
+ }
+
+ inline float getMinWidth() const {
+ return mCssStyle->mMinWidth;
+ }
+
+ inline void setMaxWidth(const float maxWidth, const bool updating) {
+ if (mCssStyle->mMaxWidth != maxWidth) {
+ mCssStyle->mMaxWidth = maxWidth;
+ markDirty();
+ if(updating) {
+ markChildrenDirty(true);
+ }
+ }
+ }
+
+ inline float getMaxWidth() const {
+ return mCssStyle->mMaxWidth;
+ }
+
+ inline void setMinHeight(const float minHeight) {
+ if (mCssStyle->mMinHeight != minHeight) {
+ mCssStyle->mMinHeight = minHeight;
+ markDirty();
+ }
+ }
+
+ inline float getMinHeight() const {
+ return mCssStyle->mMinHeight;
+ }
+
+ inline void setMaxHeight(const float maxHeight) {
+ if (mCssStyle->mMaxHeight != maxHeight) {
+ mCssStyle->mMaxHeight = maxHeight;
+ markDirty();
+ }
+ }
+
+ inline float getMaxHeight() const {
+ return mCssStyle->mMaxHeight;
+ }
+
+
+ /** ================================ flex-style =================================== **/
+
+ inline void setFlexDirection(const WXCoreFlexDirection flexDirection, const bool updating) {
+ if (mCssStyle->mFlexDirection != flexDirection) {
+ mCssStyle->mFlexDirection = flexDirection;
+ markDirty();
+ if (updating) {
+ for (auto it = ChildListIterBegin(); it != ChildListIterEnd(); it++) {
+ (*it)->markDirty(false);
+ }
+ }
+ }
+ }
+
+ inline WXCoreFlexDirection getFlexDirection() const {
+ return mCssStyle->mFlexDirection;
+ }
+
+ inline void setFlexWrap(const WXCoreFlexWrap flexWrap) {
+ if (mCssStyle->mFlexWrap != flexWrap) {
+ mCssStyle->mFlexWrap = flexWrap;
+ markDirty();
+ }
+ }
+
+ inline WXCoreFlexWrap getFlexWrap() const {
+ return mCssStyle->mFlexWrap;
+ }
+
+ inline void setJustifyContent(const WXCoreJustifyContent justifyContent) {
+ if (mCssStyle->mJustifyContent != justifyContent) {
+ mCssStyle->mJustifyContent = justifyContent;
+ }
+ }
+
+ inline WXCoreJustifyContent getJustifyContent() const {
+ return mCssStyle->mJustifyContent;
+ }
+
+ inline void setAlignItems(const WXCoreAlignItems alignItems) {
+ if (mCssStyle->mAlignItems != alignItems) {
+ mCssStyle->mAlignItems = alignItems;
+ markDirty();
+ }
+ }
+
+ inline WXCoreAlignItems getAlignItems() const {
+ return mCssStyle->mAlignItems;
+ }
+
+ inline void setAlignSelf(const WXCoreAlignSelf alignSelf) {
+ if (mCssStyle->mAlignSelf != alignSelf) {
+ mCssStyle->mAlignSelf = alignSelf;
+ markDirty();
+ }
+ }
+
+ inline WXCoreAlignSelf getAlignSelf() const {
+ return mCssStyle->mAlignSelf;
+ }
+
+ virtual void setFlex(const float flex) {
+ if (mCssStyle->mFlexGrow != flex) {
+ mCssStyle->mFlexGrow = flex;
+ markDirty();
+ }
+ }
+
+ inline float getFlex() const {
+ return mCssStyle->mFlexGrow;
+ }
+
+ /** ================================ layout-result =================================== **/
+
+ inline float getLayoutWidth() const {
+ return mLayoutResult->mLayoutSize.width;
+ }
+
+ inline float getLayoutHeight() const {
+ return mLayoutResult->mLayoutSize.height;
+ }
+
+ inline float getLayoutPositionTop() const {
+ return mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeTop);
+ }
+
+ inline float getLayoutPositionBottom() const {
+ return mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeBottom);
+ }
+
+ inline float getLayoutPositionLeft() const {
+ return mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeLeft);
+ }
+
+ inline float getLayoutPositionRight() const {
+ return mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeRight);
+ }
+
+ inline bool hasNewLayout() const {
+ return mHasNewLayout;
+ }
+
+ inline bool isDirty() const {
+ return dirty;
+ }
+
+ inline void markDirty(const bool recursion = true) {
+ if (!isDirty()) {
+ dirty = true;
+ if (getParent() != nullptr && recursion) {
+ getParent()->markDirty();
+ }
+ }
+ }
+
+ bool markChildrenDirty(const bool updatedNode = false) {
+ bool ret = false;
+ if(getChildCount() == 0){
+ if(measureFunc!= nullptr){
+ ret = true;
+ }
+ }
+ else {
+ //isnan(mCssStyle->mStyleWidth) XOR updatedNode
+ if(isnan(mCssStyle->mStyleWidth) != updatedNode){
+ for (auto it = ChildListIterBegin(); it != ChildListIterEnd(); it++) {
+ ret = ((*it)->markChildrenDirty() || ret) ;
+ }
+ }
+ }
+ dirty = ret || dirty;
+ return ret;
+ }
+
+ inline void setHasNewLayout(const bool hasNewLayout) {
+ this->mHasNewLayout = hasNewLayout;
+ }
+
+ inline float getLargestMainSize() const {
+ float largestSize = 0;
+ for (WXCoreFlexLine *flexLine : mFlexLines) {
+ largestSize = std::max(largestSize, flexLine->mMainSize);
+ }
+ return largestSize + sumPaddingBorderAlongAxis(this, isMainAxisHorizontal(this));
+ }
+ };
+}
+#endif //WEEXCORE_FLEXLAYOUT_WXCORELAYOUTNODE_H
+#endif
[13/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.mm b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.mm
new file mode 100644
index 0000000..8406cfe
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.mm
@@ -0,0 +1,768 @@
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXRecyclerComponent.h"
+#import "WXComponent_internal.h"
+#import "WXSDKInstance_private.h"
+#import "WXRecyclerDataController.h"
+#import "WXRecyclerUpdateController.h"
+#import "WXMultiColumnLayout.h"
+#import "WXHeaderComponent.h"
+#import "WXFooterComponent.h"
+#import "WXCellComponent.h"
+#import "WXAssert.h"
+#import "WXConvert.h"
+#import "WXUtility.h"
+#import "WXMonitor.h"
+#import "NSObject+WXSwizzle.h"
+#import "WXComponent+Events.h"
+#import "WXRecyclerDragController.h"
+#import "WXComponent+Layout.h"
+#import "WXScrollerComponent+Layout.h"
+
+static NSString * const kCollectionCellReuseIdentifier = @"WXRecyclerCell";
+static NSString * const kCollectionHeaderReuseIdentifier = @"WXRecyclerHeader";
+static float const kRecyclerNormalColumnGap = 32;
+
+typedef enum : NSUInteger {
+ WXRecyclerLayoutTypeMultiColumn,
+ WXRecyclerLayoutTypeFlex,
+ WXRecyclerLayoutTypeGrid,
+} WXRecyclerLayoutType;
+
+@interface WXCollectionView : UICollectionView
+
+@end
+
+@implementation WXCollectionView
+
+- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index
+{
+ [super insertSubview:view atIndex:index];
+}
+
+- (void)layoutSubviews
+{
+ [super layoutSubviews];
+ [self.wx_component layoutDidFinish];
+}
+
+- (void)setContentOffset:(CGPoint)contentOffset
+{
+ // FIXME: side effect caused by hooking _adjustContentOffsetIfNecessary.
+ // When UICollectionView is pulled down and finger releases,contentOffset will be set from -xxxx to about -0.5(greater than -0.5), then contentOffset will be reset to zero by calling _adjustContentOffsetIfNecessary.
+ // So hooking _adjustContentOffsetIfNecessary will always cause remaining 1px space between list's top and navigator.
+ // Demo: http://dotwe.org/895630945793a9a044e49abe39cbb77f
+ // Have to reset contentOffset to zero manually here.
+ if (fabs(contentOffset.y) < 0.5) {
+ contentOffset.y = 0;
+ }
+ if (isnan(contentOffset.x)) {
+ contentOffset.x = 0;
+ }
+ if(isnan(contentOffset.y)) {
+ contentOffset.y = 0;
+ }
+
+ [super setContentOffset:contentOffset];
+}
+
+@end
+
+@interface WXCollectionViewCell : UICollectionViewCell
+
+@end
+
+@implementation WXCollectionViewCell
+
+- (void)prepareForReuse
+{
+ [super prepareForReuse];
+
+ WXCellComponent *cellComponent = (WXCellComponent *)self.wx_component;
+ if (cellComponent.isRecycle && [cellComponent isViewLoaded] && [self.contentView.subviews containsObject:cellComponent.view]) {
+ [cellComponent _unloadViewWithReusing:YES];
+ }
+}
+
+@end
+
+@interface WXRecyclerComponent () <UICollectionViewDataSource, UICollectionViewDelegate, WXMultiColumnLayoutDelegate, WXRecyclerUpdateControllerDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate, WXRecyclerDragControllerDelegate>
+
+@property (nonatomic, strong, readonly) WXRecyclerDataController *dataController;
+@property (nonatomic, strong, readonly) WXRecyclerUpdateController *updateController;
+@property (nonatomic, weak, readonly) UICollectionView *collectionView;
+@property (nonatomic, strong) WXRecyclerDragController *dragController;
+
+@end
+
+@implementation WXRecyclerComponent
+{
+ WXRecyclerLayoutType _layoutType;
+ UICollectionViewLayout *_collectionViewlayout;
+
+ UIEdgeInsets _padding;
+ NSUInteger _previousLoadMoreCellNumber;
+}
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+ if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
+ [self _fillPadding];
+
+ if ([type isEqualToString:@"waterfall"] || (attributes[@"layout"] && [attributes[@"layout"] isEqualToString:@"multi-column"])) {
+ // TODO: abstraction
+ _layoutType = WXRecyclerLayoutTypeMultiColumn;
+ CGFloat scaleFactor = weexInstance.pixelScaleFactor;
+ _collectionViewlayout = [WXMultiColumnLayout new];
+ WXMultiColumnLayout *layout = (WXMultiColumnLayout *)_collectionViewlayout;
+ layout.columnWidth = [WXConvert WXLength:attributes[@"columnWidth"] isFloat:YES scaleFactor:scaleFactor] ? : [WXLength lengthWithFloat:0.0 type:WXLengthTypeAuto];
+ layout.columnCount = [WXConvert WXLength:attributes[@"columnCount"] isFloat:NO scaleFactor:1.0] ? : [WXLength lengthWithInt:1 type:WXLengthTypeFixed];
+ if (attributes[@"leftGap"]) {
+ layout.leftGap = [WXConvert WXPixelType:attributes[@"leftGap"] scaleFactor:scaleFactor];
+ }
+ if (attributes[@"rightGap"]) {
+ layout.rightGap = [WXConvert WXPixelType:attributes[@"rightGap"] scaleFactor:scaleFactor];
+ }
+ layout.columnGap = [self _floatValueForColumnGap:([WXConvert WXLength:attributes[@"columnGap"] isFloat:YES scaleFactor:scaleFactor] ? : [WXLength lengthWithFloat:0.0 type:WXLengthTypeNormal])];
+
+ layout.delegate = self;
+ } else {
+ _collectionViewlayout = [UICollectionViewLayout new];
+ }
+
+ _dataController = [WXRecyclerDataController new];
+ _updateController = [WXRecyclerUpdateController new];
+ _updateController.delegate = self;
+ [self fixFlicker];
+
+ if ([attributes[@"draggable"] boolValue]) {
+ // lazy load
+ _dragController = [WXRecyclerDragController new];
+ _dragController.delegate = self;
+ if([attributes[@"dragTriggerType"] isEqual: @"pan"]){
+ _dragController.dragTriggerType = WXRecyclerDragTriggerPan;
+ }
+ _dragController.isDragable = YES;
+ }
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ _collectionView.delegate = nil;
+ _collectionView.dataSource = nil;
+}
+
+#pragma mark - Public Subclass Methods
+
+- (UIView *)loadView
+{
+ return [[WXCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_collectionViewlayout];
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ _collectionView = (UICollectionView *)self.view;
+ _collectionView.allowsSelection = NO;
+ _collectionView.allowsMultipleSelection = NO;
+ _collectionView.dataSource = self;
+ _collectionView.delegate = self;
+
+ [_collectionView registerClass:[WXCollectionViewCell class] forCellWithReuseIdentifier:kCollectionCellReuseIdentifier];
+ [_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:kCollectionSupplementaryViewKindHeader withReuseIdentifier:kCollectionHeaderReuseIdentifier];
+
+ _dragController.dragingCell = [[WXCollectionViewCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100/2.0f)];
+ _dragController.collectionView = _collectionView;
+
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+
+ }];
+}
+
+- (void)viewWillUnload
+{
+ [super viewWillUnload];
+
+ _collectionView.dataSource = nil;
+ _collectionView.delegate = nil;
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ [super updateAttributes:attributes];
+
+ if (_layoutType == WXRecyclerLayoutTypeMultiColumn) {
+ CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
+ WXMultiColumnLayout *layout = (WXMultiColumnLayout *)_collectionViewlayout;
+ BOOL needUpdateLayout = NO;
+
+ if ([attributes[@"draggable"] boolValue]) {
+ if (!_dragController) { // lazy load
+ _dragController = [WXRecyclerDragController new];
+ _dragController.delegate = self;
+ }
+ if([attributes[@"dragTriggerType"] isEqual: @"pan"]){
+ _dragController.dragTriggerType = WXRecyclerDragTriggerPan;
+ }
+ _dragController.isDragable = YES;
+ } else {
+ _dragController.isDragable = NO;
+ }
+
+ if (attributes[@"columnWidth"]) {
+ layout.columnWidth = [WXConvert WXLength:attributes[@"columnWidth"] isFloat:YES scaleFactor:scaleFactor];
+ needUpdateLayout = YES;
+ }
+
+ if (attributes[@"columnCount"]) {
+ layout.columnCount = [WXConvert WXLength:attributes[@"columnCount"] isFloat:NO scaleFactor:1.0];
+
+ needUpdateLayout = YES;
+ }
+ if (attributes[@"columnGap"]) {
+ layout.columnGap = [self _floatValueForColumnGap:([WXConvert WXLength:attributes[@"columnGap"] isFloat:YES scaleFactor:scaleFactor])];
+ needUpdateLayout = YES;
+ }
+ if (attributes[@"leftGap"]) {
+ layout.leftGap = [WXConvert WXPixelType:attributes[@"leftGap"] scaleFactor:scaleFactor];
+ }
+ if (attributes[@"rightGap"]) {
+ layout.rightGap = [WXConvert WXPixelType:attributes[@"rightGap"] scaleFactor:scaleFactor];
+ }
+
+ if (needUpdateLayout) {
+ for (WXComponent *component in self.subcomponents) {
+ [component setNeedsLayout];
+ }
+
+ [self.collectionView reloadData];
+ [self.collectionView.collectionViewLayout invalidateLayout];
+ }
+ }
+
+}
+
+- (void)setContentSize:(CGSize)contentSize
+{
+ // Do Nothing
+}
+
+- (void)adjustSticky
+{
+ // Do Nothing, sticky is adjusted by layout
+}
+
+#pragma mark - Private Subclass Methods
+
+- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles
+{
+ [super _updateStylesOnComponentThread:styles resetStyles:resetStyles isUpdateStyles:isUpdateStyles];
+
+ [self _fillPadding];
+}
+
+- (void)_handleFirstScreenTime
+{
+ // Do Nothing, firstScreenTime is set by cellDidRendered:
+}
+
+- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated
+{
+ CGPoint contentOffset = _collectionView.contentOffset;
+ CGFloat contentOffsetY = 0;
+
+ CGRect rect;
+ while (component) {
+ if ([component isKindOfClass:[WXCellComponent class]]) {
+ NSIndexPath *toIndexPath = [self.dataController indexPathForCell:(WXCellComponent *)component];
+ UICollectionViewLayoutAttributes *attributes = [_collectionView layoutAttributesForItemAtIndexPath:toIndexPath];
+ rect = attributes.frame;
+ break;
+ }
+ if ([component isKindOfClass:[WXHeaderComponent class]]) {
+ NSUInteger toIndex = [self.dataController indexForHeader:(WXHeaderComponent *)component];
+ UICollectionViewLayoutAttributes *attributes = [_collectionView layoutAttributesForSupplementaryElementOfKind:kCollectionSupplementaryViewKindHeader atIndexPath:[NSIndexPath indexPathWithIndex:toIndex]];
+ rect = attributes.frame;
+ break;
+ }
+ contentOffsetY += component.calculatedFrame.origin.y;
+ component = component.supercomponent;
+ }
+
+ contentOffsetY += rect.origin.y;
+ contentOffsetY += offset * self.weexInstance.pixelScaleFactor;
+
+ if (_collectionView.contentSize.height >= _collectionView.frame.size.height && contentOffsetY > _collectionView.contentSize.height - _collectionView.frame.size.height) {
+ contentOffset.y = _collectionView.contentSize.height - _collectionView.frame.size.height;
+ } else {
+ contentOffset.y = contentOffsetY;
+ }
+
+ [_collectionView setContentOffset:contentOffset animated:animated];
+
+}
+
+- (void)performUpdatesWithCompletion:(void (^)(BOOL finished))completion
+{
+ WXAssertMainThread();
+
+ //TODO: support completion
+
+ if (![self isViewLoaded]) {
+ completion(NO);
+ }
+
+ NSArray *oldData = [self.dataController.sections copy];
+ NSArray *newData = [self _sectionArrayFromComponents:self.subcomponents];
+
+ [_updateController performUpdatesWithNewData:newData oldData:oldData view:_collectionView];
+}
+
+- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ if ([subcomponent isKindOfClass:[WXCellComponent class]]) {
+ ((WXCellComponent *)subcomponent).delegate = self;
+ } else if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
+ ((WXHeaderComponent *)subcomponent).delegate = self;
+ }
+
+ [super _insertSubcomponent:subcomponent atIndex:index];
+
+ if (![subcomponent isKindOfClass:[WXHeaderComponent class]]
+ && ![subcomponent isKindOfClass:[WXCellComponent class]]) {
+ return;
+ }
+
+ WXPerformBlockOnMainThread(^{
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+
+ }];
+ });
+}
+
+- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ //Here will not insert cell/header/footer's view again
+ if (![subcomponent isKindOfClass:[WXCellComponent class]]
+ && ![subcomponent isKindOfClass:[WXHeaderComponent class]]
+ && ![subcomponent isKindOfClass:[WXFooterComponent class]]) {
+ [super insertSubview:subcomponent atIndex:index];
+ }
+}
+
+#pragma mark - WXRecyclerUpdateControllerDelegate
+
+- (void)updateController:(WXRecyclerUpdateController *)controller willPerformUpdateWithNewData:(NSArray<WXSectionDataController *> *)newData
+{
+ if (newData) {
+ [self.dataController updateData:newData];
+ }
+}
+
+- (void)updateController:(WXRecyclerUpdateController *)controller didPerformUpdateWithFinished:(BOOL)finished
+{
+
+}
+
+#pragma mark - UICollectionViewDataSource
+
+- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
+{
+ WXLogDebug(@"section number:%li", (long)[self.dataController numberOfSections]);
+ return [self.dataController numberOfSections];
+}
+
+- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
+{
+ NSInteger numberOfItems = [self.dataController numberOfItemsInSection:section];
+
+ WXLogDebug(@"Number of items is %ld in section:%ld", (long)numberOfItems, (long)section);
+
+ return numberOfItems;
+}
+
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXLogDebug(@"Getting cell at indexPath:%@", indexPath);
+
+ WXCollectionViewCell *cellView = [_collectionView dequeueReusableCellWithReuseIdentifier:kCollectionCellReuseIdentifier forIndexPath:indexPath];
+
+ UIView *contentView = [self.dataController cellForItemAtIndexPath:indexPath];
+
+ cellView.wx_component = contentView.wx_component;
+
+ [self.dragController goThroughAnchor:cellView.wx_component indexPath:indexPath];
+
+ if (contentView.superview == cellView.contentView) {
+ return cellView;
+ }
+
+ for (UIView *view in cellView.contentView.subviews) {
+ [view removeFromSuperview];
+ }
+
+ [cellView.contentView addSubview:contentView];
+ [cellView setAccessibilityIdentifier:contentView.accessibilityIdentifier];
+
+ return cellView;
+}
+
+- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
+{
+ UICollectionReusableView *reusableView = nil;
+ if ([kind isEqualToString:kCollectionSupplementaryViewKindHeader]) {
+ reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kCollectionHeaderReuseIdentifier forIndexPath:indexPath];
+ UIView *contentView = [self.dataController viewForHeaderAtIndexPath:indexPath];
+ if (contentView.superview != reusableView) {
+ for (UIView *view in reusableView.subviews) {
+ [view removeFromSuperview];
+ }
+
+ [reusableView addSubview:contentView];
+ }
+ }
+
+ return reusableView;
+}
+
+#pragma mark - UICollectionViewDelegate
+
+- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXLogDebug(@"will display cell:%@, at index path:%@", cell, indexPath);
+}
+
+- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath);
+}
+
+#pragma mark - WXMultiColumnLayoutDelegate
+
+- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView insetForLayout:(UICollectionViewLayout *)collectionViewLayout
+{
+ return _padding;
+}
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView contentWidthForLayout:(UICollectionViewLayout *)collectionViewLayout
+{
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
+ }
+//#else
+ else
+ {
+ return self.flexScrollerCSSNode->getStyleWidth();
+ }
+//#endif
+}
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ CGSize itemSize = [self.dataController sizeForItemAtIndexPath:indexPath];
+ return itemSize.height;
+}
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForHeaderInSection:(NSInteger)section
+{
+ CGSize headerSize = [self.dataController sizeForHeaderAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
+ return headerSize.height;
+}
+
+- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout hasHeaderInSection:(NSInteger)section
+{
+ return [self.dataController hasHeaderInSection:section];
+}
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout isNeedStickyForHeaderInSection:(NSInteger)section
+{
+ return [self.dataController isStickyForHeaderAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
+}
+
+#pragma mark - WXHeaderRenderDelegate
+
+- (float)headerWidthForLayout:(WXHeaderComponent *)header
+{
+ if (_layoutType == WXRecyclerLayoutTypeMultiColumn) {
+ return ((WXMultiColumnLayout *)_collectionViewlayout).computedHeaderWidth;
+ }
+
+ return 0.0;
+}
+
+- (void)headerDidLayout:(WXHeaderComponent *)header
+{
+ WXPerformBlockOnMainThread(^{
+ [self.collectionView.collectionViewLayout invalidateLayout];
+ });
+}
+
+- (void)headerDidRemove:(WXHeaderComponent *)header
+{
+ WXPerformBlockOnMainThread(^{
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+
+ }];
+ });
+}
+
+#pragma mark - WXCellRenderDelegate
+
+- (float)containerWidthForLayout:(WXCellComponent *)cell
+{
+ if (_layoutType == WXRecyclerLayoutTypeMultiColumn) {
+ return ((WXMultiColumnLayout *)_collectionViewlayout).computedColumnWidth;
+ }
+
+ return 0.0;
+}
+
+- (void)cellDidLayout:(WXCellComponent *)cell
+{
+ BOOL previousLayoutComplete = cell.isLayoutComplete;
+ cell.isLayoutComplete = YES;
+ WXPerformBlockOnMainThread(^{
+ if (previousLayoutComplete) {
+ [self.updateController reloadItemsAtIndexPath:[self.dataController indexPathForCell:cell]];
+ } else {
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+ }];
+ }
+ });
+}
+
+- (void)cellDidRendered:(WXCellComponent *)cell
+{
+ if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance) && !self.weexInstance.onRenderProgress) {
+ return;
+ }
+
+ NSIndexPath *indexPath = [self.dataController indexPathForCell:cell];
+
+ UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];
+ CGRect cellRect = attributes.frame;
+ if (cellRect.origin.y + cellRect.size.height >= _collectionView.frame.size.height) {
+ WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance);
+ }
+
+ if (self.weexInstance.onRenderProgress) {
+ CGRect renderRect = [_collectionView convertRect:cellRect toView:self.weexInstance.rootView];
+ self.weexInstance.onRenderProgress(renderRect);
+ }
+}
+
+- (void)cellDidRemove:(WXCellComponent *)cell
+{
+ if (cell.isLayoutComplete) {
+ WXPerformBlockOnMainThread(^{
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+ }];
+ });
+ }
+}
+
+- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index
+{
+ if (cell.isLayoutComplete) {
+ WXPerformBlockOnMainThread(^{
+ [self performUpdatesWithCompletion:^(BOOL finished) {
+ }];
+ });
+ }
+}
+
+#pragma mark - Load More Event
+
+- (void)setLoadmoreretry:(NSUInteger)loadmoreretry
+{
+ if (loadmoreretry != self.loadmoreretry) {
+ _previousLoadMoreCellNumber = 0;
+ }
+
+ [super setLoadmoreretry:loadmoreretry];
+}
+
+- (void)loadMore
+{
+ [super loadMore];
+
+ _previousLoadMoreCellNumber = [self totalNumberOfCells];
+}
+
+- (BOOL)isNeedLoadMore
+{
+ BOOL superNeedLoadMore = [super isNeedLoadMore];
+ return superNeedLoadMore && _previousLoadMoreCellNumber != [self totalNumberOfCells];
+}
+
+- (NSUInteger)totalNumberOfCells
+{
+ NSUInteger cellNumber = 0;
+ NSUInteger sectionCount = [_collectionView numberOfSections];
+ for (int section = 0; section < sectionCount; section ++) {
+ cellNumber += [_collectionView numberOfItemsInSection:section];
+ }
+
+ return cellNumber;
+}
+
+- (void)resetLoadmore{
+ [super resetLoadmore];
+ _previousLoadMoreCellNumber = 0;
+}
+
+#pragma mark - Private
+
+- (float)_floatValueForColumnGap:(WXLength *)gap
+{
+ if (gap.isNormal) {
+ return kRecyclerNormalColumnGap * self.weexInstance.pixelScaleFactor;
+ } else {
+ return gap.floatValue;
+ }
+}
+
+- (void)_fillPadding
+{
+ UIEdgeInsets padding;
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ padding = {
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_TOP] + self.cssNode->style.border[CSS_TOP]),
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_LEFT] + self.cssNode->style.border[CSS_LEFT]),
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_BOTTOM] + self.cssNode->style.border[CSS_BOTTOM]),
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_RIGHT] + self.cssNode->style.border[CSS_RIGHT])
+ };
+ }
+//#else
+ else
+ {
+ padding = {
+ WXFloorPixelValue(self.flexCssNode->getPaddingTop() + self.flexCssNode->getBorderWidthTop()),
+ WXFloorPixelValue(self.flexCssNode->getPaddingLeft() + self.flexCssNode->getBorderWidthLeft()),
+ WXFloorPixelValue(self.flexCssNode->getPaddingBottom() + self.flexCssNode->getBorderWidthBottom()),
+ WXFloorPixelValue(self.flexCssNode->getPaddingRight() + self.flexCssNode->getBorderWidthRight())
+ };
+ }
+//#endif
+
+
+ if (!UIEdgeInsetsEqualToEdgeInsets(padding, _padding)) {
+ _padding = padding;
+ [self setNeedsLayout];
+
+ for (WXComponent *component in self.subcomponents) {
+ [component setNeedsLayout];
+ }
+
+ if (_collectionView) {
+ WXPerformBlockOnMainThread(^{
+ [_collectionView.collectionViewLayout invalidateLayout];
+ });
+ }
+ }
+}
+
+- (NSArray<WXSectionDataController *> *)_sectionArrayFromComponents:(NSArray<WXComponent *> *)components
+{
+ NSMutableArray<WXSectionDataController *> *sectionArray = [NSMutableArray array];
+ NSMutableArray<WXCellComponent *> *cellArray = [NSMutableArray array];
+ WXSectionDataController *currentSection;
+
+ for (int i = 0; i < components.count; i++) {
+ if (!currentSection) {
+ currentSection = [WXSectionDataController new];
+ }
+
+ WXComponent* component = components[i];
+
+ if ([component isKindOfClass:[WXHeaderComponent class]]) {
+ if (i != 0 && (currentSection.headerComponent || cellArray.count > 0)) {
+ currentSection.cellComponents = [cellArray copy];
+ [sectionArray addObject:currentSection];
+ currentSection = [WXSectionDataController new];
+ [cellArray removeAllObjects];
+ }
+ currentSection.headerComponent = (WXHeaderComponent *)component;
+ } else if ([component isKindOfClass:[WXCellComponent class]]
+ && ((WXCellComponent *)component).isLayoutComplete) {
+ [cellArray addObject:(WXCellComponent *)component];
+ } else if ([component isKindOfClass:[WXFooterComponent class]]) {
+ currentSection.footerComponent = component;
+ } else {
+ continue;
+ }
+ }
+
+ if (cellArray.count > 0 || currentSection.headerComponent) {
+ currentSection.cellComponents = [cellArray copy];
+ [sectionArray addObject:currentSection];
+ }
+
+ return sectionArray;
+}
+
+- (void)fixFlicker
+{
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ // FIXME:(ง •̀_•́)ง┻━┻ Stupid scoll view, always reset content offset to zero by calling _adjustContentOffsetIfNecessary after insert cells.
+ // So if you pull down list while list is rendering, the list will be flickering.
+ // Demo:
+ // Have to hook _adjustContentOffsetIfNecessary here.
+ // Any other more elegant way?
+ NSString *a = @"ntOffsetIfNe";
+ NSString *b = @"adjustConte";
+
+ NSString *originSelector = [NSString stringWithFormat:@"_%@%@cessary", b, a];
+ [[self class] weex_swizzle:[WXCollectionView class] Method:NSSelectorFromString(originSelector) withMethod:@selector(fixedFlickerSelector)];
+ });
+}
+
+#define mark dragControllerDelegate
+
+- (void)updateDataSource{
+ NSMutableArray *oldComponents = [[NSMutableArray alloc] initWithArray:self.dataController.sections[self.dragController.startIndexPath.section].cellComponents];
+ if(oldComponents.count > 1){
+ WXCellComponent *startComponent = self.dataController.sections[self.dragController.startIndexPath.section].cellComponents[self.dragController.startIndexPath.item];
+ [oldComponents removeObject:startComponent];
+ [oldComponents insertObject:startComponent atIndex:self.dragController.targetIndexPath.item];
+ self.dataController.sections[self.dragController.startIndexPath.section].cellComponents = oldComponents;
+ }
+}
+
+- (void)dragFireEvent:(NSString *)eventName params:(NSDictionary *)params{
+ [self fireEvent:eventName params:params];
+}
+
+- (void)fixedFlickerSelector
+{
+ // DO NOT delete this method.
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m
deleted file mode 100644
index b8feb31..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.m
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXSDKInstance.h"
-#import "WXLog.h"
-#import "WXCellComponent.h"
-#import "WXListComponent.h"
-#import "WXComponent_internal.h"
-#import "WXDiffUtil.h"
-
-@interface WXCellComponent ()
-
-@end
-
-@implementation WXCellComponent
-{
- NSIndexPath *_indexPathBeforeMove;
- BOOL _isUseContainerWidth;
-}
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
- self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
-
- if (self) {
- _async = attributes[@"async"] ? [WXConvert BOOL:attributes[@"async"]] : YES;
- _isRecycle = attributes[@"recycle"] ? [WXConvert BOOL:attributes[@"recycle"]] : YES;
- _insertAnimation = [WXConvert UITableViewRowAnimation:attributes[@"insertAnimation"]];
- _deleteAnimation = [WXConvert UITableViewRowAnimation:attributes[@"deleteAnimation"]];
- _keepScrollPosition = attributes[@"keepScrollPosition"] ? [WXConvert BOOL:attributes[@"keepScrollPosition"]] : NO;
- _lazyCreateView = YES;
- _isNeedJoinLayoutSystem = NO;
- if (attributes[@"zIndex"]) {
- _zIndex = [WXConvert NSString:attributes[@"zIndex"]];
- }
- }
-
- return self;
-}
-
-- (void)dealloc
-{
-
-}
-
-- (BOOL)weex_isEqualTo:(id<WXDiffable>)object
-{
- return self == object;
-}
-
-- (void)_frameDidCalculated:(BOOL)isChanged
-{
- [super _frameDidCalculated:isChanged];
-
- if (isChanged) {
- [self.delegate cellDidLayout:self];
- }
-}
-
-- (void)didFinishDrawingLayer:(BOOL)success
-{
- [self.delegate cellDidRendered:self];
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- if (attributes[@"async"]) {
- _async = [WXConvert BOOL:attributes[@"async"]];
- }
-
- if (attributes[@"recycle"]) {
- _isRecycle = [WXConvert BOOL:attributes[@"recycle"]];
- }
-
- if (attributes[@"insertAnimation"]) {
- _insertAnimation = [WXConvert UITableViewRowAnimation:attributes[@"insertAnimation"]];
- }
-
- if (attributes[@"deleteAnimation"]) {
- _deleteAnimation = [WXConvert UITableViewRowAnimation:attributes[@"deleteAnimation"]];
- }
-
- if (attributes[@"keepScrollPosition"]) {
- _keepScrollPosition = [WXConvert BOOL:attributes[@"keepScrollPosition"]];
- }
-}
-
-- (void)_moveToSupercomponent:(WXComponent *)newSupercomponent atIndex:(NSUInteger)index
-{
- if (self.delegate == (id<WXCellRenderDelegate>)newSupercomponent) {
- [self.delegate cell:self didMoveToIndex:index];
- [super _removeFromSupercomponent];
- [newSupercomponent _insertSubcomponent:self atIndex:index];
- } else {
- [super _moveToSupercomponent:newSupercomponent atIndex:index];
- }
-}
-
-- (void)_removeFromSupercomponent
-{
- [super _removeFromSupercomponent];
-
- [self.delegate cellDidRemove:self];
-}
-
-- (void)removeFromSuperview
-{
- // do nothing
-}
-
-- (void)_calculateFrameWithSuperAbsolutePosition:(CGPoint)superAbsolutePosition gatherDirtyComponents:(NSMutableSet<WXComponent *> *)dirtyComponents
-{
- if (self.delegate && (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH]) || _isUseContainerWidth)) {
- self.cssNode->style.dimensions[CSS_WIDTH] = [self.delegate containerWidthForLayout:self];
- //TODO: set _isUseContainerWidth to NO if updateStyles have width
- _isUseContainerWidth = YES;
- }
-
- if ([self needsLayout]) {
- layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
- if ([WXLog logLevel] >= WXLogLevelDebug) {
- print_css_node(self.cssNode, CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN);
- }
- }
-
- [super _calculateFrameWithSuperAbsolutePosition:superAbsolutePosition gatherDirtyComponents:dirtyComponents];
-}
-@end
-
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.mm
new file mode 100644
index 0000000..62a542c
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.mm
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXSDKInstance.h"
+#import "WXLog.h"
+#import "WXCellComponent.h"
+#import "WXListComponent.h"
+#import "WXComponent_internal.h"
+#import "WXDiffUtil.h"
+#import "WXComponent+Layout.h"
+
+@interface WXCellComponent ()
+
+@end
+
+@implementation WXCellComponent
+{
+ NSIndexPath *_indexPathBeforeMove;
+ BOOL _isUseContainerWidth;
+}
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> init Cell: ref:%@, styles:%@",ref,styles);
+#endif
+
+ self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
+
+ if (self) {
+ _async = attributes[@"async"] ? [WXConvert BOOL:attributes[@"async"]] : YES;
+ _isRecycle = attributes[@"recycle"] ? [WXConvert BOOL:attributes[@"recycle"]] : YES;
+ _insertAnimation = [WXConvert UITableViewRowAnimation:attributes[@"insertAnimation"]];
+ _deleteAnimation = [WXConvert UITableViewRowAnimation:attributes[@"deleteAnimation"]];
+ _keepScrollPosition = attributes[@"keepScrollPosition"] ? [WXConvert BOOL:attributes[@"keepScrollPosition"]] : NO;
+ _lazyCreateView = YES;
+ _isNeedJoinLayoutSystem = NO;
+ if (attributes[@"zIndex"]) {
+ _zIndex = [WXConvert NSString:attributes[@"zIndex"]];
+ }
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+
+}
+
+- (BOOL)weex_isEqualTo:(id<WXDiffable>)object
+{
+ return self == object;
+}
+
+- (void)_frameDidCalculated:(BOOL)isChanged
+{
+ [super _frameDidCalculated:isChanged];
+
+ if (isChanged) {
+ [self.delegate cellDidLayout:self];
+ }
+}
+
+- (void)didFinishDrawingLayer:(BOOL)success
+{
+ [self.delegate cellDidRendered:self];
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ if (attributes[@"async"]) {
+ _async = [WXConvert BOOL:attributes[@"async"]];
+ }
+
+ if (attributes[@"recycle"]) {
+ _isRecycle = [WXConvert BOOL:attributes[@"recycle"]];
+ }
+
+ if (attributes[@"insertAnimation"]) {
+ _insertAnimation = [WXConvert UITableViewRowAnimation:attributes[@"insertAnimation"]];
+ }
+
+ if (attributes[@"deleteAnimation"]) {
+ _deleteAnimation = [WXConvert UITableViewRowAnimation:attributes[@"deleteAnimation"]];
+ }
+
+ if (attributes[@"keepScrollPosition"]) {
+ _keepScrollPosition = [WXConvert BOOL:attributes[@"keepScrollPosition"]];
+ }
+}
+
+- (void)_moveToSupercomponent:(WXComponent *)newSupercomponent atIndex:(NSUInteger)index
+{
+ if (self.delegate == (id<WXCellRenderDelegate>)newSupercomponent) {
+ [self.delegate cell:self didMoveToIndex:index];
+ [super _removeFromSupercomponent];
+ [newSupercomponent _insertSubcomponent:self atIndex:index];
+ } else {
+ [super _moveToSupercomponent:newSupercomponent atIndex:index];
+ }
+}
+
+- (void)_removeFromSupercomponent
+{
+ [super _removeFromSupercomponent];
+
+ [self.delegate cellDidRemove:self];
+}
+
+- (void)removeFromSuperview
+{
+ // do nothing
+}
+
+- (void)_calculateFrameWithSuperAbsolutePosition:(CGPoint)superAbsolutePosition gatherDirtyComponents:(NSMutableSet<WXComponent *> *)dirtyComponents
+{
+
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ if (self.delegate && (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH]) || _isUseContainerWidth)) {
+ self.cssNode->style.dimensions[CSS_WIDTH] = [self.delegate containerWidthForLayout:self];
+ //TODO: set _isUseContainerWidth to NO if updateStyles have width
+ _isUseContainerWidth = YES;
+ }
+
+ if ([self needsLayout]) {
+ layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
+ if ([WXLog logLevel] >= WXLogLevelDebug) {
+ print_css_node(self.cssNode, (css_print_options_t)(CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN));
+ }
+ }
+ }
+
+//#else
+ else
+ {
+ if (self.delegate && (flexIsUndefined(self.flexCssNode->getStyleWidth()) || _isUseContainerWidth)) {
+ self.flexCssNode->setStyleWidth([self.delegate containerWidthForLayout:self],NO);
+ _isUseContainerWidth = YES;
+ }
+
+ if ([self needsLayout]) {
+ std::pair<float, float> renderPageSize;
+ renderPageSize.first = self.weexInstance.frame.size.width;
+ renderPageSize.second = self.weexInstance.frame.size.height;
+ self.flexCssNode->calculateLayout(renderPageSize);
+ if ([WXLog logLevel] >= WXLogLevelDebug) {
+
+ }
+ }
+ }
+
+//#endif
+ [super _calculateFrameWithSuperAbsolutePosition:superAbsolutePosition gatherDirtyComponents:dirtyComponents];
+}
+@end
+
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
index bf54a91..5f63065 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
@@ -36,15 +36,6 @@ typedef id (^WXDataBindingBlock)(NSDictionary *data, BOOL *needUpdate);
@package
NSString *_type;
NSMutableArray *_subcomponents;
- /**
- * Layout
- */
- css_node_t *_cssNode;
- BOOL _isLayoutDirty;
- CGRect _calculatedFrame;
- CGPoint _absolutePosition;
- WXPositionType _positionType;
-
//Transition
WXTransition *_transition;
@@ -212,6 +203,8 @@ typedef id (^WXDataBindingBlock)(NSDictionary *data, BOOL *needUpdate);
- (void)_initCSSNodeWithStyles:(NSDictionary *)styles;
+- (void)_initFlexCssNodeWithStyles:(NSDictionary *)styles;
+
- (void)_updateCSSNodeStyles:(NSDictionary *)styles;
- (void)_resetCSSNodeStyles:(NSArray *)styles;
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.m
deleted file mode 100644
index 929dd1a..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.m
+++ /dev/null
@@ -1,681 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXCycleSliderComponent.h"
-#import "WXIndicatorComponent.h"
-#import "WXComponent_internal.h"
-#import "NSTimer+Weex.h"
-#import "WXSDKManager.h"
-#import "WXUtility.h"
-
-typedef NS_ENUM(NSInteger, Direction) {
- DirectionNone = 1 << 0,
- DirectionLeft = 1 << 1,
- DirectionRight = 1 << 2
-};
-
-@class WXRecycleSliderView;
-@class WXIndicatorView;
-
-@protocol WXRecycleSliderViewDelegate <UIScrollViewDelegate>
-
-- (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScroll:(UIScrollView *)scrollView;
-- (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScrollToItemAtIndex:(NSInteger)index;
-- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
-- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
-
-@end
-
-@interface WXRecycleSliderView : UIView <UIScrollViewDelegate>
-
-@property (nonatomic, strong) WXIndicatorView *indicator;
-@property (nonatomic, weak) id<WXRecycleSliderViewDelegate> delegate;
-
-@property (nonatomic, strong) UIScrollView *scrollView;
-@property (nonatomic, strong) NSMutableArray *itemViews;
-@property (nonatomic, assign) Direction direction;
-@property (nonatomic, assign) NSInteger currentIndex;
-@property (nonatomic, assign) NSInteger nextIndex;
-@property (nonatomic, assign) CGRect currentItemFrame;
-@property (nonatomic, assign) CGRect nextItemFrame;
-@property (nonatomic, assign) BOOL infinite;
-
-- (void)insertItemView:(UIView *)view atIndex:(NSInteger)index;
-- (void)removeItemView:(UIView *)view;
-
-@end
-
-@implementation WXRecycleSliderView
-
-- (id)initWithFrame:(CGRect)frame
-{
- self = [super initWithFrame:frame];
- if (self) {
- _currentIndex = 0;
- _itemViews = [[NSMutableArray alloc] init];
- _scrollView = [[UIScrollView alloc] init];
- _scrollView.backgroundColor = [UIColor clearColor];
- _scrollView.delegate = self;
- _scrollView.showsHorizontalScrollIndicator = NO;
- _scrollView.showsVerticalScrollIndicator = NO;
- _scrollView.scrollsToTop = NO;
- [self addSubview:_scrollView];
- }
- return self;
-}
-
-- (void)dealloc
-{
- if (_scrollView) {
- _scrollView.delegate = nil;
- }
-}
-
-- (void)layoutSubviews
-{
- [super layoutSubviews];
- [self resetAllViewsFrame];
-}
-
-- (void)accessibilityDecrement
-{
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- [self.wx_component performSelector:NSSelectorFromString(@"resumeAutoPlay:") withObject:@(false)];
-#pragma clang diagnostic pop
-
- [self nextPage];
-}
-
-- (void)accessibilityIncrement
-{
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- [self.wx_component performSelector:NSSelectorFromString(@"resumeAutoPlay:") withObject:@(false)];
-#pragma clang diagnostic pop
-
- [self lastPage];
-}
-
-- (void)accessibilityElementDidLoseFocus
-{
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- [self.wx_component performSelector:NSSelectorFromString(@"resumeAutoPlay:") withObject:@(true)];
-#pragma clang diagnostic pop
-}
-
-#pragma mark Private Methods
-- (CGFloat)height {
- return self.frame.size.height;
-}
-
-- (CGFloat)width {
- return self.frame.size.width;
-}
-
-- (UIView *)getItemAtIndex:(NSInteger)index
-{
- if (self.itemViews.count > index) {
- return [self.itemViews objectAtIndex:index];
- }else{
- return nil;
- }
-}
-
-- (void)setCurrentIndex:(NSInteger)currentIndex
-{
- if (currentIndex >= _itemViews.count || currentIndex < 0) {
- currentIndex = 0;
- }
- NSInteger oldIndex = _currentIndex;
- _currentIndex = currentIndex;
- if (_infinite) {
- if (_direction == DirectionRight) {
- self.nextItemFrame = CGRectMake(0, 0, self.width, self.height);
- self.nextIndex = self.currentIndex - 1;
- if (self.nextIndex < 0)
- {
- self.nextIndex = _itemViews.count - 1;
- }
- }else if (_direction == DirectionLeft) {
- self.nextItemFrame = CGRectMake(self.width * 2, 0, self.width, self.height);
- self.nextIndex = _itemViews.count?(self.currentIndex + 1) % _itemViews.count:0;
- }else {
- self.nextIndex = _itemViews.count?(self.currentIndex + 1) % _itemViews.count:0;
- }
- [self resetAllViewsFrame];
- } else {
- [_scrollView setContentOffset:CGPointMake(_currentIndex * self.width, 0) animated:YES];
- }
- [self resetIndicatorPoint];
- if (self.delegate && [self.delegate respondsToSelector:@selector(recycleSliderView:didScrollToItemAtIndex:)]) {
- if (oldIndex != _currentIndex) {
- [self.delegate recycleSliderView:self didScrollToItemAtIndex:_currentIndex];
- }
- }
-}
-
-- (void)resetIndicatorPoint
-{
- [self.indicator setPointCount:self.itemViews.count];
- [self.indicator setCurrentPoint:_currentIndex];
-}
-
-#pragma mark Scroll & Frames
-- (void)setDirection:(Direction)direction {
- if (_direction == direction) return;
- _direction = direction;
- if (_direction == DirectionNone) return;
- if (_direction == DirectionRight) {
- self.nextItemFrame = CGRectMake(0, 0, self.width, self.height);
- self.nextIndex = self.currentIndex - 1;
- if (self.nextIndex < 0)
- {
- self.nextIndex = _itemViews.count - 1;
- }
- UIView *view = [self getItemAtIndex:_nextIndex];
- if (view) {
- view.frame = _nextItemFrame;
- }
- }else if (_direction == DirectionLeft){
- self.nextItemFrame = CGRectMake(self.width * 2, 0, self.width, self.height);
- self.nextIndex = _itemViews.count?(self.currentIndex + 1) % _itemViews.count:0;
- UIView *view = [self getItemAtIndex:_nextIndex];
- if (view) {
- view.frame = _nextItemFrame;
- }
- }
-}
-
-- (void)resetAllViewsFrame
-{
- if (_infinite && _itemViews.count > 1) {
- self.scrollView.frame = CGRectMake(0, 0, self.width, self.height);
- self.scrollView.contentOffset = CGPointMake(self.width, 0);
- if (self.itemViews.count > 1) {
- self.scrollView.contentSize = CGSizeMake(self.width * 3, 0);
- } else {
- self.scrollView.contentSize = CGSizeZero;
- }
- _currentItemFrame = CGRectMake(self.width, 0, self.width, self.height);
- for (int i = 0; i < self.itemViews.count; i++) {
- UIView *view = [self.itemViews objectAtIndex:i];
- if (i != self.currentIndex) {
- view.frame = CGRectMake(self.frame.size.width * 3, 0, self.width, self.height);;
- }
- }
- [self getItemAtIndex:_currentIndex].frame = _currentItemFrame;
- if (_itemViews.count == 2) {
- _nextItemFrame = CGRectMake(self.width * 2, 0, self.width, self.height);
- [self getItemAtIndex:_nextIndex].frame = _nextItemFrame;
- }
- } else {
- self.scrollView.frame = self.bounds;
- self.scrollView.contentSize = CGSizeMake(self.width * _itemViews.count, self.height);
- self.scrollView.contentOffset = CGPointMake(_currentIndex * self.width, 0);
- for (int i = 0; i < _itemViews.count; i ++) {
- UIView *view = [_itemViews objectAtIndex:i];
- view.frame = CGRectMake(i * self.width, 0, self.width, self.height);
- }
- [self.scrollView setContentOffset:CGPointMake(_currentIndex * self.width, 0) animated:NO];
- }
- [self resetIndicatorPoint];
-}
-
-- (void)nextPage {
- if (_itemViews.count > 1) {
- if (_infinite) {
- [self.scrollView setContentOffset:CGPointMake(self.width * 2, 0) animated:YES];
- } else {
- // the currentindex will be set at the end of animation
- NSInteger nextIndex = self.currentIndex + 1;
- if(nextIndex < _itemViews.count) {
- [self.scrollView setContentOffset:CGPointMake(nextIndex * self.width, 0) animated:YES];
- }
- }
- }
-}
-
-- (void)lastPage
-{
-
- NSInteger lastIndex = [self currentIndex]-1;
- if (_itemViews.count > 1) {
- if (_infinite) {
- if (lastIndex < 0) {
- lastIndex = [_itemViews count]-1;
- }
- }
- [self setCurrentIndex:lastIndex];
- }
-}
-
-- (void)resetScrollView
-{
- if (WXFloatEqual(self.scrollView.contentOffset.x / self.width , 1.0))
- {
- return;
- }
- [self setCurrentIndex:self.nextIndex];
- self.scrollView.contentOffset = CGPointMake(self.width, 0);
-}
-
-#pragma mark Public Methods
-
-- (void)setIndicator:(WXIndicatorView *)indicator
-{
- _indicator = indicator;
- [_indicator setPointCount:self.itemViews.count];
- [_indicator setCurrentPoint:_currentIndex];
-}
-
-- (void)insertItemView:(UIView *)view atIndex:(NSInteger)index
-{
- if (![self.itemViews containsObject:view]) {
- view.tag = self.itemViews.count;
- if (index < 0) {
- [self.itemViews addObject:view];
- } else {
- [self.itemViews insertObject:view atIndex:index];
- }
- }
-
- if (![self.scrollView.subviews containsObject:view]) {
- if (index < 0) {
- [self.scrollView addSubview:view];
- } else {
- [self.scrollView insertSubview:view atIndex:index];
- }
- }
- [self layoutSubviews];
- [self setCurrentIndex:_currentIndex];
-}
-
-- (void)removeItemView:(UIView *)view
-{
- if ([self.itemViews containsObject:view]) {
- [self.itemViews removeObject:view];
- }
-
- if ([self.scrollView.subviews containsObject:view]) {
- [view removeFromSuperview];
- }
- [self layoutSubviews];
- [self setCurrentIndex:_currentIndex];
-}
-
-#pragma mark ScrollView Delegate
-
-- (void)scrollViewDidScroll:(UIScrollView *)scrollView
-{
- if (_infinite) {
- CGFloat offX = scrollView.contentOffset.x;
- self.direction = offX > self.width ? DirectionLeft : offX < self.width ? DirectionRight : DirectionNone;
- }
- if (self.delegate && [self.delegate respondsToSelector:@selector(recycleSliderView:didScroll:)]) {
- [self.delegate recycleSliderView:self didScroll:self.scrollView];
- }
-}
-
-- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
-{
- if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) {
- [self.delegate scrollViewWillBeginDragging:self.scrollView];
- }
-}
-
-- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
-{
- if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewDidEndDragging: willDecelerate:)]) {
- [self.delegate scrollViewDidEndDragging:self.scrollView willDecelerate:decelerate];
- }
-}
-
-- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
- if (_infinite) {
- [self resetScrollView];
- } else {
- NSInteger index = _scrollView.contentOffset.x / self.width;
- [self setCurrentIndex:index];
- }
-}
-
-- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
- if (_infinite) {
- [self resetScrollView];
- } else {
- NSInteger index = _scrollView.contentOffset.x / self.width;
- [self setCurrentIndex:index];
- }
-}
-
-@end
-
-@interface WXCycleSliderComponent () <WXRecycleSliderViewDelegate,WXIndicatorComponentDelegate>
-
-@property (nonatomic, strong) WXRecycleSliderView *recycleSliderView;
-@property (nonatomic, strong) NSTimer *autoTimer;
-@property (nonatomic, assign) NSInteger currentIndex;
-@property (nonatomic, assign) BOOL autoPlay;
-@property (nonatomic, assign) NSUInteger interval;
-@property (nonatomic, assign) NSInteger index;
-@property (nonatomic, assign) CGFloat lastOffsetXRatio;
-@property (nonatomic, assign) CGFloat offsetXAccuracy;
-@property (nonatomic, assign) BOOL sliderChangeEvent;
-@property (nonatomic, assign) BOOL sliderScrollEvent;
-@property (nonatomic, assign) BOOL sliderScrollStartEvent;
-@property (nonatomic, assign) BOOL sliderScrollEndEvent;
-@property (nonatomic, assign) BOOL sliderStartEventFired;
-@property (nonatomic, strong) NSMutableArray *childrenView;
-@property (nonatomic, assign) BOOL scrollable;
-@property (nonatomic, assign) BOOL infinite;
-
-@end
-
-@implementation WXCycleSliderComponent
-
-- (void) dealloc
-{
- [self _stopAutoPlayTimer];
-}
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
- if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
- _sliderChangeEvent = NO;
- _sliderScrollEvent = NO;
- _interval = 3000;
- _childrenView = [NSMutableArray new];
- _lastOffsetXRatio = 0;
-
- if (attributes[@"autoPlay"]) {
- _autoPlay = [WXConvert BOOL:attributes[@"autoPlay"]];
- }
-
- if (attributes[@"interval"]) {
- _interval = [WXConvert NSInteger:attributes[@"interval"]];
- }
-
- if (attributes[@"index"]) {
- _index = [WXConvert NSInteger:attributes[@"index"]];
- }
- _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
- if (attributes[@"offsetXAccuracy"]) {
- _offsetXAccuracy = [WXConvert CGFloat:attributes[@"offsetXAccuracy"]];
- }
- _infinite = attributes[@"infinite"] ? [WXConvert BOOL:attributes[@"infinite"]] : YES;
- self.cssNode->style.flex_direction = CSS_FLEX_DIRECTION_ROW;
- }
- return self;
-}
-
-- (UIView *)loadView
-{
- return [[WXRecycleSliderView alloc] init];
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
-
- _recycleSliderView = (WXRecycleSliderView *)self.view;
- _recycleSliderView.delegate = self;
- _recycleSliderView.scrollView.pagingEnabled = YES;
- _recycleSliderView.exclusiveTouch = YES;
- _recycleSliderView.scrollView.scrollEnabled = _scrollable;
- _recycleSliderView.infinite = _infinite;
- UIAccessibilityTraits traits = UIAccessibilityTraitAdjustable;
- if (_autoPlay) {
- traits |= UIAccessibilityTraitUpdatesFrequently;
- [self _startAutoPlayTimer];
- } else {
- [self _stopAutoPlayTimer];
- }
- _recycleSliderView.accessibilityTraits = traits;
-}
-
-- (void)layoutDidFinish
-{
- _recycleSliderView.currentIndex = _index;
-}
-
-- (void)viewDidUnload
-{
- [_childrenView removeAllObjects];
-}
-
-- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- if (subcomponent->_positionType == WXPositionTypeFixed) {
- [self.weexInstance.rootView addSubview:subcomponent.view];
- return;
- }
-
- // use _lazyCreateView to forbid component like cell's view creating
- if(_lazyCreateView) {
- subcomponent->_lazyCreateView = YES;
- }
-
- if (!subcomponent->_lazyCreateView || (self->_lazyCreateView && [self isViewLoaded])) {
- UIView *view = subcomponent.view;
-
- if(index < 0) {
- [self.childrenView addObject:view];
- }
- else {
- [self.childrenView insertObject:view atIndex:index];
- }
-
- WXRecycleSliderView *recycleSliderView = (WXRecycleSliderView *)self.view;
- if ([view isKindOfClass:[WXIndicatorView class]]) {
- ((WXIndicatorComponent *)subcomponent).delegate = self;
- [recycleSliderView addSubview:view];
- [self setIndicatorView:(WXIndicatorView *)view];
- return;
- }
-
- subcomponent.isViewFrameSyncWithCalculated = NO;
-
- if (index == -1) {
- [recycleSliderView insertItemView:view atIndex:index];
- } else {
- NSInteger offset = 0;
- for (int i = 0; i < [self.childrenView count]; ++i) {
- if (index == i) break;
-
- if ([self.childrenView[i] isKindOfClass:[WXIndicatorView class]]) {
- offset++;
- }
- }
- [recycleSliderView insertItemView:view atIndex:index - offset];
-
- // check if should apply current contentOffset
- // in case inserting subviews after layoutDidFinish
- if (index-offset == _index && _index>0) {
- recycleSliderView.currentIndex = _index;
- }
- }
- [recycleSliderView layoutSubviews];
- }
-}
-
-- (void)willRemoveSubview:(WXComponent *)component
-{
- UIView *view = component.view;
-
- if(self.childrenView && [self.childrenView containsObject:view]) {
- [self.childrenView removeObject:view];
- }
-
- WXRecycleSliderView *recycleSliderView = (WXRecycleSliderView *)_view;
- [recycleSliderView removeItemView:view];
- [recycleSliderView setCurrentIndex:0];
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- if (attributes[@"autoPlay"]) {
- _autoPlay = [WXConvert BOOL:attributes[@"autoPlay"]];
- if (_autoPlay) {
- [self _startAutoPlayTimer];
- } else {
- [self _stopAutoPlayTimer];
- }
- }
-
- if (attributes[@"interval"]) {
- _interval = [WXConvert NSInteger:attributes[@"interval"]];
- [self _stopAutoPlayTimer];
-
- if (_autoPlay) {
- [self _startAutoPlayTimer];
- }
- }
-
- if (attributes[@"index"]) {
- _index = [WXConvert NSInteger:attributes[@"index"]];
- self.currentIndex = _index;
- self.recycleSliderView.currentIndex = _index;
- }
-
- if (attributes[@"scrollable"]) {
- _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
- ((WXRecycleSliderView *)self.view).scrollView.scrollEnabled = _scrollable;
- }
-
- if (attributes[@"offsetXAccuracy"]) {
- _offsetXAccuracy = [WXConvert CGFloat:attributes[@"offsetXAccuracy"]];
- }
- if (attributes[@"infinite"]) {
- _infinite = [WXConvert BOOL:attributes[@"infinite"]];
- }
-}
-
-- (void)addEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"change"]) {
- _sliderChangeEvent = YES;
- }
- if ([eventName isEqualToString:@"scroll"]) {
- _sliderScrollEvent = YES;
- }
-}
-
-- (void)removeEvent:(NSString *)eventName
-{
- if ([eventName isEqualToString:@"change"]) {
- _sliderChangeEvent = NO;
- }
- if ([eventName isEqualToString:@"scroll"]) {
- _sliderScrollEvent = NO;
- }
-}
-
-#pragma mark WXIndicatorComponentDelegate Methods
-
--(void)setIndicatorView:(WXIndicatorView *)indicatorView
-{
- NSAssert(_recycleSliderView, @"");
- [_recycleSliderView setIndicator:indicatorView];
-}
-
-- (void)resumeAutoPlay:(id)resume
-{
- if (_autoPlay) {
- if ([resume boolValue]) {
- [self _startAutoPlayTimer];
- } else {
- [self _stopAutoPlayTimer];
- }
- }
-}
-
-#pragma mark Private Methods
-
-- (void)_startAutoPlayTimer
-{
- if (!self.autoTimer || ![self.autoTimer isValid]) {
- __weak __typeof__(self) weakSelf = self;
- self.autoTimer = [NSTimer wx_scheduledTimerWithTimeInterval:_interval/1000.0f block:^() {
- [weakSelf _autoPlayOnTimer];
- } repeats:YES];
- [[NSRunLoop currentRunLoop] addTimer:self.autoTimer forMode:NSRunLoopCommonModes];
- }
-}
-
-- (void)_stopAutoPlayTimer
-{
- if (self.autoTimer && [self.autoTimer isValid]) {
- [self.autoTimer invalidate];
- self.autoTimer = nil;
- }
-}
-
-- (void)_autoPlayOnTimer
-{
- if (!_infinite && (_currentIndex == _recycleSliderView.itemViews.count - 1)) {
- [self _stopAutoPlayTimer];
- }else {
- [self.recycleSliderView nextPage];
- }
-}
-
-#pragma mark ScrollView Delegate
-
-- (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScroll:(UIScrollView *)scrollView
-{
- if (_sliderScrollEvent) {
- CGFloat width = scrollView.frame.size.width;
- CGFloat XDeviation = 0;
- if (_infinite) {
- XDeviation = - (scrollView.contentOffset.x - width);
- } else {
- XDeviation = - (scrollView.contentOffset.x - width * _currentIndex);
- }
- CGFloat offsetXRatio = (XDeviation / width);
- if (fabs(offsetXRatio - _lastOffsetXRatio) >= _offsetXAccuracy) {
- _lastOffsetXRatio = offsetXRatio;
- [self fireEvent:@"scroll" params:@{@"offsetXRatio":[NSNumber numberWithFloat:offsetXRatio]} domChanges:nil];
- }
- }
-}
-
-- (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScrollToItemAtIndex:(NSInteger)index
-{
-
- if (_sliderChangeEvent) {
- [self fireEvent:@"change" params:@{@"index":@(index)} domChanges:@{@"attrs": @{@"index": @(index)}}];
- }
- self.currentIndex = index;
-}
-
-- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
-{
- [self _stopAutoPlayTimer];
-}
-
-- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
-{
- if (_autoPlay) {
- [self _startAutoPlayTimer];
- }
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.mm
new file mode 100644
index 0000000..db77b46
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.mm
@@ -0,0 +1,692 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXCycleSliderComponent.h"
+#import "WXIndicatorComponent.h"
+#import "WXComponent_internal.h"
+#import "NSTimer+Weex.h"
+#import "WXSDKManager.h"
+#import "WXUtility.h"
+#import "WXComponent+Layout.h"
+
+typedef NS_ENUM(NSInteger, Direction) {
+ DirectionNone = 1 << 0,
+ DirectionLeft = 1 << 1,
+ DirectionRight = 1 << 2
+};
+
+@class WXRecycleSliderView;
+@class WXIndicatorView;
+
+@protocol WXRecycleSliderViewDelegate <UIScrollViewDelegate>
+
+- (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScroll:(UIScrollView *)scrollView;
+- (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScrollToItemAtIndex:(NSInteger)index;
+- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
+- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
+
+@end
+
+@interface WXRecycleSliderView : UIView <UIScrollViewDelegate>
+
+@property (nonatomic, strong) WXIndicatorView *indicator;
+@property (nonatomic, weak) id<WXRecycleSliderViewDelegate> delegate;
+
+@property (nonatomic, strong) UIScrollView *scrollView;
+@property (nonatomic, strong) NSMutableArray *itemViews;
+@property (nonatomic, assign) Direction direction;
+@property (nonatomic, assign) NSInteger currentIndex;
+@property (nonatomic, assign) NSInteger nextIndex;
+@property (nonatomic, assign) CGRect currentItemFrame;
+@property (nonatomic, assign) CGRect nextItemFrame;
+@property (nonatomic, assign) BOOL infinite;
+
+- (void)insertItemView:(UIView *)view atIndex:(NSInteger)index;
+- (void)removeItemView:(UIView *)view;
+
+@end
+
+@implementation WXRecycleSliderView
+
+- (id)initWithFrame:(CGRect)frame
+{
+ self = [super initWithFrame:frame];
+ if (self) {
+ _currentIndex = 0;
+ _itemViews = [[NSMutableArray alloc] init];
+ _scrollView = [[UIScrollView alloc] init];
+ _scrollView.backgroundColor = [UIColor clearColor];
+ _scrollView.delegate = self;
+ _scrollView.showsHorizontalScrollIndicator = NO;
+ _scrollView.showsVerticalScrollIndicator = NO;
+ _scrollView.scrollsToTop = NO;
+ [self addSubview:_scrollView];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ if (_scrollView) {
+ _scrollView.delegate = nil;
+ }
+}
+
+- (void)layoutSubviews
+{
+ [super layoutSubviews];
+ [self resetAllViewsFrame];
+}
+
+- (void)accessibilityDecrement
+{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [self.wx_component performSelector:NSSelectorFromString(@"resumeAutoPlay:") withObject:@(false)];
+#pragma clang diagnostic pop
+
+ [self nextPage];
+}
+
+- (void)accessibilityIncrement
+{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [self.wx_component performSelector:NSSelectorFromString(@"resumeAutoPlay:") withObject:@(false)];
+#pragma clang diagnostic pop
+
+ [self lastPage];
+}
+
+- (void)accessibilityElementDidLoseFocus
+{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [self.wx_component performSelector:NSSelectorFromString(@"resumeAutoPlay:") withObject:@(true)];
+#pragma clang diagnostic pop
+}
+
+#pragma mark Private Methods
+- (CGFloat)height {
+ return self.frame.size.height;
+}
+
+- (CGFloat)width {
+ return self.frame.size.width;
+}
+
+- (UIView *)getItemAtIndex:(NSInteger)index
+{
+ if (self.itemViews.count > index) {
+ return [self.itemViews objectAtIndex:index];
+ }else{
+ return nil;
+ }
+}
+
+- (void)setCurrentIndex:(NSInteger)currentIndex
+{
+ if (currentIndex >= _itemViews.count || currentIndex < 0) {
+ currentIndex = 0;
+ }
+ NSInteger oldIndex = _currentIndex;
+ _currentIndex = currentIndex;
+ if (_infinite) {
+ if (_direction == DirectionRight) {
+ self.nextItemFrame = CGRectMake(0, 0, self.width, self.height);
+ self.nextIndex = self.currentIndex - 1;
+ if (self.nextIndex < 0)
+ {
+ self.nextIndex = _itemViews.count - 1;
+ }
+ }else if (_direction == DirectionLeft) {
+ self.nextItemFrame = CGRectMake(self.width * 2, 0, self.width, self.height);
+ self.nextIndex = _itemViews.count?(self.currentIndex + 1) % _itemViews.count:0;
+ }else {
+ self.nextIndex = _itemViews.count?(self.currentIndex + 1) % _itemViews.count:0;
+ }
+ [self resetAllViewsFrame];
+ } else {
+ [_scrollView setContentOffset:CGPointMake(_currentIndex * self.width, 0) animated:YES];
+ }
+ [self resetIndicatorPoint];
+ if (self.delegate && [self.delegate respondsToSelector:@selector(recycleSliderView:didScrollToItemAtIndex:)]) {
+ if (oldIndex != _currentIndex) {
+ [self.delegate recycleSliderView:self didScrollToItemAtIndex:_currentIndex];
+ }
+ }
+}
+
+- (void)resetIndicatorPoint
+{
+ [self.indicator setPointCount:self.itemViews.count];
+ [self.indicator setCurrentPoint:_currentIndex];
+}
+
+#pragma mark Scroll & Frames
+- (void)setDirection:(Direction)direction {
+ if (_direction == direction) return;
+ _direction = direction;
+ if (_direction == DirectionNone) return;
+ if (_direction == DirectionRight) {
+ self.nextItemFrame = CGRectMake(0, 0, self.width, self.height);
+ self.nextIndex = self.currentIndex - 1;
+ if (self.nextIndex < 0)
+ {
+ self.nextIndex = _itemViews.count - 1;
+ }
+ UIView *view = [self getItemAtIndex:_nextIndex];
+ if (view) {
+ view.frame = _nextItemFrame;
+ }
+ }else if (_direction == DirectionLeft){
+ self.nextItemFrame = CGRectMake(self.width * 2, 0, self.width, self.height);
+ self.nextIndex = _itemViews.count?(self.currentIndex + 1) % _itemViews.count:0;
+ UIView *view = [self getItemAtIndex:_nextIndex];
+ if (view) {
+ view.frame = _nextItemFrame;
+ }
+ }
+}
+
+- (void)resetAllViewsFrame
+{
+ if (_infinite && _itemViews.count > 1) {
+ self.scrollView.frame = CGRectMake(0, 0, self.width, self.height);
+ self.scrollView.contentOffset = CGPointMake(self.width, 0);
+ if (self.itemViews.count > 1) {
+ self.scrollView.contentSize = CGSizeMake(self.width * 3, 0);
+ } else {
+ self.scrollView.contentSize = CGSizeZero;
+ }
+ _currentItemFrame = CGRectMake(self.width, 0, self.width, self.height);
+ for (int i = 0; i < self.itemViews.count; i++) {
+ UIView *view = [self.itemViews objectAtIndex:i];
+ if (i != self.currentIndex) {
+ view.frame = CGRectMake(self.frame.size.width * 3, 0, self.width, self.height);;
+ }
+ }
+ [self getItemAtIndex:_currentIndex].frame = _currentItemFrame;
+ if (_itemViews.count == 2) {
+ _nextItemFrame = CGRectMake(self.width * 2, 0, self.width, self.height);
+ [self getItemAtIndex:_nextIndex].frame = _nextItemFrame;
+ }
+ } else {
+ self.scrollView.frame = self.bounds;
+ self.scrollView.contentSize = CGSizeMake(self.width * _itemViews.count, self.height);
+ self.scrollView.contentOffset = CGPointMake(_currentIndex * self.width, 0);
+ for (int i = 0; i < _itemViews.count; i ++) {
+ UIView *view = [_itemViews objectAtIndex:i];
+ view.frame = CGRectMake(i * self.width, 0, self.width, self.height);
+ }
+ [self.scrollView setContentOffset:CGPointMake(_currentIndex * self.width, 0) animated:NO];
+ }
+ [self resetIndicatorPoint];
+}
+
+- (void)nextPage {
+ if (_itemViews.count > 1) {
+ if (_infinite) {
+ [self.scrollView setContentOffset:CGPointMake(self.width * 2, 0) animated:YES];
+ } else {
+ // the currentindex will be set at the end of animation
+ NSInteger nextIndex = self.currentIndex + 1;
+ if(nextIndex < _itemViews.count) {
+ [self.scrollView setContentOffset:CGPointMake(nextIndex * self.width, 0) animated:YES];
+ }
+ }
+ }
+}
+
+- (void)lastPage
+{
+
+ NSInteger lastIndex = [self currentIndex]-1;
+ if (_itemViews.count > 1) {
+ if (_infinite) {
+ if (lastIndex < 0) {
+ lastIndex = [_itemViews count]-1;
+ }
+ }
+ [self setCurrentIndex:lastIndex];
+ }
+}
+
+- (void)resetScrollView
+{
+ if (WXFloatEqual(self.scrollView.contentOffset.x / self.width , 1.0))
+ {
+ return;
+ }
+ [self setCurrentIndex:self.nextIndex];
+ self.scrollView.contentOffset = CGPointMake(self.width, 0);
+}
+
+#pragma mark Public Methods
+
+- (void)setIndicator:(WXIndicatorView *)indicator
+{
+ _indicator = indicator;
+ [_indicator setPointCount:self.itemViews.count];
+ [_indicator setCurrentPoint:_currentIndex];
+}
+
+- (void)insertItemView:(UIView *)view atIndex:(NSInteger)index
+{
+ if (![self.itemViews containsObject:view]) {
+ view.tag = self.itemViews.count;
+ if (index < 0) {
+ [self.itemViews addObject:view];
+ } else {
+ [self.itemViews insertObject:view atIndex:index];
+ }
+ }
+
+ if (![self.scrollView.subviews containsObject:view]) {
+ if (index < 0) {
+ [self.scrollView addSubview:view];
+ } else {
+ [self.scrollView insertSubview:view atIndex:index];
+ }
+ }
+ [self layoutSubviews];
+ [self setCurrentIndex:_currentIndex];
+}
+
+- (void)removeItemView:(UIView *)view
+{
+ if ([self.itemViews containsObject:view]) {
+ [self.itemViews removeObject:view];
+ }
+
+ if ([self.scrollView.subviews containsObject:view]) {
+ [view removeFromSuperview];
+ }
+ [self layoutSubviews];
+ [self setCurrentIndex:_currentIndex];
+}
+
+#pragma mark ScrollView Delegate
+
+- (void)scrollViewDidScroll:(UIScrollView *)scrollView
+{
+ if (_infinite) {
+ CGFloat offX = scrollView.contentOffset.x;
+ self.direction = offX > self.width ? DirectionLeft : offX < self.width ? DirectionRight : DirectionNone;
+ }
+ if (self.delegate && [self.delegate respondsToSelector:@selector(recycleSliderView:didScroll:)]) {
+ [self.delegate recycleSliderView:self didScroll:self.scrollView];
+ }
+}
+
+- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
+{
+ if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) {
+ [self.delegate scrollViewWillBeginDragging:self.scrollView];
+ }
+}
+
+- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
+{
+ if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewDidEndDragging: willDecelerate:)]) {
+ [self.delegate scrollViewDidEndDragging:self.scrollView willDecelerate:decelerate];
+ }
+}
+
+- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
+ if (_infinite) {
+ [self resetScrollView];
+ } else {
+ NSInteger index = _scrollView.contentOffset.x / self.width;
+ [self setCurrentIndex:index];
+ }
+}
+
+- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
+ if (_infinite) {
+ [self resetScrollView];
+ } else {
+ NSInteger index = _scrollView.contentOffset.x / self.width;
+ [self setCurrentIndex:index];
+ }
+}
+
+@end
+
+@interface WXCycleSliderComponent () <WXRecycleSliderViewDelegate,WXIndicatorComponentDelegate>
+
+@property (nonatomic, strong) WXRecycleSliderView *recycleSliderView;
+@property (nonatomic, strong) NSTimer *autoTimer;
+@property (nonatomic, assign) NSInteger currentIndex;
+@property (nonatomic, assign) BOOL autoPlay;
+@property (nonatomic, assign) NSUInteger interval;
+@property (nonatomic, assign) NSInteger index;
+@property (nonatomic, assign) CGFloat lastOffsetXRatio;
+@property (nonatomic, assign) CGFloat offsetXAccuracy;
+@property (nonatomic, assign) BOOL sliderChangeEvent;
+@property (nonatomic, assign) BOOL sliderScrollEvent;
+@property (nonatomic, assign) BOOL sliderScrollStartEvent;
+@property (nonatomic, assign) BOOL sliderScrollEndEvent;
+@property (nonatomic, assign) BOOL sliderStartEventFired;
+@property (nonatomic, strong) NSMutableArray *childrenView;
+@property (nonatomic, assign) BOOL scrollable;
+@property (nonatomic, assign) BOOL infinite;
+
+@end
+
+@implementation WXCycleSliderComponent
+
+- (void) dealloc
+{
+ [self _stopAutoPlayTimer];
+}
+
+- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
+{
+ if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
+ _sliderChangeEvent = NO;
+ _sliderScrollEvent = NO;
+ _interval = 3000;
+ _childrenView = [NSMutableArray new];
+ _lastOffsetXRatio = 0;
+
+ if (attributes[@"autoPlay"]) {
+ _autoPlay = [WXConvert BOOL:attributes[@"autoPlay"]];
+ }
+
+ if (attributes[@"interval"]) {
+ _interval = [WXConvert NSInteger:attributes[@"interval"]];
+ }
+
+ if (attributes[@"index"]) {
+ _index = [WXConvert NSInteger:attributes[@"index"]];
+ }
+ _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
+ if (attributes[@"offsetXAccuracy"]) {
+ _offsetXAccuracy = [WXConvert CGFloat:attributes[@"offsetXAccuracy"]];
+ }
+ _infinite = attributes[@"infinite"] ? [WXConvert BOOL:attributes[@"infinite"]] : YES;
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ self.cssNode->style.flex_direction = CSS_FLEX_DIRECTION_ROW;
+ }
+//#else
+ else
+ {
+ self.flexCssNode->setFlexDirection(WeexCore::kFlexDirectionRow,NO);
+ }
+//#endif
+
+ }
+ return self;
+}
+
+- (UIView *)loadView
+{
+ return [[WXRecycleSliderView alloc] init];
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ _recycleSliderView = (WXRecycleSliderView *)self.view;
+ _recycleSliderView.delegate = self;
+ _recycleSliderView.scrollView.pagingEnabled = YES;
+ _recycleSliderView.exclusiveTouch = YES;
+ _recycleSliderView.scrollView.scrollEnabled = _scrollable;
+ _recycleSliderView.infinite = _infinite;
+ UIAccessibilityTraits traits = UIAccessibilityTraitAdjustable;
+ if (_autoPlay) {
+ traits |= UIAccessibilityTraitUpdatesFrequently;
+ [self _startAutoPlayTimer];
+ } else {
+ [self _stopAutoPlayTimer];
+ }
+ _recycleSliderView.accessibilityTraits = traits;
+}
+
+- (void)layoutDidFinish
+{
+ _recycleSliderView.currentIndex = _index;
+}
+
+- (void)viewDidUnload
+{
+ [_childrenView removeAllObjects];
+}
+
+- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ if (subcomponent->_positionType == WXPositionTypeFixed) {
+ [self.weexInstance.rootView addSubview:subcomponent.view];
+ return;
+ }
+
+ // use _lazyCreateView to forbid component like cell's view creating
+ if(_lazyCreateView) {
+ subcomponent->_lazyCreateView = YES;
+ }
+
+ if (!subcomponent->_lazyCreateView || (self->_lazyCreateView && [self isViewLoaded])) {
+ UIView *view = subcomponent.view;
+
+ if(index < 0) {
+ [self.childrenView addObject:view];
+ }
+ else {
+ [self.childrenView insertObject:view atIndex:index];
+ }
+
+ WXRecycleSliderView *recycleSliderView = (WXRecycleSliderView *)self.view;
+ if ([view isKindOfClass:[WXIndicatorView class]]) {
+ ((WXIndicatorComponent *)subcomponent).delegate = self;
+ [recycleSliderView addSubview:view];
+ [self setIndicatorView:(WXIndicatorView *)view];
+ return;
+ }
+
+ subcomponent.isViewFrameSyncWithCalculated = NO;
+
+ if (index == -1) {
+ [recycleSliderView insertItemView:view atIndex:index];
+ } else {
+ NSInteger offset = 0;
+ for (int i = 0; i < [self.childrenView count]; ++i) {
+ if (index == i) break;
+
+ if ([self.childrenView[i] isKindOfClass:[WXIndicatorView class]]) {
+ offset++;
+ }
+ }
+ [recycleSliderView insertItemView:view atIndex:index - offset];
+
+ // check if should apply current contentOffset
+ // in case inserting subviews after layoutDidFinish
+ if (index-offset == _index && _index>0) {
+ recycleSliderView.currentIndex = _index;
+ }
+ }
+ [recycleSliderView layoutSubviews];
+ }
+}
+
+- (void)willRemoveSubview:(WXComponent *)component
+{
+ UIView *view = component.view;
+
+ if(self.childrenView && [self.childrenView containsObject:view]) {
+ [self.childrenView removeObject:view];
+ }
+
+ WXRecycleSliderView *recycleSliderView = (WXRecycleSliderView *)_view;
+ [recycleSliderView removeItemView:view];
+ [recycleSliderView setCurrentIndex:0];
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ if (attributes[@"autoPlay"]) {
+ _autoPlay = [WXConvert BOOL:attributes[@"autoPlay"]];
+ if (_autoPlay) {
+ [self _startAutoPlayTimer];
+ } else {
+ [self _stopAutoPlayTimer];
+ }
+ }
+
+ if (attributes[@"interval"]) {
+ _interval = [WXConvert NSInteger:attributes[@"interval"]];
+ [self _stopAutoPlayTimer];
+
+ if (_autoPlay) {
+ [self _startAutoPlayTimer];
+ }
+ }
+
+ if (attributes[@"index"]) {
+ _index = [WXConvert NSInteger:attributes[@"index"]];
+ self.currentIndex = _index;
+ self.recycleSliderView.currentIndex = _index;
+ }
+
+ if (attributes[@"scrollable"]) {
+ _scrollable = attributes[@"scrollable"] ? [WXConvert BOOL:attributes[@"scrollable"]] : YES;
+ ((WXRecycleSliderView *)self.view).scrollView.scrollEnabled = _scrollable;
+ }
+
+ if (attributes[@"offsetXAccuracy"]) {
+ _offsetXAccuracy = [WXConvert CGFloat:attributes[@"offsetXAccuracy"]];
+ }
+ if (attributes[@"infinite"]) {
+ _infinite = [WXConvert BOOL:attributes[@"infinite"]];
+ }
+}
+
+- (void)addEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"change"]) {
+ _sliderChangeEvent = YES;
+ }
+ if ([eventName isEqualToString:@"scroll"]) {
+ _sliderScrollEvent = YES;
+ }
+}
+
+- (void)removeEvent:(NSString *)eventName
+{
+ if ([eventName isEqualToString:@"change"]) {
+ _sliderChangeEvent = NO;
+ }
+ if ([eventName isEqualToString:@"scroll"]) {
+ _sliderScrollEvent = NO;
+ }
+}
+
+#pragma mark WXIndicatorComponentDelegate Methods
+
+-(void)setIndicatorView:(WXIndicatorView *)indicatorView
+{
+ NSAssert(_recycleSliderView, @"");
+ [_recycleSliderView setIndicator:indicatorView];
+}
+
+- (void)resumeAutoPlay:(id)resume
+{
+ if (_autoPlay) {
+ if ([resume boolValue]) {
+ [self _startAutoPlayTimer];
+ } else {
+ [self _stopAutoPlayTimer];
+ }
+ }
+}
+
+#pragma mark Private Methods
+
+- (void)_startAutoPlayTimer
+{
+ if (!self.autoTimer || ![self.autoTimer isValid]) {
+ __weak __typeof__(self) weakSelf = self;
+ self.autoTimer = [NSTimer wx_scheduledTimerWithTimeInterval:_interval/1000.0f block:^() {
+ [weakSelf _autoPlayOnTimer];
+ } repeats:YES];
+ [[NSRunLoop currentRunLoop] addTimer:self.autoTimer forMode:NSRunLoopCommonModes];
+ }
+}
+
+- (void)_stopAutoPlayTimer
+{
+ if (self.autoTimer && [self.autoTimer isValid]) {
+ [self.autoTimer invalidate];
+ self.autoTimer = nil;
+ }
+}
+
+- (void)_autoPlayOnTimer
+{
+ if (!_infinite && (_currentIndex == _recycleSliderView.itemViews.count - 1)) {
+ [self _stopAutoPlayTimer];
+ }else {
+ [self.recycleSliderView nextPage];
+ }
+}
+
+#pragma mark ScrollView Delegate
+
+- (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScroll:(UIScrollView *)scrollView
+{
+ if (_sliderScrollEvent) {
+ CGFloat width = scrollView.frame.size.width;
+ CGFloat XDeviation = 0;
+ if (_infinite) {
+ XDeviation = - (scrollView.contentOffset.x - width);
+ } else {
+ XDeviation = - (scrollView.contentOffset.x - width * _currentIndex);
+ }
+ CGFloat offsetXRatio = (XDeviation / width);
+ if (fabs(offsetXRatio - _lastOffsetXRatio) >= _offsetXAccuracy) {
+ _lastOffsetXRatio = offsetXRatio;
+ [self fireEvent:@"scroll" params:@{@"offsetXRatio":[NSNumber numberWithFloat:offsetXRatio]} domChanges:nil];
+ }
+ }
+}
+
+- (void)recycleSliderView:(WXRecycleSliderView *)recycleSliderView didScrollToItemAtIndex:(NSInteger)index
+{
+
+ if (_sliderChangeEvent) {
+ [self fireEvent:@"change" params:@{@"index":@(index)} domChanges:@{@"attrs": @{@"index": @(index)}}];
+ }
+ self.currentIndex = index;
+}
+
+- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
+{
+ [self _stopAutoPlayTimer];
+}
+
+- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
+{
+ if (_autoPlay) {
+ [self _startAutoPlayTimer];
+ }
+}
+
+@end
[04/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Layout/WXCoreStyle.cpp
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXCoreStyle.cpp b/ios/sdk/WeexSDK/Sources/Layout/WXCoreStyle.cpp
new file mode 100644
index 0000000..11f8dc8
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXCoreStyle.cpp
@@ -0,0 +1,247 @@
+#include "WXCoreStyle.h"
+
+namespace WeexCore {
+
+ bool WXCoreMargin::setMargin(const WXCoreMarginEdge &edge, float margin) {
+ bool dirty = false;
+ switch (edge) {
+ case kMarginALL:
+ if (mMarginLeft != margin
+ || mMarginTop != margin
+ || mMarginRight != margin
+ || mMarginBottom != margin) {
+ mMarginLeft = margin;
+ mMarginTop = margin;
+ mMarginRight = margin;
+ mMarginBottom = margin;
+ dirty = true;
+ }
+ break;
+ case kMarginLeft:
+ if (mMarginLeft != margin) {
+ mMarginLeft = margin;
+ dirty = true;
+ }
+ break;
+ case kMarginTop:
+ if (mMarginTop != margin) {
+ mMarginTop = margin;
+ dirty = true;
+ }
+ break;
+ case kMarginRight:
+ if (mMarginRight != margin) {
+ mMarginRight = margin;
+ dirty = true;
+ }
+ break;
+ case kMarginBottom:
+ if (mMarginBottom != margin) {
+ mMarginBottom = margin;
+ dirty = true;
+ }
+ break;
+ }
+ return dirty;
+ }
+
+ float WXCoreMargin::getMargin(const WXCoreMarginEdge &edge)const {
+ float margin = 0;
+ switch (edge) {
+ case kMarginLeft:
+ margin = mMarginLeft;
+ break;
+ case kMarginTop:
+ margin = mMarginTop;
+ break;
+ case kMarginRight:
+ margin = mMarginRight;
+ break;
+ case kMarginBottom:
+ margin = mMarginBottom;
+ break;
+ default:
+ break;
+ }
+ return margin;
+ }
+
+ bool WXCorePadding::setPadding(const WXCorePaddingEdge &edge, float padding) {
+ bool dirty = false;
+ switch (edge) {
+ case kPaddingALL:
+ if (mPaddingLeft != padding
+ || mPaddingTop != padding
+ || mPaddingRight != padding
+ || mPaddingBottom != padding) {
+ mPaddingLeft = padding;
+ mPaddingTop = padding;
+ mPaddingRight = padding;
+ mPaddingBottom = padding;
+ dirty = true;
+ }
+ break;
+ case kPaddingLeft:
+ if (mPaddingLeft != padding) {
+ mPaddingLeft = padding;
+ dirty = true;
+ }
+ break;
+ case kPaddingTop:
+ if (mPaddingTop != padding) {
+ mPaddingTop = padding;
+ dirty = true;
+ }
+ break;
+ case kPaddingRight:
+ if (mPaddingRight != padding) {
+ mPaddingRight = padding;
+ dirty = true;
+ }
+ break;
+ case kPaddingBottom:
+ if (mPaddingBottom != padding) {
+ mPaddingBottom = padding;
+ dirty = true;
+ }
+ break;
+ }
+ return dirty;
+ }
+
+ float WXCorePadding::getPadding(const WXCorePaddingEdge &edge)const {
+ float padding = 0;
+ switch (edge) {
+ case kPaddingLeft:
+ padding = mPaddingLeft;
+ break;
+ case kPaddingTop:
+ padding = mPaddingTop;
+ break;
+ case kPaddingRight:
+ padding = mPaddingRight;
+ break;
+ case kPaddingBottom:
+ padding = mPaddingBottom;
+ break;
+ default:
+ break;
+ }
+ return padding;
+ }
+
+ bool WXCoreBorderWidth::setBorderWidth(const WXCoreBorderWidthEdge &edge, float borderWidth) {
+ bool dirty = false;
+ switch (edge) {
+ case kBorderWidthALL:
+ if (mBorderWidthLeft != borderWidth
+ || mBorderWidthTop != borderWidth
+ || mBorderWidthRight != borderWidth
+ || mBorderWidthBottom != borderWidth) {
+ mBorderWidthLeft = borderWidth;
+ mBorderWidthTop = borderWidth;
+ mBorderWidthRight = borderWidth;
+ mBorderWidthBottom = borderWidth;
+ dirty = true;
+ }
+ break;
+ case kBorderWidthLeft:
+ if (mBorderWidthLeft != borderWidth) {
+ mBorderWidthLeft = borderWidth;
+ dirty = true;
+ }
+ break;
+ case kBorderWidthTop:
+ if (mBorderWidthTop != borderWidth) {
+ mBorderWidthTop = borderWidth;
+ dirty = true;
+ }
+ break;
+ case kBorderWidthRight:
+ if (mBorderWidthRight != borderWidth) {
+ mBorderWidthRight = borderWidth;
+ dirty = true;
+ }
+ break;
+ case kBorderWidthBottom:
+ if (mBorderWidthBottom != borderWidth) {
+ mBorderWidthBottom = borderWidth;
+ dirty = true;
+ }
+ break;
+ }
+ return dirty;
+ }
+
+ float WXCoreBorderWidth::getBorderWidth(const WXCoreBorderWidthEdge &edge)const {
+ float borderWidth = 0;
+ switch (edge) {
+ case kBorderWidthLeft:
+ borderWidth = mBorderWidthLeft;
+ break;
+ case kBorderWidthTop:
+ borderWidth = mBorderWidthTop;
+ break;
+ case kBorderWidthRight:
+ borderWidth = mBorderWidthRight;
+ break;
+ case kBorderWidthBottom:
+ borderWidth = mBorderWidthBottom;
+ break;
+ default:
+ break;
+ }
+ return borderWidth;
+ }
+
+ bool WXCorePosition::setPosition(const WXCorePositionEdge &edge, float position) {
+ bool dirty = false;
+ switch (edge) {
+ case kPositionEdgeLeft:
+ if (mLeft != position) {
+ mLeft = position;
+ dirty = true;
+ }
+ break;
+ case kPositionEdgeTop:
+ if (mTop != position) {
+ mTop = position;
+ dirty = true;
+ }
+ break;
+ case kPositionEdgeRight:
+ if (mRight != position) {
+ mRight = position;
+ dirty = true;
+ }
+ break;
+ case kPositionEdgeBottom:
+ if (mBottom != position) {
+ mBottom = position;
+ dirty = true;
+ }
+ break;
+ }
+ return dirty;
+ }
+
+ float WXCorePosition::getPosition(const WXCorePositionEdge &edge) {
+ float position = 0;
+ switch (edge) {
+ case kPositionEdgeLeft:
+ position = mLeft;
+ break;
+ case kPositionEdgeTop:
+ position = mTop;
+ break;
+ case kPositionEdgeRight:
+ position = mRight;
+ break;
+ case kPositionEdgeBottom:
+ position = mBottom;
+ break;
+ }
+ return position;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Layout/WXCoreStyle.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXCoreStyle.h b/ios/sdk/WeexSDK/Sources/Layout/WXCoreStyle.h
new file mode 100644
index 0000000..bc7dd06
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXCoreStyle.h
@@ -0,0 +1,285 @@
+#ifdef __cplusplus
+
+#ifndef WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUMS_H
+#define WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUMS_H
+
+
+#include "WXCoreFlexEnum.h"
+#include <cmath>
+
+namespace WeexCore {
+
+ /**
+ * Margin: margin-left、margin-right、margin-top、margin-bottom
+ */
+ class WXCoreMargin {
+ private:
+ float mMarginTop;
+ float mMarginBottom;
+ float mMarginLeft;
+ float mMarginRight;
+
+ public:
+ WXCoreMargin() : mMarginTop(0),
+ mMarginBottom(0),
+ mMarginLeft(0),
+ mMarginRight(0) {}
+
+ ~WXCoreMargin() {
+ mMarginTop = 0;
+ mMarginBottom = 0;
+ mMarginLeft = 0;
+ mMarginRight = 0;
+ }
+
+ bool setMargin(const WXCoreMarginEdge &edge, float margin);
+
+ float getMargin(const WXCoreMarginEdge &edge)const;
+ };
+
+
+ /**
+ * Padding:padding-left、padding-right、padding-top、padding-bottom
+ */
+ class WXCorePadding {
+ private:
+ float mPaddingTop;
+ float mPaddingBottom;
+ float mPaddingLeft;
+ float mPaddingRight;
+
+ public:
+ WXCorePadding() : mPaddingTop(0),
+ mPaddingBottom(0),
+ mPaddingLeft(0),
+ mPaddingRight(0) {}
+
+ ~WXCorePadding() {
+ mPaddingTop = 0;
+ mPaddingBottom = 0;
+ mPaddingLeft = 0;
+ mPaddingRight = 0;
+ }
+
+ bool setPadding(const WXCorePaddingEdge &edge, float padding);
+
+ float getPadding(const WXCorePaddingEdge &edge)const;
+ };
+
+
+ /**
+ * BorderWidth:borderwidth-left、borderwidth-right、borderwidth-top、borderwidth-bottom
+ */
+ class WXCoreBorderWidth {
+ private:
+ float mBorderWidthTop;
+ float mBorderWidthBottom;
+ float mBorderWidthLeft;
+ float mBorderWidthRight;
+
+ public:
+ WXCoreBorderWidth() : mBorderWidthTop(0),
+ mBorderWidthBottom(0),
+ mBorderWidthLeft(0),
+ mBorderWidthRight(0) {}
+
+ ~WXCoreBorderWidth() {
+ mBorderWidthTop = 0;
+ mBorderWidthBottom = 0;
+ mBorderWidthLeft = 0;
+ mBorderWidthRight = 0;
+ }
+
+ bool setBorderWidth(const WXCoreBorderWidthEdge &edge, float borderWidth);
+
+ float getBorderWidth(const WXCoreBorderWidthEdge &edge)const;
+ };
+
+
+ /**
+ * position:left、right、top、bottom
+ */
+ class WXCorePosition {
+ private:
+ float mTop;
+ float mBottom;
+ float mLeft;
+ float mRight;
+
+ public:
+ WXCorePosition() : mTop(NAN),
+ mBottom(NAN),
+ mLeft(NAN),
+ mRight(NAN) {}
+
+ ~WXCorePosition() {
+ reset();
+ }
+
+ inline bool isNAN() {
+ return isnan(mTop) || isnan(mBottom) || isnan(mLeft) || isnan(mRight);
+ }
+
+ inline void reset() {
+ mTop = 0;
+ mBottom = 0;
+ mLeft = 0;
+ mRight = 0;
+ }
+
+ bool setPosition(const WXCorePositionEdge &edge, float position);
+
+ float getPosition(const WXCorePositionEdge &edge);
+ };
+
+ enum DimensionLevel{
+ CSS_STYLE = 1,
+ INSTANCE_STYLE = 2,
+ FALLBACK_STYLE = 3
+ };
+
+ /**
+ * css-style
+ */
+ class WXCoreCSSStyle {
+ public:
+ /**
+ * The direction children items are placed inside the Flexbox layout, it determines the
+ * direction of the main axis (and the cross axis, perpendicular to the main axis).
+ * The default value is {@link WXCoreFlexDirection #WXCore_Flex_Direction_Row}.
+ */
+ WXCoreFlexDirection mFlexDirection;
+
+ /**
+ * This attribute controls whether the flex container is single-line or multi-line, and the
+ * direction of the cross axis.
+ * <ul>
+ * <li>{@link WXCoreFlexWrap}: The flex container is single-line.</li>
+ * <li>{@link WXCoreFlexWrap}: The flex container is multi-line.</li>
+ * <li>{@link WXCoreFlexWrap}: The flex container is multi-line. The direction of the
+ * cross axis is opposed to the direction as the {@link WXCoreFlexWrap}</li>
+ * </ul>
+ * The default value is {@link WXCoreFlexWrap #WXCore_Wrap_NoWrap}.
+ */
+ WXCoreFlexWrap mFlexWrap;
+
+ /**
+ * This attribute controls the alignment along the main axis.
+ * The default value is {@link WXCoreJustifyContent #WXCore_Justify_Flex_Start}.
+ */
+ WXCoreJustifyContent mJustifyContent;
+
+ /**
+ * This attribute controls the alignment along the cross axis.
+ * The default value is {@link WXCoreAlignItems #WXCore_AlignItems_Stretch}.
+ */
+ WXCoreAlignItems mAlignItems;
+
+ /**
+ * This attribute controls the alignment along the cross axis.
+ * The default value is {@link WXCoreAlignSelf #WXCore_AlignSelf_Auto}.
+ */
+ WXCoreAlignSelf mAlignSelf;
+
+ WXCorePositionType mPositionType;
+
+ float mFlexGrow;
+
+ float mMinWidth;
+
+ float mMinHeight;
+
+ float mMaxWidth;
+
+ float mMaxHeight;
+
+ float mStyleWidth;
+
+ float mStyleHeight;
+
+ DimensionLevel mStyleWidthLevel;
+
+ DimensionLevel mStyleHeightLevel;
+
+ WXCoreMargin mMargin;
+
+ WXCorePadding mPadding;
+
+ WXCoreBorderWidth mBorderWidth;
+
+ WXCorePosition mStylePosition;
+
+ constexpr static float kFlexGrowDefault = 0;
+
+ constexpr static WXCoreFlexDirection kFlexDirectionDefault= kFlexDirectionColumn;
+
+ constexpr static WXCoreFlexWrap kFlexWrapDefault = kNoWrap;
+
+ constexpr static WXCoreJustifyContent kFlexJustifyContentDefault = kJustifyFlexStart;
+
+ constexpr static WXCoreAlignItems kFlexAlignItemsDefault = kAlignItemsStretch;
+
+ constexpr static WXCoreAlignSelf kFlexAlignSelfDefault = kAlignSelfAuto;
+
+ constexpr static WXCorePositionType kWXCorePositionTypeDefault = kRelative;
+
+ WXCoreCSSStyle() : mFlexDirection(kFlexDirectionDefault),
+ mFlexWrap(kFlexWrapDefault),
+ mJustifyContent(kFlexJustifyContentDefault),
+ mAlignItems(kFlexAlignItemsDefault),
+ mAlignSelf(kFlexAlignSelfDefault),
+ mFlexGrow(kFlexGrowDefault),
+ mPositionType(kWXCorePositionTypeDefault),
+ mStyleWidth(NAN), mStyleHeight(NAN),
+ mStyleHeightLevel(FALLBACK_STYLE), mStyleWidthLevel(FALLBACK_STYLE),
+ mMaxWidth(NAN), mMaxHeight(NAN),
+ mMinWidth(NAN), mMinHeight(NAN) {
+
+ }
+
+ ~WXCoreCSSStyle() {
+ mFlexDirection = kFlexDirectionDefault;
+ mFlexWrap = kFlexWrapDefault;
+ mJustifyContent = kFlexJustifyContentDefault;
+ mAlignItems = kFlexAlignItemsDefault;
+ mAlignSelf = kFlexAlignSelfDefault;
+ mFlexGrow = kFlexGrowDefault;
+ mStyleWidth = NAN;
+ mStyleHeight = NAN;
+ mStyleWidthLevel = FALLBACK_STYLE;
+ mStyleHeightLevel = FALLBACK_STYLE;
+ mMaxWidth = NAN;
+ mMaxHeight = NAN;
+ mMinWidth = NAN;
+ mMinHeight = NAN;
+ }
+
+ inline float sumPaddingBorderOfEdge(const WXCoreEdge edge){
+ switch (edge) {
+ case kTop:
+ return mPadding.getPadding(kPaddingTop)
+ + mBorderWidth.getBorderWidth(kBorderWidthTop);
+ case kRight:
+ return mPadding.getPadding(kPaddingRight)
+ + mBorderWidth.getBorderWidth(kBorderWidthRight);
+ case kBottom:
+ return mPadding.getPadding(kPaddingBottom)
+ + mBorderWidth.getBorderWidth(kBorderWidthBottom);
+ case kLeft:
+ return mPadding.getPadding(kPaddingLeft)
+ + mBorderWidth.getBorderWidth(kBorderWidthLeft);
+ }
+ }
+
+ float sumMarginOfDirection(bool horizontal){
+ if(horizontal){
+ return mMargin.getMargin(kMarginLeft) + mMargin.getMargin(kMarginRight);
+ }
+ else{
+ return mMargin.getMargin(kMarginTop) + mMargin.getMargin(kMarginBottom);
+ }
+ }
+ };
+}
+#endif //WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUMS_H
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Layout/WXLayoutDefine.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXLayoutDefine.h b/ios/sdk/WeexSDK/Sources/Layout/WXLayoutDefine.h
index 7670085..93f43e9 100644
--- a/ios/sdk/WeexSDK/Sources/Layout/WXLayoutDefine.h
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXLayoutDefine.h
@@ -17,6 +17,9 @@
* under the License.
*/
+#if defined __cplusplus
+extern "C" {
+#endif
#define WX_LAYOUT_NAMESPACE wx_
@@ -53,5 +56,8 @@
#import "Layout.h"
+#if defined __cplusplus
+};
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Layout/WXScrollerComponent+Layout.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXScrollerComponent+Layout.h b/ios/sdk/WeexSDK/Sources/Layout/WXScrollerComponent+Layout.h
new file mode 100644
index 0000000..c9b633f
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXScrollerComponent+Layout.h
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXScrollerComponent.h"
+#import "WXComponent+Layout.h"
+
+
+@interface WXScrollerComponent ()
+{
+//#ifndef USE_FLEX
+ css_node_t *_scrollerCSSNode;
+//#else
+ WeexCore::WXCoreLayoutNode *_flexScrollerCSSNode;
+//#endif
+}
+@end
+
+@interface WXScrollerComponent (FlexLayout)
+
+//#ifndef USE_FLEX
+@property (nonatomic, readonly, assign) css_node_t *scrollerCSSNode;
+//#else
+@property (nonatomic, readonly, assign) WeexCore::WXCoreLayoutNode *flexScrollerCSSNode;
+//#endif
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Layout/WXScrollerComponent+Layout.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXScrollerComponent+Layout.mm b/ios/sdk/WeexSDK/Sources/Layout/WXScrollerComponent+Layout.mm
new file mode 100644
index 0000000..6eb8c9d
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXScrollerComponent+Layout.mm
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXScrollerComponent+Layout.h"
+#import "WXLog.h"
+
+@implementation WXScrollerComponent (FlexLayout)
+
+//#ifndef USE_FLEX
+- (css_node_t *)scrollerCSSNode
+{
+ return _scrollerCSSNode;
+}
+//#else
+- (WeexCore::WXCoreLayoutNode *)flexScrollerCSSNode{
+ return _flexScrollerCSSNode;
+}
+
+- (void)_insertChildCssNode:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ self.flexScrollerCSSNode->addChildAt(subcomponent.flexCssNode, (uint32_t) index);
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> ref:%@,subNodeIndex:%ld,childCount:%ld",self.ref,(long)index,_flexScrollerCSSNode->getChildCount());
+#endif
+
+// WXLogInfo(@"FlexLayout -- P:%@ -> C:%@",self,subcomponent);
+}
+- (void)_rmChildCssNode:(WXComponent *)subcomponent
+{
+ self.flexScrollerCSSNode->removeChild(subcomponent->_flexCssNode);
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> ref:%@ ,scrollerCSSNode->removeChild ,childRef:%@",self.ref,subcomponent.ref);
+#endif
+}
+
+//#endif
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m
deleted file mode 100644
index 175aeb7..0000000
--- a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m
+++ /dev/null
@@ -1,958 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXComponentManager.h"
-#import "WXComponent.h"
-#import "WXComponent_internal.h"
-#import "WXComponent+DataBinding.h"
-#import "WXComponentFactory.h"
-#import "WXDefine.h"
-#import "NSArray+Weex.h"
-#import "WXSDKInstance.h"
-#import "WXAssert.h"
-#import "WXUtility.h"
-#import "WXMonitor.h"
-#import "WXScrollerProtocol.h"
-#import "WXSDKManager.h"
-#import "WXSDKError.h"
-#import "WXInvocationConfig.h"
-#import "WXHandlerFactory.h"
-#import "WXValidateProtocol.h"
-#import "WXPrerenderManager.h"
-#import "WXTracingManager.h"
-#import "WXLayoutDefine.h"
-#import "WXSDKInstance_performance.h"
-#import "WXRootView.h"
-
-static NSThread *WXComponentThread;
-
-#define WXAssertComponentExist(component) WXAssert(component, @"component not exists")
-
-@implementation WXComponentManager
-{
- __weak WXSDKInstance *_weexInstance;
- BOOL _isValid;
-
- BOOL _stopRunning;
- NSUInteger _noTaskTickCount;
-
- // access only on component thread
- NSMapTable<NSString *, WXComponent *> *_indexDict;
- NSMutableArray<dispatch_block_t> *_uiTaskQueue;
- NSMutableDictionary *_uiPrerenderTaskQueue;
-
- WXComponent *_rootComponent;
- NSMutableArray *_fixedComponents;
-
- css_node_t *_rootCSSNode;
- CADisplayLink *_displayLink;
-}
-
-+ (instancetype)sharedManager
-{
- static id _sharedInstance = nil;
- static dispatch_once_t oncePredicate;
- dispatch_once(&oncePredicate, ^{
- _sharedInstance = [[self alloc] init];
- });
- return _sharedInstance;
-}
-
-- (instancetype)initWithWeexInstance:(id)weexInstance
-{
- if (self = [self init]) {
- _weexInstance = weexInstance;
-
- _indexDict = [NSMapTable strongToWeakObjectsMapTable];
- _fixedComponents = [NSMutableArray wx_mutableArrayUsingWeakReferences];
- _uiTaskQueue = [NSMutableArray array];
- _isValid = YES;
- [self _startDisplayLink];
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- free_css_node(_rootCSSNode);
- [NSMutableArray wx_releaseArray:_fixedComponents];
-}
-
-#pragma mark Thread Management
-
-+ (NSThread *)componentThread
-{
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- WXComponentThread = [[NSThread alloc] initWithTarget:[self sharedManager] selector:@selector(_runLoopThread) object:nil];
- [WXComponentThread setName:WX_COMPONENT_THREAD_NAME];
- if(WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
- [WXComponentThread setQualityOfService:[[NSThread mainThread] qualityOfService]];
- } else {
- [WXComponentThread setThreadPriority:[[NSThread mainThread] threadPriority]];
- }
-
- [WXComponentThread start];
- });
-
- return WXComponentThread;
-}
-
-- (void)_runLoopThread
-{
- [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
-
- while (!_stopRunning) {
- @autoreleasepool {
- [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
- }
- }
-}
-
-+ (void)_performBlockOnComponentThread:(void (^)(void))block
-{
- if([NSThread currentThread] == [self componentThread]){
- block();
- } else {
- [self performSelector:@selector(_performBlockOnComponentThread:)
- onThread:WXComponentThread
- withObject:[block copy]
- waitUntilDone:NO];
- }
-}
-
-+ (void)_performBlockSyncOnComponentThread:(void (^)(void))block
-{
- if([NSThread currentThread] == [self componentThread]){
- block();
- } else {
- [self performSelector:@selector(_performBlockOnComponentThread:)
- onThread:WXComponentThread
- withObject:[block copy]
- waitUntilDone:YES];
- }
-}
-
-- (void)startComponentTasks
-{
- [self _awakeDisplayLink];
-}
-
-- (void)rootViewFrameDidChange:(CGRect)frame
-{
- WXAssertComponentThread();
-
- if (_rootCSSNode) {
- [self _applyRootFrame:frame toRootCSSNode:_rootCSSNode];
- if (!_rootComponent.styles[@"width"]) {
- _rootComponent.cssNode->style.dimensions[CSS_WIDTH] = frame.size.width ?: CSS_UNDEFINED;
- }
- if (!_rootComponent.styles[@"height"]) {
- _rootComponent.cssNode->style.dimensions[CSS_HEIGHT] = frame.size.height ?: CSS_UNDEFINED;
- }
- [_rootComponent setNeedsLayout];
- [self startComponentTasks];
- }
-}
-
-- (void)_applyRootFrame:(CGRect)rootFrame toRootCSSNode:(css_node_t *)rootCSSNode
-{
- _rootCSSNode->style.position[CSS_LEFT] = self.weexInstance.frame.origin.x;
- _rootCSSNode->style.position[CSS_TOP] = self.weexInstance.frame.origin.y;
-
- // if no instance width/height, use layout width/height, as Android's wrap_content
- _rootCSSNode->style.dimensions[CSS_WIDTH] = self.weexInstance.frame.size.width ?: CSS_UNDEFINED;
- _rootCSSNode->style.dimensions[CSS_HEIGHT] = self.weexInstance.frame.size.height ?: CSS_UNDEFINED;
-}
-
-- (void)_addUITask:(void (^)(void))block
-{
- if(!_uiPrerenderTaskQueue){
- _uiPrerenderTaskQueue = [NSMutableDictionary new];
- }
- if(self.weexInstance.needPrerender){
- NSMutableArray<dispatch_block_t> *tasks = [_uiPrerenderTaskQueue objectForKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]];
- if(!tasks){
- tasks = [NSMutableArray new];
- }
- [tasks addObject:block];
- [_uiPrerenderTaskQueue setObject:tasks forKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]];
- }else{
- [_uiTaskQueue addObject:block];
- }
-}
-
-- (void)excutePrerenderUITask:(NSString *)url
-{
- NSMutableArray *tasks = [_uiPrerenderTaskQueue objectForKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]];
- for (id block in tasks) {
- [_uiTaskQueue addObject:block];
- }
- tasks = [NSMutableArray new];
- [_uiPrerenderTaskQueue setObject:tasks forKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]];
-}
-
-#pragma mark Component Tree Building
-
-- (void)createRoot:(NSDictionary *)data
-{
- WXAssertComponentThread();
- WXAssertParam(data);
-
- _rootComponent = [self _buildComponentForData:data supercomponent:nil];
-
- [self _initRootCSSNode];
-
- NSArray *subcomponentsData = [data valueForKey:@"children"];
- if (subcomponentsData) {
- BOOL appendTree = [_rootComponent.attributes[@"append"] isEqualToString:@"tree"];
- for(NSDictionary *subcomponentData in subcomponentsData){
- [self _recursivelyAddComponent:subcomponentData toSupercomponent:_rootComponent atIndex:-1 appendingInTree:appendTree];
- }
- }
-
- __weak typeof(self) weakSelf = self;
- WX_MONITOR_INSTANCE_PERF_END(WXFirstScreenJSFExecuteTime, self.weexInstance);
- [self _addUITask:^{
- [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:data[@"ref"] className:nil name:data[@"type"] phase:WXTracingBegin functionName:@"createBody" options:@{@"threadName":WXTUIThread}];
- __strong typeof(self) strongSelf = weakSelf;
- strongSelf.weexInstance.rootView.wx_component = strongSelf->_rootComponent;
- [strongSelf.weexInstance.rootView addSubview:strongSelf->_rootComponent.view];
- [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:data[@"ref"] className:nil name:data[@"type"] phase:WXTracingEnd functionName:@"createBody" options:@{@"threadName":WXTUIThread}];
- }];
-
-
-}
-
-static bool rootNodeIsDirty(void *context)
-{
- WXComponentManager *manager = (__bridge WXComponentManager *)(context);
- return [manager->_rootComponent needsLayout];
-}
-
-static css_node_t * rootNodeGetChild(void *context, int i)
-{
- WXComponentManager *manager = (__bridge WXComponentManager *)(context);
- if (i == 0) {
- return manager->_rootComponent.cssNode;
- } else if(manager->_fixedComponents.count >= i) {
- return ((WXComponent *)((manager->_fixedComponents)[i-1])).cssNode;
- }
-
- return NULL;
-}
-
-- (void)addComponent:(NSDictionary *)componentData toSupercomponent:(NSString *)superRef atIndex:(NSInteger)index appendingInTree:(BOOL)appendingInTree
-{
- WXAssertComponentThread();
- WXAssertParam(componentData);
- WXAssertParam(superRef);
-
- WXComponent *supercomponent = [_indexDict objectForKey:superRef];
- WXAssertComponentExist(supercomponent);
-
- [self _recursivelyAddComponent:componentData toSupercomponent:supercomponent atIndex:index appendingInTree:appendingInTree];
-}
-
-- (void)_recursivelyAddComponent:(NSDictionary *)componentData toSupercomponent:(WXComponent *)supercomponent atIndex:(NSInteger)index appendingInTree:(BOOL)appendingInTree
-{
- WXComponent *component = [self _buildComponentForData:componentData supercomponent:supercomponent];
- if (!supercomponent.subcomponents) {
- index = 0;
- } else {
- index = (index == -1 ? supercomponent->_subcomponents.count : index);
- }
-
- [supercomponent _insertSubcomponent:component atIndex:index];
- // use _lazyCreateView to forbid component like cell's view creating
- if(supercomponent && component && supercomponent->_lazyCreateView) {
- component->_lazyCreateView = YES;
- }
-
- [self recordMaximumVirtualDom:component];
-
- if (!component->_isTemplate) {
- __weak typeof(self) weakSelf = self;
- BOOL isFSCreateFinish = [self weexInstance].isJSCreateFinish;
- [self _addUITask:^{
- [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTUIThread}];
- [supercomponent insertSubview:component atIndex:index];
- [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingEnd functionName:@"addElement" options:@{@"threadName":WXTUIThread}];
- [weakSelf onElementChange:isFSCreateFinish];
- }];
- }
-
- NSArray *subcomponentsData = [componentData valueForKey:@"children"];
-
- BOOL appendTree = !appendingInTree && [component.attributes[@"append"] isEqualToString:@"tree"];
- // if ancestor is appending tree, child should not be laid out again even it is appending tree.
- for(NSDictionary *subcomponentData in subcomponentsData){
- [self _recursivelyAddComponent:subcomponentData toSupercomponent:component atIndex:-1 appendingInTree:appendTree || appendingInTree];
- }
-
- [component _didInserted];
-
- if (appendTree) {
- // If appending tree,force layout in case of too much tasks piling up in syncQueue
- [self _layoutAndSyncUI];
- }
-}
-
-- (void)moveComponent:(NSString *)ref toSuper:(NSString *)superRef atIndex:(NSInteger)index
-{
- WXAssertComponentThread();
- WXAssertParam(ref);
- WXAssertParam(superRef);
-
- WXComponent *component = [_indexDict objectForKey:ref];
- WXComponent *newSupercomponent = [_indexDict objectForKey:superRef];
- WXAssertComponentExist(component);
- WXAssertComponentExist(newSupercomponent);
-
- if (component.supercomponent == newSupercomponent && [newSupercomponent.subcomponents indexOfObject:component] < index) {
- // if the supercomponent moved to is the same as original supercomponent,
- // unify it into the index after removing.
- index--;
- }
-
- [component _moveToSupercomponent:newSupercomponent atIndex:index];
- __weak typeof(self) weakSelf = self;
- [self _addUITask:^{
- [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTUIThread}];
- [component moveToSuperview:newSupercomponent atIndex:index];
- [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingEnd functionName:@"addElement" options:@{@"threadName":WXTUIThread}];
- }];
-}
-
-- (void)removeComponent:(NSString *)ref
-{
- WXAssertComponentThread();
- WXAssertParam(ref);
-
- WXComponent *component = [_indexDict objectForKey:ref];
- WXAssertComponentExist(component);
-
- [component _removeFromSupercomponent];
-
- [_indexDict removeObjectForKey:ref];
-
- __weak typeof(self) weakSelf = self;
- BOOL isFSCreateFinish = [self weexInstance].isJSCreateFinish;
- [self _addUITask:^{
- [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingBegin functionName:@"removeElement" options:@{@"threadName":WXTUIThread}];
- if (component.supercomponent) {
- [component.supercomponent willRemoveSubview:component];
- }
- [component removeFromSuperview];
- [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingEnd functionName:@"removeElement" options:@{@"threadName":WXTUIThread}];
- [weakSelf onElementChange:isFSCreateFinish];
- }];
-
- [self _checkFixedSubcomponentToRemove:component];
-
-}
-
-- (void)onElementChange:(BOOL)isFSCreateFinish
-{
- if (!isFSCreateFinish) {
- return;
- }
-
- UIView *root = [self weexInstance].rootView;
- BOOL hasEvent = TRUE;
- if (root && [root isKindOfClass:[WXRootView class]]) {
- WXRootView* wxRootView = (WXRootView *)root;
- hasEvent = [wxRootView isHasEvent];
- }
- if (hasEvent) {
- return;
- }
- double current = CACurrentMediaTime()*1000;
-
- double diff = current - [self weexInstance].performance.jsCreateFinishTime;
- if (diff > 8000) {
- return;
- }
- [self weexInstance].performance.interactionTime = current - self.weexInstance.performance.renderTimeOrigin;
-}
-
-- (void)recordMaximumVirtualDom:(WXComponent*) component
-{
- WXAssertComponentExist(component);
- if(!component){
- return;
- }
- int maxDeep =0;
- while (component) {
- maxDeep++;
- component = component.supercomponent;
- }
- if(maxDeep > [self weexInstance].performance.maxVdomDeep)
- {
- [self weexInstance].performance.maxVdomDeep = maxDeep;
- }
-
-}
-
-- (void)_checkFixedSubcomponentToRemove:(WXComponent *)component
-{
- for (WXComponent *subcomponent in component.subcomponents) {
- if (subcomponent->_positionType == WXPositionTypeFixed) {
- [self _addUITask:^{
- [subcomponent removeFromSuperview];
- }];
- }
-
- [self _checkFixedSubcomponentToRemove:subcomponent];
- }
-}
-
-- (WXComponent *)componentForRef:(NSString *)ref
-{
- WXAssertComponentThread();
-
- return [_indexDict objectForKey:ref];
-}
-
-- (WXComponent *)componentForRoot
-{
- return _rootComponent;
-}
-
-- (NSUInteger)numberOfComponents
-{
- WXAssertComponentThread();
-
- return _indexDict.count;
-}
-
-- (WXComponent *)_buildComponentForData:(NSDictionary *)data supercomponent:(WXComponent *)supercomponent
-{
- NSString *ref = data[@"ref"];
- NSString *type = data[@"type"];
- NSDictionary *styles = data[@"style"];
- NSDictionary *attributes = data[@"attr"];
- NSArray *events = data[@"event"];
-
- if (self.weexInstance.needValidate) {
- id<WXValidateProtocol> validateHandler = [WXHandlerFactory handlerForProtocol:@protocol(WXValidateProtocol)];
- if (validateHandler) {
- WXComponentValidateResult* validateResult;
- if ([validateHandler respondsToSelector:@selector(validateWithWXSDKInstance:component:supercomponent:)]) {
- validateResult = [validateHandler validateWithWXSDKInstance:self.weexInstance component:type supercomponent:supercomponent];
- }
- if (validateResult==nil || !validateResult.isSuccess) {
- type = validateResult.replacedComponent? validateResult.replacedComponent : @"div";
- WXLogError(@"%@",[validateResult.error.userInfo objectForKey:@"errorMsg"]);
- }
- }
- }
-
- WXComponentConfig *config = [WXComponentFactory configWithComponentName:type];
- BOOL isTemplate = [config.properties[@"isTemplate"] boolValue] || (supercomponent && supercomponent->_isTemplate);
- NSDictionary *bindingStyles;
- NSDictionary *bindingAttibutes;
- NSDictionary *bindingEvents;
- NSDictionary *bindingProps;
- if (isTemplate) {
- bindingProps = [self _extractBindingProps:&attributes];
- bindingStyles = [self _extractBindings:&styles];
- bindingAttibutes = [self _extractBindings:&attributes];
- bindingEvents = [self _extractBindingEvents:&events];
- }
-
- Class clazz = NSClassFromString(config.clazz);;
- WXComponent *component = [[clazz alloc] initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:self.weexInstance];
- if (isTemplate) {
- component->_isTemplate = YES;
- [component _storeBindingsWithProps:bindingProps styles:bindingStyles attributes:bindingAttibutes events:bindingEvents];
- }
-
- WXAssert(component, @"Component build failed for data:%@", data);
-
- [_indexDict setObject:component forKey:component.ref];
- [component readyToRender];// notify redyToRender event when init
- return component;
-}
-
-- (void)addComponent:(WXComponent *)component toIndexDictForRef:(NSString *)ref
-{
- [_indexDict setObject:component forKey:ref];
-}
-
-- (NSDictionary *)_extractBindings:(NSDictionary **)attributesOrStylesPoint
-{
- NSDictionary *attributesOrStyles = *attributesOrStylesPoint;
- if (!attributesOrStyles) {
- return nil;
- }
-
- NSMutableDictionary *newAttributesOrStyles = [attributesOrStyles mutableCopy];
- NSMutableDictionary *bindingAttributesOrStyles = [NSMutableDictionary dictionary];
-
- [attributesOrStyles enumerateKeysAndObjectsUsingBlock:^(id _Nonnull attributeOrStyleName, id _Nonnull attributeOrStyle, BOOL * _Nonnull stop) {
- if ([WXBindingMatchIdentify isEqualToString:attributeOrStyleName] // match
- || [WXBindingRepeatIdentify isEqualToString:attributeOrStyleName] // repeat
- || [WXBindingOnceIdentify isEqualToString:attributeOrStyleName] // once
- ||([attributeOrStyle isKindOfClass:[NSDictionary class]] && attributeOrStyle[WXBindingIdentify])) { // {"attributeOrStyleName": {"@binding":"bindingExpression"}
- bindingAttributesOrStyles[attributeOrStyleName] = attributeOrStyle;
- [newAttributesOrStyles removeObjectForKey:attributeOrStyleName];
- } else if ([attributeOrStyle isKindOfClass:[NSArray class]]) {
- // {"attributeOrStyleName":[..., "string", {"@binding":"bindingExpression"}, "string", {"@binding":"bindingExpression"}, ...]
- __block BOOL isBinding = NO;
- [attributeOrStyle enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
- if ([obj isKindOfClass:[NSDictionary class]] && obj[WXBindingIdentify]) {
- isBinding = YES;
- *stop = YES;
- }
- }];
-
- if (isBinding) {
- bindingAttributesOrStyles[attributeOrStyleName] = attributeOrStyle;
- [newAttributesOrStyles removeObjectForKey:attributeOrStyleName];
- }
- }
- }];
-
- *attributesOrStylesPoint = newAttributesOrStyles;
-
- return bindingAttributesOrStyles;
-}
-
-- (NSDictionary *)_extractBindingEvents:(NSArray **)eventsPoint
-{
- NSArray *events = *eventsPoint;
- NSMutableArray *newEvents = [events mutableCopy];
- NSMutableDictionary *bindingEvents = [NSMutableDictionary dictionary];
- [events enumerateObjectsUsingBlock:^(id _Nonnull event, NSUInteger idx, BOOL * _Nonnull stop) {
- if ([event isKindOfClass:[NSDictionary class]] && event[@"type"] && event[@"params"]) {
- NSString *eventName = event[@"type"];
- NSString *bindingParams = event[@"params"];
- bindingEvents[eventName] = bindingParams;
- newEvents[idx] = eventName;
- }
- }];
-
- *eventsPoint = newEvents;
- return bindingEvents;
-}
-
-- (NSDictionary *)_extractBindingProps:(NSDictionary **)attributesPoint
-{
- NSDictionary *attributes = *attributesPoint;
- if (attributes[@"@componentProps"]) {
- NSMutableDictionary *newAttributes = [attributes mutableCopy];
- [newAttributes removeObjectForKey:@"@componentProps"];
- *attributesPoint = newAttributes;
- return attributes[@"@componentProps"];
- }
-
- return nil;
-}
-
-#pragma mark Reset
--(BOOL)isShouldReset:(id )value
-{
- if([value isKindOfClass:[NSString class]]) {
- if(!value || [@"" isEqualToString:value]) {
- return YES;
- }
- }
- return NO;
-}
-
--(void)filterStyles:(NSDictionary *)styles normalStyles:(NSMutableDictionary *)normalStyles resetStyles:(NSMutableArray *)resetStyles
-{
- for (NSString *key in styles) {
- id value = [styles objectForKey:key];
- if([self isShouldReset:value]) {
- [resetStyles addObject:key];
- }else{
- [normalStyles setObject:styles[key] forKey:key];
- }
- }
-}
-
-- (void)updateStyles:(NSDictionary *)styles forComponent:(NSString *)ref
-{
- [self handleStyles:styles forComponent:ref isUpdateStyles:YES];
-}
-
-- (void)updatePseudoClassStyles:(NSDictionary *)styles forComponent:(NSString *)ref
-{
- [self handleStyles:styles forComponent:ref isUpdateStyles:NO];
-}
-
-- (void)handleStyleOnMainThread:(NSDictionary*)styles forComponent:(WXComponent *)component isUpdateStyles:(BOOL)isUpdateStyles
-{
- WXAssertParam(styles);
- WXAssertParam(component);
- WXAssertMainThread();
-
- NSMutableDictionary *normalStyles = [NSMutableDictionary new];
- NSMutableArray *resetStyles = [NSMutableArray new];
- [self filterStyles:styles normalStyles:normalStyles resetStyles:resetStyles];
- [component _updateStylesOnMainThread:normalStyles resetStyles:resetStyles];
- [component readyToRender];
-
- WXPerformBlockOnComponentThread(^{
- [component _updateStylesOnComponentThread:normalStyles resetStyles:resetStyles isUpdateStyles:isUpdateStyles];
- });
-}
-
-- (void)handleStyles:(NSDictionary *)styles forComponent:(NSString *)ref isUpdateStyles:(BOOL)isUpdateStyles
-{
- WXAssertParam(styles);
- WXAssertParam(ref);
-
- WXComponent *component = [_indexDict objectForKey:ref];
- WXAssertComponentExist(component);
-
- NSMutableDictionary *normalStyles = [NSMutableDictionary new];
- NSMutableArray *resetStyles = [NSMutableArray new];
- [self filterStyles:styles normalStyles:normalStyles resetStyles:resetStyles];
- [component _updateStylesOnComponentThread:normalStyles resetStyles:resetStyles isUpdateStyles:isUpdateStyles];
- [self _addUITask:^{
- [component _updateStylesOnMainThread:normalStyles resetStyles:resetStyles];
- [component readyToRender];
- }];
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes forComponent:(NSString *)ref
-{
- WXAssertParam(attributes);
- WXAssertParam(ref);
-
- WXComponent *component = [_indexDict objectForKey:ref];
- WXAssertComponentExist(component);
-
- [component _updateAttributesOnComponentThread:attributes];
- __weak typeof(self) weakSelf = self;
- [self _addUITask:^{
- [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingBegin functionName:@"updateAttrs" options:@{@"threadName":WXTUIThread}];
- [component _updateAttributesOnMainThread:attributes];
- [component readyToRender];
- [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingEnd functionName:@"updateAttrs" options:@{@"threadName":WXTUIThread}];
- }];
-}
-
-- (void)addEvent:(NSString *)eventName toComponent:(NSString *)ref
-{
- WXAssertComponentThread();
- WXAssertParam(eventName);
- WXAssertParam(ref);
-
- WXComponent *component = [_indexDict objectForKey:ref];
- WXAssertComponentExist(component);
-
- [component _addEventOnComponentThread:eventName];
-
- [self _addUITask:^{
- [component _addEventOnMainThread:eventName];
- }];
-}
-
-- (void)removeEvent:(NSString *)eventName fromComponent:(NSString *)ref
-{
- WXAssertComponentThread();
- WXAssertParam(eventName);
- WXAssertParam(ref);
-
- WXComponent *component = [_indexDict objectForKey:ref];
- WXAssertComponentExist(component);
-
- [component _removeEventOnComponentThread:eventName];
-
- [self _addUITask:^{
- [component _removeEventOnMainThread:eventName];
- }];
-}
-
-- (void)scrollToComponent:(NSString *)ref options:(NSDictionary *)options
-{
- WXAssertComponentThread();
- WXAssertParam(ref);
-
- WXComponent *toComponent = [_indexDict objectForKey:ref];
- WXAssertComponentExist(toComponent);
-
- id<WXScrollerProtocol> scrollerComponent = toComponent.ancestorScroller;
- if (!scrollerComponent) {
- return;
- }
-
- CGFloat offset = [[options objectForKey:@"offset"] floatValue];
- BOOL animated = YES;
- if ([options objectForKey:@"animated"]) {
- animated = [[options objectForKey:@"animated"] boolValue];
- }
-
- [self _addUITask:^{
- [scrollerComponent scrollToComponent:toComponent withOffset:offset animated:animated];
- }];
-}
-
-#pragma mark Life Cycle
-
-- (void)createFinish
-{
- WXAssertComponentThread();
-
- WXSDKInstance *instance = self.weexInstance;
- [self _addUITask:^{
- UIView *rootView = instance.rootView;
-
- //WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, instance);
- WX_MONITOR_INSTANCE_PERF_END(WXPTAllRender, instance);
- WX_MONITOR_SUCCESS(WXMTJSBridge);
- WX_MONITOR_SUCCESS(WXMTNativeRender);
-
- if(instance.renderFinish){
- [WXTracingManager startTracingWithInstanceId:instance.instanceId ref:nil className:nil name:nil phase:WXTracingInstant functionName:WXTRenderFinish options:@{@"threadName":WXTUIThread}];
- instance.renderFinish(rootView);
- }
- }];
- [instance updatePerDicAfterCreateFinish];
-}
-
-- (void)updateFinish
-{
- WXAssertComponentThread();
-
- WXSDKInstance *instance = self.weexInstance;
- WXComponent *root = [_indexDict objectForKey:WX_SDK_ROOT_REF];
-
- [self _addUITask:^{
- if(instance.updateFinish){
- instance.updateFinish(root.view);
- }
- }];
-}
-
-- (void)refreshFinish
-{
- WXAssertComponentThread();
-
- WXSDKInstance *instance = self.weexInstance;
- WXComponent *root = [_indexDict objectForKey:WX_SDK_ROOT_REF];
-
- [self _addUITask:^{
- if(instance.refreshFinish){
- instance.refreshFinish(root.view);
- }
- }];
-}
-
-- (void)unload
-{
- WXAssertComponentThread();
- [self invalidate];
- [self _stopDisplayLink];
- NSEnumerator *enumerator = [[_indexDict copy] objectEnumerator];
- dispatch_async(dispatch_get_main_queue(), ^{
- WXComponent *component;
- while ((component = [enumerator nextObject])) {
- [component _unloadViewWithReusing:NO];
- }
- _rootComponent = nil;
- });
-
- [_indexDict removeAllObjects];
- [_uiTaskQueue removeAllObjects];
-}
-
-- (void)invalidate
-{
- _isValid = NO;
-}
-
-- (BOOL)isValid
-{
- return _isValid;
-}
-
-#pragma mark Layout Batch
-
-- (void)_startDisplayLink
-{
- WXAssertComponentThread();
-
- if(!_displayLink){
- _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_handleDisplayLink)];
- [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- }
-}
-
-- (void)_stopDisplayLink
-{
- WXAssertComponentThread();
-
- if(_displayLink){
- [_displayLink invalidate];
- _displayLink = nil;
- }
-}
-
-- (void)_suspendDisplayLink
-{
- WXAssertComponentThread();
-
- if(_displayLink && !_displayLink.paused) {
- _displayLink.paused = YES;
- }
-}
-
-- (void)_awakeDisplayLink
-{
- WXAssertComponentThread();
-
- if(_displayLink && _displayLink.paused) {
- _displayLink.paused = NO;
- }
-}
-
-- (void)_handleDisplayLink
-{
- WXAssertComponentThread();
-
- [self _layoutAndSyncUI];
-}
-
-- (void)_layoutAndSyncUI
-{
- [self _layout];
- if(_uiTaskQueue.count > 0){
- [self _syncUITasks];
- _noTaskTickCount = 0;
- } else {
- // suspend display link when there's no task for 1 second, in order to save CPU time.
- _noTaskTickCount ++;
- if (_noTaskTickCount > 60) {
- [self _suspendDisplayLink];
- }
- }
-}
-
-- (void)_layout
-{
- BOOL needsLayout = NO;
-
- NSEnumerator *enumerator = [_indexDict objectEnumerator];
- WXComponent *component;
- while ((component = [enumerator nextObject])) {
- if ([component needsLayout]) {
- needsLayout = YES;
- break;
- }
- }
-
- if (!needsLayout) {
- return;
- }
-
- layoutNode(_rootCSSNode, _rootCSSNode->style.dimensions[CSS_WIDTH], _rootCSSNode->style.dimensions[CSS_HEIGHT], CSS_DIRECTION_INHERIT);
-
- if ([_rootComponent needsLayout]) {
- if ([WXLog logLevel] >= WXLogLevelDebug) {
- print_css_node(_rootCSSNode, CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN);
- }
- }
-
- NSMutableSet<WXComponent *> *dirtyComponents = [NSMutableSet set];
- [_rootComponent _calculateFrameWithSuperAbsolutePosition:CGPointZero gatherDirtyComponents:dirtyComponents];
- [self _calculateRootFrame];
-
- for (WXComponent *dirtyComponent in dirtyComponents) {
- [self _addUITask:^{
- [dirtyComponent _layoutDidFinish];
- }];
- }
-}
-
-- (void)_syncUITasks
-{
- NSArray<dispatch_block_t> *blocks = _uiTaskQueue;
- _uiTaskQueue = [NSMutableArray array];
- dispatch_async(dispatch_get_main_queue(), ^{
- for(dispatch_block_t block in blocks) {
- block();
- }
- });
-}
-
-- (void)_initRootCSSNode
-{
- _rootCSSNode = new_css_node();
-
- [self _applyRootFrame:self.weexInstance.frame toRootCSSNode:_rootCSSNode];
-
- _rootCSSNode->style.flex_wrap = CSS_NOWRAP;
- _rootCSSNode->is_dirty = rootNodeIsDirty;
- _rootCSSNode->get_child = rootNodeGetChild;
- _rootCSSNode->context = (__bridge void *)(self);
- _rootCSSNode->children_count = 1;
-}
-
-- (void)_calculateRootFrame
-{
- if (!_rootCSSNode->layout.should_update) {
- return;
- }
- _rootCSSNode->layout.should_update = false;
-
- CGRect frame = CGRectMake(WXRoundPixelValue(_rootCSSNode->layout.position[CSS_LEFT]),
- WXRoundPixelValue(_rootCSSNode->layout.position[CSS_TOP]),
- WXRoundPixelValue(_rootCSSNode->layout.dimensions[CSS_WIDTH]),
- WXRoundPixelValue(_rootCSSNode->layout.dimensions[CSS_HEIGHT]));
- WXPerformBlockOnMainThread(^{
- if(!self.weexInstance.isRootViewFrozen) {
- self.weexInstance.rootView.frame = frame;
- }
- });
-
- resetNodeLayout(_rootCSSNode);
-}
-
-
-#pragma mark Fixed
-
-- (void)addFixedComponent:(WXComponent *)fixComponent
-{
- [_fixedComponents addObject:fixComponent];
- _rootCSSNode->children_count = (int)[_fixedComponents count] + 1;
-}
-
-- (void)removeFixedComponent:(WXComponent *)fixComponent
-{
- [_fixedComponents removeObject:fixComponent];
- _rootCSSNode->children_count = (int)[_fixedComponents count] + 1;
-}
-
-@end
-
-void WXPerformBlockOnComponentThread(void (^block)(void))
-{
- [WXComponentManager _performBlockOnComponentThread:block];
-}
-
-void WXPerformBlockSyncOnComponentThread(void (^block)(void))
-{
- [WXComponentManager _performBlockSyncOnComponentThread:block];
-}
[02/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm b/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm
new file mode 100644
index 0000000..0556ca5
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm
@@ -0,0 +1,953 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXComponent.h"
+#import "WXComponent_internal.h"
+#import "WXComponentManager.h"
+#import "WXSDKManager.h"
+#import "WXSDKInstance.h"
+#import "WXSDKInstance_private.h"
+#import "WXDefine.h"
+#import "WXLog.h"
+#import "WXWeakObjectWrapper.h"
+#import "WXUtility.h"
+#import "WXConvert.h"
+#import "WXMonitor.h"
+#import "WXAssert.h"
+#import "WXThreadSafeMutableDictionary.h"
+#import "WXThreadSafeMutableArray.h"
+#import "WXTransform.h"
+#import "WXRoundedRect.h"
+#import <pthread/pthread.h>
+#import "WXComponent+PseudoClassManagement.h"
+#import "WXComponent+BoxShadow.h"
+#import "WXTracingManager.h"
+#import "WXComponent+Events.h"
+#import "WXComponent+Layout.h"
+
+#pragma clang diagnostic ignored "-Wincomplete-implementation"
+#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+
+@interface WXComponent () <UIGestureRecognizerDelegate>
+
+@end
+
+@implementation WXComponent
+{
+@private
+ NSString *_ref;
+ NSMutableDictionary *_styles;
+ NSMutableDictionary *_attributes;
+ NSMutableArray *_events;
+
+ // Protects properties styles/attributes/events/subcomponents which will be accessed from multiple threads.
+ pthread_mutex_t _propertyMutex;
+ pthread_mutexattr_t _propertMutexAttr;
+
+ __weak WXComponent *_supercomponent;
+ __weak id<WXScrollerProtocol> _ancestorScroller;
+ __weak WXSDKInstance *_weexInstance;
+}
+
+#pragma mark Life Cycle
+
+- (instancetype)initWithRef:(NSString *)ref
+ type:(NSString *)type
+ styles:(NSDictionary *)styles
+ attributes:(NSDictionary *)attributes
+ events:(NSArray *)events
+ weexInstance:(WXSDKInstance *)weexInstance
+{
+ if (self = [super init]) {
+ pthread_mutexattr_init(&_propertMutexAttr);
+ pthread_mutexattr_settype(&_propertMutexAttr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&_propertyMutex, &_propertMutexAttr);
+
+ _ref = ref;
+ _type = type;
+ _weexInstance = weexInstance;
+ _componentType = WXComponentTypeCommon;
+ _styles = [self parseStyles:styles];
+ _attributes = attributes ? [NSMutableDictionary dictionaryWithDictionary:attributes] : [NSMutableDictionary dictionary];
+ _events = events ? [NSMutableArray arrayWithArray:events] : [NSMutableArray array];
+ _subcomponents = [NSMutableArray array];
+ _absolutePosition = CGPointMake(NAN, NAN);
+
+ _displayType = WXDisplayTypeBlock;
+ _isNeedJoinLayoutSystem = YES;
+ _isLayoutDirty = YES;
+ _isViewFrameSyncWithCalculated = YES;
+ _ariaHidden = nil;
+ _accessible = nil;
+ _accessibilityHintContent = nil;
+
+ _async = NO;
+
+ if (styles[kWXTransitionProperty]) {
+ _transition = [[WXTransition alloc]initWithStyles:styles];
+ }
+
+ //TODO set indicator style
+ if ([type isEqualToString:@"indicator"]) {
+ _styles[@"position"] = @"absolute";
+ if (!_styles[@"left"] && !_styles[@"right"]) {
+ _styles[@"left"] = @0.0f;
+ }
+ if (!_styles[@"top"] && !_styles[@"bottom"]) {
+ _styles[@"top"] = @0.0f;
+ }
+ }
+
+ if (attributes[@"ariaHidden"]) {
+
+ _ariaHidden = [WXConvert NSString:attributes[@"ariaHidden"]];
+ }
+ if (attributes[@"role"]) {
+ _roles = attributes[@"role"];
+ }
+ if (attributes[@"ariaLabel"]) {
+ _ariaLabel = [WXConvert NSString:attributes[@"ariaLabel"]];
+ }
+ if (attributes[@"accessible"]) {
+ _accessible = [WXConvert NSString:attributes[@"accessible"]];
+ }
+ if(attributes[@"accessibilityHint"]) {
+ _accessibilityHintContent = [WXConvert NSString:attributes[@"accessibilityHint"]];
+ }
+ if (attributes[@"groupAccessibilityChildren"]) {
+ _groupAccessibilityChildren = [WXConvert NSString:attributes[@"groupAccessibilityChildren"]];
+ }
+
+ if (attributes[@"testId"]) {
+ _testId = [WXConvert NSString:attributes[@"testId"]];
+ }
+
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> init component: ref : %@ , styles: %@",ref,styles);
+ WXLogDebug(@"flexLayout -> init component: ref : %@ , attributes: %@",ref,attributes);
+#endif
+ [self _setupNavBarWithStyles:_styles attributes:_attributes];
+
+ [self _initCSSNodeWithStyles:_styles];
+ [self _initViewPropertyWithStyles:_styles];
+ [self _initCompositingAttribute:_attributes];
+ [self _handleBorders:styles isUpdating:NO];
+
+ }
+
+ return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ NSInteger copyId = 0;
+ @synchronized(self){
+ static NSInteger __copy = 0;
+ copyId = __copy % (1024*1024);
+ __copy++;
+ }
+ NSString *copyRef = [NSString stringWithFormat:@"%ldcopy_of%@", (long)copyId, _isTemplate ? self.ref : self->_templateComponent.ref];
+ WXComponent *component = [[[self class] allocWithZone:zone] initWithRef:copyRef type:self.type styles:self.styles attributes:self.attributes events:self.events weexInstance:self.weexInstance];
+ if (_isTemplate) {
+ component->_templateComponent = self;
+ } else {
+ component->_templateComponent = self->_templateComponent;
+ }
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ memcpy(component->_cssNode, self.cssNode, sizeof(css_node_t));
+ component->_cssNode->context = (__bridge void *)component;
+ }
+//#else
+ else
+ {
+ //memcpy((void*)component->_flexCssNode,self.flexCssNode,sizeof(WeexCore::WXCoreLayoutNode));
+ component->_flexCssNode->copyStyle(self.flexCssNode);
+ component->_flexCssNode->copyMeasureFunc(self.flexCssNode);
+ component->_flexCssNode->setContext((__bridge void *)component);
+ }
+//#endif
+ component->_calculatedFrame = self.calculatedFrame;
+
+ NSMutableArray *subcomponentsCopy = [NSMutableArray array];
+
+ if ([WXComponent isUseFlex]) {
+ component->_subcomponents = subcomponentsCopy;
+ NSUInteger count = [self.subcomponents count];
+ for (NSInteger i = 0 ; i < count;i++){
+ WXComponent *subcomponentCopy = [[self.subcomponents objectAtIndex:i] copy];
+ [component _insertSubcomponent:subcomponentCopy atIndex:i];
+ }
+ }else{
+ for (WXComponent *subcomponent in self.subcomponents) {
+ WXComponent *subcomponentCopy = [subcomponent copy];
+ subcomponentCopy->_supercomponent = component;
+ [subcomponentsCopy addObject:subcomponentCopy];
+ }
+ component->_subcomponents = subcomponentsCopy;
+ }
+
+ WXPerformBlockOnComponentThread(^{
+ [self.weexInstance.componentManager addComponent:component toIndexDictForRef:copyRef];
+ });
+
+ return component;
+}
+
+- (UIAccessibilityTraits)_parseAccessibilityTraitsWithTraits:(UIAccessibilityTraits)trait roles:(NSString*)roleStr
+{
+ UIAccessibilityTraits newTrait = trait;
+ for (NSString * role in [roleStr componentsSeparatedByString:@" "]) {
+ newTrait |= [WXConvert WXUIAccessibilityTraits: role];
+ }
+
+ return newTrait;
+}
+
+- (void)dealloc
+{
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ free_css_node(_cssNode);
+ }
+//#else
+ else
+ {
+ if(self.flexCssNode){
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> dealloc %@",self.ref);
+#endif
+ delete self.flexCssNode;
+ }
+ }
+//#endif
+
+ // remove all gesture and all
+ if (_isTemplate && self.attributes[@"@templateId"]) {
+ [[WXSDKManager bridgeMgr] callComponentHook:_weexInstance.instanceId componentId:self.attributes[@"@templateId"] type:@"lifecycle" hook:@"destroy" args:nil competion:nil];
+ }
+ if (_tapGesture) {
+ [_tapGesture removeTarget:nil action:NULL];
+ }
+ if ([_swipeGestures count]) {
+ for (UISwipeGestureRecognizer *swipeGestures in _swipeGestures) {
+ [swipeGestures removeTarget:nil action:NULL];
+ }
+ }
+
+ if (_longPressGesture) {
+ [_longPressGesture removeTarget:nil action:NULL];
+ }
+
+ if (_panGesture) {
+ [_panGesture removeTarget:nil action:NULL];
+ }
+
+ if (_positionType == WXPositionTypeFixed) {
+ [self.weexInstance.componentManager removeFixedComponent:self];
+ }
+
+ pthread_mutex_destroy(&_propertyMutex);
+ pthread_mutexattr_destroy(&_propertMutexAttr);
+
+}
+
+- (NSDictionary *)styles
+{
+ NSDictionary *styles;
+ pthread_mutex_lock(&_propertyMutex);
+ styles = _styles;
+ pthread_mutex_unlock(&_propertyMutex);
+ return styles;
+}
+
+- (NSDictionary *)pseudoClassStyles
+{
+ NSDictionary *pseudoClassStyles;
+ pthread_mutex_lock(&_propertyMutex);
+ pseudoClassStyles = _pseudoClassStyles;
+ pthread_mutex_unlock(&_propertyMutex);
+
+ return pseudoClassStyles;
+}
+
+- (NSString *)type
+{
+ return _type;
+}
+
+- (NSDictionary *)attributes
+{
+ NSDictionary *attributes;
+ pthread_mutex_lock(&_propertyMutex);
+ attributes = _attributes;
+ pthread_mutex_unlock(&_propertyMutex);
+
+ return attributes;
+}
+
+- (NSArray *)events
+{
+ NSArray *events;
+ pthread_mutex_lock(&_propertyMutex);
+ events = [_events copy];
+ pthread_mutex_unlock(&_propertyMutex);
+
+ return events;
+}
+
+- (void)setDisplayType:(WXDisplayType)displayType
+{
+ if (_displayType != displayType) {
+ _displayType = displayType;
+ if (displayType == WXDisplayTypeNone) {
+ _isNeedJoinLayoutSystem = NO;
+ [self.supercomponent _recomputeCSSNodeChildren];
+ WXPerformBlockOnMainThread(^{
+ [self removeFromSuperview];
+ });
+ } else {
+ _isNeedJoinLayoutSystem = YES;
+ [self.supercomponent _recomputeCSSNodeChildren];
+ WXPerformBlockOnMainThread(^{
+ [self _buildViewHierarchyLazily];
+ // TODO: insert into the correct index
+ [self.supercomponent.view addSubview:self.view];
+ });
+ }
+ [self setNeedsLayout];
+ }
+}
+
+- (WXSDKInstance *)weexInstance
+{
+ return _weexInstance;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@:%p ref=%@> %@", _type, self, _ref, _view];
+}
+
+#pragma mark Property
+
+- (UIView *)view
+{
+ if (_componentType != WXComponentTypeCommon) {
+ return nil;
+ }
+ if ([self isViewLoaded]) {
+ return _view;
+ } else {
+ WXAssertMainThread();
+
+ // compositing child will be drew by its composited ancestor
+ if (_isCompositingChild) {
+ return nil;
+ }
+
+ [self viewWillLoad];
+
+ _view = [self loadView];
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> loadView:addr-(%p),componentRef-(%@)",_view,self.ref);
+#endif
+ _layer = _view.layer;
+ _view.frame = [self _fixIllegalFrame:_calculatedFrame];
+ _view.hidden = _visibility == WXVisibilityShow ? NO : YES;
+ _view.clipsToBounds = _clipToBounds;
+ if (![self _needsDrawBorder]) {
+ _layer.borderColor = _borderTopColor.CGColor;
+ _layer.borderWidth = _borderTopWidth;
+ [self _resetNativeBorderRadius];
+ _layer.opacity = _opacity;
+ _view.backgroundColor = _backgroundColor;
+ }
+
+ if (_backgroundImage) {
+ [self setGradientLayer];
+ }
+
+ if (_transform) {
+ [_transform applyTransformForView:_view];
+ }
+
+ if (_boxShadow) {
+ [self configBoxShadow:_boxShadow];
+ }
+
+ _view.wx_component = self;
+ _view.wx_ref = self.ref;
+ _layer.wx_component = self;
+
+ if (_roles) {
+ [_view setAccessibilityTraits:[self _parseAccessibilityTraitsWithTraits:self.view.accessibilityTraits roles:_roles]];
+ }
+
+ if (_testId) {
+ _view.accessibilityIdentifier = _testId;
+ }
+
+ if (_accessibilityHintContent) {
+ [_view setAccessibilityHint:_accessibilityHintContent];
+ }
+
+ if (_ariaLabel) {
+ _view.accessibilityLabel = _ariaLabel;
+ }
+ if (_accessible) {
+ [_view setIsAccessibilityElement:[WXConvert BOOL:_accessible]];
+ }
+
+ if (_ariaHidden) {
+ [_view setAccessibilityElementsHidden:[WXConvert BOOL:_ariaHidden]];
+ }
+ if (_groupAccessibilityChildren) {
+ [_view setShouldGroupAccessibilityChildren:[WXConvert BOOL:_groupAccessibilityChildren]];
+ }
+
+ [self _initEvents:self.events];
+ [self _initPseudoEvents:_isListenPseudoTouch];
+
+ if (_positionType == WXPositionTypeSticky) {
+ [self.ancestorScroller addStickyComponent:self];
+ }
+
+ if (self.supercomponent && self.supercomponent->_async) {
+ self->_async = YES;
+ }
+
+ [self setNeedsDisplay];
+ [[NSNotificationCenter defaultCenter] postNotificationName:WX_COMPONENT_NOTIFICATION_VIEW_LOADED object:self];
+ [self viewDidLoad];
+
+ if (_lazyCreateView) {
+ [self _buildViewHierarchyLazily];
+ }
+
+ [self _handleFirstScreenTime];
+
+ return _view;
+ }
+}
+
+- (CGRect)_fixIllegalFrame:(CGRect)frame{
+ CGPoint origin = frame.origin;
+ CGSize size = frame.size;
+ CGRect fixedFrame = CGRectMake(isnan(origin.x)?0.0f:origin.x
+ , isnan(origin.y)?0.0f:origin.y
+ , isnan(size.width)?0.0f:size.width
+ , isnan(size.height)?0.0f:size.height);
+ return fixedFrame;
+}
+
+- (void)_buildViewHierarchyLazily
+{
+ if (self.supercomponent && !((WXComponent *)self.supercomponent)->_lazyCreateView) {
+ NSArray *subcomponents = ((WXComponent *)self.supercomponent).subcomponents;
+
+ NSInteger index = [subcomponents indexOfObject:self];
+ if (index != NSNotFound) {
+ [(WXComponent *)self.supercomponent insertSubview:self atIndex:index];
+ }
+ }
+
+ NSArray *subcomponents = self.subcomponents;
+ for (int i = 0; i < subcomponents.count; i++) {
+ WXComponent *subcomponent = subcomponents[i];
+ [self insertSubview:subcomponent atIndex:i];
+ }
+}
+
+- (void)_resetNativeBorderRadius
+{
+ WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:_calculatedFrame topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius];
+ _layer.cornerRadius = borderRect.radii.topLeft;
+}
+
+- (void)_handleFirstScreenTime
+{
+ if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance)) {
+ return;
+ }
+ CGPoint absolutePosition = [self.supercomponent.view convertPoint:_view.frame.origin toView:_weexInstance.rootView];
+ if (absolutePosition.y + _view.frame.size.height > self.weexInstance.rootView.frame.size.height + 1) {
+ WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance);
+ }
+}
+
+- (CALayer *)layer
+{
+ return _layer;
+}
+
+- (CGRect)calculatedFrame
+{
+ return _calculatedFrame;
+}
+
+- (CGPoint)absolutePosition
+{
+ return _absolutePosition;
+}
+
+//#ifndef USE_FLEX
+- (css_node_t *)cssNode
+{
+ return _cssNode;
+}
+//#else
+//#endif
+
+- (void)_addEventParams:(NSDictionary *)params
+{
+ pthread_mutex_lock(&_propertyMutex);
+ if (!_eventParameters) {
+ _eventParameters = [NSMutableDictionary dictionary];
+ }
+ [_eventParameters addEntriesFromDictionary:params];
+ pthread_mutex_unlock(&_propertyMutex);
+}
+
+- (NSArray *)_paramsForEvent:(NSString *)eventName
+{
+ NSArray *params;
+ pthread_mutex_lock(&_propertyMutex);
+ params = _eventParameters[eventName];
+ pthread_mutex_unlock(&_propertyMutex);
+
+ return params;
+}
+
+#pragma mark Component Hierarchy
+
+- (NSArray<WXComponent *> *)subcomponents
+{
+ NSArray<WXComponent *> *subcomponents;
+ pthread_mutex_lock(&_propertyMutex);
+ subcomponents = [_subcomponents copy];
+ pthread_mutex_unlock(&_propertyMutex);
+
+ return subcomponents;
+}
+
+- (WXComponent *)supercomponent
+{
+ return _supercomponent;
+}
+
+- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ WXAssert(subcomponent, @"The subcomponent to insert to %@ at index %d must not be nil", self, index);
+ if (index > [_subcomponents count]) {
+ WXLogError(@"the index of inserted %ld is out of range as the current is %lu", (long)index, (unsigned long)[_subcomponents count]);
+ return;
+ }
+
+ subcomponent->_supercomponent = self;
+
+ pthread_mutex_lock(&_propertyMutex);
+ [_subcomponents insertObject:subcomponent atIndex:index];
+ pthread_mutex_unlock(&_propertyMutex);
+
+ if (subcomponent->_positionType == WXPositionTypeFixed) {
+ [self.weexInstance.componentManager addFixedComponent:subcomponent];
+ subcomponent->_isNeedJoinLayoutSystem = NO;
+ }
+
+ if (_useCompositing || _isCompositingChild) {
+ subcomponent->_isCompositingChild = YES;
+ }
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+
+ }
+//#else
+ else
+ {
+ if (subcomponent->_isNeedJoinLayoutSystem) {
+ NSInteger actualIndex = [self getActualNodeIndex:subcomponent atIndex:index];
+ [self _insertChildCssNode:subcomponent atIndex:actualIndex];
+ }else{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> no need JoinLayoutSystem parent ref:%@ type:%@, self ref:%@ type:%@ ",
+ self.ref,
+ self.type,
+ subcomponent.ref,
+ subcomponent.type
+ );
+#endif
+ }
+ }
+//#endif
+
+ [self _recomputeCSSNodeChildren];
+ [self setNeedsLayout];
+}
+
+- (void)_removeSubcomponent:(WXComponent *)subcomponent
+{
+ pthread_mutex_lock(&_propertyMutex);
+ [_subcomponents removeObject:subcomponent];
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ }
+//#else
+ else
+ {
+ //subcomponent->_isNeedJoinLayoutSystem = NO;
+ [self _rmChildCssNode:subcomponent];
+ }
+//#endif
+ pthread_mutex_unlock(&_propertyMutex);
+}
+
+- (void)_removeFromSupercomponent
+{
+ [self.supercomponent _removeSubcomponent:self];
+ [self.supercomponent _recomputeCSSNodeChildren];
+ [self.supercomponent setNeedsLayout];
+
+ if (_positionType == WXPositionTypeFixed) {
+ [self.weexInstance.componentManager removeFixedComponent:self];
+ self->_isNeedJoinLayoutSystem = YES;
+ }
+}
+
+- (void)_moveToSupercomponent:(WXComponent *)newSupercomponent atIndex:(NSUInteger)index
+{
+ [self _removeFromSupercomponent];
+ [newSupercomponent _insertSubcomponent:self atIndex:index];
+}
+
+- (void)_didInserted
+{
+
+}
+
+- (id<WXScrollerProtocol>)ancestorScroller
+{
+ if(!_ancestorScroller) {
+ WXComponent *supercomponent = self.supercomponent;
+ while (supercomponent) {
+ if([supercomponent conformsToProtocol:@protocol(WXScrollerProtocol)]) {
+ _ancestorScroller = (id<WXScrollerProtocol>)supercomponent;
+ break;
+ }
+ supercomponent = supercomponent.supercomponent;
+ }
+ }
+
+ return _ancestorScroller;
+}
+
+#pragma mark Updating
+- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles
+{
+ NSLog(@"tempTest updating");
+
+ BOOL isTransitionTag = _transition ? [self _isTransitionTag:styles] : NO;
+ if (isTransitionTag) {
+ [_transition _handleTransitionWithStyles:styles resetStyles:resetStyles target:self];
+ } else {
+ styles = [self parseStyles:styles];
+ [self _updateCSSNodeStyles:styles];
+ [self _resetCSSNodeStyles:resetStyles];
+ }
+ if (isUpdateStyles) {
+ [self _modifyStyles:styles];
+ if ([self needsLayout]) {
+ // call update style may take effect on layout, maybe the component
+ // displaylink has been paused, so we need to restart the component task, and it will auto-pause when task queue is empty.
+ [self.weexInstance.componentManager startComponentTasks];
+ }
+ }
+}
+
+- (BOOL)_isTransitionTag:(NSDictionary *)styles
+{
+ BOOL yesOrNo = false;
+ if (_transition.transitionOptions != WXTransitionOptionsNone) {
+ yesOrNo = true;
+ }
+ return yesOrNo;
+}
+
+- (BOOL)_isTransitionOnMainThreadStyles:(NSDictionary *)styles
+{
+ BOOL yesOrNo = false;
+ if (_transition.transitionOptions != WXTransitionOptionsNone) {
+ if ((_transition.transitionOptions & WXTransitionOptionsBackgroundColor &&styles[@"backgroundColor"])
+ ||(_transition.transitionOptions & WXTransitionOptionsTransform &&styles[@"transform"])
+ ||(_transition.transitionOptions & WXTransitionOptionsOpacity &&styles[@"opacity"])) {
+ yesOrNo = true;
+ }
+ }
+ return yesOrNo;
+}
+
+- (void)_modifyStyles:(NSDictionary *)styles
+{
+ pthread_mutex_lock(&_propertyMutex);
+ [_styles addEntriesFromDictionary:styles];
+ pthread_mutex_unlock(&_propertyMutex);
+}
+
+- (void)_updateAttributesOnComponentThread:(NSDictionary *)attributes
+{
+ pthread_mutex_lock(&_propertyMutex);
+ [_attributes addEntriesFromDictionary:attributes];
+ pthread_mutex_unlock(&_propertyMutex);
+}
+
+- (void)_addEventOnComponentThread:(NSString *)eventName
+{
+ pthread_mutex_lock(&_propertyMutex);
+ [_events addObject:eventName];
+ pthread_mutex_unlock(&_propertyMutex);
+}
+
+- (void)_removeEventOnComponentThread:(NSString *)eventName
+{
+ pthread_mutex_lock(&_propertyMutex);
+ [_events removeObject:eventName];
+ pthread_mutex_unlock(&_propertyMutex);
+}
+
+- (void)_updateStylesOnMainThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles
+{
+ WXAssertMainThread();
+ if (![self _isTransitionOnMainThreadStyles:styles]) {
+ [self _updateViewStyles:styles];
+ } else {
+ [self _transitionUpdateViewProperty:styles];
+ }
+#ifdef DEBUG
+ NSDictionary *copySelfStyle = [NSDictionary dictionaryWithDictionary:self.styles];
+ WXLogDebug(@"flexLayout -> ref:%@ style before : %@",self.ref,copySelfStyle);
+
+ if (styles) {
+ WXLogDebug(@"flexLayout -> ref:%@ update styles : %@",self.ref,styles);
+ }
+
+ if (resetStyles) {
+ WXLogDebug(@"flexLayout -> ref:%@ update resetStyles : %@",self.ref,resetStyles);
+ }
+#endif
+
+ [self _resetStyles:resetStyles];
+ [self _handleBorders:styles isUpdating:YES];
+ [self updateStyles:styles];
+ [self resetStyles:resetStyles];
+
+#ifdef DEBUG
+ //self.styles may change
+ copySelfStyle = [NSDictionary dictionaryWithDictionary:self.styles];
+ WXLogDebug(@"flexLayout -> ref:%@ style after : %@",self.ref,copySelfStyle);
+#endif
+}
+
+- (void)_updateAttributesOnMainThread:(NSDictionary *)attributes
+{
+ WXAssertMainThread();
+
+ [self _updateNavBarAttributes:attributes];
+
+ [self updateAttributes:attributes];
+ [self _configWXComponentA11yWithAttributes:attributes];
+}
+
+- (void)updateStyles:(NSDictionary *)styles
+{
+ WXAssertMainThread();
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ WXAssertMainThread();
+}
+
+- (void)setNativeTransform:(CGAffineTransform)transform
+{
+ WXAssertMainThread();
+
+ _transform = [[WXTransform alloc] initWithNativeTransform:CATransform3DMakeAffineTransform(transform) instance:self.weexInstance];
+ if (!CGRectEqualToRect(self.calculatedFrame, CGRectZero)) {
+ [_transform applyTransformForView:_view];
+ [_layer setNeedsDisplay];
+ }
+}
+
+- (void)readyToRender
+{
+ if (self.weexInstance.trackComponent) {
+ [self.supercomponent readyToRender];
+ }
+}
+
+
+- (void)setGradientLayer
+{
+ if (CGRectEqualToRect(self.view.frame, CGRectZero)) {
+ return;
+ }
+ NSDictionary * linearGradient = [WXUtility linearGradientWithBackgroundImage:_backgroundImage];
+ if (!linearGradient) {
+ return ;
+ }
+
+ __weak typeof(self) weakSelf = self;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ __strong typeof(self) strongSelf = weakSelf;
+ if(strongSelf) {
+ UIColor * startColor = (UIColor*)linearGradient[@"startColor"];
+ UIColor * endColor = (UIColor*)linearGradient[@"endColor"];
+ CAGradientLayer * gradientLayer = [WXUtility gradientLayerFromColors:@[startColor, endColor] locations:nil frame:strongSelf.view.bounds gradientType:(WXGradientType)[linearGradient[@"gradientType"] integerValue]];
+ if (gradientLayer) {
+ _backgroundColor = [UIColor colorWithPatternImage:[strongSelf imageFromLayer:gradientLayer]];
+ strongSelf.view.backgroundColor = _backgroundColor;
+ }
+ }
+ });
+}
+
+- (void)_configWXComponentA11yWithAttributes:(NSDictionary *)attributes
+{
+ WX_CHECK_COMPONENT_TYPE(self.componentType)
+ if (attributes[@"role"]){
+ _roles = attributes[@"role"];
+ [self.view setAccessibilityTraits:[self _parseAccessibilityTraitsWithTraits:self.view.accessibilityTraits roles:_roles]];
+ }
+ if (attributes[@"ariaHidden"]) {
+ _ariaHidden = [WXConvert NSString:attributes[@"ariaHidden"]];
+ [self.view setAccessibilityElementsHidden:[WXConvert BOOL:_ariaHidden]];
+ }
+ if (attributes[@"accessible"]) {
+ _accessible = [WXConvert NSString:attributes[@"accessible"]];
+ [self.view setIsAccessibilityElement:[WXConvert BOOL:_accessible]];
+ }
+ if (attributes[@"ariaLabel"]) {
+ _ariaLabel = [WXConvert NSString:attributes[@"ariaLabel"]];
+ self.view.accessibilityValue = _ariaLabel;
+ }
+ if (attributes[@"accessibilityHint"]) {
+ _accessibilityHintContent = [WXConvert NSString:attributes[@"accessibilityHint"]];
+ [self.view setAccessibilityHint:_accessibilityHintContent];
+ }
+
+ if (attributes[@"groupAccessibilityChildren"]) {
+ _groupAccessibilityChildren = [WXConvert NSString:attributes[@"groupAccessibilityChildren"]];
+ [self.view setShouldGroupAccessibilityChildren:[WXConvert BOOL:_groupAccessibilityChildren]];
+ }
+
+
+ if (attributes[@"testId"]) {
+ [self.view setAccessibilityIdentifier:[WXConvert NSString:attributes[@"testId"]]];
+ }
+
+}
+
+- (UIImage *)imageFromLayer:(CALayer *)layer
+{
+ UIGraphicsBeginImageContextWithOptions(layer.frame.size, NO, 0);
+ [layer renderInContext:UIGraphicsGetCurrentContext()];
+ UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return outputImage;
+}
+
+#pragma mark Reset
+- (void)resetStyles:(NSArray *)styles
+{
+ WXAssertMainThread();
+}
+
+#pragma mark Layout
+
+/**
+ * @see WXComponent+Layout.m
+ */
+
+#pragma mark View Management
+
+/**
+ * @see WXComponent+ViewManagement.m
+ */
+
+#pragma mark Events
+
+/**
+ * @see WXComponent+Events.m
+ */
+
+#pragma mark Display
+
+/**
+ * @see WXComponent+Display.m
+ */
+
+@end
+
+
+@implementation UIView (WXComponent)
+
+- (WXComponent *)wx_component
+{
+ WXWeakObjectWrapper *weakWrapper = objc_getAssociatedObject(self, @selector(wx_component));
+ return [weakWrapper weakObject];
+}
+
+- (void)setWx_component:(WXComponent *)wx_component
+{
+ id weakWrapper = [[WXWeakObjectWrapper alloc] initWithWeakObject:wx_component];
+ objc_setAssociatedObject(self, @selector(wx_component), weakWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (NSString *)wx_ref
+{
+ WXWeakObjectWrapper *weakWrapper = objc_getAssociatedObject(self, @selector(wx_ref));
+ return [weakWrapper weakObject];
+}
+
+- (void)setWx_ref:(NSString *)wx_ref
+{
+ id weakWrapper = [[WXWeakObjectWrapper alloc] initWithWeakObject:wx_ref];
+ objc_setAssociatedObject(self, @selector(wx_ref), weakWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
+
+@implementation CALayer (WXComponents_new)
+
+- (WXComponent *)wx_component
+{
+ WXWeakObjectWrapper *weakWrapper = objc_getAssociatedObject(self, @selector(wx_component));
+ return [weakWrapper weakObject];
+}
+
+- (void)setWx_component:(WXComponent *)wx_component
+{
+ id weakWrapper = [[WXWeakObjectWrapper alloc] initWithWeakObject:wx_component];
+ objc_setAssociatedObject(self, @selector(wx_component), weakWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
index 13fc4cf..b4c5a2e 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
+++ b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
@@ -76,7 +76,8 @@ typedef enum : NSUInteger {
BOOL _needDestroy;
BOOL _syncDestroyComponentManager;
BOOL _debugJS;
- id<WXBridgeProtocol> _instanceJavaScriptContext; // sandbox javaScript context
+ id<WXBridgeProtocol> _instanceJavaScriptContext; // sandbox javaScript context
+ CGFloat _defaultPixelScaleFactor;
}
- (void)dealloc
@@ -102,7 +103,7 @@ typedef enum : NSUInteger {
__instance++;
}
_instanceId = [NSString stringWithFormat:@"%ld", (long)instanceId];
-
+
[WXSDKManager storeInstance:self forID:_instanceId];
_bizType = @"";
@@ -122,6 +123,7 @@ typedef enum : NSUInteger {
if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
_syncDestroyComponentManager = [[configCenter configForKey:@"iOS_weex_ext_config.syncDestroyComponentManager" defaultValue:@(YES) isDefault:NULL] boolValue];
}
+ _defaultPixelScaleFactor = CGFLOAT_MIN;
[self addObservers];
}
@@ -183,6 +185,9 @@ typedef enum : NSUInteger {
- (void)setFrame:(CGRect)frame
{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> setFrame :%@,instance :%@",NSStringFromCGRect(frame),self);
+#endif
if (!CGRectEqualToRect(frame, _frame)) {
_frame = frame;
WXPerformBlockOnMainThread(^{
@@ -583,7 +588,12 @@ typedef enum : NSUInteger {
if (self.viewportWidth > 0) {
return [WXUtility portraitScreenSize].width / self.viewportWidth;
} else {
- return [WXUtility defaultPixelScaleFactor];
+ if (_defaultPixelScaleFactor != CGFLOAT_MIN) {
+ return _defaultPixelScaleFactor;
+ }
+
+ _defaultPixelScaleFactor = [WXUtility defaultPixelScaleFactor];
+ return _defaultPixelScaleFactor;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Module/WXTransition.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXTransition.m b/ios/sdk/WeexSDK/Sources/Module/WXTransition.m
deleted file mode 100644
index 8e982ac..0000000
--- a/ios/sdk/WeexSDK/Sources/Module/WXTransition.m
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#define SOLVE_EPS(dur) (1. / (1000. * (dur)))
-
-#import <QuartzCore/CATransaction.h>
-#import <QuartzCore/CADisplayLink.h>
-#import "WXComponentManager.h"
-#import "WXSDKInstance.h"
-#import "WXComponent+Layout.h"
-#import "WXComponent_internal.h"
-#import "WXTransition.h"
-#import "WXUtility.h"
-#import "WXAssert.h"
-#import "WXSDKInstance_private.h"
-#import "WXLength.h"
-
-@implementation WXTransitionInfo
-@end
-
-@interface WXTransition()
-{
- WXComponent *_targetComponent;
- double ax;
- double bx;
- double cx;
-
- double ay;
- double by;
- double cy;
-
- float _transitionDuration;
- float _transitionDelay;
- NSUInteger _transitionCount;
-
- CAMediaTimingFunction *_transitionTimingFunction;
- CADisplayLink *_transitionDisplayLink;
-
- NSMutableDictionary *_filterStyles;
- NSMutableDictionary *_oldFilterStyles;
-}
-
-@end
-
-@implementation WXTransition
-
-- (instancetype)initWithStyles:(NSDictionary *)styles
-{
- if (self = [super init]) {
- NSString *property = styles[kWXTransitionProperty];
- NSArray *properties = [property componentsSeparatedByString:@","];
- for (NSString *string in properties) {
- _transitionOptions |= [self transitionOptionsFromString:string];
- }
- }
- return self;
-}
-
-#pragma mark - HandleStyle
-- (WXTransitionOptions)transitionOptionsFromString:(NSString *)string
-{
- NSDictionary<NSString*,NSNumber*> *options = @{
- @"width": @(WXTransitionOptionsWidth),
- @"height": @(WXTransitionOptionsHeight),
- @"right": @(WXTransitionOptionsRight),
- @"left": @(WXTransitionOptionsLeft),
- @"bottom": @(WXTransitionOptionsBottom),
- @"top": @(WXTransitionOptionsTop),
- @"backgroundColor": @(WXTransitionOptionsBackgroundColor),
- @"transform": @(WXTransitionOptionsTransform),
- @"opacity": @(WXTransitionOptionsOpacity),
- };
- return options[string].integerValue;
-}
-
-- (void)_handleTransitionWithStyles:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles target:(WXComponent *)targetComponent
-{
- BOOL isRunning = [self _isTransitionRunning];
- if (isRunning) {
- [self _rollBackTransitionWithStyles:styles];
- }
- else
- {
- [self _suspendTransitionDisplayLink];
- }
-
- _filterStyles = _filterStyles ?:[NSMutableDictionary new];
- _oldFilterStyles = _oldFilterStyles ?: [NSMutableDictionary new];
- NSMutableDictionary *futileStyles = [NSMutableDictionary new];
-
- for (NSString *key in styles) {
- if (self.transitionOptions & [self transitionOptionsFromString:key]) {
- [_filterStyles setObject:styles[key] forKey:key];
- if (![key isEqualToString:@"transform"]) {
- if (!isRunning) {
- [_oldFilterStyles setObject:targetComponent.styles[key] forKey:key];
- }
- }
- }
- else
- {
- [futileStyles setObject:styles[key] forKey:key];
- }
- }
- [self updateFutileStyles:futileStyles resetStyles:nil target:targetComponent];
-
- _targetComponent = targetComponent;
- NSMutableDictionary *componentStyles = [NSMutableDictionary dictionaryWithDictionary:styles];
- [componentStyles addEntriesFromDictionary:targetComponent.styles];
-
- _transitionDuration = componentStyles[kWXTransitionDuration] ? [WXConvert CGFloat:componentStyles[kWXTransitionDuration]] : 0;
- _transitionDelay = componentStyles[kWXTransitionDelay] ? [WXConvert CGFloat:componentStyles[kWXTransitionDelay]] : 0;
- _transitionTimingFunction = [WXConvert CAMediaTimingFunction:componentStyles[kWXTransitionTimingFunction]];
-
- if (_transitionDuration == 0 ) {
- [self updateFutileStyles:_filterStyles resetStyles:nil target:targetComponent];
- return;
- }
-
- if (![[NSString stringWithFormat:@"%@",_transitionTimingFunction] isEqualToString: kCAMediaTimingFunctionLinear]) {
- float vec[4] = {0.};
- [_transitionTimingFunction getControlPointAtIndex:1 values:&vec[0]];
- [_transitionTimingFunction getControlPointAtIndex:2 values:&vec[2]];
- [self unitBezierp1x:vec[0] p1y:vec[1] p2x:vec[2] p2y:vec[3]];
- }
-
- [self _resloveTransitionProperty];
- [self performSelector:@selector(_startTransitionDisplayLink) withObject:self afterDelay:_transitionDelay/1000];
-}
-
-- (void)updateFutileStyles:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles target:(WXComponent *)targetComponent
-{
- [targetComponent _updateCSSNodeStyles:styles];
- [targetComponent _resetCSSNodeStyles:resetStyles];
- WXPerformBlockOnMainThread(^{
- [targetComponent _updateViewStyles:styles];
- });
-}
-
-- (void)_rollBackTransitionWithStyles:(NSDictionary *)styles
-{
- _transitionDuration = _transitionCount * 1000 / 60;
- _transitionCount = 0;
- _propertyArray = nil;
-}
-
-- (void)_resloveTransitionProperty
-{
- if (_filterStyles.count == 0) {
- return;
- }
- for (NSString * name in _filterStyles.allKeys) {
- [self _dealTransitionWithProperty:name];
- }
-}
-
-- (void)_dealTransitionWithProperty:(NSString *)singleProperty
-{
- if (_filterStyles[singleProperty])
- {
- if (!_propertyArray) {
- _propertyArray = [NSMutableArray new];
- }
- if ([singleProperty isEqualToString:@"backgroundColor"]) {
- WXTransitionInfo *info = [WXTransitionInfo new];
- info.fromValue = [self _dealWithColor:[WXConvert UIColor:_oldFilterStyles[singleProperty]]];
- info.toValue = [self _dealWithColor:[WXConvert UIColor:_filterStyles[singleProperty]]];
- info.perValue = [self _calculatePerColorRGB1:info.toValue RGB2:info.fromValue];
- info.propertyName = singleProperty;
- [_propertyArray addObject:info];
- }
- else if ([singleProperty isEqualToString:@"transform"]) {
- NSString *transformOrigin = _filterStyles[@"transformOrigin"];
- WXTransform *wxTransform = [[WXTransform alloc] initWithCSSValue:_filterStyles[singleProperty] origin:transformOrigin instance:_targetComponent.weexInstance];
- WXTransform *oldTransform = _targetComponent->_transform;
- if (wxTransform.rotateAngle != oldTransform.rotateAngle) {
- WXTransitionInfo *info = [WXTransitionInfo new];
- info.propertyName = @"transform.rotation";
- info.fromValue = @(oldTransform.rotateAngle);
- info.toValue = [NSNumber numberWithDouble:wxTransform.rotateAngle];
- info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
- [_propertyArray addObject:info];
- }
- if (wxTransform.rotateX != oldTransform.rotateX)
- {
- WXTransitionInfo *info = [WXTransitionInfo new];
- info.propertyName = @"transform.rotation.x";
- info.fromValue = @(oldTransform.rotateX);
- info.toValue = [NSNumber numberWithDouble:wxTransform.rotateX];
- info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
- [_propertyArray addObject:info];
- }
- if (wxTransform.rotateY != oldTransform.rotateY)
- {
- WXTransitionInfo *info = [WXTransitionInfo new];
- info.propertyName = @"transform.rotation.y";
- info.fromValue = @(oldTransform.rotateY);
- info.toValue = [NSNumber numberWithDouble:wxTransform.rotateY];
- info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
- [_propertyArray addObject:info];
- }
- if (wxTransform.rotateZ != oldTransform.rotateZ)
- {
- WXTransitionInfo *info = [WXTransitionInfo new];
- info.propertyName = @"transform.rotation.z";
- info.fromValue = @(oldTransform.rotateZ);
- info.toValue = [NSNumber numberWithDouble:wxTransform.rotateZ];
- info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
- [_propertyArray addObject:info];
- }
- if (wxTransform.scaleX != oldTransform.scaleX) {
- WXTransitionInfo *info = [WXTransitionInfo new];
- info.propertyName = @"transform.scale.x";
- info.fromValue = @(oldTransform.scaleX);
- info.toValue = @(wxTransform.scaleX);
- info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
- [_propertyArray addObject:info];
- }
- if (wxTransform.scaleY != oldTransform.scaleY) {
- WXTransitionInfo *info = [WXTransitionInfo new];
- info.propertyName = @"transform.scale.y";
- info.fromValue = @(oldTransform.scaleY);
- info.toValue = @(wxTransform.scaleY);
- info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
- [_propertyArray addObject:info];
- }
- if (wxTransform.translateX && [wxTransform.translateX floatValue] !=[oldTransform.translateX floatValue]) {
- WXTransitionInfo *info = [WXTransitionInfo new];
- info.propertyName = @"transform.translation.x";
- info.fromValue = @([oldTransform.translateX floatValue]);
- info.toValue = @([wxTransform.translateX floatValue]);
- info.perValue = @([wxTransform.translateX floatValue] - [oldTransform.translateX floatValue]);
- [_propertyArray addObject:info];
- }
- if (wxTransform.translateY && [wxTransform.translateY floatValue] !=[oldTransform.translateY floatValue]) {
- WXTransitionInfo *info = [WXTransitionInfo new];
- info.propertyName = @"transform.translation.y";
- info.fromValue = @([oldTransform.translateY floatValue]);
- info.toValue = @([wxTransform.translateY floatValue]);
- info.perValue = @([wxTransform.translateY floatValue] - [oldTransform.translateY floatValue]);
- [_propertyArray addObject:info];
- }
- _targetComponent->_transform = wxTransform;
- }
- else
- {
- WXTransitionInfo *info = [WXTransitionInfo new];
- info.fromValue = @(_oldFilterStyles[singleProperty] ? [WXConvert CGFloat:_oldFilterStyles[singleProperty]] : 0);
- info.toValue = @(_filterStyles[singleProperty] ? [WXConvert CGFloat:_filterStyles[singleProperty]] : 0 );
- info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
- info.propertyName = singleProperty;
- [_propertyArray addObject:info];
- }
- }
-}
-
-- (NSArray *)_dealWithColor:(UIColor *)color
-{
- CGFloat R, G, B, A;
- [color getRed:&R green:&G blue:&B alpha:&A];
- return @[@(R),@(G),@(B),@(A)];
-}
-
-- (NSArray *)_calculatePerColorRGB1:(NSArray *)RGB1 RGB2:(NSArray *)RGB2
-{
- CGFloat R = [RGB1[0] doubleValue] - [RGB2[0] doubleValue];
- CGFloat G = [RGB1[1] doubleValue] - [RGB2[1] doubleValue];
- CGFloat B = [RGB1[2] doubleValue] - [RGB2[2] doubleValue];
- CGFloat A = [RGB1[3] doubleValue] - [RGB2[3] doubleValue];
- return @[@(R),@(G),@(B),@(A)];
-}
-
-- (void)_calculatetransitionProcessingStyle
-{
- if (_propertyArray.count == 0) {
- return;
- }
- double per = 1000 * (_transitionCount + 1 ) / (60 * _transitionDuration);//linear
- if (![[NSString stringWithFormat:@"%@",_transitionTimingFunction] isEqualToString: kCAMediaTimingFunctionLinear]) {
- per = [self solveWithx:((_transitionCount+2)*16)/_transitionDuration epsilon:SOLVE_EPS(_transitionDuration)];
- }
- NSString *transformString = [NSString string];
- for (WXTransitionInfo *info in _propertyArray) {
- if ([info.propertyName isEqualToString:@"backgroundColor"]) {
- NSArray *array = @[
- @([info.fromValue[0] floatValue] + [info.perValue[0] floatValue] * per),
- @([info.fromValue[1] floatValue] + [info.perValue[1] floatValue] * per),
- @([info.fromValue[2] floatValue] + [info.perValue[2] floatValue] * per),
- @([info.fromValue[3] floatValue] + [info.perValue[3] floatValue] * per)];
- UIColor *color = [UIColor colorWithRed:[array[0] floatValue] green:[array[1] floatValue] blue:[array[2] floatValue] alpha:[array[3] floatValue]];
- WXPerformBlockOnMainThread(^{
- _targetComponent.view.backgroundColor = color;
- [_targetComponent.view setNeedsDisplay];
- });
- NSString *colorString = [WXConvert HexWithColor:color];
- [_oldFilterStyles setObject:colorString forKey:info.propertyName];
- }
- else if ([info.propertyName hasPrefix:@"transform"])
- {
- double currentValue = [info.fromValue doubleValue] + [info.perValue doubleValue] * per;
- NSString *newString = [NSString string];
- if ([info.propertyName isEqualToString:@"transform.rotation"]) {
- newString = [NSString stringWithFormat:@"rotate(%lfdeg)",currentValue * 180.0 / M_PI];
- transformString = [transformString stringByAppendingFormat:@" %@",newString];
- }
- if ([info.propertyName isEqualToString:@"transform.rotation.x"]) {
- newString = [NSString stringWithFormat:@"rotateX(%lfdeg)",currentValue * 180.0 / M_PI];
- transformString = [transformString stringByAppendingFormat:@" %@",newString];
- }
- if ([info.propertyName isEqualToString:@"transform.rotation.y"]) {
- newString = [NSString stringWithFormat:@"rotateY(%lfdeg)",currentValue * 180.0 / M_PI];
- transformString = [transformString stringByAppendingFormat:@" %@",newString];
- }
- if ([info.propertyName isEqualToString:@"transform.rotation.z"]) {
- newString = [NSString stringWithFormat:@"rotateZ(%lfdeg)",currentValue * 180.0 / M_PI];
- transformString = [transformString stringByAppendingFormat:@" %@",newString];
- }
- if ([info.propertyName isEqualToString:@"transform.scale.x"]) {
- newString = [NSString stringWithFormat:@"scaleX(%lf)",currentValue];
- transformString = [transformString stringByAppendingFormat:@" %@",newString];
- }
- if ([info.propertyName isEqualToString:@"transform.scale.y"]) {
- newString = [NSString stringWithFormat:@"scaleY(%lf)",currentValue];
- transformString = [transformString stringByAppendingFormat:@" %@",newString];
- }
- if ([info.propertyName isEqualToString:@"transform.translation.x"]) {
- newString = [NSString stringWithFormat:@"translateX(%lfpx)",currentValue / _targetComponent.weexInstance.pixelScaleFactor];
- transformString = [transformString stringByAppendingFormat:@" %@",newString];
- }
- if ([info.propertyName isEqualToString:@"transform.translation.y"]) {
- newString = [NSString stringWithFormat:@"translateY(%lfpx)",currentValue / _targetComponent.weexInstance.pixelScaleFactor];
- transformString = [transformString stringByAppendingFormat:@" %@",newString];
- }
- [_oldFilterStyles setObject:transformString forKey:@"transform"];
- }
- else
- {
- double currentValue = [info.fromValue doubleValue] + [info.perValue doubleValue] * per;
- [_oldFilterStyles setObject:@(currentValue) forKey:info.propertyName];
- }
- }
- WXPerformBlockOnMainThread(^{
- [_targetComponent _updateViewStyles:_oldFilterStyles];
- });
- [_targetComponent _updateCSSNodeStyles:_oldFilterStyles];
- [_targetComponent.weexInstance.componentManager startComponentTasks];
-}
-
-#pragma mark CADisplayLink
-- (void)_startTransitionDisplayLink
-{
- WXAssertComponentThread();
- if (!_transitionDisplayLink) {
- _transitionDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_handleTransitionDisplayLink)];
- [_transitionDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- }
- else{
- [self _awakeTransitionDisplayLink];
- }
-}
-
-- (void)_stopTransitionDisplayLink
-{
- WXAssertComponentThread();
- if (_transitionDisplayLink) {
- [_transitionDisplayLink invalidate];
- _transitionDisplayLink = nil;
- }
-}
-
-- (BOOL)_isTransitionRunning
-{
- WXAssertComponentThread();
- BOOL yesOrNo = NO;
- if (!_transitionDisplayLink.paused && _transitionCount >= 5) {
- yesOrNo = YES;
- }
- return yesOrNo;
-}
-
-- (void)_suspendTransitionDisplayLink
-{
- WXAssertComponentThread();
- if(_transitionDisplayLink && !_transitionDisplayLink.paused){
- _transitionDisplayLink.paused = YES;
- }
-}
-
-- (void)_awakeTransitionDisplayLink
-{
- WXAssertComponentThread();
- if (_transitionDisplayLink && _transitionDisplayLink.paused) {
- _transitionDisplayLink.paused = NO;
- }
-}
-
-- (void)_handleTransitionDisplayLink
-{
- WXAssertComponentThread();
- int count = _transitionDuration * 60 / 1000;
- if (_transitionCount >= count) {
- [self _suspendTransitionDisplayLink];
- [self _resetProcessAnimationParameter];
- return;
- }
- else
- {
- [self _calculatetransitionProcessingStyle];
- }
- _transitionCount ++;
-}
-
-- (void)_resetProcessAnimationParameter
-{
- _transitionCount = 0;
- _transitionDuration = 0;
- _propertyArray = nil;
- _oldFilterStyles = nil;
- _filterStyles= nil;
-}
-
-- (NSMutableDictionary *)_filterStyles
-{
- return self.filterStyles;
-}
-
-- (NSMutableDictionary *)_oldFilterStyles
-{
- return self.oldFilterStyles;
-}
-
-#pragma mark UnitBezierp
-- (void)unitBezierp1x:(double)p1x p1y:(double)p1y p2x:(double)p2x p2y:(double)p2y
-{
- cx = 3.0 * p1x;
- bx = 3.0 * (p2x - p1x) - cx;
- ax = 1.0 - cx -bx;
-
- cy = 3.0 * p1y;
- by = 3.0 * (p2y - p1y) - cy;
- ay = 1.0 - cy - by;
-}
-
-- (double)sampleCurveX:(double)t
-{
- return ((ax * t + bx) * t + cx) * t;
-}
-
-- (double)sampleCurveY:(double)t
-{
- return ((ay * t + by) * t + cy) * t;
-}
-
-- (double)sampleCurveDerivativeX:(double)t
-{
- return (3.0 * ax * t + 2.0 * bx) * t + cx;
-}
-
-- (double)solveCurveX:(double)x epsilon:(double)epsilon
-{
- double t0;
- double t1;
- double t2;
- double x2;
- double d2;
- int i;
-
- for (t2 = x, i = 0; i < 8; i++) {
- x2 = [self sampleCurveX:t2] - x;
- if (fabs (x2) < epsilon)
- return t2;
- d2 = [self sampleCurveDerivativeX:t2];
- if (fabs(d2) < 1e-6)
- break;
- t2 = t2 - x2 / d2;
- }
- t0 = 0.0;
- t1 = 1.0;
- t2 = x;
-
- if (t2 < t0)
- return t0;
- if (t2 > t1)
- return t1;
-
- while (t0 < t1) {
- x2 = [self sampleCurveX:t2];
- if (fabs(x2 - x) < epsilon)
- return t2;
- if (x > x2)
- t0 = t2;
- else
- t1 = t2;
- t2 = (t1 - t0) * .5 + t0;
- }
- return t2;
-}
-
-- (double)solveWithx:(double)x epsilon:(double)epsilon
-{
- return [self sampleCurveY:([self solveCurveX:x epsilon:epsilon])];
-}
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Module/WXTransition.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXTransition.mm b/ios/sdk/WeexSDK/Sources/Module/WXTransition.mm
new file mode 100644
index 0000000..8e982ac
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Module/WXTransition.mm
@@ -0,0 +1,519 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#define SOLVE_EPS(dur) (1. / (1000. * (dur)))
+
+#import <QuartzCore/CATransaction.h>
+#import <QuartzCore/CADisplayLink.h>
+#import "WXComponentManager.h"
+#import "WXSDKInstance.h"
+#import "WXComponent+Layout.h"
+#import "WXComponent_internal.h"
+#import "WXTransition.h"
+#import "WXUtility.h"
+#import "WXAssert.h"
+#import "WXSDKInstance_private.h"
+#import "WXLength.h"
+
+@implementation WXTransitionInfo
+@end
+
+@interface WXTransition()
+{
+ WXComponent *_targetComponent;
+ double ax;
+ double bx;
+ double cx;
+
+ double ay;
+ double by;
+ double cy;
+
+ float _transitionDuration;
+ float _transitionDelay;
+ NSUInteger _transitionCount;
+
+ CAMediaTimingFunction *_transitionTimingFunction;
+ CADisplayLink *_transitionDisplayLink;
+
+ NSMutableDictionary *_filterStyles;
+ NSMutableDictionary *_oldFilterStyles;
+}
+
+@end
+
+@implementation WXTransition
+
+- (instancetype)initWithStyles:(NSDictionary *)styles
+{
+ if (self = [super init]) {
+ NSString *property = styles[kWXTransitionProperty];
+ NSArray *properties = [property componentsSeparatedByString:@","];
+ for (NSString *string in properties) {
+ _transitionOptions |= [self transitionOptionsFromString:string];
+ }
+ }
+ return self;
+}
+
+#pragma mark - HandleStyle
+- (WXTransitionOptions)transitionOptionsFromString:(NSString *)string
+{
+ NSDictionary<NSString*,NSNumber*> *options = @{
+ @"width": @(WXTransitionOptionsWidth),
+ @"height": @(WXTransitionOptionsHeight),
+ @"right": @(WXTransitionOptionsRight),
+ @"left": @(WXTransitionOptionsLeft),
+ @"bottom": @(WXTransitionOptionsBottom),
+ @"top": @(WXTransitionOptionsTop),
+ @"backgroundColor": @(WXTransitionOptionsBackgroundColor),
+ @"transform": @(WXTransitionOptionsTransform),
+ @"opacity": @(WXTransitionOptionsOpacity),
+ };
+ return options[string].integerValue;
+}
+
+- (void)_handleTransitionWithStyles:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles target:(WXComponent *)targetComponent
+{
+ BOOL isRunning = [self _isTransitionRunning];
+ if (isRunning) {
+ [self _rollBackTransitionWithStyles:styles];
+ }
+ else
+ {
+ [self _suspendTransitionDisplayLink];
+ }
+
+ _filterStyles = _filterStyles ?:[NSMutableDictionary new];
+ _oldFilterStyles = _oldFilterStyles ?: [NSMutableDictionary new];
+ NSMutableDictionary *futileStyles = [NSMutableDictionary new];
+
+ for (NSString *key in styles) {
+ if (self.transitionOptions & [self transitionOptionsFromString:key]) {
+ [_filterStyles setObject:styles[key] forKey:key];
+ if (![key isEqualToString:@"transform"]) {
+ if (!isRunning) {
+ [_oldFilterStyles setObject:targetComponent.styles[key] forKey:key];
+ }
+ }
+ }
+ else
+ {
+ [futileStyles setObject:styles[key] forKey:key];
+ }
+ }
+ [self updateFutileStyles:futileStyles resetStyles:nil target:targetComponent];
+
+ _targetComponent = targetComponent;
+ NSMutableDictionary *componentStyles = [NSMutableDictionary dictionaryWithDictionary:styles];
+ [componentStyles addEntriesFromDictionary:targetComponent.styles];
+
+ _transitionDuration = componentStyles[kWXTransitionDuration] ? [WXConvert CGFloat:componentStyles[kWXTransitionDuration]] : 0;
+ _transitionDelay = componentStyles[kWXTransitionDelay] ? [WXConvert CGFloat:componentStyles[kWXTransitionDelay]] : 0;
+ _transitionTimingFunction = [WXConvert CAMediaTimingFunction:componentStyles[kWXTransitionTimingFunction]];
+
+ if (_transitionDuration == 0 ) {
+ [self updateFutileStyles:_filterStyles resetStyles:nil target:targetComponent];
+ return;
+ }
+
+ if (![[NSString stringWithFormat:@"%@",_transitionTimingFunction] isEqualToString: kCAMediaTimingFunctionLinear]) {
+ float vec[4] = {0.};
+ [_transitionTimingFunction getControlPointAtIndex:1 values:&vec[0]];
+ [_transitionTimingFunction getControlPointAtIndex:2 values:&vec[2]];
+ [self unitBezierp1x:vec[0] p1y:vec[1] p2x:vec[2] p2y:vec[3]];
+ }
+
+ [self _resloveTransitionProperty];
+ [self performSelector:@selector(_startTransitionDisplayLink) withObject:self afterDelay:_transitionDelay/1000];
+}
+
+- (void)updateFutileStyles:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles target:(WXComponent *)targetComponent
+{
+ [targetComponent _updateCSSNodeStyles:styles];
+ [targetComponent _resetCSSNodeStyles:resetStyles];
+ WXPerformBlockOnMainThread(^{
+ [targetComponent _updateViewStyles:styles];
+ });
+}
+
+- (void)_rollBackTransitionWithStyles:(NSDictionary *)styles
+{
+ _transitionDuration = _transitionCount * 1000 / 60;
+ _transitionCount = 0;
+ _propertyArray = nil;
+}
+
+- (void)_resloveTransitionProperty
+{
+ if (_filterStyles.count == 0) {
+ return;
+ }
+ for (NSString * name in _filterStyles.allKeys) {
+ [self _dealTransitionWithProperty:name];
+ }
+}
+
+- (void)_dealTransitionWithProperty:(NSString *)singleProperty
+{
+ if (_filterStyles[singleProperty])
+ {
+ if (!_propertyArray) {
+ _propertyArray = [NSMutableArray new];
+ }
+ if ([singleProperty isEqualToString:@"backgroundColor"]) {
+ WXTransitionInfo *info = [WXTransitionInfo new];
+ info.fromValue = [self _dealWithColor:[WXConvert UIColor:_oldFilterStyles[singleProperty]]];
+ info.toValue = [self _dealWithColor:[WXConvert UIColor:_filterStyles[singleProperty]]];
+ info.perValue = [self _calculatePerColorRGB1:info.toValue RGB2:info.fromValue];
+ info.propertyName = singleProperty;
+ [_propertyArray addObject:info];
+ }
+ else if ([singleProperty isEqualToString:@"transform"]) {
+ NSString *transformOrigin = _filterStyles[@"transformOrigin"];
+ WXTransform *wxTransform = [[WXTransform alloc] initWithCSSValue:_filterStyles[singleProperty] origin:transformOrigin instance:_targetComponent.weexInstance];
+ WXTransform *oldTransform = _targetComponent->_transform;
+ if (wxTransform.rotateAngle != oldTransform.rotateAngle) {
+ WXTransitionInfo *info = [WXTransitionInfo new];
+ info.propertyName = @"transform.rotation";
+ info.fromValue = @(oldTransform.rotateAngle);
+ info.toValue = [NSNumber numberWithDouble:wxTransform.rotateAngle];
+ info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
+ [_propertyArray addObject:info];
+ }
+ if (wxTransform.rotateX != oldTransform.rotateX)
+ {
+ WXTransitionInfo *info = [WXTransitionInfo new];
+ info.propertyName = @"transform.rotation.x";
+ info.fromValue = @(oldTransform.rotateX);
+ info.toValue = [NSNumber numberWithDouble:wxTransform.rotateX];
+ info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
+ [_propertyArray addObject:info];
+ }
+ if (wxTransform.rotateY != oldTransform.rotateY)
+ {
+ WXTransitionInfo *info = [WXTransitionInfo new];
+ info.propertyName = @"transform.rotation.y";
+ info.fromValue = @(oldTransform.rotateY);
+ info.toValue = [NSNumber numberWithDouble:wxTransform.rotateY];
+ info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
+ [_propertyArray addObject:info];
+ }
+ if (wxTransform.rotateZ != oldTransform.rotateZ)
+ {
+ WXTransitionInfo *info = [WXTransitionInfo new];
+ info.propertyName = @"transform.rotation.z";
+ info.fromValue = @(oldTransform.rotateZ);
+ info.toValue = [NSNumber numberWithDouble:wxTransform.rotateZ];
+ info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
+ [_propertyArray addObject:info];
+ }
+ if (wxTransform.scaleX != oldTransform.scaleX) {
+ WXTransitionInfo *info = [WXTransitionInfo new];
+ info.propertyName = @"transform.scale.x";
+ info.fromValue = @(oldTransform.scaleX);
+ info.toValue = @(wxTransform.scaleX);
+ info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
+ [_propertyArray addObject:info];
+ }
+ if (wxTransform.scaleY != oldTransform.scaleY) {
+ WXTransitionInfo *info = [WXTransitionInfo new];
+ info.propertyName = @"transform.scale.y";
+ info.fromValue = @(oldTransform.scaleY);
+ info.toValue = @(wxTransform.scaleY);
+ info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
+ [_propertyArray addObject:info];
+ }
+ if (wxTransform.translateX && [wxTransform.translateX floatValue] !=[oldTransform.translateX floatValue]) {
+ WXTransitionInfo *info = [WXTransitionInfo new];
+ info.propertyName = @"transform.translation.x";
+ info.fromValue = @([oldTransform.translateX floatValue]);
+ info.toValue = @([wxTransform.translateX floatValue]);
+ info.perValue = @([wxTransform.translateX floatValue] - [oldTransform.translateX floatValue]);
+ [_propertyArray addObject:info];
+ }
+ if (wxTransform.translateY && [wxTransform.translateY floatValue] !=[oldTransform.translateY floatValue]) {
+ WXTransitionInfo *info = [WXTransitionInfo new];
+ info.propertyName = @"transform.translation.y";
+ info.fromValue = @([oldTransform.translateY floatValue]);
+ info.toValue = @([wxTransform.translateY floatValue]);
+ info.perValue = @([wxTransform.translateY floatValue] - [oldTransform.translateY floatValue]);
+ [_propertyArray addObject:info];
+ }
+ _targetComponent->_transform = wxTransform;
+ }
+ else
+ {
+ WXTransitionInfo *info = [WXTransitionInfo new];
+ info.fromValue = @(_oldFilterStyles[singleProperty] ? [WXConvert CGFloat:_oldFilterStyles[singleProperty]] : 0);
+ info.toValue = @(_filterStyles[singleProperty] ? [WXConvert CGFloat:_filterStyles[singleProperty]] : 0 );
+ info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
+ info.propertyName = singleProperty;
+ [_propertyArray addObject:info];
+ }
+ }
+}
+
+- (NSArray *)_dealWithColor:(UIColor *)color
+{
+ CGFloat R, G, B, A;
+ [color getRed:&R green:&G blue:&B alpha:&A];
+ return @[@(R),@(G),@(B),@(A)];
+}
+
+- (NSArray *)_calculatePerColorRGB1:(NSArray *)RGB1 RGB2:(NSArray *)RGB2
+{
+ CGFloat R = [RGB1[0] doubleValue] - [RGB2[0] doubleValue];
+ CGFloat G = [RGB1[1] doubleValue] - [RGB2[1] doubleValue];
+ CGFloat B = [RGB1[2] doubleValue] - [RGB2[2] doubleValue];
+ CGFloat A = [RGB1[3] doubleValue] - [RGB2[3] doubleValue];
+ return @[@(R),@(G),@(B),@(A)];
+}
+
+- (void)_calculatetransitionProcessingStyle
+{
+ if (_propertyArray.count == 0) {
+ return;
+ }
+ double per = 1000 * (_transitionCount + 1 ) / (60 * _transitionDuration);//linear
+ if (![[NSString stringWithFormat:@"%@",_transitionTimingFunction] isEqualToString: kCAMediaTimingFunctionLinear]) {
+ per = [self solveWithx:((_transitionCount+2)*16)/_transitionDuration epsilon:SOLVE_EPS(_transitionDuration)];
+ }
+ NSString *transformString = [NSString string];
+ for (WXTransitionInfo *info in _propertyArray) {
+ if ([info.propertyName isEqualToString:@"backgroundColor"]) {
+ NSArray *array = @[
+ @([info.fromValue[0] floatValue] + [info.perValue[0] floatValue] * per),
+ @([info.fromValue[1] floatValue] + [info.perValue[1] floatValue] * per),
+ @([info.fromValue[2] floatValue] + [info.perValue[2] floatValue] * per),
+ @([info.fromValue[3] floatValue] + [info.perValue[3] floatValue] * per)];
+ UIColor *color = [UIColor colorWithRed:[array[0] floatValue] green:[array[1] floatValue] blue:[array[2] floatValue] alpha:[array[3] floatValue]];
+ WXPerformBlockOnMainThread(^{
+ _targetComponent.view.backgroundColor = color;
+ [_targetComponent.view setNeedsDisplay];
+ });
+ NSString *colorString = [WXConvert HexWithColor:color];
+ [_oldFilterStyles setObject:colorString forKey:info.propertyName];
+ }
+ else if ([info.propertyName hasPrefix:@"transform"])
+ {
+ double currentValue = [info.fromValue doubleValue] + [info.perValue doubleValue] * per;
+ NSString *newString = [NSString string];
+ if ([info.propertyName isEqualToString:@"transform.rotation"]) {
+ newString = [NSString stringWithFormat:@"rotate(%lfdeg)",currentValue * 180.0 / M_PI];
+ transformString = [transformString stringByAppendingFormat:@" %@",newString];
+ }
+ if ([info.propertyName isEqualToString:@"transform.rotation.x"]) {
+ newString = [NSString stringWithFormat:@"rotateX(%lfdeg)",currentValue * 180.0 / M_PI];
+ transformString = [transformString stringByAppendingFormat:@" %@",newString];
+ }
+ if ([info.propertyName isEqualToString:@"transform.rotation.y"]) {
+ newString = [NSString stringWithFormat:@"rotateY(%lfdeg)",currentValue * 180.0 / M_PI];
+ transformString = [transformString stringByAppendingFormat:@" %@",newString];
+ }
+ if ([info.propertyName isEqualToString:@"transform.rotation.z"]) {
+ newString = [NSString stringWithFormat:@"rotateZ(%lfdeg)",currentValue * 180.0 / M_PI];
+ transformString = [transformString stringByAppendingFormat:@" %@",newString];
+ }
+ if ([info.propertyName isEqualToString:@"transform.scale.x"]) {
+ newString = [NSString stringWithFormat:@"scaleX(%lf)",currentValue];
+ transformString = [transformString stringByAppendingFormat:@" %@",newString];
+ }
+ if ([info.propertyName isEqualToString:@"transform.scale.y"]) {
+ newString = [NSString stringWithFormat:@"scaleY(%lf)",currentValue];
+ transformString = [transformString stringByAppendingFormat:@" %@",newString];
+ }
+ if ([info.propertyName isEqualToString:@"transform.translation.x"]) {
+ newString = [NSString stringWithFormat:@"translateX(%lfpx)",currentValue / _targetComponent.weexInstance.pixelScaleFactor];
+ transformString = [transformString stringByAppendingFormat:@" %@",newString];
+ }
+ if ([info.propertyName isEqualToString:@"transform.translation.y"]) {
+ newString = [NSString stringWithFormat:@"translateY(%lfpx)",currentValue / _targetComponent.weexInstance.pixelScaleFactor];
+ transformString = [transformString stringByAppendingFormat:@" %@",newString];
+ }
+ [_oldFilterStyles setObject:transformString forKey:@"transform"];
+ }
+ else
+ {
+ double currentValue = [info.fromValue doubleValue] + [info.perValue doubleValue] * per;
+ [_oldFilterStyles setObject:@(currentValue) forKey:info.propertyName];
+ }
+ }
+ WXPerformBlockOnMainThread(^{
+ [_targetComponent _updateViewStyles:_oldFilterStyles];
+ });
+ [_targetComponent _updateCSSNodeStyles:_oldFilterStyles];
+ [_targetComponent.weexInstance.componentManager startComponentTasks];
+}
+
+#pragma mark CADisplayLink
+- (void)_startTransitionDisplayLink
+{
+ WXAssertComponentThread();
+ if (!_transitionDisplayLink) {
+ _transitionDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_handleTransitionDisplayLink)];
+ [_transitionDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+ }
+ else{
+ [self _awakeTransitionDisplayLink];
+ }
+}
+
+- (void)_stopTransitionDisplayLink
+{
+ WXAssertComponentThread();
+ if (_transitionDisplayLink) {
+ [_transitionDisplayLink invalidate];
+ _transitionDisplayLink = nil;
+ }
+}
+
+- (BOOL)_isTransitionRunning
+{
+ WXAssertComponentThread();
+ BOOL yesOrNo = NO;
+ if (!_transitionDisplayLink.paused && _transitionCount >= 5) {
+ yesOrNo = YES;
+ }
+ return yesOrNo;
+}
+
+- (void)_suspendTransitionDisplayLink
+{
+ WXAssertComponentThread();
+ if(_transitionDisplayLink && !_transitionDisplayLink.paused){
+ _transitionDisplayLink.paused = YES;
+ }
+}
+
+- (void)_awakeTransitionDisplayLink
+{
+ WXAssertComponentThread();
+ if (_transitionDisplayLink && _transitionDisplayLink.paused) {
+ _transitionDisplayLink.paused = NO;
+ }
+}
+
+- (void)_handleTransitionDisplayLink
+{
+ WXAssertComponentThread();
+ int count = _transitionDuration * 60 / 1000;
+ if (_transitionCount >= count) {
+ [self _suspendTransitionDisplayLink];
+ [self _resetProcessAnimationParameter];
+ return;
+ }
+ else
+ {
+ [self _calculatetransitionProcessingStyle];
+ }
+ _transitionCount ++;
+}
+
+- (void)_resetProcessAnimationParameter
+{
+ _transitionCount = 0;
+ _transitionDuration = 0;
+ _propertyArray = nil;
+ _oldFilterStyles = nil;
+ _filterStyles= nil;
+}
+
+- (NSMutableDictionary *)_filterStyles
+{
+ return self.filterStyles;
+}
+
+- (NSMutableDictionary *)_oldFilterStyles
+{
+ return self.oldFilterStyles;
+}
+
+#pragma mark UnitBezierp
+- (void)unitBezierp1x:(double)p1x p1y:(double)p1y p2x:(double)p2x p2y:(double)p2y
+{
+ cx = 3.0 * p1x;
+ bx = 3.0 * (p2x - p1x) - cx;
+ ax = 1.0 - cx -bx;
+
+ cy = 3.0 * p1y;
+ by = 3.0 * (p2y - p1y) - cy;
+ ay = 1.0 - cy - by;
+}
+
+- (double)sampleCurveX:(double)t
+{
+ return ((ax * t + bx) * t + cx) * t;
+}
+
+- (double)sampleCurveY:(double)t
+{
+ return ((ay * t + by) * t + cy) * t;
+}
+
+- (double)sampleCurveDerivativeX:(double)t
+{
+ return (3.0 * ax * t + 2.0 * bx) * t + cx;
+}
+
+- (double)solveCurveX:(double)x epsilon:(double)epsilon
+{
+ double t0;
+ double t1;
+ double t2;
+ double x2;
+ double d2;
+ int i;
+
+ for (t2 = x, i = 0; i < 8; i++) {
+ x2 = [self sampleCurveX:t2] - x;
+ if (fabs (x2) < epsilon)
+ return t2;
+ d2 = [self sampleCurveDerivativeX:t2];
+ if (fabs(d2) < 1e-6)
+ break;
+ t2 = t2 - x2 / d2;
+ }
+ t0 = 0.0;
+ t1 = 1.0;
+ t2 = x;
+
+ if (t2 < t0)
+ return t0;
+ if (t2 > t1)
+ return t1;
+
+ while (t0 < t1) {
+ x2 = [self sampleCurveX:t2];
+ if (fabs(x2 - x) < epsilon)
+ return t2;
+ if (x > x2)
+ t0 = t2;
+ else
+ t1 = t2;
+ t2 = (t1 - t0) * .5 + t0;
+ }
+ return t2;
+}
+
+- (double)solveWithx:(double)x epsilon:(double)epsilon
+{
+ return [self sampleCurveY:([self solveCurveX:x epsilon:epsilon])];
+}
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m b/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
index 2744e60..a303ddb 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
@@ -63,6 +63,9 @@ WX_NUMBER_CONVERT(NSUInteger, unsignedIntegerValue)
{
if ([value isKindOfClass:[NSString class]]) {
NSString *valueString = (NSString *)value;
+ if (valueString.length <=0) {
+ return NAN;
+ }
if ([valueString hasSuffix:@"px"] || [valueString hasSuffix:@"wx"]) {
valueString = [valueString substringToIndex:(valueString.length - 2)];
}
@@ -72,12 +75,32 @@ WX_NUMBER_CONVERT(NSUInteger, unsignedIntegerValue)
value = [value substringWithRange:NSMakeRange(start, end-start)];
return [self safeAreaInset:value];
}
+ //value maybe not number ,such as 100%
+ if (![WXConvert checkStringIsRealNum:valueString]) {
+ return NAN;
+ }
return [valueString doubleValue];
}
return [self double:value];
}
++ (BOOL)checkStringIsRealNum:(NSString *)checkedNumString {
+ NSScanner* scan = [NSScanner scannerWithString:checkedNumString];
+ int intVal;
+ BOOL isInt = [scan scanInt:&intVal] && [scan isAtEnd];
+ if (isInt) {
+ return YES;
+ }
+ float floatVal;
+ BOOL isFloat = [scan scanFloat:&floatVal] && [scan isAtEnd];
+ if (isFloat) {
+ return YES;
+ }
+
+ return NO;
+}
+
+ (CGFloat)safeAreaInset:(NSString*)value
{
static NSArray * directionArray = nil;
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
index 0e2bb06..b1b852a 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
@@ -253,6 +253,9 @@ _Nonnull SEL WXSwizzledSelectorForSelector(_Nonnull SEL selector);
*/
+ (CGFloat)defaultPixelScaleFactor;
+#if defined __cplusplus
+extern "C" {
+#endif
/**
* @abstract Returns the scale of the main screen.
*
@@ -276,7 +279,11 @@ CGFloat WXFloorPixelValue(CGFloat value);
*
*/
CGFloat WXCeilPixelValue(CGFloat value);
-
+
+#if defined __cplusplus
+};
+#endif
+
/**
* @abstract check whether the file is exist
*
@@ -404,6 +411,10 @@ CGPoint WXPixelPointResize(CGPoint value) DEPRECATED_MSG_ATTRIBUTE("Use WXPixelS
*/
+ (NSDictionary *_Nullable)linearGradientWithBackgroundImage:(NSString *_Nullable)backgroundImage;
+#if defined __cplusplus
+extern "C" {
+#endif
+
/**
* @abstract compare float a and b, if a equal b, return true,or reture false.
*
@@ -435,6 +446,10 @@ BOOL WXFloatGreaterThan(CGFloat a, CGFloat b);
*/
BOOL WXFloatGreaterThanWithPrecision(CGFloat a,CGFloat b,double precision);
+#if defined __cplusplus
+};
+#endif
+
/**
* @abstract convert returnKeyType to type string .
*
[14/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m b/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m
index b76ac90..e50e15a 100644
--- a/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m
+++ b/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m
@@ -136,6 +136,9 @@ _Pragma("clang diagnostic pop") \
}
[WXTracingManager startTracingWithInstanceId:instanceId ref:elementData[@"ref"] className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"addElement" options:nil];
WXPerformBlockOnComponentThread(^{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action: addElement : %@",elementData[@"type"]);
+#endif
WXComponentManager *manager = instance.componentManager;
if (!manager.isValid) {
return;
@@ -159,6 +162,9 @@ _Pragma("clang diagnostic pop") \
}
[WXTracingManager startTracingWithInstanceId:instanceId ref:bodyData[@"ref"] className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"createBody" options:@{@"threadName":WXTJSBridgeThread}];
WXPerformBlockOnComponentThread(^{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action: createBody %@ ref:%@",bodyData[@"type"],bodyData[@"ref"]);
+#endif
WXComponentManager *manager = instance.componentManager;
if (!manager.isValid) {
return;
@@ -183,6 +189,9 @@ _Pragma("clang diagnostic pop") \
}
[WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"removeElement" options:nil];
WXPerformBlockOnComponentThread(^{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action: removeElement ref:%@",ref);
+#endif
WXComponentManager *manager = instance.componentManager;
if (!manager.isValid) {
return;
@@ -206,6 +215,9 @@ _Pragma("clang diagnostic pop") \
}
[WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"moveElement" options:nil];
WXPerformBlockOnComponentThread(^{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action: moveElement ,ref:%@ to ref:%@",ref,parentRef);
+#endif
WXComponentManager *manager = instance.componentManager;
if (!manager.isValid) {
return;
@@ -228,6 +240,9 @@ _Pragma("clang diagnostic pop") \
[WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"updateAttrs" options:@{@"threadName":WXTJSBridgeThread}];
WXPerformBlockOnComponentThread(^{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action: updateAttrs ref:%@,attr:%@",ref,attrsData);
+#endif
WXComponentManager *manager = instance.componentManager;
if (!manager.isValid) {
return;
@@ -251,6 +266,10 @@ _Pragma("clang diagnostic pop") \
}
[WXTracingManager startTracingWithInstanceId:instanceId ref:ref className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"updateStyles" options:@{@"threadName":WXTJSBridgeThread}];
WXPerformBlockOnComponentThread(^{
+
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action: updateStyles ref:%@,styles:%@",ref,stylesData);
+#endif
WXComponentManager *manager = instance.componentManager;
if (!manager.isValid) {
return;
@@ -273,6 +292,10 @@ _Pragma("clang diagnostic pop") \
}
WXPerformBlockOnComponentThread(^{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action: addEvent ref:%@",ref);
+#endif
+
WXComponentManager *manager = instance.componentManager;
if (!manager.isValid) {
return;
@@ -295,6 +318,9 @@ _Pragma("clang diagnostic pop") \
}
WXPerformBlockOnComponentThread(^{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action :removeEvent ref:%@",ref);
+#endif
WXComponentManager *manager = instance.componentManager;
if (!manager.isValid) {
return;
@@ -317,6 +343,10 @@ _Pragma("clang diagnostic pop") \
}
[WXTracingManager startTracingWithInstanceId:instanceId ref:nil className:nil name:WXTJSCall phase:WXTracingEnd functionName:@"createFinish" options:@{@"threadName":WXTJSBridgeThread}];
WXPerformBlockOnComponentThread(^{
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action: createFinish :%@",instanceId);
+#endif
+
WXComponentManager *manager = instance.componentManager;
if (!manager.isValid) {
return;
@@ -339,6 +369,9 @@ _Pragma("clang diagnostic pop") \
WXLogInfo(@"instance not found for callNativeModule:%@.%@, maybe already destroyed", moduleName, methodName);
return nil;
}
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action: callNativeModule : %@ . %@",moduleName,methodName);
+#endif
NSMutableDictionary * newOptions = [options mutableCopy];
NSMutableArray * newArguments = [arguments mutableCopy];
@@ -367,6 +400,11 @@ _Pragma("clang diagnostic pop") \
}];
[_jsBridge registerCallNativeComponent:^void(NSString *instanceId, NSString *componentRef, NSString *methodName, NSArray *args, NSDictionary *options) {
+
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action: callNativeComponent ref:%@",componentRef);
+#endif
+
WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId];
WXComponentMethod *method = [[WXComponentMethod alloc] initWithComponentRef:componentRef methodName:methodName arguments:args instance:instance];
[method invoke];
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.m b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.m
deleted file mode 100644
index a6adec0..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.m
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXCellSlotComponent.h"
-#import "WXComponent_internal.h"
-#import "WXComponentManager.h"
-#import "WXSDKInstance_private.h"
-#import "WXConvert.h"
-#import "WXAssert.h"
-#import "WXScrollerComponent.h"
-
-@implementation WXCellSlotComponent
-
-- (instancetype)initWithRef:(NSString *)ref
- type:(NSString *)type
- styles:(NSDictionary *)styles
- attributes:(NSDictionary *)attributes
- events:(NSArray *)events
- weexInstance:(WXSDKInstance *)weexInstance
-{
- self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
- if (self) {
- // TODO: isRecycle / insertAnimation / deleteAnimation / keepScrollPosition
- if (attributes[@"default"]) {
- _templateCaseType = @"default";
- }
- _templateCaseType = attributes[@"case"] ? [WXConvert NSString:attributes[@"case"]] : WXDefaultRecycleTemplateType;
- _lazyCreateView = YES;
- _isNeedJoinLayoutSystem = NO;
- }
-
- return self;
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
-
-}
-
-- (void)updateCellData:(NSDictionary *)data
-{
- WXAssertComponentThread();
-
- [self updateBindingData:data];
- [self triggerLayout];
-}
-
-- (void)_didInserted
-{
- [self triggerLayout];
-}
-
-- (void)triggerLayout
-{
- WXAssertComponentThread();
-
- //TODO: _isUseContainerWidth?
- if (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH])) {
- self.cssNode->style.dimensions[CSS_WIDTH] = ((WXScrollerComponent *)(self.supercomponent)).scrollerCSSNode->style.dimensions[CSS_WIDTH];
- }
-
- if ([self needsLayout]) {
- layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
- if ([WXLog logLevel] >= WXLogLevelDebug) {
- print_css_node(self.cssNode, CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN);
- }
- }
-
- NSMutableSet<WXComponent *> *dirtyComponents = [NSMutableSet set];
- [self _calculateFrameWithSuperAbsolutePosition:CGPointZero gatherDirtyComponents:dirtyComponents];
- for (WXComponent *dirtyComponent in dirtyComponents) {
- [self.weexInstance.componentManager _addUITask:^{
- [dirtyComponent _layoutDidFinish];
- }];
- }
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.mm b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.mm
new file mode 100644
index 0000000..b1b757a
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXCellSlotComponent.mm
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXCellSlotComponent.h"
+#import "WXComponent_internal.h"
+#import "WXComponentManager.h"
+#import "WXSDKInstance_private.h"
+#import "WXConvert.h"
+#import "WXAssert.h"
+#import "WXScrollerComponent+Layout.h"
+
+@implementation WXCellSlotComponent
+
+- (instancetype)initWithRef:(NSString *)ref
+ type:(NSString *)type
+ styles:(NSDictionary *)styles
+ attributes:(NSDictionary *)attributes
+ events:(NSArray *)events
+ weexInstance:(WXSDKInstance *)weexInstance
+{
+ self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
+ if (self) {
+ // TODO: isRecycle / insertAnimation / deleteAnimation / keepScrollPosition
+ if (attributes[@"default"]) {
+ _templateCaseType = @"default";
+ }
+ _templateCaseType = attributes[@"case"] ? [WXConvert NSString:attributes[@"case"]] :const_cast<NSString *>(WXDefaultRecycleTemplateType) ;
+ _lazyCreateView = YES;
+ _isNeedJoinLayoutSystem = NO;
+ }
+
+ return self;
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+
+}
+
+- (void)updateCellData:(NSDictionary *)data
+{
+ WXAssertComponentThread();
+
+ [self updateBindingData:data];
+ [self triggerLayout];
+}
+
+- (void)_didInserted
+{
+ [self triggerLayout];
+}
+
+- (void)triggerLayout
+{
+ WXAssertComponentThread();
+
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ //TODO: _isUseContainerWidth?
+ if (isUndefined(self.cssNode->style.dimensions[CSS_WIDTH])) {
+ self.cssNode->style.dimensions[CSS_WIDTH] = ((WXScrollerComponent *)(self.supercomponent)).scrollerCSSNode->style.dimensions[CSS_WIDTH];
+ }
+
+ if ([self needsLayout]) {
+ layoutNode(self.cssNode, CSS_UNDEFINED, CSS_UNDEFINED, CSS_DIRECTION_INHERIT);
+ if ([WXLog logLevel] >= WXLogLevelDebug) {
+ print_css_node(self.cssNode, (css_print_options_t)(CSS_PRINT_LAYOUT | CSS_PRINT_STYLE | CSS_PRINT_CHILDREN));
+ }
+ }
+ }
+
+//#else
+ else
+ {
+ if (flexIsUndefined(self.flexCssNode->getStyleWidth())) {
+ self.flexCssNode->setStyleWidth(((WXScrollerComponent *)(self.supercomponent)).flexScrollerCSSNode->getStyleWidth(),NO);
+ }
+
+ if ([self needsLayout]) {
+ std::pair<float, float> renderPageSize;
+ renderPageSize.first = self.weexInstance.frame.size.width;
+ renderPageSize.second = self.weexInstance.frame.size.height;
+ self.flexCssNode->calculateLayout(renderPageSize);
+ if ([WXLog logLevel] >= WXLogLevelDebug) {
+
+ }
+ }
+ }
+//#endif
+
+ NSMutableSet<WXComponent *> *dirtyComponents = [NSMutableSet set];
+ [self _calculateFrameWithSuperAbsolutePosition:CGPointZero gatherDirtyComponents:dirtyComponents];
+ for (WXComponent *dirtyComponent in dirtyComponents) {
+ [self.weexInstance.componentManager _addUITask:^{
+ [dirtyComponent _layoutDidFinish];
+ }];
+ }
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.m b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.m
deleted file mode 100644
index 8f71797..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.m
+++ /dev/null
@@ -1,618 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXLog.h"
-#import "WXUtility.h"
-#import "WXComponent_internal.h"
-#import "WXComponentManager.h"
-#import "WXSDKInstance_private.h"
-
-#import "WXCellSlotComponent.h"
-#import "WXRecycleListLayout.h"
-#import "WXRecycleListComponent.h"
-#import "WXRecycleListDataManager.h"
-#import "WXRecycleListTemplateManager.h"
-#import "WXRecycleListUpdateManager.h"
-#import "WXBridgeManager.h"
-#import "WXSDKManager.h"
-#import "WXComponent+DataBinding.h"
-
-@interface WXRecycleListComponentView:UICollectionView
-@end
-
-@implementation WXRecycleListComponentView
-- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
-{
- if ([(id <WXScrollerProtocol>) self.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) {
- return [(id <WXScrollerProtocol>) self.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
- }
- else{
- return YES;
- }
-}
-
-@end
-
-@interface WXRecycleListComponent () <WXRecycleListLayoutDelegate, WXRecycleListUpdateDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource>
-
-@end
-
-@implementation WXRecycleListComponent
-{
- NSString *_templateSwitchKey;
- NSString *_aliasKey;
- NSString *_indexKey;
- __weak UICollectionView *_collectionView;
-
- NSMutableDictionary *_sizeCache;
- NSMutableDictionary *_stickyCache;
-
- NSUInteger _previousLoadMoreCellNumber;
-}
-
-WX_EXPORT_METHOD(@selector(appendData:))
-WX_EXPORT_METHOD(@selector(appendRange:))
-WX_EXPORT_METHOD(@selector(insertData:data:))
-WX_EXPORT_METHOD(@selector(updateData:data:))
-WX_EXPORT_METHOD(@selector(removeData:count:))
-WX_EXPORT_METHOD(@selector(moveData:toIndex:))
-WX_EXPORT_METHOD(@selector(scrollTo:options:))
-WX_EXPORT_METHOD(@selector(insertRange:range:))
-WX_EXPORT_METHOD(@selector(setListData:))
-
-- (instancetype)initWithRef:(NSString *)ref
- type:(NSString *)type
- styles:(NSDictionary *)styles
- attributes:(NSDictionary *)attributes
- events:(NSArray *)events
- weexInstance:(WXSDKInstance *)weexInstance
-{
- if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
- _dataManager = attributes[@"listData"]? [[WXRecycleListDataManager alloc] initWithData:attributes[@"listData"]] : [WXRecycleListDataManager new];
- _templateManager = [WXRecycleListTemplateManager new];
- _updateManager = [WXRecycleListUpdateManager new];
- _updateManager.delegate = self;
- _templateSwitchKey = [WXConvert NSString:attributes[@"switch"]];
- _aliasKey = [WXConvert NSString:attributes[@"alias"]];
- _indexKey = [WXConvert NSString:attributes[@"index"]];
- _sizeCache = [NSMutableDictionary dictionary];
- _stickyCache = [NSMutableDictionary dictionary];
- }
-
- return self;
-}
-
-#pragma mark - WXComponent Methods
-
-- (UIView *)loadView
-{
- WXRecycleListLayout *layout = [self recycleListLayout];
- return [[WXRecycleListComponentView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
-
- _collectionView = (UICollectionView *)self.view;
- _collectionView.allowsSelection = NO;
- _collectionView.allowsMultipleSelection = NO;
- _collectionView.dataSource = self;
- _collectionView.delegate = self;
-
- _templateManager.collectionView = _collectionView;
- _updateManager.collectionView = _collectionView;
-}
-
-- (void)viewWillUnload
-{
- [super viewWillUnload];
-
- _collectionView.dataSource = nil;
- _collectionView.delegate = nil;
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- [super updateAttributes:attributes];
-
- if (attributes[@"listData"]) {
- NSArray *listData = attributes[@"listData"];
- [self _updateListData:listData withCompletion:nil animation:NO];
- }
- if (attributes[@"switch"]) {
- _templateSwitchKey = [WXConvert NSString:attributes[@"switch"]];
- }
- if (attributes[@"alias"]) {
- _aliasKey = [WXConvert NSString:attributes[@"alias"]];
- }
- if (attributes[@"index"]) {
- _indexKey = [WXConvert NSString:attributes[@"index"]];
- }
- if (attributes[@"scrollDirection"]) {
- WXScrollDirection newScrollDirection = attributes[@"scrollDirection"] ? [WXConvert WXScrollDirection:attributes[@"scrollDirection"]] : WXScrollDirectionVertical;
- [self _updateScrollDirection:newScrollDirection];
- }
-}
-
-- (CGPoint)absolutePositionForComponent:(WXComponent *)component
-{
- CGPoint position = CGPointZero;
- UIView *view = component->_view;
- while (view) {
- if ([view isKindOfClass:[UICollectionViewCell class]]) {
- NSIndexPath *indexPath = [_collectionView indexPathForCell:(UICollectionViewCell *)view];
- if (!indexPath) {
- return CGPointMake(NAN, NAN);
- }
- UICollectionViewLayoutAttributes *attributes = [_collectionView layoutAttributesForItemAtIndexPath:indexPath];
- CGPoint cellOrigin = attributes.frame.origin;
- position = CGPointMake(position.x + cellOrigin.x,
- position.y + cellOrigin.y);
- break;
- }
- position = CGPointMake(position.x + view.frame.origin.x,
- position.y + view.frame.origin.y);
- view = view.superview;
- }
-
- return position;
-}
-
-- (void)setContentSize:(CGSize)contentSize
-{
- // Do Nothing
-}
-
-- (void)adjustSticky
-{
- // Do Nothing, sticky is adjusted by layout
-}
-
-#pragma mark - Load More Event
-
-- (void)loadMore
-{
- [super loadMore];
-
- _previousLoadMoreCellNumber = [_collectionView numberOfItemsInSection:0];
-}
-
-- (BOOL)isNeedLoadMore
-{
- BOOL superNeedLoadMore = [super isNeedLoadMore];
- return superNeedLoadMore && _previousLoadMoreCellNumber != [_collectionView numberOfItemsInSection:0];
-}
-
-- (void)resetLoadmore
-{
- [super resetLoadmore];
- _previousLoadMoreCellNumber = 0;
-}
-
-#pragma mark - Exported Component Methods
-
-- (void)appendData:(id)appendingData
-{
- if (!appendingData){
- return;
- }
- NSMutableArray * newListData = [[_dataManager data] mutableCopy];
- [newListData addObject:appendingData];
-}
-
-- (void)appendRange:(NSArray*)data
-{
- if (![data isKindOfClass:[NSArray class]]) {
- WXLogError(@"wrong format of appending data:%@", data);
- return;
- }
-
- NSArray *oldData = [_dataManager data];
- [_updateManager updateWithAppendingData:data oldData:oldData completion:nil animation:NO];
-}
-
-- (void)setListData:(NSArray*)data
-{
- if ([data count]) {
- [_dataManager updateData:data];
- }
-}
-- (void)insertData:(NSUInteger)index data:(id)data
-{
- // TODO: bring the update logic to UpdateManager
- // TODO: update cell because index has changed
- NSMutableArray *newListData = [[_dataManager data] mutableCopy];
- if (index <= newListData.count) {
- [newListData insertObject:data atIndex:index];
- [_dataManager updateData:newListData];
-
- NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];
-
- [UIView performWithoutAnimation:^{
- [self->_collectionView insertItemsAtIndexPaths:@[indexPath]];
- }];
- }
-}
-
-- (void)updateComponentData:(NSString*)componentDataId componentData:(NSDictionary*)componentData callback:(NSString*)callbackId
-{
- NSMutableDictionary * virtualComponentData = [[_dataManager virtualComponentDataWithId:componentDataId] mutableCopy];
- NSIndexPath * indexPath = virtualComponentData[@"indexPath"];
- if (!indexPath) {
- return;
- }
- virtualComponentData = virtualComponentData?:[NSMutableDictionary new];
- [virtualComponentData addEntriesFromDictionary:componentData];
- [_dataManager updateVirtualComponentData:componentDataId data:[virtualComponentData copy]];
- virtualComponentData[@"@phase"] = @"update";
- virtualComponentData[@"callbackId"] = callbackId;
- [self _updateDataForCellSlotAtIndexPath:indexPath data:virtualComponentData];
-}
-
-- (void)_updateDataForCellSlotAtIndexPath:(NSIndexPath*)indexPath data:(NSDictionary*)data
-{
- if(!indexPath || !data) {
- return;
- }
- WXPerformBlockOnMainThread(^{
- UICollectionViewCell * cellView = [self->_collectionView cellForItemAtIndexPath:indexPath];
- WXCellSlotComponent * cellSlotComponent = (WXCellSlotComponent*)cellView.wx_component;
- if (cellSlotComponent) {
- [self _updateBindingData:data forCell:cellSlotComponent atIndexPath:indexPath];
- }
- // callback when update virtual component data success.
- NSString * callbackId = data[@"callbackId"];
- if (callbackId) {
- [[WXSDKManager bridgeMgr] callBack:self.weexInstance.instanceId funcId:callbackId params:@{@"result":@"success"}];
- }
- });
-}
-
-- (void)updateData:(NSUInteger)index data:(id)data
-{
- NSMutableArray * newListData = [[_dataManager data] mutableCopy];
- if (!data && index > [newListData count]) {
- return;
- }
- NSIndexPath * indexPath = [NSIndexPath indexPathForRow:index inSection:0];
- NSDictionary * virtualComponentData = [_dataManager virtualComponentDataWithIndexPath:indexPath];
- if ([virtualComponentData[WXBindingOnceIdentify] boolValue]) {
- return;
- }
-
- // TODO: bring the update logic to UpdateManager
- newListData[index] = data;
- [_dataManager updateData:newListData];
- NSString* virtualComponentId = [_dataManager virtualComponentIdWithIndexPath:indexPath];
- [_dataManager updateVirtualComponentData:virtualComponentId data:data];
- NSMutableDictionary * newData = nil;
- if (![data isKindOfClass:[NSDictionary class]]) {
- newData = [NSMutableDictionary new];
- [newData setObject:@"data" forKey:data];
- data = newData;
- }
- newData = [data mutableCopy];
- newData[@"@phase"] = @"update";
- [self _updateDataForCellSlotAtIndexPath:indexPath data:[newData copy]];
-}
-
-- (void)insertRange:(NSInteger)index range:(NSArray*)data
-{
- if (![data count]) {
- WXLogDebug(@"ignore invalid insertRange");
- return;
- }
-
- NSMutableArray * newListData = [[_dataManager data] mutableCopy];
- NSRange range = NSMakeRange(index,[data count]);
- NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
- [newListData insertObjects:data atIndexes:indexSet];
- [_dataManager updateData:newListData];
- [_collectionView reloadData];
-}
-
-- (void)removeData:(NSInteger)index count:(NSInteger)count
-{
- // TODO: bring the update logic to UpdateManager
-
- NSMutableArray *newListData = [[_dataManager data] mutableCopy];
- if (index > [newListData count] || index + count - 1 > [newListData count]) {
-
- return;
- }
- NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, count)];
- [newListData removeObjectsAtIndexes:indexSet];
- __block NSMutableArray<NSIndexPath*>* indexPaths = [NSMutableArray new];
- [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
- NSIndexPath* indexPath = [NSIndexPath indexPathForRow:idx inSection:0];
- [indexPaths addObject:indexPath];
- }];
-
- [_dataManager updateData:newListData];
- [_dataManager deleteVirtualComponentAtIndexPaths:indexPaths];
- [UIView performWithoutAnimation:^{
- [self->_collectionView deleteItemsAtIndexPaths:indexPaths];
- [self->_collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
- }];
-}
-
-- (void)moveData:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex
-{
- // TODO: bring the update logic to UpdateManager
- NSMutableArray *newListData = [[_dataManager data] mutableCopy];
- id data = newListData[fromIndex];
- [newListData removeObjectAtIndex:fromIndex];
- [newListData insertObject:data atIndex:toIndex];
- [_dataManager updateData:newListData];
-
- NSIndexPath *fromIndexPath = [NSIndexPath indexPathForItem:fromIndex inSection:0];
- NSIndexPath *toIndexPath = [NSIndexPath indexPathForItem:toIndex inSection:0];
- [UIView performWithoutAnimation:^{
- [self->_collectionView moveItemAtIndexPath:fromIndexPath toIndexPath:toIndexPath];
- }];
-}
-
-- (void)scrollTo:(NSUInteger)index options:(NSDictionary *)options
-{
- NSIndexPath *toIndexPath = [NSIndexPath indexPathForItem:index inSection:0];
- BOOL animated = options[@"animated"] ? [WXConvert BOOL:options[@"animated"]] : NO;
- [_collectionView scrollToItemAtIndexPath:toIndexPath atScrollPosition:UICollectionViewScrollPositionTop animated:animated];
-}
-
-#pragma mark - WXComponent Internal Methods
-
-- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- [super _insertSubcomponent:subcomponent atIndex:index];
-
- if ([subcomponent isKindOfClass:[WXCellSlotComponent class]]) {
- WXCellSlotComponent *cell = (WXCellSlotComponent*)subcomponent;
- [self.weexInstance.componentManager _addUITask:^{
- [_templateManager addTemplate:cell];
- }];
-
- //TODO: update collection view if adding template
- }
-}
-
-#pragma mark - Private
-
-- (void)_updateBindingData:(id)data forCell:(WXCellSlotComponent *)cellComponent atIndexPath:(NSIndexPath *)indexPath
-{
- id originalData = data;
- if (![originalData isKindOfClass:[NSDictionary class]]) {
- if (_aliasKey) {
- NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
- [dictionary setObject:data forKey:_aliasKey];
- data = dictionary;
- } else {
- return;
- }
- }
-
- if (!data[@"indexPath"] || !data[@"recycleListComponentRef"]) {
- NSMutableDictionary * dataNew = [data mutableCopy];
- dataNew[@"recycleListComponentRef"] = self.ref;
- dataNew[@"indexPath"] = indexPath;
- data = dataNew;
- }
-
- if ([originalData isKindOfClass:[NSDictionary class]] && _aliasKey &&!data[@"phase"]) {
- data = @{_aliasKey:data,@"aliasKey":_aliasKey};
- }
-
- if (_indexKey) {
- NSMutableDictionary *dataNew = [data mutableCopy];
- dataNew[_indexKey] = @(indexPath.item);
- data = dataNew;
- }
-
-#ifdef DEBUG
- NSDate *startTime = [NSDate date];
-#endif
-
- WXPerformBlockSyncOnComponentThread(^{
- [cellComponent updateCellData:[data copy]];
- });
-#ifdef DEBUG
- double duration = -[startTime timeIntervalSinceNow] * 1000;
- WXLogDebug(@"cell:%li update data time:%f", (long)indexPath.item, duration);
-#endif
-
- NSValue *cachedSize = _sizeCache[indexPath];
- if (!cachedSize || !CGSizeEqualToSize([cachedSize CGSizeValue] , cellComponent.calculatedFrame.size)) {
- _sizeCache[indexPath] = [NSValue valueWithCGSize:cellComponent.calculatedFrame.size];
- [_collectionView.collectionViewLayout invalidateLayout];
- }
- NSNumber *cachedSticky = _stickyCache[indexPath];
- BOOL isSticky = cellComponent->_positionType == WXPositionTypeSticky;
- if (!cachedSticky || [cachedSticky boolValue] != isSticky) {
- _stickyCache[indexPath] = @(isSticky);
- }
-}
-
-- (void)_updateListData:(NSArray *)newData
- withCompletion:(WXRecycleListUpdateCompletion)completion
- animation:(BOOL)animation
-{
- if (![newData isKindOfClass:[NSArray class]]) {
- WXLogError(@"wrong format of list data:%@", newData);
- completion(NO);
- return;
- }
-
- NSArray *oldData = [_dataManager data];
- [_updateManager updateWithNewData:newData oldData:oldData completion:completion animation:animation];
-}
-
-- (void)_updateScrollDirection:(WXScrollDirection)newScrollDirection
-{
- WXRecycleListLayout *layout = [self recycleListLayout];
- _collectionView.collectionViewLayout = layout;
-}
-
-- (WXRecycleListLayout *)recycleListLayout
-{
- WXRecycleListLayout *layout = [WXRecycleListLayout new];
- layout.delegate = self;
- // to show cells that original width / height is zero, otherwise cellForItemAtIndexPath will not be called
- layout.minimumLineSpacing = 0.01;
- layout.minimumInteritemSpacing = 0.01;
- if (WXScrollDirectionHorizontal == self.scrollDirection) {
- layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
- }
- return layout;
-}
-
-#pragma mark - UICollectionViewDataSource
-
-- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
-{
- return 1;
-}
-
-- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
-{
- return [_dataManager numberOfItems];
-}
-
-- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
-{
- // 1. get the data relating to the cell
- id data = [_dataManager dataAtIndex:indexPath.row];
-
- // 2. get the template type specified by data
- NSString * templateType = [self templateType:indexPath];
- _templateManager.collectionView = collectionView;
- if (!templateType) {
- WXLogError(@"Each data should have a value for %@ to indicate template type", _templateSwitchKey);
- return nil;
- }
-
- // 3. dequeue a cell component by template type
- UICollectionViewCell *cellView = [_collectionView dequeueReusableCellWithReuseIdentifier:templateType forIndexPath:indexPath];
- WXCellSlotComponent *cellComponent = (WXCellSlotComponent *)cellView.wx_component;
- if (!cellComponent) {
- cellComponent = [_templateManager dequeueCellSlotWithType:templateType forIndexPath:indexPath];
- cellView.wx_component = cellComponent;
- WXPerformBlockOnComponentThread(^{
- //TODO: How can we avoid this?
- [super _insertSubcomponent:cellComponent atIndex:self.subcomponents.count];
- });
- }
-
- // 4. binding the data to the cell component
- [self _updateBindingData:data forCell:cellComponent atIndexPath:indexPath];
-
- // 5. Add cell component's view to content view.
- UIView *contentView = cellComponent.view;
- if (contentView.superview == cellView.contentView) {
- return cellView;
- }
-
- for (UIView *view in cellView.contentView.subviews) {
- [view removeFromSuperview];
- }
- [cellView.contentView addSubview:contentView];
- [cellView setAccessibilityIdentifier:contentView.accessibilityIdentifier];
-
- WXLogDebug(@"Return cell view:%@, indexPath:%@", cellView, indexPath);
-
- [self handleAppear];
-
- return cellView;
-}
-
-- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
-{
- return nil;
-}
-
-#pragma mark - UICollectionViewDelegate
-
-- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
-{
- WXLogDebug(@"will display cell:%@, at index path:%@", cell, indexPath);
-}
-
-- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
-{
- WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath);
-}
-
-#pragma mark - UICollectionViewDelegateFlowLayout
-
-- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
-{
- NSValue *size = _sizeCache[indexPath];
- if (size) {
- return [size CGSizeValue];
- } else {
-
- WXCellSlotComponent *cell = [_templateManager templateWithType:[self templateType:indexPath]];
- CGSize size = cell.calculatedFrame.size;
- _sizeCache[indexPath] = [NSValue valueWithCGSize:size];
- return CGSizeMake(_collectionView.frame.size.width, size.height);
- }
-}
-
-#pragma mark - WXRecycleListLayoutDelegate
-
-- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout isNeedStickyForIndexPath:(NSIndexPath *)indexPath
-{
- NSNumber *cachedSticky = _stickyCache[indexPath];
- if (cachedSticky) {
- return [cachedSticky boolValue];
- } else {
- return NO;
- }
-}
-
-#pragma mark - WXRecycleListUpdateDelegate
-
-- (void)updateManager:(WXRecycleListUpdateManager *)manager willUpdateData:(id)newData
-{
- [_dataManager updateData:newData];
-}
-
-- (void)updateManager:(WXRecycleListUpdateManager *)manager didUpdateData:(id)newData withSuccess:(BOOL)finished
-{
-
-}
-
-- (NSString*)templateType:(NSIndexPath*)indexPath
-{
- NSDictionary *data = [_dataManager dataAtIndex:indexPath.row];
- // default is first template.
- NSString *templateType = [_templateManager topTemplate].templateCaseType;
- if (!data || ![data isKindOfClass:[NSDictionary class]]) {
- return templateType;
- }
-
- if (_templateSwitchKey &&data[_templateSwitchKey]){
- templateType = data[_templateSwitchKey];
- } else if (data[WXDefaultRecycleTemplateType]){
- // read the default type.
- templateType = data[WXDefaultRecycleTemplateType];
- }
- return templateType;
-}
-
-@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.mm b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.mm
new file mode 100644
index 0000000..551acd9
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/RecycleList/WXRecycleListComponent.mm
@@ -0,0 +1,619 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXLog.h"
+#import "WXUtility.h"
+#import "WXComponent_internal.h"
+#import "WXComponentManager.h"
+#import "WXSDKInstance_private.h"
+
+#import "WXCellSlotComponent.h"
+#import "WXRecycleListLayout.h"
+#import "WXRecycleListComponent.h"
+#import "WXRecycleListDataManager.h"
+#import "WXRecycleListTemplateManager.h"
+#import "WXRecycleListUpdateManager.h"
+#import "WXBridgeManager.h"
+#import "WXSDKManager.h"
+#import "WXComponent+DataBinding.h"
+#import "WXComponent+Layout.h"
+
+@interface WXRecycleListComponentView:UICollectionView
+@end
+
+@implementation WXRecycleListComponentView
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
+{
+ if ([(id <WXScrollerProtocol>) self.wx_component respondsToSelector:@selector(requestGestureShouldStopPropagation:shouldReceiveTouch:)]) {
+ return [(id <WXScrollerProtocol>) self.wx_component requestGestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
+ }
+ else{
+ return YES;
+ }
+}
+
+@end
+
+@interface WXRecycleListComponent () <WXRecycleListLayoutDelegate, WXRecycleListUpdateDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource>
+
+@end
+
+@implementation WXRecycleListComponent
+{
+ NSString *_templateSwitchKey;
+ NSString *_aliasKey;
+ NSString *_indexKey;
+ __weak UICollectionView *_collectionView;
+
+ NSMutableDictionary *_sizeCache;
+ NSMutableDictionary *_stickyCache;
+
+ NSUInteger _previousLoadMoreCellNumber;
+}
+
+WX_EXPORT_METHOD(@selector(appendData:))
+WX_EXPORT_METHOD(@selector(appendRange:))
+WX_EXPORT_METHOD(@selector(insertData:data:))
+WX_EXPORT_METHOD(@selector(updateData:data:))
+WX_EXPORT_METHOD(@selector(removeData:count:))
+WX_EXPORT_METHOD(@selector(moveData:toIndex:))
+WX_EXPORT_METHOD(@selector(scrollTo:options:))
+WX_EXPORT_METHOD(@selector(insertRange:range:))
+WX_EXPORT_METHOD(@selector(setListData:))
+
+- (instancetype)initWithRef:(NSString *)ref
+ type:(NSString *)type
+ styles:(NSDictionary *)styles
+ attributes:(NSDictionary *)attributes
+ events:(NSArray *)events
+ weexInstance:(WXSDKInstance *)weexInstance
+{
+ if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
+ _dataManager = attributes[@"listData"]? [[WXRecycleListDataManager alloc] initWithData:attributes[@"listData"]] : [WXRecycleListDataManager new];
+ _templateManager = [WXRecycleListTemplateManager new];
+ _updateManager = [WXRecycleListUpdateManager new];
+ _updateManager.delegate = self;
+ _templateSwitchKey = [WXConvert NSString:attributes[@"switch"]];
+ _aliasKey = [WXConvert NSString:attributes[@"alias"]];
+ _indexKey = [WXConvert NSString:attributes[@"index"]];
+ _sizeCache = [NSMutableDictionary dictionary];
+ _stickyCache = [NSMutableDictionary dictionary];
+ }
+
+ return self;
+}
+
+#pragma mark - WXComponent Methods
+
+- (UIView *)loadView
+{
+ WXRecycleListLayout *layout = [self recycleListLayout];
+ return [[WXRecycleListComponentView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ _collectionView = (UICollectionView *)self.view;
+ _collectionView.allowsSelection = NO;
+ _collectionView.allowsMultipleSelection = NO;
+ _collectionView.dataSource = self;
+ _collectionView.delegate = self;
+
+ _templateManager.collectionView = _collectionView;
+ _updateManager.collectionView = _collectionView;
+}
+
+- (void)viewWillUnload
+{
+ [super viewWillUnload];
+
+ _collectionView.dataSource = nil;
+ _collectionView.delegate = nil;
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes
+{
+ [super updateAttributes:attributes];
+
+ if (attributes[@"listData"]) {
+ NSArray *listData = attributes[@"listData"];
+ [self _updateListData:listData withCompletion:nil animation:NO];
+ }
+ if (attributes[@"switch"]) {
+ _templateSwitchKey = [WXConvert NSString:attributes[@"switch"]];
+ }
+ if (attributes[@"alias"]) {
+ _aliasKey = [WXConvert NSString:attributes[@"alias"]];
+ }
+ if (attributes[@"index"]) {
+ _indexKey = [WXConvert NSString:attributes[@"index"]];
+ }
+ if (attributes[@"scrollDirection"]) {
+ WXScrollDirection newScrollDirection = attributes[@"scrollDirection"] ? [WXConvert WXScrollDirection:attributes[@"scrollDirection"]] : WXScrollDirectionVertical;
+ [self _updateScrollDirection:newScrollDirection];
+ }
+}
+
+- (CGPoint)absolutePositionForComponent:(WXComponent *)component
+{
+ CGPoint position = CGPointZero;
+ UIView *view = component->_view;
+ while (view) {
+ if ([view isKindOfClass:[UICollectionViewCell class]]) {
+ NSIndexPath *indexPath = [_collectionView indexPathForCell:(UICollectionViewCell *)view];
+ if (!indexPath) {
+ return CGPointMake(NAN, NAN);
+ }
+ UICollectionViewLayoutAttributes *attributes = [_collectionView layoutAttributesForItemAtIndexPath:indexPath];
+ CGPoint cellOrigin = attributes.frame.origin;
+ position = CGPointMake(position.x + cellOrigin.x,
+ position.y + cellOrigin.y);
+ break;
+ }
+ position = CGPointMake(position.x + view.frame.origin.x,
+ position.y + view.frame.origin.y);
+ view = view.superview;
+ }
+
+ return position;
+}
+
+- (void)setContentSize:(CGSize)contentSize
+{
+ // Do Nothing
+}
+
+- (void)adjustSticky
+{
+ // Do Nothing, sticky is adjusted by layout
+}
+
+#pragma mark - Load More Event
+
+- (void)loadMore
+{
+ [super loadMore];
+
+ _previousLoadMoreCellNumber = [_collectionView numberOfItemsInSection:0];
+}
+
+- (BOOL)isNeedLoadMore
+{
+ BOOL superNeedLoadMore = [super isNeedLoadMore];
+ return superNeedLoadMore && _previousLoadMoreCellNumber != [_collectionView numberOfItemsInSection:0];
+}
+
+- (void)resetLoadmore
+{
+ [super resetLoadmore];
+ _previousLoadMoreCellNumber = 0;
+}
+
+#pragma mark - Exported Component Methods
+
+- (void)appendData:(id)appendingData
+{
+ if (!appendingData){
+ return;
+ }
+ NSMutableArray * newListData = [[_dataManager data] mutableCopy];
+ [newListData addObject:appendingData];
+}
+
+- (void)appendRange:(NSArray*)data
+{
+ if (![data isKindOfClass:[NSArray class]]) {
+ WXLogError(@"wrong format of appending data:%@", data);
+ return;
+ }
+
+ NSArray *oldData = [_dataManager data];
+ [_updateManager updateWithAppendingData:data oldData:oldData completion:nil animation:NO];
+}
+
+- (void)setListData:(NSArray*)data
+{
+ if ([data count]) {
+ [_dataManager updateData:data];
+ }
+}
+- (void)insertData:(NSUInteger)index data:(id)data
+{
+ // TODO: bring the update logic to UpdateManager
+ // TODO: update cell because index has changed
+ NSMutableArray *newListData = [[_dataManager data] mutableCopy];
+ if (index <= newListData.count) {
+ [newListData insertObject:data atIndex:index];
+ [_dataManager updateData:newListData];
+
+ NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];
+
+ [UIView performWithoutAnimation:^{
+ [self->_collectionView insertItemsAtIndexPaths:@[indexPath]];
+ }];
+ }
+}
+
+- (void)updateComponentData:(NSString*)componentDataId componentData:(NSDictionary*)componentData callback:(NSString*)callbackId
+{
+ NSMutableDictionary * virtualComponentData = [[_dataManager virtualComponentDataWithId:componentDataId] mutableCopy];
+ NSIndexPath * indexPath = virtualComponentData[@"indexPath"];
+ if (!indexPath) {
+ return;
+ }
+ virtualComponentData = virtualComponentData?:[NSMutableDictionary new];
+ [virtualComponentData addEntriesFromDictionary:componentData];
+ [_dataManager updateVirtualComponentData:componentDataId data:[virtualComponentData copy]];
+ virtualComponentData[@"@phase"] = @"update";
+ virtualComponentData[@"callbackId"] = callbackId;
+ [self _updateDataForCellSlotAtIndexPath:indexPath data:virtualComponentData];
+}
+
+- (void)_updateDataForCellSlotAtIndexPath:(NSIndexPath*)indexPath data:(NSDictionary*)data
+{
+ if(!indexPath || !data) {
+ return;
+ }
+ WXPerformBlockOnMainThread(^{
+ UICollectionViewCell * cellView = [self->_collectionView cellForItemAtIndexPath:indexPath];
+ WXCellSlotComponent * cellSlotComponent = (WXCellSlotComponent*)cellView.wx_component;
+ if (cellSlotComponent) {
+ [self _updateBindingData:data forCell:cellSlotComponent atIndexPath:indexPath];
+ }
+ // callback when update virtual component data success.
+ NSString * callbackId = data[@"callbackId"];
+ if (callbackId) {
+ [[WXSDKManager bridgeMgr] callBack:self.weexInstance.instanceId funcId:callbackId params:@{@"result":@"success"}];
+ }
+ });
+}
+
+- (void)updateData:(NSUInteger)index data:(id)data
+{
+ NSMutableArray * newListData = [[_dataManager data] mutableCopy];
+ if (!data && index > [newListData count]) {
+ return;
+ }
+ NSIndexPath * indexPath = [NSIndexPath indexPathForRow:index inSection:0];
+ NSDictionary * virtualComponentData = [_dataManager virtualComponentDataWithIndexPath:indexPath];
+ if ([virtualComponentData[WXBindingOnceIdentify] boolValue]) {
+ return;
+ }
+
+ // TODO: bring the update logic to UpdateManager
+ newListData[index] = data;
+ [_dataManager updateData:newListData];
+ NSString* virtualComponentId = [_dataManager virtualComponentIdWithIndexPath:indexPath];
+ [_dataManager updateVirtualComponentData:virtualComponentId data:data];
+ NSMutableDictionary * newData = nil;
+ if (![data isKindOfClass:[NSDictionary class]]) {
+ newData = [NSMutableDictionary new];
+ [newData setObject:@"data" forKey:data];
+ data = newData;
+ }
+ newData = [data mutableCopy];
+ newData[@"@phase"] = @"update";
+ [self _updateDataForCellSlotAtIndexPath:indexPath data:[newData copy]];
+}
+
+- (void)insertRange:(NSInteger)index range:(NSArray*)data
+{
+ if (![data count]) {
+ WXLogDebug(@"ignore invalid insertRange");
+ return;
+ }
+
+ NSMutableArray * newListData = [[_dataManager data] mutableCopy];
+ NSRange range = NSMakeRange(index,[data count]);
+ NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
+ [newListData insertObjects:data atIndexes:indexSet];
+ [_dataManager updateData:newListData];
+ [_collectionView reloadData];
+}
+
+- (void)removeData:(NSInteger)index count:(NSInteger)count
+{
+ // TODO: bring the update logic to UpdateManager
+
+ NSMutableArray *newListData = [[_dataManager data] mutableCopy];
+ if (index > [newListData count] || index + count - 1 > [newListData count]) {
+
+ return;
+ }
+ NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, count)];
+ [newListData removeObjectsAtIndexes:indexSet];
+ __block NSMutableArray<NSIndexPath*>* indexPaths = [NSMutableArray new];
+ [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
+ NSIndexPath* indexPath = [NSIndexPath indexPathForRow:idx inSection:0];
+ [indexPaths addObject:indexPath];
+ }];
+
+ [_dataManager updateData:newListData];
+ [_dataManager deleteVirtualComponentAtIndexPaths:indexPaths];
+ [UIView performWithoutAnimation:^{
+ [self->_collectionView deleteItemsAtIndexPaths:indexPaths];
+ [self->_collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
+ }];
+}
+
+- (void)moveData:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex
+{
+ // TODO: bring the update logic to UpdateManager
+ NSMutableArray *newListData = [[_dataManager data] mutableCopy];
+ id data = newListData[fromIndex];
+ [newListData removeObjectAtIndex:fromIndex];
+ [newListData insertObject:data atIndex:toIndex];
+ [_dataManager updateData:newListData];
+
+ NSIndexPath *fromIndexPath = [NSIndexPath indexPathForItem:fromIndex inSection:0];
+ NSIndexPath *toIndexPath = [NSIndexPath indexPathForItem:toIndex inSection:0];
+ [UIView performWithoutAnimation:^{
+ [self->_collectionView moveItemAtIndexPath:fromIndexPath toIndexPath:toIndexPath];
+ }];
+}
+
+- (void)scrollTo:(NSUInteger)index options:(NSDictionary *)options
+{
+ NSIndexPath *toIndexPath = [NSIndexPath indexPathForItem:index inSection:0];
+ BOOL animated = options[@"animated"] ? [WXConvert BOOL:options[@"animated"]] : NO;
+ [_collectionView scrollToItemAtIndexPath:toIndexPath atScrollPosition:UICollectionViewScrollPositionTop animated:animated];
+}
+
+#pragma mark - WXComponent Internal Methods
+
+- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
+{
+ [super _insertSubcomponent:subcomponent atIndex:index];
+
+ if ([subcomponent isKindOfClass:[WXCellSlotComponent class]]) {
+ WXCellSlotComponent *cell = (WXCellSlotComponent*)subcomponent;
+ [self.weexInstance.componentManager _addUITask:^{
+ [_templateManager addTemplate:cell];
+ }];
+
+ //TODO: update collection view if adding template
+ }
+}
+
+#pragma mark - Private
+
+- (void)_updateBindingData:(id)data forCell:(WXCellSlotComponent *)cellComponent atIndexPath:(NSIndexPath *)indexPath
+{
+ id originalData = data;
+ if (![originalData isKindOfClass:[NSDictionary class]]) {
+ if (_aliasKey) {
+ NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
+ [dictionary setObject:data forKey:_aliasKey];
+ data = dictionary;
+ } else {
+ return;
+ }
+ }
+
+ if (!data[@"indexPath"] || !data[@"recycleListComponentRef"]) {
+ NSMutableDictionary * dataNew = [data mutableCopy];
+ dataNew[@"recycleListComponentRef"] = self.ref;
+ dataNew[@"indexPath"] = indexPath;
+ data = dataNew;
+ }
+
+ if ([originalData isKindOfClass:[NSDictionary class]] && _aliasKey &&!data[@"phase"]) {
+ data = @{_aliasKey:data,@"aliasKey":_aliasKey};
+ }
+
+ if (_indexKey) {
+ NSMutableDictionary *dataNew = [data mutableCopy];
+ dataNew[_indexKey] = @(indexPath.item);
+ data = dataNew;
+ }
+
+#ifdef DEBUG
+ NSDate *startTime = [NSDate date];
+#endif
+
+ WXPerformBlockSyncOnComponentThread(^{
+ [cellComponent updateCellData:[data copy]];
+ });
+#ifdef DEBUG
+ double duration = -[startTime timeIntervalSinceNow] * 1000;
+ WXLogDebug(@"cell:%li update data time:%f", (long)indexPath.item, duration);
+#endif
+
+ NSValue *cachedSize = _sizeCache[indexPath];
+ if (!cachedSize || !CGSizeEqualToSize([cachedSize CGSizeValue] , cellComponent.calculatedFrame.size)) {
+ _sizeCache[indexPath] = [NSValue valueWithCGSize:cellComponent.calculatedFrame.size];
+ [_collectionView.collectionViewLayout invalidateLayout];
+ }
+ NSNumber *cachedSticky = _stickyCache[indexPath];
+ BOOL isSticky = cellComponent->_positionType == WXPositionTypeSticky;
+ if (!cachedSticky || [cachedSticky boolValue] != isSticky) {
+ _stickyCache[indexPath] = @(isSticky);
+ }
+}
+
+- (void)_updateListData:(NSArray *)newData
+ withCompletion:(WXRecycleListUpdateCompletion)completion
+ animation:(BOOL)animation
+{
+ if (![newData isKindOfClass:[NSArray class]]) {
+ WXLogError(@"wrong format of list data:%@", newData);
+ completion(NO);
+ return;
+ }
+
+ NSArray *oldData = [_dataManager data];
+ [_updateManager updateWithNewData:newData oldData:oldData completion:completion animation:animation];
+}
+
+- (void)_updateScrollDirection:(WXScrollDirection)newScrollDirection
+{
+ WXRecycleListLayout *layout = [self recycleListLayout];
+ _collectionView.collectionViewLayout = layout;
+}
+
+- (WXRecycleListLayout *)recycleListLayout
+{
+ WXRecycleListLayout *layout = [WXRecycleListLayout new];
+ layout.delegate = self;
+ // to show cells that original width / height is zero, otherwise cellForItemAtIndexPath will not be called
+ layout.minimumLineSpacing = 0.01;
+ layout.minimumInteritemSpacing = 0.01;
+ if (WXScrollDirectionHorizontal == self.scrollDirection) {
+ layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
+ }
+ return layout;
+}
+
+#pragma mark - UICollectionViewDataSource
+
+- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
+{
+ return 1;
+}
+
+- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
+{
+ return [_dataManager numberOfItems];
+}
+
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ // 1. get the data relating to the cell
+ id data = [_dataManager dataAtIndex:indexPath.row];
+
+ // 2. get the template type specified by data
+ NSString * templateType = [self templateType:indexPath];
+ _templateManager.collectionView = collectionView;
+ if (!templateType) {
+ WXLogError(@"Each data should have a value for %@ to indicate template type", _templateSwitchKey);
+ return nil;
+ }
+
+ // 3. dequeue a cell component by template type
+ UICollectionViewCell *cellView = [_collectionView dequeueReusableCellWithReuseIdentifier:templateType forIndexPath:indexPath];
+ WXCellSlotComponent *cellComponent = (WXCellSlotComponent *)cellView.wx_component;
+ if (!cellComponent) {
+ cellComponent = [_templateManager dequeueCellSlotWithType:templateType forIndexPath:indexPath];
+ cellView.wx_component = cellComponent;
+ WXPerformBlockOnComponentThread(^{
+ //TODO: How can we avoid this?
+ [super _insertSubcomponent:cellComponent atIndex:self.subcomponents.count];
+ });
+ }
+
+ // 4. binding the data to the cell component
+ [self _updateBindingData:data forCell:cellComponent atIndexPath:indexPath];
+
+ // 5. Add cell component's view to content view.
+ UIView *contentView = cellComponent.view;
+ if (contentView.superview == cellView.contentView) {
+ return cellView;
+ }
+
+ for (UIView *view in cellView.contentView.subviews) {
+ [view removeFromSuperview];
+ }
+ [cellView.contentView addSubview:contentView];
+ [cellView setAccessibilityIdentifier:contentView.accessibilityIdentifier];
+
+ WXLogDebug(@"Return cell view:%@, indexPath:%@", cellView, indexPath);
+
+ [self handleAppear];
+
+ return cellView;
+}
+
+- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
+{
+ return nil;
+}
+
+#pragma mark - UICollectionViewDelegate
+
+- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXLogDebug(@"will display cell:%@, at index path:%@", cell, indexPath);
+}
+
+- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath);
+}
+
+#pragma mark - UICollectionViewDelegateFlowLayout
+
+- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ NSValue *size = _sizeCache[indexPath];
+ if (size) {
+ return [size CGSizeValue];
+ } else {
+
+ WXCellSlotComponent *cell = [_templateManager templateWithType:[self templateType:indexPath]];
+ CGSize size = cell.calculatedFrame.size;
+ _sizeCache[indexPath] = [NSValue valueWithCGSize:size];
+ return CGSizeMake(_collectionView.frame.size.width, size.height);
+ }
+}
+
+#pragma mark - WXRecycleListLayoutDelegate
+
+- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout isNeedStickyForIndexPath:(NSIndexPath *)indexPath
+{
+ NSNumber *cachedSticky = _stickyCache[indexPath];
+ if (cachedSticky) {
+ return [cachedSticky boolValue];
+ } else {
+ return NO;
+ }
+}
+
+#pragma mark - WXRecycleListUpdateDelegate
+
+- (void)updateManager:(WXRecycleListUpdateManager *)manager willUpdateData:(id)newData
+{
+ [_dataManager updateData:newData];
+}
+
+- (void)updateManager:(WXRecycleListUpdateManager *)manager didUpdateData:(id)newData withSuccess:(BOOL)finished
+{
+
+}
+
+- (NSString*)templateType:(NSIndexPath*)indexPath
+{
+ NSDictionary *data = [_dataManager dataAtIndex:indexPath.row];
+ // default is first template.
+ NSString *templateType = [_templateManager topTemplate].templateCaseType;
+ if (!data || ![data isKindOfClass:[NSDictionary class]]) {
+ return templateType;
+ }
+
+ if (_templateSwitchKey &&data[_templateSwitchKey]){
+ templateType = data[_templateSwitchKey];
+ } else if (data[WXDefaultRecycleTemplateType]){
+ // read the default type.
+ templateType = data[WXDefaultRecycleTemplateType];
+ }
+ return templateType;
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m b/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m
deleted file mode 100644
index 7ca1de6..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/Recycler/WXRecyclerComponent.m
+++ /dev/null
@@ -1,738 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXRecyclerComponent.h"
-#import "WXComponent_internal.h"
-#import "WXSDKInstance_private.h"
-#import "WXRecyclerDataController.h"
-#import "WXRecyclerUpdateController.h"
-#import "WXMultiColumnLayout.h"
-#import "WXHeaderComponent.h"
-#import "WXFooterComponent.h"
-#import "WXCellComponent.h"
-#import "WXAssert.h"
-#import "WXConvert.h"
-#import "WXUtility.h"
-#import "WXMonitor.h"
-#import "NSObject+WXSwizzle.h"
-#import "WXComponent+Events.h"
-#import "WXRecyclerDragController.h"
-
-static NSString * const kCollectionCellReuseIdentifier = @"WXRecyclerCell";
-static NSString * const kCollectionHeaderReuseIdentifier = @"WXRecyclerHeader";
-static float const kRecyclerNormalColumnGap = 32;
-
-typedef enum : NSUInteger {
- WXRecyclerLayoutTypeMultiColumn,
- WXRecyclerLayoutTypeFlex,
- WXRecyclerLayoutTypeGrid,
-} WXRecyclerLayoutType;
-
-@interface WXCollectionView : UICollectionView
-
-@end
-
-@implementation WXCollectionView
-
-- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index
-{
- [super insertSubview:view atIndex:index];
-}
-
-- (void)layoutSubviews
-{
- [super layoutSubviews];
- [self.wx_component layoutDidFinish];
-}
-
-- (void)setContentOffset:(CGPoint)contentOffset
-{
- // FIXME: side effect caused by hooking _adjustContentOffsetIfNecessary.
- // When UICollectionView is pulled down and finger releases,contentOffset will be set from -xxxx to about -0.5(greater than -0.5), then contentOffset will be reset to zero by calling _adjustContentOffsetIfNecessary.
- // So hooking _adjustContentOffsetIfNecessary will always cause remaining 1px space between list's top and navigator.
- // Demo: http://dotwe.org/895630945793a9a044e49abe39cbb77f
- // Have to reset contentOffset to zero manually here.
- if (fabs(contentOffset.y) < 0.5) {
- contentOffset.y = 0;
- }
- if (isnan(contentOffset.x)) {
- contentOffset.x = 0;
- }
- if(isnan(contentOffset.y)) {
- contentOffset.y = 0;
- }
-
- [super setContentOffset:contentOffset];
-}
-
-@end
-
-@interface WXCollectionViewCell : UICollectionViewCell
-
-@end
-
-@implementation WXCollectionViewCell
-
-- (void)prepareForReuse
-{
- [super prepareForReuse];
-
- WXCellComponent *cellComponent = (WXCellComponent *)self.wx_component;
- if (cellComponent.isRecycle && [cellComponent isViewLoaded] && [self.contentView.subviews containsObject:cellComponent.view]) {
- [cellComponent _unloadViewWithReusing:YES];
- }
-}
-
-@end
-
-@interface WXRecyclerComponent () <UICollectionViewDataSource, UICollectionViewDelegate, WXMultiColumnLayoutDelegate, WXRecyclerUpdateControllerDelegate, WXCellRenderDelegate, WXHeaderRenderDelegate, WXRecyclerDragControllerDelegate>
-
-@property (nonatomic, strong, readonly) WXRecyclerDataController *dataController;
-@property (nonatomic, strong, readonly) WXRecyclerUpdateController *updateController;
-@property (nonatomic, weak, readonly) UICollectionView *collectionView;
-@property (nonatomic, strong) WXRecyclerDragController *dragController;
-
-@end
-
-@implementation WXRecyclerComponent
-{
- WXRecyclerLayoutType _layoutType;
- UICollectionViewLayout *_collectionViewlayout;
-
- UIEdgeInsets _padding;
- NSUInteger _previousLoadMoreCellNumber;
-}
-
-- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
-{
- if (self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
- [self _fillPadding];
-
- if ([type isEqualToString:@"waterfall"] || (attributes[@"layout"] && [attributes[@"layout"] isEqualToString:@"multi-column"])) {
- // TODO: abstraction
- _layoutType = WXRecyclerLayoutTypeMultiColumn;
- CGFloat scaleFactor = weexInstance.pixelScaleFactor;
- _collectionViewlayout = [WXMultiColumnLayout new];
- WXMultiColumnLayout *layout = (WXMultiColumnLayout *)_collectionViewlayout;
- layout.columnWidth = [WXConvert WXLength:attributes[@"columnWidth"] isFloat:YES scaleFactor:scaleFactor] ? : [WXLength lengthWithFloat:0.0 type:WXLengthTypeAuto];
- layout.columnCount = [WXConvert WXLength:attributes[@"columnCount"] isFloat:NO scaleFactor:1.0] ? : [WXLength lengthWithInt:1 type:WXLengthTypeFixed];
- if (attributes[@"leftGap"]) {
- layout.leftGap = [WXConvert WXPixelType:attributes[@"leftGap"] scaleFactor:scaleFactor];
- }
- if (attributes[@"rightGap"]) {
- layout.rightGap = [WXConvert WXPixelType:attributes[@"rightGap"] scaleFactor:scaleFactor];
- }
- layout.columnGap = [self _floatValueForColumnGap:([WXConvert WXLength:attributes[@"columnGap"] isFloat:YES scaleFactor:scaleFactor] ? : [WXLength lengthWithFloat:0.0 type:WXLengthTypeNormal])];
-
- layout.delegate = self;
- } else {
- _collectionViewlayout = [UICollectionViewLayout new];
- }
-
- _dataController = [WXRecyclerDataController new];
- _updateController = [WXRecyclerUpdateController new];
- _updateController.delegate = self;
- [self fixFlicker];
-
- if ([attributes[@"draggable"] boolValue]) {
- // lazy load
- _dragController = [WXRecyclerDragController new];
- _dragController.delegate = self;
- if([attributes[@"dragTriggerType"] isEqual: @"pan"]){
- _dragController.dragTriggerType = WXRecyclerDragTriggerPan;
- }
- _dragController.isDragable = YES;
- }
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- _collectionView.delegate = nil;
- _collectionView.dataSource = nil;
-}
-
-#pragma mark - Public Subclass Methods
-
-- (UIView *)loadView
-{
- return [[WXCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_collectionViewlayout];
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
-
- _collectionView = (UICollectionView *)self.view;
- _collectionView.allowsSelection = NO;
- _collectionView.allowsMultipleSelection = NO;
- _collectionView.dataSource = self;
- _collectionView.delegate = self;
-
- [_collectionView registerClass:[WXCollectionViewCell class] forCellWithReuseIdentifier:kCollectionCellReuseIdentifier];
- [_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:kCollectionSupplementaryViewKindHeader withReuseIdentifier:kCollectionHeaderReuseIdentifier];
-
- _dragController.dragingCell = [[WXCollectionViewCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100/2.0f)];
- _dragController.collectionView = _collectionView;
-
- [self performUpdatesWithCompletion:^(BOOL finished) {
-
- }];
-}
-
-- (void)viewWillUnload
-{
- [super viewWillUnload];
-
- _collectionView.dataSource = nil;
- _collectionView.delegate = nil;
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- [super updateAttributes:attributes];
-
- if (_layoutType == WXRecyclerLayoutTypeMultiColumn) {
- CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
- WXMultiColumnLayout *layout = (WXMultiColumnLayout *)_collectionViewlayout;
- BOOL needUpdateLayout = NO;
-
- if ([attributes[@"draggable"] boolValue]) {
- if (!_dragController) { // lazy load
- _dragController = [WXRecyclerDragController new];
- _dragController.delegate = self;
- }
- if([attributes[@"dragTriggerType"] isEqual: @"pan"]){
- _dragController.dragTriggerType = WXRecyclerDragTriggerPan;
- }
- _dragController.isDragable = YES;
- } else {
- _dragController.isDragable = NO;
- }
-
- if (attributes[@"columnWidth"]) {
- layout.columnWidth = [WXConvert WXLength:attributes[@"columnWidth"] isFloat:YES scaleFactor:scaleFactor];
- needUpdateLayout = YES;
- }
-
- if (attributes[@"columnCount"]) {
- layout.columnCount = [WXConvert WXLength:attributes[@"columnCount"] isFloat:NO scaleFactor:1.0];
-
- needUpdateLayout = YES;
- }
- if (attributes[@"columnGap"]) {
- layout.columnGap = [self _floatValueForColumnGap:([WXConvert WXLength:attributes[@"columnGap"] isFloat:YES scaleFactor:scaleFactor])];
- needUpdateLayout = YES;
- }
- if (attributes[@"leftGap"]) {
- layout.leftGap = [WXConvert WXPixelType:attributes[@"leftGap"] scaleFactor:scaleFactor];
- }
- if (attributes[@"rightGap"]) {
- layout.rightGap = [WXConvert WXPixelType:attributes[@"rightGap"] scaleFactor:scaleFactor];
- }
-
- if (needUpdateLayout) {
- for (WXComponent *component in self.subcomponents) {
- [component setNeedsLayout];
- }
-
- [self.collectionView reloadData];
- [self.collectionView.collectionViewLayout invalidateLayout];
- }
- }
-
-}
-
-- (void)setContentSize:(CGSize)contentSize
-{
- // Do Nothing
-}
-
-- (void)adjustSticky
-{
- // Do Nothing, sticky is adjusted by layout
-}
-
-#pragma mark - Private Subclass Methods
-
-- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles
-{
- [super _updateStylesOnComponentThread:styles resetStyles:resetStyles isUpdateStyles:isUpdateStyles];
-
- [self _fillPadding];
-}
-
-- (void)_handleFirstScreenTime
-{
- // Do Nothing, firstScreenTime is set by cellDidRendered:
-}
-
-- (void)scrollToComponent:(WXComponent *)component withOffset:(CGFloat)offset animated:(BOOL)animated
-{
- CGPoint contentOffset = _collectionView.contentOffset;
- CGFloat contentOffsetY = 0;
-
- CGRect rect;
- while (component) {
- if ([component isKindOfClass:[WXCellComponent class]]) {
- NSIndexPath *toIndexPath = [self.dataController indexPathForCell:(WXCellComponent *)component];
- UICollectionViewLayoutAttributes *attributes = [_collectionView layoutAttributesForItemAtIndexPath:toIndexPath];
- rect = attributes.frame;
- break;
- }
- if ([component isKindOfClass:[WXHeaderComponent class]]) {
- NSUInteger toIndex = [self.dataController indexForHeader:(WXHeaderComponent *)component];
- UICollectionViewLayoutAttributes *attributes = [_collectionView layoutAttributesForSupplementaryElementOfKind:kCollectionSupplementaryViewKindHeader atIndexPath:[NSIndexPath indexPathWithIndex:toIndex]];
- rect = attributes.frame;
- break;
- }
- contentOffsetY += component.calculatedFrame.origin.y;
- component = component.supercomponent;
- }
-
- contentOffsetY += rect.origin.y;
- contentOffsetY += offset * self.weexInstance.pixelScaleFactor;
-
- if (_collectionView.contentSize.height >= _collectionView.frame.size.height && contentOffsetY > _collectionView.contentSize.height - _collectionView.frame.size.height) {
- contentOffset.y = _collectionView.contentSize.height - _collectionView.frame.size.height;
- } else {
- contentOffset.y = contentOffsetY;
- }
-
- [_collectionView setContentOffset:contentOffset animated:animated];
-
-}
-
-- (void)performUpdatesWithCompletion:(void (^)(BOOL finished))completion
-{
- WXAssertMainThread();
-
- //TODO: support completion
-
- if (![self isViewLoaded]) {
- completion(NO);
- }
-
- NSArray *oldData = [self.dataController.sections copy];
- NSArray *newData = [self _sectionArrayFromComponents:self.subcomponents];
-
- [_updateController performUpdatesWithNewData:newData oldData:oldData view:_collectionView];
-}
-
-- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- if ([subcomponent isKindOfClass:[WXCellComponent class]]) {
- ((WXCellComponent *)subcomponent).delegate = self;
- } else if ([subcomponent isKindOfClass:[WXHeaderComponent class]]) {
- ((WXHeaderComponent *)subcomponent).delegate = self;
- }
-
- [super _insertSubcomponent:subcomponent atIndex:index];
-
- if (![subcomponent isKindOfClass:[WXHeaderComponent class]]
- && ![subcomponent isKindOfClass:[WXCellComponent class]]) {
- return;
- }
-
- WXPerformBlockOnMainThread(^{
- [self performUpdatesWithCompletion:^(BOOL finished) {
-
- }];
- });
-}
-
-- (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- //Here will not insert cell/header/footer's view again
- if (![subcomponent isKindOfClass:[WXCellComponent class]]
- && ![subcomponent isKindOfClass:[WXHeaderComponent class]]
- && ![subcomponent isKindOfClass:[WXFooterComponent class]]) {
- [super insertSubview:subcomponent atIndex:index];
- }
-}
-
-#pragma mark - WXRecyclerUpdateControllerDelegate
-
-- (void)updateController:(WXRecyclerUpdateController *)controller willPerformUpdateWithNewData:(NSArray<WXSectionDataController *> *)newData
-{
- if (newData) {
- [self.dataController updateData:newData];
- }
-}
-
-- (void)updateController:(WXRecyclerUpdateController *)controller didPerformUpdateWithFinished:(BOOL)finished
-{
-
-}
-
-#pragma mark - UICollectionViewDataSource
-
-- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
-{
- WXLogDebug(@"section number:%li", (long)[self.dataController numberOfSections]);
- return [self.dataController numberOfSections];
-}
-
-- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
-{
- NSInteger numberOfItems = [self.dataController numberOfItemsInSection:section];
-
- WXLogDebug(@"Number of items is %ld in section:%ld", (long)numberOfItems, (long)section);
-
- return numberOfItems;
-}
-
-- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
-{
- WXLogDebug(@"Getting cell at indexPath:%@", indexPath);
-
- WXCollectionViewCell *cellView = [_collectionView dequeueReusableCellWithReuseIdentifier:kCollectionCellReuseIdentifier forIndexPath:indexPath];
-
- UIView *contentView = [self.dataController cellForItemAtIndexPath:indexPath];
-
- cellView.wx_component = contentView.wx_component;
-
- [self.dragController goThroughAnchor:cellView.wx_component indexPath:indexPath];
-
- if (contentView.superview == cellView.contentView) {
- return cellView;
- }
-
- for (UIView *view in cellView.contentView.subviews) {
- [view removeFromSuperview];
- }
-
- [cellView.contentView addSubview:contentView];
- [cellView setAccessibilityIdentifier:contentView.accessibilityIdentifier];
-
- return cellView;
-}
-
-- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
-{
- UICollectionReusableView *reusableView = nil;
- if ([kind isEqualToString:kCollectionSupplementaryViewKindHeader]) {
- reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kCollectionHeaderReuseIdentifier forIndexPath:indexPath];
- UIView *contentView = [self.dataController viewForHeaderAtIndexPath:indexPath];
- if (contentView.superview != reusableView) {
- for (UIView *view in reusableView.subviews) {
- [view removeFromSuperview];
- }
-
- [reusableView addSubview:contentView];
- }
- }
-
- return reusableView;
-}
-
-#pragma mark - UICollectionViewDelegate
-
-- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
-{
- WXLogDebug(@"will display cell:%@, at index path:%@", cell, indexPath);
-}
-
-- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
-{
- WXLogDebug(@"Did end displaying cell:%@, at index path:%@", cell, indexPath);
-}
-
-#pragma mark - WXMultiColumnLayoutDelegate
-
-- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView insetForLayout:(UICollectionViewLayout *)collectionViewLayout
-{
- return _padding;
-}
-
-- (CGFloat)collectionView:(UICollectionView *)collectionView contentWidthForLayout:(UICollectionViewLayout *)collectionViewLayout
-{
- return self.scrollerCSSNode->style.dimensions[CSS_WIDTH];
-}
-
-- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForItemAtIndexPath:(NSIndexPath *)indexPath
-{
- CGSize itemSize = [self.dataController sizeForItemAtIndexPath:indexPath];
- return itemSize.height;
-}
-
-- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForHeaderInSection:(NSInteger)section
-{
- CGSize headerSize = [self.dataController sizeForHeaderAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
- return headerSize.height;
-}
-
-- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout hasHeaderInSection:(NSInteger)section
-{
- return [self.dataController hasHeaderInSection:section];
-}
-
-- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout isNeedStickyForHeaderInSection:(NSInteger)section
-{
- return [self.dataController isStickyForHeaderAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
-}
-
-#pragma mark - WXHeaderRenderDelegate
-
-- (float)headerWidthForLayout:(WXHeaderComponent *)header
-{
- if (_layoutType == WXRecyclerLayoutTypeMultiColumn) {
- return ((WXMultiColumnLayout *)_collectionViewlayout).computedHeaderWidth;
- }
-
- return 0.0;
-}
-
-- (void)headerDidLayout:(WXHeaderComponent *)header
-{
- WXPerformBlockOnMainThread(^{
- [self.collectionView.collectionViewLayout invalidateLayout];
- });
-}
-
-- (void)headerDidRemove:(WXHeaderComponent *)header
-{
- WXPerformBlockOnMainThread(^{
- [self performUpdatesWithCompletion:^(BOOL finished) {
-
- }];
- });
-}
-
-#pragma mark - WXCellRenderDelegate
-
-- (float)containerWidthForLayout:(WXCellComponent *)cell
-{
- if (_layoutType == WXRecyclerLayoutTypeMultiColumn) {
- return ((WXMultiColumnLayout *)_collectionViewlayout).computedColumnWidth;
- }
-
- return 0.0;
-}
-
-- (void)cellDidLayout:(WXCellComponent *)cell
-{
- BOOL previousLayoutComplete = cell.isLayoutComplete;
- cell.isLayoutComplete = YES;
- WXPerformBlockOnMainThread(^{
- if (previousLayoutComplete) {
- [self.updateController reloadItemsAtIndexPath:[self.dataController indexPathForCell:cell]];
- } else {
- [self performUpdatesWithCompletion:^(BOOL finished) {
- }];
- }
- });
-}
-
-- (void)cellDidRendered:(WXCellComponent *)cell
-{
- if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance) && !self.weexInstance.onRenderProgress) {
- return;
- }
-
- NSIndexPath *indexPath = [self.dataController indexPathForCell:cell];
-
- UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];
- CGRect cellRect = attributes.frame;
- if (cellRect.origin.y + cellRect.size.height >= _collectionView.frame.size.height) {
- WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance);
- }
-
- if (self.weexInstance.onRenderProgress) {
- CGRect renderRect = [_collectionView convertRect:cellRect toView:self.weexInstance.rootView];
- self.weexInstance.onRenderProgress(renderRect);
- }
-}
-
-- (void)cellDidRemove:(WXCellComponent *)cell
-{
- if (cell.isLayoutComplete) {
- WXPerformBlockOnMainThread(^{
- [self performUpdatesWithCompletion:^(BOOL finished) {
- }];
- });
- }
-}
-
-- (void)cell:(WXCellComponent *)cell didMoveToIndex:(NSUInteger)index
-{
- if (cell.isLayoutComplete) {
- WXPerformBlockOnMainThread(^{
- [self performUpdatesWithCompletion:^(BOOL finished) {
- }];
- });
- }
-}
-
-#pragma mark - Load More Event
-
-- (void)setLoadmoreretry:(NSUInteger)loadmoreretry
-{
- if (loadmoreretry != self.loadmoreretry) {
- _previousLoadMoreCellNumber = 0;
- }
-
- [super setLoadmoreretry:loadmoreretry];
-}
-
-- (void)loadMore
-{
- [super loadMore];
-
- _previousLoadMoreCellNumber = [self totalNumberOfCells];
-}
-
-- (BOOL)isNeedLoadMore
-{
- BOOL superNeedLoadMore = [super isNeedLoadMore];
- return superNeedLoadMore && _previousLoadMoreCellNumber != [self totalNumberOfCells];
-}
-
-- (NSUInteger)totalNumberOfCells
-{
- NSUInteger cellNumber = 0;
- NSUInteger sectionCount = [_collectionView numberOfSections];
- for (int section = 0; section < sectionCount; section ++) {
- cellNumber += [_collectionView numberOfItemsInSection:section];
- }
-
- return cellNumber;
-}
-
-- (void)resetLoadmore{
- [super resetLoadmore];
- _previousLoadMoreCellNumber = 0;
-}
-
-#pragma mark - Private
-
-- (float)_floatValueForColumnGap:(WXLength *)gap
-{
- if (gap.isNormal) {
- return kRecyclerNormalColumnGap * self.weexInstance.pixelScaleFactor;
- } else {
- return gap.floatValue;
- }
-}
-
-- (void)_fillPadding
-{
- UIEdgeInsets padding = {
- WXFloorPixelValue(self.cssNode->style.padding[CSS_TOP] + self.cssNode->style.border[CSS_TOP]),
- WXFloorPixelValue(self.cssNode->style.padding[CSS_LEFT] + self.cssNode->style.border[CSS_LEFT]),
- WXFloorPixelValue(self.cssNode->style.padding[CSS_BOTTOM] + self.cssNode->style.border[CSS_BOTTOM]),
- WXFloorPixelValue(self.cssNode->style.padding[CSS_RIGHT] + self.cssNode->style.border[CSS_RIGHT])
- };
-
- if (!UIEdgeInsetsEqualToEdgeInsets(padding, _padding)) {
- _padding = padding;
- [self setNeedsLayout];
-
- for (WXComponent *component in self.subcomponents) {
- [component setNeedsLayout];
- }
-
- if (_collectionView) {
- WXPerformBlockOnMainThread(^{
- [_collectionView.collectionViewLayout invalidateLayout];
- });
- }
- }
-}
-
-- (NSArray<WXSectionDataController *> *)_sectionArrayFromComponents:(NSArray<WXComponent *> *)components
-{
- NSMutableArray<WXSectionDataController *> *sectionArray = [NSMutableArray array];
- NSMutableArray<WXCellComponent *> *cellArray = [NSMutableArray array];
- WXSectionDataController *currentSection;
-
- for (int i = 0; i < components.count; i++) {
- if (!currentSection) {
- currentSection = [WXSectionDataController new];
- }
-
- WXComponent* component = components[i];
-
- if ([component isKindOfClass:[WXHeaderComponent class]]) {
- if (i != 0 && (currentSection.headerComponent || cellArray.count > 0)) {
- currentSection.cellComponents = [cellArray copy];
- [sectionArray addObject:currentSection];
- currentSection = [WXSectionDataController new];
- [cellArray removeAllObjects];
- }
- currentSection.headerComponent = (WXHeaderComponent *)component;
- } else if ([component isKindOfClass:[WXCellComponent class]]
- && ((WXCellComponent *)component).isLayoutComplete) {
- [cellArray addObject:(WXCellComponent *)component];
- } else if ([component isKindOfClass:[WXFooterComponent class]]) {
- currentSection.footerComponent = component;
- } else {
- continue;
- }
- }
-
- if (cellArray.count > 0 || currentSection.headerComponent) {
- currentSection.cellComponents = [cellArray copy];
- [sectionArray addObject:currentSection];
- }
-
- return sectionArray;
-}
-
-- (void)fixFlicker
-{
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- // FIXME:(ง •̀_•́)ง┻━┻ Stupid scoll view, always reset content offset to zero by calling _adjustContentOffsetIfNecessary after insert cells.
- // So if you pull down list while list is rendering, the list will be flickering.
- // Demo:
- // Have to hook _adjustContentOffsetIfNecessary here.
- // Any other more elegant way?
- NSString *a = @"ntOffsetIfNe";
- NSString *b = @"adjustConte";
-
- NSString *originSelector = [NSString stringWithFormat:@"_%@%@cessary", b, a];
- [[self class] weex_swizzle:[WXCollectionView class] Method:NSSelectorFromString(originSelector) withMethod:@selector(fixedFlickerSelector)];
- });
-}
-
-#define mark dragControllerDelegate
-
-- (void)updateDataSource{
- NSMutableArray *oldComponents = [[NSMutableArray alloc] initWithArray:self.dataController.sections[self.dragController.startIndexPath.section].cellComponents];
- if(oldComponents.count > 1){
- WXCellComponent *startComponent = self.dataController.sections[self.dragController.startIndexPath.section].cellComponents[self.dragController.startIndexPath.item];
- [oldComponents removeObject:startComponent];
- [oldComponents insertObject:startComponent atIndex:self.dragController.targetIndexPath.item];
- self.dataController.sections[self.dragController.startIndexPath.section].cellComponents = oldComponents;
- }
-}
-
-- (void)dragFireEvent:(NSString *)eventName params:(NSDictionary *)params{
- [self fireEvent:eventName params:params];
-}
-
-- (void)fixedFlickerSelector
-{
- // DO NOT delete this method.
-}
-
-@end
[03/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm
new file mode 100644
index 0000000..c8c6e3f
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm
@@ -0,0 +1,1158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXComponentManager.h"
+#import "WXComponent.h"
+#import "WXComponent_internal.h"
+#import "WXComponent+DataBinding.h"
+#import "WXComponentFactory.h"
+#import "WXDefine.h"
+#import "NSArray+Weex.h"
+#import "WXSDKInstance.h"
+#import "WXAssert.h"
+#import "WXUtility.h"
+#import "WXMonitor.h"
+#import "WXScrollerProtocol.h"
+#import "WXSDKManager.h"
+#import "WXSDKError.h"
+#import "WXInvocationConfig.h"
+#import "WXHandlerFactory.h"
+#import "WXValidateProtocol.h"
+#import "WXPrerenderManager.h"
+#import "WXTracingManager.h"
+#import "WXLayoutDefine.h"
+#import "WXSDKInstance_performance.h"
+#import "WXRootView.h"
+#import "WXComponent+Layout.h"
+
+
+
+static NSThread *WXComponentThread;
+
+#define WXAssertComponentExist(component) WXAssert(component, @"component not exists")
+
+
+@implementation WXComponentManager
+{
+ __weak WXSDKInstance *_weexInstance;
+ BOOL _isValid;
+
+ BOOL _stopRunning;
+ NSUInteger _noTaskTickCount;
+
+ // access only on component thread
+ NSMapTable<NSString *, WXComponent *> *_indexDict;
+ NSMutableArray<dispatch_block_t> *_uiTaskQueue;
+ NSMutableDictionary *_uiPrerenderTaskQueue;
+
+ WXComponent *_rootComponent;
+ NSMutableArray *_fixedComponents;
+//#ifndef USE_FLEX
+ css_node_t *_rootCSSNode;
+//#else
+ WeexCore::WXCoreLayoutNode* _rootFlexCSSNode;
+//#endif
+ CADisplayLink *_displayLink;
+}
+
++ (instancetype)sharedManager
+{
+ static id _sharedInstance = nil;
+ static dispatch_once_t oncePredicate;
+ dispatch_once(&oncePredicate, ^{
+ _sharedInstance = [[self alloc] init];
+ });
+ return _sharedInstance;
+}
+
+- (instancetype)initWithWeexInstance:(id)weexInstance
+{
+ if (self = [self init]) {
+ _weexInstance = weexInstance;
+
+ _indexDict = [NSMapTable strongToWeakObjectsMapTable];
+ _fixedComponents = [NSMutableArray wx_mutableArrayUsingWeakReferences];
+ _uiTaskQueue = [NSMutableArray array];
+ _isValid = YES;
+ [self _startDisplayLink];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ free_css_node(_rootCSSNode);
+ }
+
+//#else
+
+ if(_rootFlexCSSNode){
+ delete _rootFlexCSSNode;
+
+ // WeexCore::WXCoreLayoutNode::freeNodeTree(_rootFlexCSSNode);
+ _rootFlexCSSNode=nullptr;
+ }
+//#endif
+ [NSMutableArray wx_releaseArray:_fixedComponents];
+}
+
+#pragma mark Thread Management
+
++ (NSThread *)componentThread
+{
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ WXComponentThread = [[NSThread alloc] initWithTarget:[self sharedManager] selector:@selector(_runLoopThread) object:nil];
+ [WXComponentThread setName:WX_COMPONENT_THREAD_NAME];
+ if(WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
+ [WXComponentThread setQualityOfService:[[NSThread mainThread] qualityOfService]];
+ } else {
+ [WXComponentThread setThreadPriority:[[NSThread mainThread] threadPriority]];
+ }
+
+ [WXComponentThread start];
+ });
+
+ return WXComponentThread;
+}
+
+- (void)_runLoopThread
+{
+ [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
+
+ while (!_stopRunning) {
+ @autoreleasepool {
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
+ }
+ }
+}
+
++ (void)_performBlockOnComponentThread:(void (^)(void))block
+{
+ if([NSThread currentThread] == [self componentThread]){
+ block();
+ } else {
+ [self performSelector:@selector(_performBlockOnComponentThread:)
+ onThread:WXComponentThread
+ withObject:[block copy]
+ waitUntilDone:NO];
+ }
+}
+
++ (void)_performBlockSyncOnComponentThread:(void (^)(void))block
+{
+ if([NSThread currentThread] == [self componentThread]){
+ block();
+ } else {
+ [self performSelector:@selector(_performBlockOnComponentThread:)
+ onThread:WXComponentThread
+ withObject:[block copy]
+ waitUntilDone:YES];
+ }
+}
+
+- (void)startComponentTasks
+{
+ [self _awakeDisplayLink];
+}
+
+- (void)rootViewFrameDidChange:(CGRect)frame
+{
+ WXAssertComponentThread();
+//#ifndef USE_FLEX
+
+ if (![WXComponent isUseFlex]) {
+ if (_rootCSSNode) {
+ [self _applyRootFrame:frame toRootCSSNode:_rootCSSNode];
+ if (!_rootComponent.styles[@"width"]) {
+ _rootComponent.cssNode->style.dimensions[CSS_WIDTH] = frame.size.width ?: CSS_UNDEFINED;
+ }
+ if (!_rootComponent.styles[@"height"]) {
+ _rootComponent.cssNode->style.dimensions[CSS_HEIGHT] = frame.size.height ?: CSS_UNDEFINED;
+ }
+ }
+ }
+//#else
+ else
+ {
+ if (_rootFlexCSSNode) {
+ [self _applyRootFrame:frame];
+ if (!_rootComponent.styles[@"width"]) {
+ _rootComponent.flexCssNode->setStyleWidth(frame.size.width ?:FlexUndefined,NO);
+ }
+ if (!_rootComponent.styles[@"height"]) {
+ _rootComponent.flexCssNode->setStyleHeight(frame.size.height ?:FlexUndefined);
+ }
+ }
+ }
+//#endif
+ [_rootComponent setNeedsLayout];
+ [self startComponentTasks];
+}
+
+//#ifndef USE_FLEX
+- (void)_applyRootFrame:(CGRect)rootFrame toRootCSSNode:(css_node_t *)rootCSSNode
+{
+ _rootCSSNode->style.position[CSS_LEFT] = self.weexInstance.frame.origin.x;
+ _rootCSSNode->style.position[CSS_TOP] = self.weexInstance.frame.origin.y;
+ // if no instance width/height, use layout width/height, as Android's wrap_content
+ _rootCSSNode->style.dimensions[CSS_WIDTH] = self.weexInstance.frame.size.width ?: CSS_UNDEFINED;
+ _rootCSSNode->style.dimensions[CSS_HEIGHT] = self.weexInstance.frame.size.height ?: CSS_UNDEFINED;
+}
+//#else
+- (void)_applyRootFrame:(CGRect)rootFrame{
+ _rootFlexCSSNode->setStylePosition(WeexCore::kPositionEdgeLeft, self.weexInstance.frame.origin.x);
+ _rootFlexCSSNode->setStylePosition(WeexCore::kPositionEdgeTop, self.weexInstance.frame.origin.y);
+ _rootFlexCSSNode->setStyleWidth(self.weexInstance.frame.size.width ?: FlexUndefined,NO);
+ _rootFlexCSSNode->setStyleHeight(self.weexInstance.frame.size.height ?: FlexUndefined);
+}
+//#endif
+
+- (void)_addUITask:(void (^)(void))block
+{
+ if(!_uiPrerenderTaskQueue){
+ _uiPrerenderTaskQueue = [NSMutableDictionary new];
+ }
+ if(self.weexInstance.needPrerender){
+ NSMutableArray<dispatch_block_t> *tasks = [_uiPrerenderTaskQueue objectForKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]];
+ if(!tasks){
+ tasks = [NSMutableArray new];
+ }
+ [tasks addObject:block];
+ [_uiPrerenderTaskQueue setObject:tasks forKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]];
+ }else{
+ [_uiTaskQueue addObject:block];
+ }
+}
+
+- (void)excutePrerenderUITask:(NSString *)url
+{
+ NSMutableArray *tasks = [_uiPrerenderTaskQueue objectForKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]];
+ for (id block in tasks) {
+ [_uiTaskQueue addObject:block];
+ }
+ tasks = [NSMutableArray new];
+ [_uiPrerenderTaskQueue setObject:tasks forKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]];
+}
+
+#pragma mark Component Tree Building
+
+- (void)createRoot:(NSDictionary *)data
+{
+ WXAssertComponentThread();
+ WXAssertParam(data);
+
+ _rootComponent = [self _buildComponentForData:data supercomponent:nil];
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ [self _initRootCSSNode];
+ }
+//#else
+ else
+ {
+ [self _initRootFlexCssNode];
+ _rootFlexCSSNode->addChildAt(_rootComponent.flexCssNode, (uint32_t)[_fixedComponents count]);
+ }
+//#endif
+
+ NSArray *subcomponentsData = [data valueForKey:@"children"];
+ if (subcomponentsData) {
+ BOOL appendTree = [_rootComponent.attributes[@"append"] isEqualToString:@"tree"];
+ for(NSDictionary *subcomponentData in subcomponentsData){
+ [self _recursivelyAddComponent:subcomponentData toSupercomponent:_rootComponent atIndex:-1 appendingInTree:appendTree];
+ }
+ }
+
+ __weak typeof(self) weakSelf = self;
+ WX_MONITOR_INSTANCE_PERF_END(WXFirstScreenJSFExecuteTime, self.weexInstance);
+ [self _addUITask:^{
+ [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:data[@"ref"] className:nil name:data[@"type"] phase:WXTracingBegin functionName:@"createBody" options:@{@"threadName":WXTUIThread}];
+ __strong typeof(self) strongSelf = weakSelf;
+ strongSelf.weexInstance.rootView.wx_component = strongSelf->_rootComponent;
+ [strongSelf.weexInstance.rootView addSubview:strongSelf->_rootComponent.view];
+ [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:data[@"ref"] className:nil name:data[@"type"] phase:WXTracingEnd functionName:@"createBody" options:@{@"threadName":WXTUIThread}];
+ }];
+
+
+}
+
+//#ifndef USE_FLEX
+static bool rootNodeIsDirty(void *context)
+{
+ WXComponentManager *manager = (__bridge WXComponentManager *)(context);
+ return [manager->_rootComponent needsLayout];
+}
+
+static css_node_t * rootNodeGetChild(void *context, int i)
+{
+ WXComponentManager *manager = (__bridge WXComponentManager *)(context);
+ if (i == 0) {
+ return manager->_rootComponent.cssNode;
+ } else if(manager->_fixedComponents.count >= i) {
+ return ((WXComponent *)((manager->_fixedComponents)[i-1])).cssNode;
+ }
+
+ return NULL;
+}
+//#endif
+
+- (void)addComponent:(NSDictionary *)componentData toSupercomponent:(NSString *)superRef atIndex:(NSInteger)index appendingInTree:(BOOL)appendingInTree
+{
+ WXAssertComponentThread();
+ WXAssertParam(componentData);
+ WXAssertParam(superRef);
+
+ WXComponent *supercomponent = [_indexDict objectForKey:superRef];
+ WXAssertComponentExist(supercomponent);
+
+ if ([WXComponent isUseFlex] && !supercomponent) {
+ WXLogWarning(@"addComponent,superRef from js never exit ! check JS action, supRef:%@",superRef);
+ return;
+ }
+
+ [self _recursivelyAddComponent:componentData toSupercomponent:supercomponent atIndex:index appendingInTree:appendingInTree];
+}
+
+- (void)_recursivelyAddComponent:(NSDictionary *)componentData toSupercomponent:(WXComponent *)supercomponent atIndex:(NSInteger)index appendingInTree:(BOOL)appendingInTree
+{
+ WXComponent *component = [self _buildComponentForData:componentData supercomponent:supercomponent];
+ if (!supercomponent.subcomponents) {
+ index = 0;
+ } else {
+ index = (index == -1 ? supercomponent->_subcomponents.count : index);
+ }
+
+#ifdef DEBUG
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ WXLogDebug(@"flexLayout -> _recursivelyAddComponent : super:(%@,%@):[%f,%f] ,child:(%@,%@):[%f,%f],childClass:%@",
+ supercomponent.type,
+ supercomponent.ref,
+ supercomponent.cssNode->style.dimensions[CSS_WIDTH],
+ supercomponent.cssNode->style.dimensions[CSS_HEIGHT],
+ component.type,
+ component.ref,
+ component.cssNode->style.dimensions[CSS_WIDTH],
+ component.cssNode->style.dimensions[CSS_HEIGHT]
+ ,NSStringFromClass([component class])
+ );
+ }
+//#else
+ else
+ {
+ WXLogDebug(@"flexLayout -> _recursivelyAddComponent : super:(%@,%@):[%f,%f] ,child:(%@,%@):[%f,%f],childClass:%@",
+ supercomponent.type,
+ supercomponent.ref,
+ supercomponent.flexCssNode->getStyleWidth(),
+ supercomponent.flexCssNode->getStyleHeight(),
+ component.type,
+ component.ref,
+ component.flexCssNode->getStyleWidth(),
+ component.flexCssNode->getStyleHeight()
+ ,NSStringFromClass([component class])
+ );
+ }
+//#endif
+#endif //DEBUG
+
+
+ [supercomponent _insertSubcomponent:component atIndex:index];
+ // use _lazyCreateView to forbid component like cell's view creating
+ if(supercomponent && component && supercomponent->_lazyCreateView) {
+ component->_lazyCreateView = YES;
+ }
+
+ [self recordMaximumVirtualDom:component];
+
+ if (!component->_isTemplate) {
+ __weak typeof(self) weakSelf = self;
+ BOOL isFSCreateFinish = [self weexInstance].isJSCreateFinish;
+ [self _addUITask:^{
+ [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTUIThread}];
+ [supercomponent insertSubview:component atIndex:index];
+ [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingEnd functionName:@"addElement" options:@{@"threadName":WXTUIThread}];
+ [weakSelf onElementChange:isFSCreateFinish];
+ }];
+ }
+
+ NSArray *subcomponentsData = [componentData valueForKey:@"children"];
+
+ BOOL appendTree = !appendingInTree && [component.attributes[@"append"] isEqualToString:@"tree"];
+ // if ancestor is appending tree, child should not be laid out again even it is appending tree.
+ for(NSDictionary *subcomponentData in subcomponentsData){
+ [self _recursivelyAddComponent:subcomponentData toSupercomponent:component atIndex:-1 appendingInTree:appendTree || appendingInTree];
+ }
+
+ [component _didInserted];
+
+ if (appendTree) {
+ // If appending tree,force layout in case of too much tasks piling up in syncQueue
+ [self _layoutAndSyncUI];
+ }
+}
+
+- (void)moveComponent:(NSString *)ref toSuper:(NSString *)superRef atIndex:(NSInteger)index
+{
+ WXAssertComponentThread();
+ WXAssertParam(ref);
+ WXAssertParam(superRef);
+
+ WXComponent *component = [_indexDict objectForKey:ref];
+ WXComponent *newSupercomponent = [_indexDict objectForKey:superRef];
+ WXAssertComponentExist(component);
+ WXAssertComponentExist(newSupercomponent);
+
+ if (component.supercomponent == newSupercomponent && [newSupercomponent.subcomponents indexOfObject:component] < index) {
+ // if the supercomponent moved to is the same as original supercomponent,
+ // unify it into the index after removing.
+ index--;
+ }
+
+ [component _moveToSupercomponent:newSupercomponent atIndex:index];
+ __weak typeof(self) weakSelf = self;
+ [self _addUITask:^{
+ [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTUIThread}];
+ [component moveToSuperview:newSupercomponent atIndex:index];
+ [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingEnd functionName:@"addElement" options:@{@"threadName":WXTUIThread}];
+ }];
+}
+
+- (void)removeComponent:(NSString *)ref
+{
+ WXAssertComponentThread();
+ WXAssertParam(ref);
+
+ WXComponent *component = [_indexDict objectForKey:ref];
+ WXAssertComponentExist(component);
+
+ if ([WXComponent isUseFlex] && !component) {
+ WXLogWarning(@"removeComponent ref from js never exit ! check JS action, ref :%@",ref);
+ return;
+ }
+
+ [component _removeFromSupercomponent];
+
+ [_indexDict removeObjectForKey:ref];
+
+ __weak typeof(self) weakSelf = self;
+ BOOL isFSCreateFinish = [self weexInstance].isJSCreateFinish;
+ [self _addUITask:^{
+ [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingBegin functionName:@"removeElement" options:@{@"threadName":WXTUIThread}];
+ if (component.supercomponent) {
+ [component.supercomponent willRemoveSubview:component];
+ }
+ [component removeFromSuperview];
+ [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingEnd functionName:@"removeElement" options:@{@"threadName":WXTUIThread}];
+ [weakSelf onElementChange:isFSCreateFinish];
+ }];
+
+ [self _checkFixedSubcomponentToRemove:component];
+
+}
+
+- (void)onElementChange:(BOOL)isFSCreateFinish
+{
+ if (!isFSCreateFinish) {
+ return;
+ }
+
+ UIView *root = [self weexInstance].rootView;
+ BOOL hasEvent = TRUE;
+ if (root && [root isKindOfClass:[WXRootView class]]) {
+ WXRootView* wxRootView = (WXRootView *)root;
+ hasEvent = [wxRootView isHasEvent];
+ }
+ if (hasEvent) {
+ return;
+ }
+ double current = CACurrentMediaTime()*1000;
+
+ double diff = current - [self weexInstance].performance.jsCreateFinishTime;
+ if (diff > 8000) {
+ return;
+ }
+ [self weexInstance].performance.interactionTime = current - self.weexInstance.performance.renderTimeOrigin;
+}
+
+- (void)recordMaximumVirtualDom:(WXComponent*) component
+{
+ WXAssertComponentExist(component);
+ if(!component){
+ return;
+ }
+ int maxDeep =0;
+ while (component) {
+ maxDeep++;
+ component = component.supercomponent;
+ }
+ if(maxDeep > [self weexInstance].performance.maxVdomDeep)
+ {
+ [self weexInstance].performance.maxVdomDeep = maxDeep;
+ }
+
+}
+
+- (void)_checkFixedSubcomponentToRemove:(WXComponent *)component
+{
+ for (WXComponent *subcomponent in component.subcomponents) {
+ if (subcomponent->_positionType == WXPositionTypeFixed) {
+ [self _addUITask:^{
+ [subcomponent removeFromSuperview];
+ }];
+ }
+
+ [self _checkFixedSubcomponentToRemove:subcomponent];
+ }
+}
+
+- (WXComponent *)componentForRef:(NSString *)ref
+{
+ WXAssertComponentThread();
+
+ return [_indexDict objectForKey:ref];
+}
+
+- (WXComponent *)componentForRoot
+{
+ return _rootComponent;
+}
+
+- (NSUInteger)numberOfComponents
+{
+ WXAssertComponentThread();
+
+ return _indexDict.count;
+}
+
+- (WXComponent *)_buildComponentForData:(NSDictionary *)data supercomponent:(WXComponent *)supercomponent
+{
+ NSString *ref = data[@"ref"];
+ NSString *type = data[@"type"];
+ NSDictionary *styles = data[@"style"];
+ NSDictionary *attributes = data[@"attr"];
+ NSArray *events = data[@"event"];
+
+ if (self.weexInstance.needValidate) {
+ id<WXValidateProtocol> validateHandler = [WXHandlerFactory handlerForProtocol:@protocol(WXValidateProtocol)];
+ if (validateHandler) {
+ WXComponentValidateResult* validateResult;
+ if ([validateHandler respondsToSelector:@selector(validateWithWXSDKInstance:component:supercomponent:)]) {
+ validateResult = [validateHandler validateWithWXSDKInstance:self.weexInstance component:type supercomponent:supercomponent];
+ }
+ if (validateResult==nil || !validateResult.isSuccess) {
+ type = validateResult.replacedComponent? validateResult.replacedComponent : @"div";
+ WXLogError(@"%@",[validateResult.error.userInfo objectForKey:@"errorMsg"]);
+ }
+ }
+ }
+
+ WXComponentConfig *config = [WXComponentFactory configWithComponentName:type];
+ BOOL isTemplate = [config.properties[@"isTemplate"] boolValue] || (supercomponent && supercomponent->_isTemplate);
+ NSDictionary *bindingStyles;
+ NSDictionary *bindingAttibutes;
+ NSDictionary *bindingEvents;
+ NSDictionary *bindingProps;
+ if (isTemplate) {
+ bindingProps = [self _extractBindingProps:&attributes];
+ bindingStyles = [self _extractBindings:&styles];
+ bindingAttibutes = [self _extractBindings:&attributes];
+ bindingEvents = [self _extractBindingEvents:&events];
+ }
+
+ Class clazz = NSClassFromString(config.clazz);;
+ WXComponent *component = [[clazz alloc] initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:self.weexInstance];
+ if (isTemplate) {
+ component->_isTemplate = YES;
+ [component _storeBindingsWithProps:bindingProps styles:bindingStyles attributes:bindingAttibutes events:bindingEvents];
+ }
+
+ WXAssert(component, @"Component build failed for data:%@", data);
+
+ [_indexDict setObject:component forKey:component.ref];
+ [component readyToRender];// notify redyToRender event when init
+ return component;
+}
+
+- (void)addComponent:(WXComponent *)component toIndexDictForRef:(NSString *)ref
+{
+ [_indexDict setObject:component forKey:ref];
+}
+
+- (NSDictionary *)_extractBindings:(NSDictionary **)attributesOrStylesPoint
+{
+ NSDictionary *attributesOrStyles = *attributesOrStylesPoint;
+ if (!attributesOrStyles) {
+ return nil;
+ }
+
+ NSMutableDictionary *newAttributesOrStyles = [attributesOrStyles mutableCopy];
+ NSMutableDictionary *bindingAttributesOrStyles = [NSMutableDictionary dictionary];
+
+ [attributesOrStyles enumerateKeysAndObjectsUsingBlock:^(id _Nonnull attributeOrStyleName, id _Nonnull attributeOrStyle, BOOL * _Nonnull stop) {
+ if ([WXBindingMatchIdentify isEqualToString:attributeOrStyleName] // match
+ || [WXBindingRepeatIdentify isEqualToString:attributeOrStyleName] // repeat
+ || [WXBindingOnceIdentify isEqualToString:attributeOrStyleName] // once
+ ||([attributeOrStyle isKindOfClass:[NSDictionary class]] && attributeOrStyle[WXBindingIdentify])) { // {"attributeOrStyleName": {"@binding":"bindingExpression"}
+ bindingAttributesOrStyles[attributeOrStyleName] = attributeOrStyle;
+ [newAttributesOrStyles removeObjectForKey:attributeOrStyleName];
+ } else if ([attributeOrStyle isKindOfClass:[NSArray class]]) {
+ // {"attributeOrStyleName":[..., "string", {"@binding":"bindingExpression"}, "string", {"@binding":"bindingExpression"}, ...]
+ __block BOOL isBinding = NO;
+ [attributeOrStyle enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+ if ([obj isKindOfClass:[NSDictionary class]] && obj[WXBindingIdentify]) {
+ isBinding = YES;
+ *stop = YES;
+ }
+ }];
+
+ if (isBinding) {
+ bindingAttributesOrStyles[attributeOrStyleName] = attributeOrStyle;
+ [newAttributesOrStyles removeObjectForKey:attributeOrStyleName];
+ }
+ }
+ }];
+
+ *attributesOrStylesPoint = newAttributesOrStyles;
+
+ return bindingAttributesOrStyles;
+}
+
+- (NSDictionary *)_extractBindingEvents:(NSArray **)eventsPoint
+{
+ NSArray *events = *eventsPoint;
+ NSMutableArray *newEvents = [events mutableCopy];
+ NSMutableDictionary *bindingEvents = [NSMutableDictionary dictionary];
+ [events enumerateObjectsUsingBlock:^(id _Nonnull event, NSUInteger idx, BOOL * _Nonnull stop) {
+ if ([event isKindOfClass:[NSDictionary class]] && event[@"type"] && event[@"params"]) {
+ NSString *eventName = event[@"type"];
+ NSString *bindingParams = event[@"params"];
+ bindingEvents[eventName] = bindingParams;
+ newEvents[idx] = eventName;
+ }
+ }];
+
+ *eventsPoint = newEvents;
+ return bindingEvents;
+}
+
+- (NSDictionary *)_extractBindingProps:(NSDictionary **)attributesPoint
+{
+ NSDictionary *attributes = *attributesPoint;
+ if (attributes[@"@componentProps"]) {
+ NSMutableDictionary *newAttributes = [attributes mutableCopy];
+ [newAttributes removeObjectForKey:@"@componentProps"];
+ *attributesPoint = newAttributes;
+ return attributes[@"@componentProps"];
+ }
+
+ return nil;
+}
+
+#pragma mark Reset
+-(BOOL)isShouldReset:(id )value
+{
+ if([value isKindOfClass:[NSString class]]) {
+ if(!value || [@"" isEqualToString:value]) {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+-(void)filterStyles:(NSDictionary *)styles normalStyles:(NSMutableDictionary *)normalStyles resetStyles:(NSMutableArray *)resetStyles
+{
+ for (NSString *key in styles) {
+ id value = [styles objectForKey:key];
+ if([self isShouldReset:value]) {
+ [resetStyles addObject:key];
+ }else{
+ [normalStyles setObject:styles[key] forKey:key];
+ }
+ }
+}
+
+- (void)updateStyles:(NSDictionary *)styles forComponent:(NSString *)ref
+{
+ [self handleStyles:styles forComponent:ref isUpdateStyles:YES];
+}
+
+- (void)updatePseudoClassStyles:(NSDictionary *)styles forComponent:(NSString *)ref
+{
+ [self handleStyles:styles forComponent:ref isUpdateStyles:NO];
+}
+
+- (void)handleStyleOnMainThread:(NSDictionary*)styles forComponent:(WXComponent *)component isUpdateStyles:(BOOL)isUpdateStyles
+{
+ WXAssertParam(styles);
+ WXAssertParam(component);
+ WXAssertMainThread();
+
+ NSMutableDictionary *normalStyles = [NSMutableDictionary new];
+ NSMutableArray *resetStyles = [NSMutableArray new];
+ [self filterStyles:styles normalStyles:normalStyles resetStyles:resetStyles];
+ [component _updateStylesOnMainThread:normalStyles resetStyles:resetStyles];
+ [component readyToRender];
+
+ WXPerformBlockOnComponentThread(^{
+ [component _updateStylesOnComponentThread:normalStyles resetStyles:resetStyles isUpdateStyles:isUpdateStyles];
+ });
+}
+
+- (void)handleStyles:(NSDictionary *)styles forComponent:(NSString *)ref isUpdateStyles:(BOOL)isUpdateStyles
+{
+ WXAssertParam(styles);
+ WXAssertParam(ref);
+
+ WXComponent *component = [_indexDict objectForKey:ref];
+ WXAssertComponentExist(component);
+
+ NSMutableDictionary *normalStyles = [NSMutableDictionary new];
+ NSMutableArray *resetStyles = [NSMutableArray new];
+ [self filterStyles:styles normalStyles:normalStyles resetStyles:resetStyles];
+ [component _updateStylesOnComponentThread:normalStyles resetStyles:resetStyles isUpdateStyles:isUpdateStyles];
+ [self _addUITask:^{
+ [component _updateStylesOnMainThread:normalStyles resetStyles:resetStyles];
+ [component readyToRender];
+ }];
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes forComponent:(NSString *)ref
+{
+ WXAssertParam(attributes);
+ WXAssertParam(ref);
+
+ WXComponent *component = [_indexDict objectForKey:ref];
+ WXAssertComponentExist(component);
+
+ [component _updateAttributesOnComponentThread:attributes];
+ __weak typeof(self) weakSelf = self;
+ [self _addUITask:^{
+ [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingBegin functionName:@"updateAttrs" options:@{@"threadName":WXTUIThread}];
+ [component _updateAttributesOnMainThread:attributes];
+ [component readyToRender];
+ [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingEnd functionName:@"updateAttrs" options:@{@"threadName":WXTUIThread}];
+ }];
+}
+
+- (void)addEvent:(NSString *)eventName toComponent:(NSString *)ref
+{
+ WXAssertComponentThread();
+ WXAssertParam(eventName);
+ WXAssertParam(ref);
+
+ WXComponent *component = [_indexDict objectForKey:ref];
+ WXAssertComponentExist(component);
+
+ [component _addEventOnComponentThread:eventName];
+
+ [self _addUITask:^{
+ [component _addEventOnMainThread:eventName];
+ }];
+}
+
+- (void)removeEvent:(NSString *)eventName fromComponent:(NSString *)ref
+{
+ WXAssertComponentThread();
+ WXAssertParam(eventName);
+ WXAssertParam(ref);
+
+ WXComponent *component = [_indexDict objectForKey:ref];
+ WXAssertComponentExist(component);
+
+ [component _removeEventOnComponentThread:eventName];
+
+ [self _addUITask:^{
+ [component _removeEventOnMainThread:eventName];
+ }];
+}
+
+- (void)scrollToComponent:(NSString *)ref options:(NSDictionary *)options
+{
+ WXAssertComponentThread();
+ WXAssertParam(ref);
+
+ WXComponent *toComponent = [_indexDict objectForKey:ref];
+ WXAssertComponentExist(toComponent);
+
+ id<WXScrollerProtocol> scrollerComponent = toComponent.ancestorScroller;
+ if (!scrollerComponent) {
+ return;
+ }
+
+ CGFloat offset = [[options objectForKey:@"offset"] floatValue];
+ BOOL animated = YES;
+ if ([options objectForKey:@"animated"]) {
+ animated = [[options objectForKey:@"animated"] boolValue];
+ }
+
+ [self _addUITask:^{
+ [scrollerComponent scrollToComponent:toComponent withOffset:offset animated:animated];
+ }];
+}
+
+#pragma mark Life Cycle
+
+- (void)createFinish
+{
+ WXAssertComponentThread();
+
+ WXSDKInstance *instance = self.weexInstance;
+ [self _addUITask:^{
+ UIView *rootView = instance.rootView;
+
+ //WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, instance);
+ WX_MONITOR_INSTANCE_PERF_END(WXPTAllRender, instance);
+ WX_MONITOR_SUCCESS(WXMTJSBridge);
+ WX_MONITOR_SUCCESS(WXMTNativeRender);
+
+ if(instance.renderFinish){
+ [WXTracingManager startTracingWithInstanceId:instance.instanceId ref:nil className:nil name:nil phase:WXTracingInstant functionName:WXTRenderFinish options:@{@"threadName":WXTUIThread}];
+ instance.renderFinish(rootView);
+ }
+ }];
+ [instance updatePerDicAfterCreateFinish];
+}
+
+- (void)updateFinish
+{
+ WXAssertComponentThread();
+
+ WXSDKInstance *instance = self.weexInstance;
+ WXComponent *root = [_indexDict objectForKey:WX_SDK_ROOT_REF];
+
+ [self _addUITask:^{
+ if(instance.updateFinish){
+ instance.updateFinish(root.view);
+ }
+ }];
+}
+
+- (void)refreshFinish
+{
+ WXAssertComponentThread();
+
+ WXSDKInstance *instance = self.weexInstance;
+ WXComponent *root = [_indexDict objectForKey:WX_SDK_ROOT_REF];
+
+ [self _addUITask:^{
+ if(instance.refreshFinish){
+ instance.refreshFinish(root.view);
+ }
+ }];
+}
+
+- (void)unload
+{
+ WXAssertComponentThread();
+ [self invalidate];
+ [self _stopDisplayLink];
+ NSEnumerator *enumerator = [[_indexDict copy] objectEnumerator];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ WXComponent *component;
+ while ((component = [enumerator nextObject])) {
+ [component _unloadViewWithReusing:NO];
+ }
+ _rootComponent = nil;
+ });
+
+ [_indexDict removeAllObjects];
+ [_uiTaskQueue removeAllObjects];
+}
+
+- (void)invalidate
+{
+ _isValid = NO;
+}
+
+- (BOOL)isValid
+{
+ return _isValid;
+}
+
+#pragma mark Layout Batch
+
+- (void)_startDisplayLink
+{
+ WXAssertComponentThread();
+
+ if(!_displayLink){
+ _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_handleDisplayLink)];
+ [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+ }
+}
+
+- (void)_stopDisplayLink
+{
+ WXAssertComponentThread();
+
+ if(_displayLink){
+ [_displayLink invalidate];
+ _displayLink = nil;
+ }
+}
+
+- (void)_suspendDisplayLink
+{
+ WXAssertComponentThread();
+
+ if(_displayLink && !_displayLink.paused) {
+ _displayLink.paused = YES;
+ }
+}
+
+- (void)_awakeDisplayLink
+{
+ WXAssertComponentThread();
+
+ if(_displayLink && _displayLink.paused) {
+ _displayLink.paused = NO;
+ }
+}
+
+- (void)_handleDisplayLink
+{
+ WXAssertComponentThread();
+
+ [self _layoutAndSyncUI];
+}
+
+- (void)_layoutAndSyncUI
+{
+ [self _layout];
+ if(_uiTaskQueue.count > 0){
+ [self _syncUITasks];
+ _noTaskTickCount = 0;
+ } else {
+ // suspend display link when there's no task for 1 second, in order to save CPU time.
+ _noTaskTickCount ++;
+ if (_noTaskTickCount > 60) {
+ [self _suspendDisplayLink];
+ }
+ }
+}
+
+- (void)_layout
+{
+ BOOL needsLayout = NO;
+
+// NSEnumerator *enumerator = [_indexDict objectEnumerator];
+// WXComponent *component;
+// while ((component = [enumerator nextObject])) {
+// if ([component needsLayout]) {
+// needsLayout = YES;
+// break;
+// }
+// }
+
+ needsLayout = [_rootComponent needsLayout];
+
+ if (!needsLayout) {
+ return;
+ }
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> action__ calculateLayout root");
+#endif
+
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ layoutNode(_rootCSSNode, _rootCSSNode->style.dimensions[CSS_WIDTH], _rootCSSNode->style.dimensions[CSS_HEIGHT], CSS_DIRECTION_INHERIT);
+ }
+//#else
+ else
+ {
+ std::pair<float, float> renderPageSize;
+ renderPageSize.first = self.weexInstance.frame.size.width;
+ renderPageSize.second = self.weexInstance.frame.size.height;
+ _rootFlexCSSNode->calculateLayout(renderPageSize);
+ }
+//#endif
+ NSMutableSet<WXComponent *> *dirtyComponents = [NSMutableSet set];
+ [_rootComponent _calculateFrameWithSuperAbsolutePosition:CGPointZero gatherDirtyComponents:dirtyComponents];
+ [self _calculateRootFrame];
+
+ for (WXComponent *dirtyComponent in dirtyComponents) {
+ [self _addUITask:^{
+ [dirtyComponent _layoutDidFinish];
+ }];
+ }
+}
+
+//#ifdef USE_FLEX
+- (void) _printFlexComonentFrame:(WXComponent *)component
+{
+#ifdef DEBUG
+ WXLogDebug(@"node ref:%@, type:%@ , frame:%@",
+ component.ref,
+ component.type,
+ NSStringFromCGRect(component.view.layer.frame)
+ );
+#endif
+
+
+
+ for (WXComponent *childComponent in component.subcomponents) {
+ [self _printFlexComonentFrame:childComponent];
+ }
+
+
+}
+//#endif
+
+- (void)_syncUITasks
+{
+ NSArray<dispatch_block_t> *blocks = _uiTaskQueue;
+ _uiTaskQueue = [NSMutableArray array];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ for(dispatch_block_t block in blocks) {
+ block();
+ }
+ });
+}
+//#ifndef USE_FLEX
+- (void)_initRootCSSNode
+{
+ _rootCSSNode = new_css_node();
+
+ [self _applyRootFrame:self.weexInstance.frame toRootCSSNode:_rootCSSNode];
+
+ _rootCSSNode->style.flex_wrap = CSS_NOWRAP;
+ _rootCSSNode->is_dirty = rootNodeIsDirty;
+ _rootCSSNode->get_child = rootNodeGetChild;
+ _rootCSSNode->context=(__bridge void *)(self);
+ _rootCSSNode->children_count = 1;
+}
+//#else
+- (void)_initRootFlexCssNode
+{
+ _rootFlexCSSNode = new WeexCore::WXCoreLayoutNode();
+ [self _applyRootFrame:self.weexInstance.frame];
+ _rootFlexCSSNode->setFlexWrap(WeexCore::kNoWrap);
+ _rootFlexCSSNode->setContext((__bridge void *)(self));
+}
+//#endif
+
+- (void)_calculateRootFrame
+{
+//#ifndef USE_FLEX
+
+ if(![WXComponent isUseFlex])
+ {
+ if (!_rootCSSNode->layout.should_update) {
+ return;
+ }
+ _rootCSSNode->layout.should_update = false;
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> root _calculateRootFrame");
+#endif
+
+ CGRect frame = CGRectMake(WXRoundPixelValue(_rootCSSNode->layout.position[CSS_LEFT]),
+ WXRoundPixelValue(_rootCSSNode->layout.position[CSS_TOP]),
+ WXRoundPixelValue(_rootCSSNode->layout.dimensions[CSS_WIDTH]),
+ WXRoundPixelValue(_rootCSSNode->layout.dimensions[CSS_HEIGHT]));
+ WXPerformBlockOnMainThread(^{
+ if(!self.weexInstance.isRootViewFrozen) {
+ self.weexInstance.rootView.frame = frame;
+ }
+ });
+
+ resetNodeLayout(_rootCSSNode);
+ }
+//#else
+ else
+ {
+ if(!_rootFlexCSSNode->hasNewLayout()){
+ return;
+ }
+ _rootFlexCSSNode->setHasNewLayout(false);
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> root _calculateRootFrame");
+#endif
+
+
+ CGRect frame = CGRectMake(WXRoundPixelValue(_rootFlexCSSNode->getLayoutPositionLeft()),
+ WXRoundPixelValue(_rootFlexCSSNode->getLayoutPositionTop()),
+ WXRoundPixelValue(_rootFlexCSSNode->getLayoutWidth()),
+ WXRoundPixelValue(_rootFlexCSSNode->getLayoutHeight()));
+ WXPerformBlockOnMainThread(^{
+ if(!self.weexInstance.isRootViewFrozen) {
+ self.weexInstance.rootView.frame = frame;
+ }
+ });
+ // _rootFlexCSSNode->reset();
+
+ // resetNodeLayout(_rootFlexCSSNode);
+ }
+//#endif
+
+
+}
+
+
+#pragma mark Fixed
+
+- (void)addFixedComponent:(WXComponent *)fixComponent
+{
+ [_fixedComponents addObject:fixComponent];
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ _rootCSSNode->children_count = (int)[_fixedComponents count] + 1;
+ }
+//#else
+ else
+ {
+ _rootFlexCSSNode->addChildAt(fixComponent.flexCssNode, (uint32_t)([_fixedComponents count]-1));
+ }
+//#endif
+}
+
+- (void)removeFixedComponent:(WXComponent *)fixComponent
+{
+ [_fixedComponents removeObject:fixComponent];
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ _rootCSSNode->children_count = (int)[_fixedComponents count] + 1;
+ }
+//#else
+ else
+ {
+ _rootFlexCSSNode->removeChild(fixComponent->_flexCssNode);
+ }
+//#endif
+}
+
+@end
+
+void WXPerformBlockOnComponentThread(void (^block)(void))
+{
+ [WXComponentManager _performBlockOnComponentThread:block];
+}
+
+void WXPerformBlockSyncOnComponentThread(void (^block)(void))
+{
+ [WXComponentManager _performBlockSyncOnComponentThread:block];
+}
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Model/WXComponent.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h
index 5e8ba70..c0e6c9a 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h
+++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h
@@ -17,6 +17,12 @@
* under the License.
*/
+/**
+ * def : use weex_flex_engin
+ * ndef: use yoga
+ **/
+
+
#import <Foundation/Foundation.h>
#import "WXLayoutDefine.h"
#import "WXType.h"
@@ -143,13 +149,6 @@ NS_ASSUME_NONNULL_BEGIN
//@property(nonatomic, assign) CGPoint absolutePosition;
/**
- * @abstract Return the css node used to layout.
- *
- * @warning Subclasses must not override this.
- */
-@property(nonatomic, readonly, assign) css_node_t *cssNode;
-
-/**
* @abstract Invalidates the component's layout and marks it as needing an update.
*
* @discussion You can call this method to indicate that the layout of a component has changed and must be updated. Weex typically calls this method automatically when the layout-related styles change or when subcomponents are added or removed.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Model/WXComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m
deleted file mode 100644
index 300dbc9..0000000
--- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m
+++ /dev/null
@@ -1,842 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-#import "WXComponent.h"
-#import "WXComponent_internal.h"
-#import "WXComponentManager.h"
-#import "WXSDKManager.h"
-#import "WXSDKInstance.h"
-#import "WXSDKInstance_private.h"
-#import "WXDefine.h"
-#import "WXLog.h"
-#import "WXWeakObjectWrapper.h"
-#import "WXUtility.h"
-#import "WXConvert.h"
-#import "WXMonitor.h"
-#import "WXAssert.h"
-#import "WXThreadSafeMutableDictionary.h"
-#import "WXThreadSafeMutableArray.h"
-#import "WXTransform.h"
-#import "WXRoundedRect.h"
-#import <pthread/pthread.h>
-#import "WXComponent+PseudoClassManagement.h"
-#import "WXComponent+BoxShadow.h"
-#import "WXTracingManager.h"
-#import "WXComponent+Events.h"
-
-#pragma clang diagnostic ignored "-Wincomplete-implementation"
-#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
-
-@interface WXComponent () <UIGestureRecognizerDelegate>
-
-@end
-
-@implementation WXComponent
-{
-@private
- NSString *_ref;
- NSMutableDictionary *_styles;
- NSMutableDictionary *_attributes;
- NSMutableArray *_events;
-
- // Protects properties styles/attributes/events/subcomponents which will be accessed from multiple threads.
- pthread_mutex_t _propertyMutex;
- pthread_mutexattr_t _propertMutexAttr;
-
- __weak WXComponent *_supercomponent;
- __weak id<WXScrollerProtocol> _ancestorScroller;
- __weak WXSDKInstance *_weexInstance;
-}
-
-#pragma mark Life Cycle
-
-- (instancetype)initWithRef:(NSString *)ref
- type:(NSString *)type
- styles:(NSDictionary *)styles
- attributes:(NSDictionary *)attributes
- events:(NSArray *)events
- weexInstance:(WXSDKInstance *)weexInstance
-{
- if (self = [super init]) {
- pthread_mutexattr_init(&_propertMutexAttr);
- pthread_mutexattr_settype(&_propertMutexAttr, PTHREAD_MUTEX_RECURSIVE);
- pthread_mutex_init(&_propertyMutex, &_propertMutexAttr);
-
- _ref = ref;
- _type = type;
- _weexInstance = weexInstance;
- _componentType = WXComponentTypeCommon;
- _styles = [self parseStyles:styles];
- _attributes = attributes ? [NSMutableDictionary dictionaryWithDictionary:attributes] : [NSMutableDictionary dictionary];
- _events = events ? [NSMutableArray arrayWithArray:events] : [NSMutableArray array];
- _subcomponents = [NSMutableArray array];
- _absolutePosition = CGPointMake(NAN, NAN);
-
- _displayType = WXDisplayTypeBlock;
- _isNeedJoinLayoutSystem = YES;
- _isLayoutDirty = YES;
- _isViewFrameSyncWithCalculated = YES;
- _ariaHidden = nil;
- _accessible = nil;
- _accessibilityHintContent = nil;
-
- _async = NO;
-
- if (styles[kWXTransitionProperty]) {
- _transition = [[WXTransition alloc]initWithStyles:styles];
- }
-
- //TODO set indicator style
- if ([type isEqualToString:@"indicator"]) {
- _styles[@"position"] = @"absolute";
- if (!_styles[@"left"] && !_styles[@"right"]) {
- _styles[@"left"] = @0.0f;
- }
- if (!_styles[@"top"] && !_styles[@"bottom"]) {
- _styles[@"top"] = @0.0f;
- }
- }
-
- if (attributes[@"ariaHidden"]) {
-
- _ariaHidden = [WXConvert NSString:attributes[@"ariaHidden"]];
- }
- if (attributes[@"role"]) {
- _roles = attributes[@"role"];
- }
- if (attributes[@"ariaLabel"]) {
- _ariaLabel = [WXConvert NSString:attributes[@"ariaLabel"]];
- }
- if (attributes[@"accessible"]) {
- _accessible = [WXConvert NSString:attributes[@"accessible"]];
- }
- if(attributes[@"accessibilityHint"]) {
- _accessibilityHintContent = [WXConvert NSString:attributes[@"accessibilityHint"]];
- }
- if (attributes[@"groupAccessibilityChildren"]) {
- _groupAccessibilityChildren = [WXConvert NSString:attributes[@"groupAccessibilityChildren"]];
- }
-
- if (attributes[@"testId"]) {
- _testId = [WXConvert NSString:attributes[@"testId"]];
- }
-
- [self _setupNavBarWithStyles:_styles attributes:_attributes];
- [self _initCSSNodeWithStyles:_styles];
- [self _initViewPropertyWithStyles:_styles];
- [self _initCompositingAttribute:_attributes];
- [self _handleBorders:styles isUpdating:NO];
-
- }
-
- return self;
-}
-
-- (id)copyWithZone:(NSZone *)zone
-{
- NSInteger copyId = 0;
- @synchronized(self){
- static NSInteger __copy = 0;
- copyId = __copy % (1024*1024);
- __copy++;
- }
- NSString *copyRef = [NSString stringWithFormat:@"%ldcopy_of%@", (long)copyId, _isTemplate ? self.ref : self->_templateComponent.ref];
- WXComponent *component = [[[self class] allocWithZone:zone] initWithRef:copyRef type:self.type styles:self.styles attributes:self.attributes events:self.events weexInstance:self.weexInstance];
- if (_isTemplate) {
- component->_templateComponent = self;
- } else {
- component->_templateComponent = self->_templateComponent;
- }
- memcpy(component->_cssNode, self.cssNode, sizeof(css_node_t));
- component->_cssNode->context = (__bridge void *)component;
- component->_calculatedFrame = self.calculatedFrame;
-
- NSMutableArray *subcomponentsCopy = [NSMutableArray array];
- for (WXComponent *subcomponent in self.subcomponents) {
- WXComponent *subcomponentCopy = [subcomponent copy];
- subcomponentCopy->_supercomponent = component;
- [subcomponentsCopy addObject:subcomponentCopy];
- }
-
- component->_subcomponents = subcomponentsCopy;
-
- WXPerformBlockOnComponentThread(^{
- [self.weexInstance.componentManager addComponent:component toIndexDictForRef:copyRef];
- });
-
- return component;
-}
-
-- (UIAccessibilityTraits)_parseAccessibilityTraitsWithTraits:(UIAccessibilityTraits)trait roles:(NSString*)roleStr
-{
- UIAccessibilityTraits newTrait = trait;
- for (NSString * role in [roleStr componentsSeparatedByString:@" "]) {
- newTrait |= [WXConvert WXUIAccessibilityTraits: role];
- }
-
- return newTrait;
-}
-
-- (void)dealloc
-{
- free_css_node(_cssNode);
-
-// [self _removeAllEvents];
- // remove all gesture and all
- if (_isTemplate && self.attributes[@"@templateId"]) {
- [[WXSDKManager bridgeMgr] callComponentHook:_weexInstance.instanceId componentId:self.attributes[@"@templateId"] type:@"lifecycle" hook:@"destroy" args:nil competion:nil];
- }
- if (_tapGesture) {
- [_tapGesture removeTarget:nil action:NULL];
- }
- if ([_swipeGestures count]) {
- for (UISwipeGestureRecognizer *swipeGestures in _swipeGestures) {
- [swipeGestures removeTarget:nil action:NULL];
- }
- }
-
- if (_longPressGesture) {
- [_longPressGesture removeTarget:nil action:NULL];
- }
-
- if (_panGesture) {
- [_panGesture removeTarget:nil action:NULL];
- }
-
- if (_positionType == WXPositionTypeFixed) {
- [self.weexInstance.componentManager removeFixedComponent:self];
- }
-
- pthread_mutex_destroy(&_propertyMutex);
- pthread_mutexattr_destroy(&_propertMutexAttr);
-
-}
-
-- (NSDictionary *)styles
-{
- NSDictionary *styles;
- pthread_mutex_lock(&_propertyMutex);
- styles = _styles;
- pthread_mutex_unlock(&_propertyMutex);
- return styles;
-}
-
-- (NSDictionary *)pseudoClassStyles
-{
- NSDictionary *pseudoClassStyles;
- pthread_mutex_lock(&_propertyMutex);
- pseudoClassStyles = _pseudoClassStyles;
- pthread_mutex_unlock(&_propertyMutex);
-
- return pseudoClassStyles;
-}
-
-- (NSString *)type
-{
- return _type;
-}
-
-- (NSDictionary *)attributes
-{
- NSDictionary *attributes;
- pthread_mutex_lock(&_propertyMutex);
- attributes = _attributes;
- pthread_mutex_unlock(&_propertyMutex);
-
- return attributes;
-}
-
-- (NSArray *)events
-{
- NSArray *events;
- pthread_mutex_lock(&_propertyMutex);
- events = [_events copy];
- pthread_mutex_unlock(&_propertyMutex);
-
- return events;
-}
-
-- (void)setDisplayType:(WXDisplayType)displayType
-{
- if (_displayType != displayType) {
- _displayType = displayType;
- if (displayType == WXDisplayTypeNone) {
- _isNeedJoinLayoutSystem = NO;
- [self.supercomponent _recomputeCSSNodeChildren];
- WXPerformBlockOnMainThread(^{
- [self removeFromSuperview];
- });
- } else {
- _isNeedJoinLayoutSystem = YES;
- [self.supercomponent _recomputeCSSNodeChildren];
- WXPerformBlockOnMainThread(^{
- [self _buildViewHierarchyLazily];
- // TODO: insert into the correct index
- [self.supercomponent.view addSubview:self.view];
- });
- }
- [self setNeedsLayout];
- }
-}
-
-- (WXSDKInstance *)weexInstance
-{
- return _weexInstance;
-}
-
-- (NSString *)description
-{
- return [NSString stringWithFormat:@"<%@:%p ref=%@> %@", _type, self, _ref, _view];
-}
-
-#pragma mark Property
-
-- (UIView *)view
-{
- if (_componentType != WXComponentTypeCommon) {
- return nil;
- }
- if ([self isViewLoaded]) {
- return _view;
- } else {
- WXAssertMainThread();
-
- // compositing child will be drew by its composited ancestor
- if (_isCompositingChild) {
- return nil;
- }
-
- [self viewWillLoad];
-
- _view = [self loadView];
-
- _layer = _view.layer;
- _view.frame = _calculatedFrame;
- _view.hidden = _visibility == WXVisibilityShow ? NO : YES;
- _view.clipsToBounds = _clipToBounds;
- if (![self _needsDrawBorder]) {
- _layer.borderColor = _borderTopColor.CGColor;
- _layer.borderWidth = _borderTopWidth;
- [self _resetNativeBorderRadius];
- _layer.opacity = _opacity;
- _view.backgroundColor = _backgroundColor;
- }
-
- if (_backgroundImage) {
- [self setGradientLayer];
- }
-
- if (_transform) {
- [_transform applyTransformForView:_view];
- }
-
- if (_boxShadow) {
- [self configBoxShadow:_boxShadow];
- }
-
- _view.wx_component = self;
- _view.wx_ref = self.ref;
- _layer.wx_component = self;
-
- if (_roles) {
- [_view setAccessibilityTraits:[self _parseAccessibilityTraitsWithTraits:self.view.accessibilityTraits roles:_roles]];
- }
-
- if (_testId) {
- _view.accessibilityIdentifier = _testId;
- }
-
- if (_accessibilityHintContent) {
- [_view setAccessibilityHint:_accessibilityHintContent];
- }
-
- if (_ariaLabel) {
- _view.accessibilityLabel = _ariaLabel;
- }
- if (_accessible) {
- [_view setIsAccessibilityElement:[WXConvert BOOL:_accessible]];
- }
-
- if (_ariaHidden) {
- [_view setAccessibilityElementsHidden:[WXConvert BOOL:_ariaHidden]];
- }
- if (_groupAccessibilityChildren) {
- [_view setShouldGroupAccessibilityChildren:[WXConvert BOOL:_groupAccessibilityChildren]];
- }
-
- [self _initEvents:self.events];
- [self _initPseudoEvents:_isListenPseudoTouch];
-
- if (_positionType == WXPositionTypeSticky) {
- [self.ancestorScroller addStickyComponent:self];
- }
-
- if (self.supercomponent && self.supercomponent->_async) {
- self->_async = YES;
- }
-
- [self setNeedsDisplay];
- [[NSNotificationCenter defaultCenter] postNotificationName:WX_COMPONENT_NOTIFICATION_VIEW_LOADED object:self];
- [self viewDidLoad];
-
- if (_lazyCreateView) {
- [self _buildViewHierarchyLazily];
- }
-
- [self _handleFirstScreenTime];
-
- return _view;
- }
-}
-
-- (void)_buildViewHierarchyLazily
-{
- if (self.supercomponent && !((WXComponent *)self.supercomponent)->_lazyCreateView) {
- NSArray *subcomponents = ((WXComponent *)self.supercomponent).subcomponents;
-
- NSInteger index = [subcomponents indexOfObject:self];
- if (index != NSNotFound) {
- [(WXComponent *)self.supercomponent insertSubview:self atIndex:index];
- }
- }
-
- NSArray *subcomponents = self.subcomponents;
- for (int i = 0; i < subcomponents.count; i++) {
- WXComponent *subcomponent = subcomponents[i];
- [self insertSubview:subcomponent atIndex:i];
- }
-}
-
-- (void)_resetNativeBorderRadius
-{
- WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:_calculatedFrame topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius];
- _layer.cornerRadius = borderRect.radii.topLeft;
-}
-
-- (void)_handleFirstScreenTime
-{
- if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance)) {
- return;
- }
- CGPoint absolutePosition = [self.supercomponent.view convertPoint:_view.frame.origin toView:_weexInstance.rootView];
- if (absolutePosition.y + _view.frame.size.height > self.weexInstance.rootView.frame.size.height + 1) {
- WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance);
- }
-}
-
-- (CALayer *)layer
-{
- return _layer;
-}
-
-- (CGRect)calculatedFrame
-{
- return _calculatedFrame;
-}
-
-- (CGPoint)absolutePosition
-{
- return _absolutePosition;
-}
-
-- (css_node_t *)cssNode
-{
- return _cssNode;
-}
-
-- (void)_addEventParams:(NSDictionary *)params
-{
- pthread_mutex_lock(&_propertyMutex);
- if (!_eventParameters) {
- _eventParameters = [NSMutableDictionary dictionary];
- }
- [_eventParameters addEntriesFromDictionary:params];
- pthread_mutex_unlock(&_propertyMutex);
-}
-
-- (NSArray *)_paramsForEvent:(NSString *)eventName
-{
- NSArray *params;
- pthread_mutex_lock(&_propertyMutex);
- params = _eventParameters[eventName];
- pthread_mutex_unlock(&_propertyMutex);
-
- return params;
-}
-
-#pragma mark Component Hierarchy
-
-- (NSArray<WXComponent *> *)subcomponents
-{
- NSArray<WXComponent *> *subcomponents;
- pthread_mutex_lock(&_propertyMutex);
- subcomponents = [_subcomponents copy];
- pthread_mutex_unlock(&_propertyMutex);
-
- return subcomponents;
-}
-
-- (WXComponent *)supercomponent
-{
- return _supercomponent;
-}
-
-- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
-{
- WXAssert(subcomponent, @"The subcomponent to insert to %@ at index %d must not be nil", self, index);
- if (index > [_subcomponents count]) {
- WXLogError(@"the index of inserted %ld is out of range as the current is %lu", (long)index, (unsigned long)[_subcomponents count]);
- return;
- }
-
- subcomponent->_supercomponent = self;
-
- pthread_mutex_lock(&_propertyMutex);
- [_subcomponents insertObject:subcomponent atIndex:index];
- pthread_mutex_unlock(&_propertyMutex);
-
- if (subcomponent->_positionType == WXPositionTypeFixed) {
- [self.weexInstance.componentManager addFixedComponent:subcomponent];
- subcomponent->_isNeedJoinLayoutSystem = NO;
- }
-
- if (_useCompositing || _isCompositingChild) {
- subcomponent->_isCompositingChild = YES;
- }
-
- [self _recomputeCSSNodeChildren];
- [self setNeedsLayout];
-}
-
-- (void)_removeSubcomponent:(WXComponent *)subcomponent
-{
- pthread_mutex_lock(&_propertyMutex);
- [_subcomponents removeObject:subcomponent];
- pthread_mutex_unlock(&_propertyMutex);
-}
-
-- (void)_removeFromSupercomponent
-{
- [self.supercomponent _removeSubcomponent:self];
- [self.supercomponent _recomputeCSSNodeChildren];
- [self.supercomponent setNeedsLayout];
-
- if (_positionType == WXPositionTypeFixed) {
- [self.weexInstance.componentManager removeFixedComponent:self];
- self->_isNeedJoinLayoutSystem = YES;
- }
-}
-
-- (void)_moveToSupercomponent:(WXComponent *)newSupercomponent atIndex:(NSUInteger)index
-{
- [self _removeFromSupercomponent];
- [newSupercomponent _insertSubcomponent:self atIndex:index];
-}
-
-- (void)_didInserted
-{
-
-}
-
-- (id<WXScrollerProtocol>)ancestorScroller
-{
- if(!_ancestorScroller) {
- WXComponent *supercomponent = self.supercomponent;
- while (supercomponent) {
- if([supercomponent conformsToProtocol:@protocol(WXScrollerProtocol)]) {
- _ancestorScroller = (id<WXScrollerProtocol>)supercomponent;
- break;
- }
- supercomponent = supercomponent.supercomponent;
- }
- }
-
- return _ancestorScroller;
-}
-
-#pragma mark Updating
-- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles
-{
- BOOL isTransitionTag = _transition ? [self _isTransitionTag:styles] : NO;
- if (isTransitionTag) {
- [_transition _handleTransitionWithStyles:styles resetStyles:resetStyles target:self];
- } else {
- styles = [self parseStyles:styles];
- [self _updateCSSNodeStyles:styles];
- [self _resetCSSNodeStyles:resetStyles];
- }
- if (isUpdateStyles) {
- [self _modifyStyles:styles];
- if ([self needsLayout]) {
- // call update style may take effect on layout, maybe the component
- // displaylink has been paused, so we need to restart the component task, and it will auto-pause when task queue is empty.
- [self.weexInstance.componentManager startComponentTasks];
- }
- }
-}
-
-- (BOOL)_isTransitionTag:(NSDictionary *)styles
-{
- BOOL yesOrNo = false;
- if (_transition.transitionOptions != WXTransitionOptionsNone) {
- yesOrNo = true;
- }
- return yesOrNo;
-}
-
-- (BOOL)_isTransitionOnMainThreadStyles:(NSDictionary *)styles
-{
- BOOL yesOrNo = false;
- if (_transition.transitionOptions != WXTransitionOptionsNone) {
- if ((_transition.transitionOptions & WXTransitionOptionsBackgroundColor &&styles[@"backgroundColor"])
- ||(_transition.transitionOptions & WXTransitionOptionsTransform &&styles[@"transform"])
- ||(_transition.transitionOptions & WXTransitionOptionsOpacity &&styles[@"opacity"])) {
- yesOrNo = true;
- }
- }
- return yesOrNo;
-}
-
-- (void)_modifyStyles:(NSDictionary *)styles
-{
- pthread_mutex_lock(&_propertyMutex);
- [_styles addEntriesFromDictionary:styles];
- pthread_mutex_unlock(&_propertyMutex);
-}
-
-- (void)_updateAttributesOnComponentThread:(NSDictionary *)attributes
-{
- pthread_mutex_lock(&_propertyMutex);
- [_attributes addEntriesFromDictionary:attributes];
- pthread_mutex_unlock(&_propertyMutex);
-}
-
-- (void)_addEventOnComponentThread:(NSString *)eventName
-{
- pthread_mutex_lock(&_propertyMutex);
- [_events addObject:eventName];
- pthread_mutex_unlock(&_propertyMutex);
-}
-
-- (void)_removeEventOnComponentThread:(NSString *)eventName
-{
- pthread_mutex_lock(&_propertyMutex);
- [_events removeObject:eventName];
- pthread_mutex_unlock(&_propertyMutex);
-}
-
-- (void)_updateStylesOnMainThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles
-{
- WXAssertMainThread();
- if (![self _isTransitionOnMainThreadStyles:styles]) {
- [self _updateViewStyles:styles];
- } else {
- [self _transitionUpdateViewProperty:styles];
- }
- [self _resetStyles:resetStyles];
- [self _handleBorders:styles isUpdating:YES];
- [self updateStyles:styles];
- [self resetStyles:resetStyles];
-}
-
-- (void)_updateAttributesOnMainThread:(NSDictionary *)attributes
-{
- WXAssertMainThread();
-
- [self _updateNavBarAttributes:attributes];
-
- [self updateAttributes:attributes];
- [self _configWXComponentA11yWithAttributes:attributes];
-}
-
-- (void)updateStyles:(NSDictionary *)styles
-{
- WXAssertMainThread();
-}
-
-- (void)updateAttributes:(NSDictionary *)attributes
-{
- WXAssertMainThread();
-}
-
-- (void)setNativeTransform:(CGAffineTransform)transform
-{
- WXAssertMainThread();
-
- _transform = [[WXTransform alloc] initWithNativeTransform:CATransform3DMakeAffineTransform(transform) instance:self.weexInstance];
- if (!CGRectEqualToRect(self.calculatedFrame, CGRectZero)) {
- [_transform applyTransformForView:_view];
- [_layer setNeedsDisplay];
- }
-}
-
-- (void)readyToRender
-{
- if (self.weexInstance.trackComponent) {
- [self.supercomponent readyToRender];
- }
-}
-
-
-- (void)setGradientLayer
-{
- if (CGRectEqualToRect(self.view.frame, CGRectZero)) {
- return;
- }
- NSDictionary * linearGradient = [WXUtility linearGradientWithBackgroundImage:_backgroundImage];
- if (!linearGradient) {
- return ;
- }
-
- __weak typeof(self) weakSelf = self;
- dispatch_async(dispatch_get_main_queue(), ^{
- __strong typeof(self) strongSelf = weakSelf;
- if(strongSelf) {
- UIColor * startColor = (UIColor*)linearGradient[@"startColor"];
- UIColor * endColor = (UIColor*)linearGradient[@"endColor"];
- CAGradientLayer * gradientLayer = [WXUtility gradientLayerFromColors:@[startColor, endColor] locations:nil frame:strongSelf.view.bounds gradientType:[linearGradient[@"gradientType"] integerValue]];
- if (gradientLayer) {
- _backgroundColor = [UIColor colorWithPatternImage:[strongSelf imageFromLayer:gradientLayer]];
- strongSelf.view.backgroundColor = _backgroundColor;
- }
- }
- });
-}
-
-- (void)_configWXComponentA11yWithAttributes:(NSDictionary *)attributes
-{
- WX_CHECK_COMPONENT_TYPE(self.componentType)
- if (attributes[@"role"]){
- _roles = attributes[@"role"];
- [self.view setAccessibilityTraits:[self _parseAccessibilityTraitsWithTraits:self.view.accessibilityTraits roles:_roles]];
- }
- if (attributes[@"ariaHidden"]) {
- _ariaHidden = [WXConvert NSString:attributes[@"ariaHidden"]];
- [self.view setAccessibilityElementsHidden:[WXConvert BOOL:_ariaHidden]];
- }
- if (attributes[@"accessible"]) {
- _accessible = [WXConvert NSString:attributes[@"accessible"]];
- [self.view setIsAccessibilityElement:[WXConvert BOOL:_accessible]];
- }
- if (attributes[@"ariaLabel"]) {
- _ariaLabel = [WXConvert NSString:attributes[@"ariaLabel"]];
- self.view.accessibilityValue = _ariaLabel;
- }
- if (attributes[@"accessibilityHint"]) {
- _accessibilityHintContent = [WXConvert NSString:attributes[@"accessibilityHint"]];
- [self.view setAccessibilityHint:_accessibilityHintContent];
- }
-
- if (attributes[@"groupAccessibilityChildren"]) {
- _groupAccessibilityChildren = [WXConvert NSString:attributes[@"groupAccessibilityChildren"]];
- [self.view setShouldGroupAccessibilityChildren:[WXConvert BOOL:_groupAccessibilityChildren]];
- }
-
-
- if (attributes[@"testId"]) {
- [self.view setAccessibilityIdentifier:[WXConvert NSString:attributes[@"testId"]]];
- }
-
-}
-
-- (UIImage *)imageFromLayer:(CALayer *)layer
-{
- UIGraphicsBeginImageContextWithOptions(layer.frame.size, NO, 0);
- [layer renderInContext:UIGraphicsGetCurrentContext()];
- UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- return outputImage;
-}
-
-#pragma mark Reset
-- (void)resetStyles:(NSArray *)styles
-{
- WXAssertMainThread();
-}
-
-#pragma mark Layout
-
-/**
- * @see WXComponent+Layout.m
- */
-
-#pragma mark View Management
-
-/**
- * @see WXComponent+ViewManagement.m
- */
-
-#pragma mark Events
-
-/**
- * @see WXComponent+Events.m
- */
-
-#pragma mark Display
-
-/**
- * @see WXComponent+Display.m
- */
-
-@end
-
-
-@implementation UIView (WXComponent)
-
-- (WXComponent *)wx_component
-{
- WXWeakObjectWrapper *weakWrapper = objc_getAssociatedObject(self, @selector(wx_component));
- return [weakWrapper weakObject];
-}
-
-- (void)setWx_component:(WXComponent *)wx_component
-{
- id weakWrapper = [[WXWeakObjectWrapper alloc] initWithWeakObject:wx_component];
- objc_setAssociatedObject(self, @selector(wx_component), weakWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-}
-
-- (NSString *)wx_ref
-{
- WXWeakObjectWrapper *weakWrapper = objc_getAssociatedObject(self, @selector(wx_ref));
- return [weakWrapper weakObject];
-}
-
-- (void)setWx_ref:(NSString *)wx_ref
-{
- id weakWrapper = [[WXWeakObjectWrapper alloc] initWithWeakObject:wx_ref];
- objc_setAssociatedObject(self, @selector(wx_ref), weakWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-}
-
-@end
-
-@implementation CALayer (WXComponents_new)
-
-- (WXComponent *)wx_component
-{
- WXWeakObjectWrapper *weakWrapper = objc_getAssociatedObject(self, @selector(wx_component));
- return [weakWrapper weakObject];
-}
-
-- (void)setWx_component:(WXComponent *)wx_component
-{
- id weakWrapper = [[WXWeakObjectWrapper alloc] initWithWeakObject:wx_component];
- objc_setAssociatedObject(self, @selector(wx_component), weakWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-}
-
-@end
[06/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
new file mode 100644
index 0000000..c951068
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
@@ -0,0 +1,1170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXTextComponent.h"
+#import "WXSDKInstance_private.h"
+#import "WXComponent_internal.h"
+#import "WXLayer.h"
+#import "WXUtility.h"
+#import "WXConvert.h"
+#import "WXRuleManager.h"
+#import "WXDefine.h"
+#import "WXView.h"
+#import "WXComponent+Layout.h"
+#import <pthread/pthread.h>
+#import <CoreText/CoreText.h>
+#import "WXComponent+Layout.h"
+
+// WXText is a non-public is not permitted
+@interface WXTextView : WXView
+@property (nonatomic, strong) NSTextStorage *textStorage;
+@end
+
+@implementation WXTextView
+
+- (instancetype)initWithFrame:(CGRect)frame
+{
+ if ((self = [super initWithFrame:frame])) {
+ self.accessibilityTraits |= UIAccessibilityTraitStaticText;
+
+ self.opaque = NO;
+ self.contentMode = UIViewContentModeRedraw;
+ self.textStorage = [NSTextStorage new];
+ }
+ return self;
+}
+
++ (Class)layerClass
+{
+ return [WXLayer class];
+}
+
+- (void)copy:(id)sender
+{
+ [[UIPasteboard generalPasteboard] setString:((WXTextComponent*)self.wx_component).text];
+}
+
+- (void)setTextStorage:(NSTextStorage *)textStorage
+{
+ if (_textStorage != textStorage) {
+ _textStorage = textStorage;
+ [self.wx_component setNeedsDisplay];
+ }
+}
+
+- (BOOL)canBecomeFirstResponder
+{
+ return YES;
+}
+
+- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
+{
+ if (action == @selector(copy:)) {
+ return [[self.wx_component valueForKey:@"_enableCopy"] boolValue];
+ }
+ return [super canPerformAction:action withSender:sender];
+}
+
+- (NSString *)description
+{
+ NSString *superDescription = super.description;
+ NSRange semicolonRange = [superDescription rangeOfString:@";"];
+ NSString * content = _textStorage.string;
+ if ([(WXTextComponent*)self.wx_component useCoreText]) {
+ content = ((WXTextComponent*)self.wx_component).text;
+ }
+ NSString *replacement = [NSString stringWithFormat:@"; text: %@; frame:%f,%f,%f,%f", content, self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height];
+ return [superDescription stringByReplacingCharactersInRange:semicolonRange withString:replacement];
+}
+
+- (NSString *)accessibilityValue
+{
+ if (self.wx_component && self.wx_component->_ariaLabel) {
+ return [super accessibilityValue];
+ }
+ if (![(WXTextComponent*)self.wx_component useCoreText]) {
+ return _textStorage.string;
+ }
+ return ((WXTextComponent*)self.wx_component).text;
+}
+
+- (NSString *)accessibilityLabel
+{
+ if (self.wx_component) {
+ if (self.wx_component->_ariaLabel) {
+ return self.wx_component->_ariaLabel;
+ }
+ }
+ return [super accessibilityLabel];
+}
+
+@end
+
+static BOOL textRenderUsingCoreText = YES;
+
+NSString *const WXTextTruncationToken = @"\u2026";
+CGFloat WXTextDefaultLineThroughWidth = 1.2;
+
+@interface WXTextComponent()
+@property (nonatomic, strong) NSString *useCoreTextAttr;
+@end
+
+@implementation WXTextComponent
+{
+ UIEdgeInsets _border;
+ UIEdgeInsets _padding;
+ NSTextStorage *_textStorage;
+ CGFloat _textStorageWidth;
+
+ UIColor *_color;
+ NSString *_fontFamily;
+ CGFloat _fontSize;
+ CGFloat _fontWeight;
+ WXTextStyle _fontStyle;
+ NSUInteger _lines;
+ NSTextAlignment _textAlign;
+ NSString *_direction;
+ WXTextDecoration _textDecoration;
+ NSString *_textOverflow;
+ CGFloat _lineHeight;
+ CGFloat _letterSpacing;
+ BOOL _truncationLine; // support trunk tail
+
+ NSAttributedString * _ctAttributedString;
+ NSString *_wordWrap;
+
+ pthread_mutex_t _ctAttributedStringMutex;
+ pthread_mutexattr_t _propertMutexAttr;
+ BOOL _observerIconfont;
+ BOOL _enableCopy;
+}
+
++ (void)setRenderUsingCoreText:(BOOL)usingCoreText
+{
+ textRenderUsingCoreText = usingCoreText;
+}
+
++ (BOOL)textRenderUsingCoreText
+{
+ return textRenderUsingCoreText;
+}
+
+- (instancetype)initWithRef:(NSString *)ref
+ type:(NSString *)type
+ styles:(NSDictionary *)styles
+ attributes:(NSDictionary *)attributes
+ events:(NSArray *)events
+ weexInstance:(WXSDKInstance *)weexInstance
+{
+ self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
+ if (self) {
+ // just for coretext and textkit render replacement
+ pthread_mutexattr_init(&(_propertMutexAttr));
+ pthread_mutexattr_settype(&(_propertMutexAttr), PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&(_ctAttributedStringMutex), &(_propertMutexAttr));
+
+ if ([attributes objectForKey:@"coretext"]) {
+ _useCoreTextAttr = [WXConvert NSString:attributes[@"coretext"]];
+ } else {
+ _useCoreTextAttr = nil;
+ }
+
+ [self fillCSSStyles:styles];
+ [self fillAttributes:attributes];
+
+ }
+ return self;
+}
+
+- (BOOL)useCoreText
+{
+ if ([_useCoreTextAttr isEqualToString:@"true"]) {
+ return YES;
+ }
+ if ([_useCoreTextAttr isEqualToString:@"false"]) {
+ return NO;
+ }
+
+ if ([WXTextComponent textRenderUsingCoreText]) {
+ return YES;
+ }
+ return NO;
+}
+
+- (void)dealloc
+{
+ if (_fontFamily && _observerIconfont) {
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:WX_ICONFONT_DOWNLOAD_NOTIFICATION object:nil];
+ }
+ pthread_mutex_destroy(&_ctAttributedStringMutex);
+ pthread_mutexattr_destroy(&_propertMutexAttr);
+}
+
+#define WX_STYLE_FILL_TEXT(key, prop, type, needLayout)\
+do {\
+ id value = styles[@#key];\
+ if (value) {\
+ _##prop = [WXConvert type:value];\
+ [self setNeedsRepaint];\
+ if (needLayout) {\
+ [self setNeedsLayout];\
+ }\
+ }\
+} while(0);
+
+#define WX_STYLE_FILL_TEXT_WITH_DEFAULT_VALUE(key, prop, type, defaultValue,needLayout)\
+do {\
+ id value = styles[@#key];\
+ if (value) {\
+ if([WXUtility isBlankString:value]){\
+ _##prop = defaultValue;\
+ }else {\
+ _##prop = [WXConvert type:value];\
+ }\
+ [self setNeedsRepaint];\
+ if (needLayout) {\
+ [self setNeedsLayout];\
+ }\
+ }\
+} while(0);
+
+
+#define WX_STYLE_FILL_TEXT_PIXEL(key, prop, needLayout)\
+do {\
+ id value = styles[@#key];\
+ if (value) {\
+ _##prop = [WXConvert WXPixelType:value scaleFactor:self.weexInstance.pixelScaleFactor];\
+ [self setNeedsRepaint];\
+ if (needLayout) {\
+ [self setNeedsLayout];\
+ }\
+}\
+} while(0);
+
+- (void)fillCSSStyles:(NSDictionary *)styles
+{
+ WX_STYLE_FILL_TEXT_WITH_DEFAULT_VALUE(color, color, UIColor, [UIColor blackColor], NO)
+ WX_STYLE_FILL_TEXT(fontFamily, fontFamily, NSString, YES)
+ WX_STYLE_FILL_TEXT_PIXEL(fontSize, fontSize, YES)
+ WX_STYLE_FILL_TEXT(fontWeight, fontWeight, WXTextWeight, YES)
+ WX_STYLE_FILL_TEXT(fontStyle, fontStyle, WXTextStyle, YES)
+ WX_STYLE_FILL_TEXT(lines, lines, NSUInteger, YES)
+ WX_STYLE_FILL_TEXT(textAlign, textAlign, NSTextAlignment, NO)
+ WX_STYLE_FILL_TEXT(textDecoration, textDecoration, WXTextDecoration, YES)
+ WX_STYLE_FILL_TEXT(textOverflow, textOverflow, NSString, NO)
+ WX_STYLE_FILL_TEXT_PIXEL(lineHeight, lineHeight, YES)
+ WX_STYLE_FILL_TEXT_PIXEL(letterSpacing, letterSpacing, YES)
+ WX_STYLE_FILL_TEXT(wordWrap, wordWrap, NSString, YES);
+ WX_STYLE_FILL_TEXT(direction, direction, NSString, YES)
+ if (_fontFamily && !_observerIconfont) {
+ // notification received when custom icon font file download finish
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(repaintText:) name:WX_ICONFONT_DOWNLOAD_NOTIFICATION object:nil];
+ _observerIconfont = YES;
+ }
+
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ UIEdgeInsets padding = {
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_TOP] + self.cssNode->style.border[CSS_TOP]),
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_LEFT] + self.cssNode->style.border[CSS_LEFT]),
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_BOTTOM] + self.cssNode->style.border[CSS_BOTTOM]),
+ WXFloorPixelValue(self.cssNode->style.padding[CSS_RIGHT] + self.cssNode->style.border[CSS_RIGHT])
+ };
+
+ if (!UIEdgeInsetsEqualToEdgeInsets(padding, _padding)) {
+ _padding = padding;
+ [self setNeedsRepaint];
+ }
+ }
+
+//#else
+ else
+ {
+ UIEdgeInsets flex_padding = {
+ WXFloorPixelValue(self.flexCssNode->getPaddingTop()+ self.flexCssNode->getBorderWidthTop()),
+ WXFloorPixelValue(self.flexCssNode->getPaddingLeft() + self.flexCssNode->getBorderWidthLeft()),
+ WXFloorPixelValue(self.flexCssNode->getPaddingBottom() + self.flexCssNode->getBorderWidthBottom()),
+ WXFloorPixelValue(self.flexCssNode->getPaddingRight() + self.flexCssNode->getBorderWidthRight())
+ };
+
+ if (!UIEdgeInsetsEqualToEdgeInsets(flex_padding, _padding)) {
+ _padding = flex_padding;
+ [self setNeedsRepaint];
+ }
+ }
+//#endif
+
+}
+
+- (void)fillAttributes:(NSDictionary *)attributes
+{
+ id text = [WXConvert NSString:attributes[@"value"]];
+ if (text && ![self.text isEqualToString:text]) {
+ self.text = text;
+ [self setNeedsRepaint];
+ [self setNeedsLayout];
+ }
+ if (attributes[@"enableCopy"]) {
+ _enableCopy = [WXConvert BOOL:attributes[@"enableCopy"]];
+ }
+}
+
+- (void)setNeedsRepaint
+{
+ _textStorage = nil;
+
+ pthread_mutex_lock(&(_ctAttributedStringMutex));
+ _ctAttributedString = nil;
+ pthread_mutex_unlock(&(_ctAttributedStringMutex));
+
+}
+
+#pragma mark - Subclass
+
+- (void)setNeedsLayout
+{
+ [super setNeedsLayout];
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+ BOOL useCoreText = NO;
+ if ([self.view.wx_component isKindOfClass:NSClassFromString(@"WXTextComponent")] && [self.view.wx_component respondsToSelector:@selector(useCoreText)]) {
+ useCoreText = [(WXTextComponent*)self.view.wx_component useCoreText];
+ }
+ if (!useCoreText) {
+ ((WXTextView *)self.view).textStorage = _textStorage;
+ }
+ if (_enableCopy) {
+ UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(displayMenuController:)];
+ [self.view addGestureRecognizer:longPress];
+ }
+ self.view.isAccessibilityElement = YES;
+
+ [self setNeedsDisplay];
+}
+
+- (void)displayMenuController:(id)sender
+{
+ if ([self.view becomeFirstResponder] && ((UILongPressGestureRecognizer*)sender).state == UIGestureRecognizerStateBegan) {
+ UIMenuController *theMenu = [UIMenuController sharedMenuController];
+ CGSize size = [self ctAttributedString].size;
+ CGRect selectionRect = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, size.width, size.height);
+ [theMenu setTargetRect:selectionRect inView:self.view.superview];
+ [theMenu setMenuVisible:YES animated:YES];
+ }
+}
+
+- (UIView *)loadView
+{
+ return [[WXTextView alloc] init];
+}
+
+- (BOOL)needsDrawRect
+{
+ return YES;
+}
+
+- (UIImage *)drawRect:(CGRect)rect;
+{
+ CGContextRef context = UIGraphicsGetCurrentContext();
+ if (_isCompositingChild) {
+ [self drawTextWithContext:context bounds:rect padding:_padding view:nil];
+ } else {
+ WXTextView *textView = (WXTextView *)_view;
+ [self drawTextWithContext:context bounds:rect padding:_padding view:textView];
+ }
+
+ return nil;
+}
+
+- (CGSize (^)(CGSize))measureBlock
+{
+ __weak typeof(self) weakSelf = self;
+ return ^CGSize (CGSize constrainedSize) {
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> measureblock %@, constrainedSize:%@",
+ self.type,
+ NSStringFromCGSize(constrainedSize)
+ );
+#endif
+ CGSize computedSize = CGSizeZero;
+ NSTextStorage *textStorage = nil;
+
+ //TODO:more elegant way to use max and min constrained size
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) {
+ constrainedSize.width = MAX(constrainedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) {
+ constrainedSize.width = MIN(constrainedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]);
+ }
+ }
+//#else
+ else
+ {
+ if (!isnan(weakSelf.flexCssNode->getMinWidth())) {
+ constrainedSize.width = MAX(constrainedSize.width, weakSelf.flexCssNode->getMinWidth());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMaxWidth())) {
+ constrainedSize.width = MIN(constrainedSize.width, weakSelf.flexCssNode->getMaxWidth());
+ }
+ }
+//#endif
+
+ if (![self useCoreText]) {
+ textStorage = [weakSelf textStorageWithWidth:constrainedSize.width];
+ NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
+ NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
+ computedSize = [layoutManager usedRectForTextContainer:textContainer].size;
+ } else {
+ computedSize = [weakSelf calculateTextHeightWithWidth:constrainedSize.width];
+ }
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ //TODO:more elegant way to use max and min constrained size
+ if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) {
+ computedSize.width = MAX(computedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) {
+ computedSize.width = MIN(computedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_HEIGHT])) {
+ computedSize.height = MAX(computedSize.height, weakSelf.cssNode->style.minDimensions[CSS_HEIGHT]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT])) {
+ computedSize.height = MIN(computedSize.height, weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT]);
+ }
+ }
+
+//#else
+ else
+ {
+ if (!isnan(weakSelf.flexCssNode->getMinWidth())) {
+ computedSize.width = MAX(computedSize.width, weakSelf.flexCssNode->getMinWidth());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMaxWidth())) {
+ computedSize.width = MIN(computedSize.width, weakSelf.flexCssNode->getMaxWidth());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMinHeight())) {
+ computedSize.height = MAX(computedSize.height, weakSelf.flexCssNode->getMinHeight());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMaxHeight())) {
+ computedSize.height = MIN(computedSize.height, weakSelf.flexCssNode->getMaxHeight());
+ }
+ }
+
+//#endif
+ if (textStorage && [WXUtility isBlankString:textStorage.string]) {
+ // if the text value is empty or nil, then set the height is 0.
+ computedSize.height = 0;
+ }
+
+ return (CGSize) {
+ WXCeilPixelValue(computedSize.width),
+ WXCeilPixelValue(computedSize.height)
+ };
+ };
+}
+
+#pragma mark Text Building
+
+- (NSAttributedString *)ctAttributedString
+{
+ if (!self.text) {
+ return nil;
+ }
+ NSAttributedString * attributedString = nil;
+ pthread_mutex_lock(&(_ctAttributedStringMutex));
+ if (!_ctAttributedString) {
+ _ctAttributedString = [self buildCTAttributeString];
+ WXPerformBlockOnComponentThread(^{
+ [self.weexInstance.componentManager startComponentTasks];
+ });
+ }
+ attributedString = [_ctAttributedString copy];
+ pthread_mutex_unlock(&(_ctAttributedStringMutex));
+ return attributedString;
+}
+
+- (void)repaintText:(NSNotification *)notification
+{
+ if (![_fontFamily isEqualToString:notification.userInfo[@"fontFamily"]]) {
+ return;
+ }
+ [self setNeedsRepaint];
+ WXPerformBlockOnComponentThread(^{
+ [self.weexInstance.componentManager startComponentTasks];
+ WXPerformBlockOnMainThread(^{
+ [self setNeedsLayout];
+ [self setNeedsDisplay];
+ });
+ });
+}
+
+- (NSMutableAttributedString *)buildCTAttributeString
+{
+ NSString * string = self.text;
+ if (![string isKindOfClass:[NSString class]]) {
+ WXLogError(@"text %@ is invalid", self.text);
+ string = @"";
+ }
+ NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: string];
+ if (_color) {
+ [attributedString addAttribute:NSForegroundColorAttributeName value:_color range:NSMakeRange(0, string.length)];
+ }
+
+ // set font
+ UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor useCoreText:[self useCoreText]];
+ CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)font.fontName,
+ font.pointSize,
+ NULL);
+ if (ctFont) {
+ [attributedString addAttribute:(id)kCTFontAttributeName value:(__bridge id)(ctFont) range:NSMakeRange(0, string.length)];
+ CFRelease(ctFont);
+ }
+
+ if(_textDecoration == WXTextDecorationUnderline){
+ [attributedString addAttribute:(id)kCTUnderlineStyleAttributeName value:@(kCTUnderlinePatternSolid | kCTUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
+ } else if(_textDecoration == WXTextDecorationLineThrough){
+ [attributedString addAttribute:NSStrikethroughStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
+ }
+
+ NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
+
+ // handle text direction style, default ltr
+ BOOL isRtl = false;
+ if ([WXComponent isUseFlex]) {
+ isRtl = [_direction isEqualToString:@"rtl"];
+ }else{
+ isRtl = _cssNode->layout.direction == CSS_DIRECTION_RTL;
+ }
+ if (isRtl) {
+ if (0 == _textAlign) {
+ //force text right-align if don't specified any align.
+ _textAlign = NSTextAlignmentRight;
+ }
+ paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft;
+ } else {
+ //if you specify NSWritingDirectionNaturalDirection, the receiver resolves the writing
+ //directionto eitherNSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft,
+ //depending on the direction for the user’s language preference setting.
+ paragraphStyle.baseWritingDirection = NSWritingDirectionNatural;
+ }
+
+ if (_textAlign) {
+ paragraphStyle.alignment = _textAlign;
+ }
+
+ if ([[_wordWrap lowercaseString] isEqualToString:@"break-word"]) {
+ paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
+ } else if ([[_wordWrap lowercaseString] isEqualToString:@"normal"]){
+ paragraphStyle.lineBreakMode = NSLineBreakByClipping;
+ } else {
+ // set default lineBreakMode
+ paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
+ }
+ _truncationLine = NO;
+ if (_textOverflow && [_textOverflow length] > 0) {
+ if (_lines && [_textOverflow isEqualToString:@"ellipsis"])
+ _truncationLine = YES;
+ }
+
+ if (_lineHeight) {
+ paragraphStyle.maximumLineHeight = _lineHeight;
+ paragraphStyle.minimumLineHeight = _lineHeight;
+ }
+ if (_lineHeight || _textAlign || [_textOverflow length] > 0) {
+ [attributedString addAttribute:NSParagraphStyleAttributeName
+ value:paragraphStyle
+ range:(NSRange){0, attributedString.length}];
+ }
+
+ if (_letterSpacing) {
+ [attributedString addAttribute:NSKernAttributeName value:@(_letterSpacing) range:(NSRange){0, attributedString.length}];
+ }
+
+ if ([self adjustLineHeight]) {
+ if (_lineHeight > font.lineHeight) {
+ [attributedString addAttribute:NSBaselineOffsetAttributeName
+ value:@((_lineHeight - font.lineHeight)/ 2)
+ range:(NSRange){0, attributedString.length}];
+ }
+ }
+
+ return attributedString;
+}
+
+- (NSAttributedString *)buildAttributeString
+{
+ NSString *string = self.text ?: @"";
+
+ NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
+
+ // set textColor
+ if(_color) {
+ [attributedString addAttribute:NSForegroundColorAttributeName value:_color range:NSMakeRange(0, string.length)];
+ }
+
+ // set font
+ UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor];
+ if (font) {
+ [attributedString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, string.length)];
+ }
+
+ if(_textDecoration == WXTextDecorationUnderline){
+ [attributedString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
+ } else if(_textDecoration == WXTextDecorationLineThrough){
+ [attributedString addAttribute:NSStrikethroughStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
+ }
+
+ NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
+
+ // handle text direction style, default ltr
+ BOOL isRtl = false;
+ if ([WXComponent isUseFlex]) {
+ isRtl = [_direction isEqualToString:@"rtl"];
+ }else{
+ isRtl = _cssNode->layout.direction == CSS_DIRECTION_RTL;
+ }
+ if (isRtl) {
+ if (0 == _textAlign) {
+ //force text right-align if don't specified any align.
+ _textAlign = NSTextAlignmentRight;
+ }
+ paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft;
+ } else {
+ //if you specify NSWritingDirectionNaturalDirection, the receiver resolves the writing
+ //directionto eitherNSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft,
+ //depending on the direction for the user’s language preference setting.
+ paragraphStyle.baseWritingDirection = NSWritingDirectionNatural;
+ }
+
+ if (_textAlign) {
+ paragraphStyle.alignment = _textAlign;
+ }
+
+ if (_lineHeight) {
+ paragraphStyle.maximumLineHeight = _lineHeight;
+ paragraphStyle.minimumLineHeight = _lineHeight;
+ }
+
+ if (_lineHeight || _textAlign) {
+ [attributedString addAttribute:NSParagraphStyleAttributeName
+ value:paragraphStyle
+ range:(NSRange){0, attributedString.length}];
+ }
+ if ([self adjustLineHeight]) {
+ if (_lineHeight > font.lineHeight) {
+ [attributedString addAttribute:NSBaselineOffsetAttributeName
+ value:@((_lineHeight - font.lineHeight)/ 2)
+ range:(NSRange){0, attributedString.length}];
+ }
+ }
+
+ return attributedString;
+}
+
+- (BOOL)adjustLineHeight
+{
+ if (WX_SYS_VERSION_LESS_THAN(@"10.0")) {
+ return true;
+ }
+ return ![self useCoreText];
+}
+
+- (NSTextStorage *)textStorageWithWidth:(CGFloat)width
+{
+ if (_textStorage && width == _textStorageWidth) {
+ return _textStorage;
+ }
+
+ NSLayoutManager *layoutManager = [NSLayoutManager new];
+
+ // build AttributeString
+ NSAttributedString *attributedString = [self buildAttributeString];
+
+ NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
+ [textStorage addLayoutManager:layoutManager];
+
+ NSTextContainer *textContainer = [NSTextContainer new];
+ textContainer.lineFragmentPadding = 0.0;
+
+ if ([[_wordWrap lowercaseString] isEqualToString:@"break-word"]) {
+ textContainer.lineBreakMode = NSLineBreakByWordWrapping;
+ } else if ([[_wordWrap lowercaseString] isEqualToString:@"normal"]){
+ textContainer.lineBreakMode = NSLineBreakByClipping;
+ } else {
+ // set default lineBreakMode
+ textContainer.lineBreakMode = NSLineBreakByCharWrapping;
+ }
+
+ if (_textOverflow && [_textOverflow length] > 0) {
+ if ([_textOverflow isEqualToString:@"ellipsis"])
+ textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
+ }
+ textContainer.maximumNumberOfLines = _lines > 0 ? _lines : 0;
+ textContainer.size = (CGSize){isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX};
+
+ [layoutManager addTextContainer:textContainer];
+ [layoutManager ensureLayoutForTextContainer:textContainer];
+
+ _textStorageWidth = width;
+ _textStorage = textStorage;
+
+ return textStorage;
+}
+
+- (void)syncTextStorageForView
+{
+ CGFloat width = self.calculatedFrame.size.width - (_padding.left + _padding.right);
+ NSTextStorage *textStorage = nil;
+ if (![self useCoreText]) {
+ textStorage = [self textStorageWithWidth:width];
+ }
+ [self.weexInstance.componentManager _addUITask:^{
+ if ([self isViewLoaded]) {
+ if (![self useCoreText]) {
+ ((WXTextView *)self.view).textStorage = textStorage;
+ }
+ [self readyToRender]; // notify super component
+ [self setNeedsDisplay];
+ }
+ }];
+}
+
+- (void)_frameDidCalculated:(BOOL)isChanged
+{
+ [super _frameDidCalculated:isChanged];
+ [self syncTextStorageForView];
+}
+
+- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles
+{
+ [super _updateStylesOnComponentThread:styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:isUpdateStyles];
+ NSMutableDictionary * newStyles = [styles mutableCopy];
+ for (NSString * key in [resetStyles copy]) {
+ [newStyles setObject:@"" forKey:key];
+ }
+ [self fillCSSStyles:newStyles];
+
+ [self syncTextStorageForView];
+}
+
+- (void)_updateAttributesOnComponentThread:(NSDictionary *)attributes
+{
+ [super _updateAttributesOnComponentThread:attributes];
+
+ [self fillAttributes:attributes];
+
+ [self syncTextStorageForView];
+}
+
+- (void)drawTextWithContext:(CGContextRef)context bounds:(CGRect)bounds padding:(UIEdgeInsets)padding view:(WXTextView *)view
+{
+ if (bounds.size.width <= 0 || bounds.size.height <= 0) {
+ return;
+ }
+
+ if ([self _needsDrawBorder]) {
+ [self _drawBorderWithContext:context size:bounds.size];
+ } else {
+ WXPerformBlockOnMainThread(^{
+ [self _resetNativeBorderRadius];
+ });
+ }
+ if (![self useCoreText]) {
+ NSLayoutManager *layoutManager = _textStorage.layoutManagers.firstObject;
+ NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
+
+ CGRect textFrame = UIEdgeInsetsInsetRect(bounds, padding);
+ NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
+
+ [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin];
+ [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin];
+ } else {
+ CGRect textFrame = UIEdgeInsetsInsetRect(bounds, padding);
+ // sufficient height for text to draw, or frame lines will be empty
+ textFrame.size.height = bounds.size.height * 2;
+ CGContextSaveGState(context);
+ //flip the coordinate system
+ CGContextSetTextMatrix(context, CGAffineTransformIdentity);
+ CGContextTranslateCTM(context, 0, textFrame.size.height);
+ CGContextScaleCTM(context, 1.0, -1.0);
+
+ NSAttributedString * attributedStringCopy = [self ctAttributedString];
+ //add path
+ CGPathRef cgPath = NULL;
+ cgPath = CGPathCreateWithRect(textFrame, NULL);
+ CTFrameRef _coreTextFrameRef = NULL;
+ if (_coreTextFrameRef) {
+ CFRelease(_coreTextFrameRef);
+ _coreTextFrameRef = NULL;
+ }
+ if(!attributedStringCopy) {
+ return;
+ }
+ CTFramesetterRef ctframesetterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attributedStringCopy));
+ _coreTextFrameRef = CTFramesetterCreateFrame(ctframesetterRef, CFRangeMake(0, attributedStringCopy.length), cgPath, NULL);
+ CFArrayRef ctLines = NULL;
+ if (NULL == _coreTextFrameRef) {
+ // try to protect crash from frame is NULL
+ return;
+ }
+ CFRelease(ctframesetterRef);
+ ctframesetterRef = NULL;
+ ctLines = CTFrameGetLines(_coreTextFrameRef);
+ CFIndex lineCount = CFArrayGetCount(ctLines);
+ NSMutableArray * mutableLines = [NSMutableArray new];
+ CGPoint lineOrigins[lineCount];
+ NSUInteger rowCount = 0;
+ BOOL needTruncation = NO;
+ CTLineRef ctTruncatedLine = NULL;
+ CTFrameGetLineOrigins(_coreTextFrameRef, CFRangeMake(0, 0), lineOrigins);
+ for (CFIndex lineIndex = 0;(!_lines || _lines > lineIndex) && lineIndex < lineCount; lineIndex ++) {
+ CTLineRef lineRef = NULL;
+ lineRef = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
+ if (!lineRef) {
+ break;
+ }
+ CGPoint lineOrigin = lineOrigins[lineIndex];
+ lineOrigin.x += padding.left;
+ lineOrigin.y -= padding.top;
+ CFArrayRef runs = CTLineGetGlyphRuns(lineRef);
+ [mutableLines addObject:(__bridge id _Nonnull)(lineRef)];
+ // lineIndex base 0
+ rowCount = lineIndex + 1;
+ if (_lines > 0 && _truncationLine) {
+ if (_truncationLine && rowCount > _lines) {
+ needTruncation = YES;
+ do {
+ NSUInteger lastRow = [mutableLines count];
+ if (lastRow < rowCount) {
+ break;
+ }
+ [mutableLines removeLastObject];
+ } while (1);
+
+ }
+ }
+ if (_lines > 0 && _truncationLine) {
+ if (rowCount >= _lines &&!needTruncation && (CTLineGetStringRange(lineRef).length + CTLineGetStringRange(lineRef).location) < attributedStringCopy.length) {
+ needTruncation = YES;
+ }
+ }
+
+ if (needTruncation) {
+ CGContextSetTextPosition(context, lineOrigin.x, lineOrigin.y);
+ ctTruncatedLine = [self buildTruncatedLineWithRuns:runs lines:mutableLines path:cgPath];
+ if (ctTruncatedLine) {
+ CFArrayRef truncatedRuns = CTLineGetGlyphRuns(ctTruncatedLine);
+ [self drawTextWithRuns:truncatedRuns context:context lineOrigin:lineOrigin];
+ CFRelease(ctTruncatedLine);
+ ctTruncatedLine = NULL;
+ continue;
+ }
+ }else {
+ [self drawTextWithRuns:runs context:context lineOrigin:lineOrigin];
+ }
+ }
+
+ [mutableLines removeAllObjects];
+ CGPathRelease(cgPath);
+ CFRelease(_coreTextFrameRef);
+ _coreTextFrameRef = NULL;
+ cgPath = NULL;
+ CGContextRestoreGState(context);
+ }
+}
+
+- (void)drawTextWithRuns:(CFArrayRef)runs context:(CGContextRef)context lineOrigin:(CGPoint)lineOrigin
+{
+ for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runs); runIndex ++) {
+ CTRunRef run = NULL;
+ run = (CTRunRef)CFArrayGetValueAtIndex(runs, runIndex);
+ CFDictionaryRef attr = NULL;
+ attr = CTRunGetAttributes(run);
+ if (0 == runIndex) {
+ NSNumber *baselineOffset = (NSNumber*)CFDictionaryGetValue(attr, (__bridge void *)NSBaselineOffsetAttributeName);
+ if (baselineOffset) {
+ lineOrigin.y += [baselineOffset doubleValue];
+ }
+ }
+ CGContextSetTextPosition(context, lineOrigin.x, lineOrigin.y);
+ CTRunDraw(run, context, CFRangeMake(0, 0));
+ CFIndex glyphCount = CTRunGetGlyphCount(run);
+ if (glyphCount <= 0) continue;
+
+ long longForStrikethroughStyleAttributeName= (long)CFDictionaryGetValue(attr, (__bridge void *)NSStrikethroughStyleAttributeName);
+ NSUnderlineStyle strikethrough = (NSUnderlineStyle)longForStrikethroughStyleAttributeName;
+
+ if (strikethrough) {
+ // draw strikethrough
+ [self drawLineThroughWithRun:runs context:context index:runIndex origin:lineOrigin];
+ }
+ }
+}
+
+- (CTLineRef)buildTruncatedLineWithRuns:(CFArrayRef)runs lines:(NSMutableArray*)mutableLines path:(CGPathRef)cgPath
+{
+ NSAttributedString * truncationToken = nil;
+ CTLineRef ctTruncatedLine = NULL;
+ CTLineRef lastLine = (__bridge CTLineRef)(mutableLines.lastObject);
+
+ CFArrayRef lastLineRuns = CTLineGetGlyphRuns(lastLine);
+ NSUInteger lastLineRunCount = CFArrayGetCount(lastLineRuns);
+
+ CTLineRef truncationTokenLine = NULL;
+ NSMutableDictionary *attrs = nil;
+ if (lastLineRunCount > 0) {
+ CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, lastLineRunCount - 1);
+ attrs = (id)CTRunGetAttributes(run);
+ attrs = attrs ? attrs.mutableCopy : [NSMutableDictionary new];
+ CTFontRef font = (__bridge CTFontRef)(attrs[(id)kCTFontAttributeName]);
+ CGFloat fontSize = font ? CTFontGetSize(font):32 * self.weexInstance.pixelScaleFactor;
+ UIFont * uiFont = [UIFont systemFontOfSize:fontSize];
+ if (uiFont) {
+ font = CTFontCreateWithName((__bridge CFStringRef)uiFont.fontName, uiFont.pointSize, NULL);
+ }
+ if (font) {
+ attrs[(id)kCTFontAttributeName] = (__bridge id)(font);
+ uiFont = nil;
+ CFRelease(font);
+ }
+ CGColorRef color = (__bridge CGColorRef)(attrs[(id)kCTForegroundColorAttributeName]);
+ if (color && CFGetTypeID(color) == CGColorGetTypeID() && CGColorGetAlpha(color) == 0) {
+ [attrs removeObjectForKey:(id)kCTForegroundColorAttributeName];
+ }
+
+ attrs = attrs?:[NSMutableDictionary new];
+ truncationToken = [[NSAttributedString alloc] initWithString:WXTextTruncationToken attributes:attrs];
+ truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)truncationToken);
+ }
+
+ if (truncationTokenLine) {
+ // default truncationType is kCTLineTruncationEnd
+ CTLineTruncationType truncationType = kCTLineTruncationEnd;
+ NSAttributedString *attributedString = [self ctAttributedString];
+ NSAttributedString * lastLineText = nil;
+ NSRange lastLineTextRange = WXNSRangeFromCFRange(CTLineGetStringRange(lastLine));
+ NSRange attributeStringRange = NSMakeRange(0, attributedString.string.length);
+ NSRange interSectionRange = NSIntersectionRange(lastLineTextRange, attributeStringRange);
+ if (!NSEqualRanges(interSectionRange, lastLineTextRange)) {
+ // out of bounds
+ lastLineTextRange = interSectionRange;
+ }
+ lastLineText = [attributedString attributedSubstringFromRange: lastLineTextRange];
+ if (!lastLineText) {
+ lastLineText = attributedString;
+ }
+ NSMutableAttributedString *mutableLastLineText = lastLineText.mutableCopy;
+ [mutableLastLineText appendAttributedString:truncationToken];
+ CTLineRef ctLastLineExtend = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)[mutableLastLineText copy]);
+ if (ctLastLineExtend) {
+ CGRect cgPathRect = CGRectZero;
+ CGFloat truncatedWidth = 0;
+ if (CGPathIsRect(cgPath, &cgPathRect)) {
+ truncatedWidth = cgPathRect.size.width;
+ }
+ ctTruncatedLine = CTLineCreateTruncatedLine(ctLastLineExtend, truncatedWidth, truncationType, truncationTokenLine);
+ CFRelease(ctLastLineExtend);
+ ctLastLineExtend = NULL;
+ CFRelease(truncationTokenLine);
+ truncationTokenLine = NULL;
+ }
+ }
+
+ return ctTruncatedLine;
+}
+
+- (void)drawLineThroughWithRun:(CFArrayRef)runs context:(CGContextRef)context index:(CFIndex)runIndex origin:(CGPoint)lineOrigin
+{
+ CFRetain(runs);
+ CGContextRetain(context);
+
+ CGContextSaveGState(context);
+ CGFloat xHeight = 0, underLinePosition = 0, lineThickness = 0;
+ CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, runIndex);
+ WXTextGetRunsMaxMetric(runs, &xHeight, &underLinePosition, &lineThickness);
+
+ CGPoint strikethroughStart;
+ strikethroughStart.x = lineOrigin.x - underLinePosition;
+ strikethroughStart.y = lineOrigin.y + xHeight/2;
+ CGPoint runPosition = CGPointZero;
+ CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition);
+ strikethroughStart.x = lineOrigin.x + runPosition.x;
+ CGContextSetLineWidth(context, WXTextDefaultLineThroughWidth);
+ double length = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL);
+ CGContextMoveToPoint(context, strikethroughStart.x, strikethroughStart.y);
+ CGContextAddLineToPoint(context, strikethroughStart.x + length, strikethroughStart.y);
+ CGContextStrokePath(context);
+
+ CGContextRestoreGState(context);
+ CFRelease(runs);
+ CGContextRelease(context);
+}
+
+- (CGSize)calculateTextHeightWithWidth:(CGFloat)aWidth
+{
+ CGFloat totalHeight = 0;
+ CGSize suggestSize = CGSizeZero;
+ NSAttributedString * attributedStringCpy = [self ctAttributedString];
+ if (!attributedStringCpy) {
+ return CGSizeZero;
+ }
+ if (isnan(aWidth)) {
+ aWidth = CGFLOAT_MAX;
+ }
+ aWidth = [attributedStringCpy boundingRectWithSize:CGSizeMake(aWidth, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil].size.width;
+ CTFramesetterRef ctframesetterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attributedStringCpy));
+ suggestSize = CTFramesetterSuggestFrameSizeWithConstraints(ctframesetterRef, CFRangeMake(0, 0), NULL, CGSizeMake(aWidth, MAXFLOAT), NULL);
+
+ CGMutablePathRef path = NULL;
+ path = CGPathCreateMutable();
+ // sufficient height to draw text
+ CGPathAddRect(path, NULL, CGRectMake(0, 0, aWidth, suggestSize.height * 10));
+
+ CTFrameRef frameRef = NULL;
+ frameRef = CTFramesetterCreateFrame(ctframesetterRef, CFRangeMake(0, attributedStringCpy.length), path, NULL);
+ CGPathRelease(path);
+
+ CFArrayRef lines = NULL;
+ if (NULL == frameRef) {
+ //try to protect unexpected crash.
+ return suggestSize;
+ }
+ CFRelease(ctframesetterRef);
+ ctframesetterRef = NULL;
+ lines = CTFrameGetLines(frameRef);
+ CFIndex lineCount = CFArrayGetCount(lines);
+ CGFloat ascent = 0;
+ CGFloat descent = 0;
+ CGFloat leading = 0;
+
+ // height = ascent + descent + lineCount*leading
+ // ignore linespaing
+ NSUInteger actualLineCount = 0;
+ for (CFIndex lineIndex = 0; (!_lines|| lineIndex < _lines) && lineIndex < lineCount; lineIndex ++)
+ {
+ CTLineRef lineRef = NULL;
+ lineRef = (CTLineRef)CFArrayGetValueAtIndex(lines, lineIndex);
+ CTLineGetTypographicBounds(lineRef, &ascent, &descent, &leading);
+ totalHeight += ascent + descent;
+ actualLineCount ++;
+ }
+
+ totalHeight = totalHeight + actualLineCount * leading;
+ CFRelease(frameRef);
+ frameRef = NULL;
+
+ if (WX_SYS_VERSION_LESS_THAN(@"10.0")) {
+ // there is something wrong with coreText drawing text height, trying to fix this with more efficent way.
+ if(actualLineCount && actualLineCount < lineCount) {
+ suggestSize.height = suggestSize.height * actualLineCount / lineCount;
+ }
+ return CGSizeMake(aWidth, suggestSize.height);
+ }
+ return CGSizeMake(aWidth, totalHeight);
+}
+
+static void WXTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat *underlinePosition, CGFloat *lineThickness)
+{
+ CFRetain(runs);
+ CGFloat maxXHeight = 0;
+ CGFloat maxUnderlinePos = 0;
+ CGFloat maxLineThickness = 0;
+ for (NSUInteger index = 0, runsCount = CFArrayGetCount(runs); index < runsCount; index ++) {
+ CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, index);
+ CFDictionaryRef attrs = CTRunGetAttributes(run);
+ if (attrs) {
+ CTFontRef font = (CTFontRef)CFDictionaryGetValue(attrs, kCTFontAttributeName);
+ if (font) {
+ CGFloat xHeight = CTFontGetXHeight(font);
+ if (xHeight > maxXHeight) {
+ maxXHeight = xHeight;
+ }
+
+ CGFloat underlinePos = CTFontGetUnderlinePosition(font);
+ if (underlinePos < maxUnderlinePos) {
+ maxUnderlinePos = underlinePos;
+ }
+
+ CGFloat lineThickness = CTFontGetUnderlineThickness(font);
+ if (lineThickness > maxLineThickness) {
+ maxLineThickness = lineThickness;
+ }
+ }
+ }
+ }
+
+ if (xHeight) {
+ *xHeight = maxXHeight;
+ }
+
+ if (underlinePosition) {
+ *underlinePosition = maxUnderlinePos;
+ }
+
+ if (lineThickness) {
+ *lineThickness = maxLineThickness;
+ }
+
+ CFRelease(runs);
+}
+
+NS_INLINE NSRange WXNSRangeFromCFRange(CFRange range) {
+ return NSMakeRange(range.location, range.length);
+}
+
+#ifdef UITEST
+- (NSString *)description
+{
+ return super.description;
+}
+#endif
+
+- (void)_resetCSSNodeStyles:(NSArray *)styles
+{
+ [super _resetCSSNodeStyles:styles];
+ if ([styles containsObject:@"color"]) {
+ _color = [UIColor blackColor];
+ [self setNeedsRepaint];
+ }
+ if ([styles containsObject:@"fontSize"]) {
+ _fontSize = WX_TEXT_FONT_SIZE;
+ [self setNeedsRepaint];
+ [self setNeedsLayout];
+ }
+}
+
+@end
+
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m b/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
index dad372c..e2d35d6 100644
--- a/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
+++ b/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
@@ -38,6 +38,8 @@
#import "WXUtility.h"
#import "WXExtendCallNativeManager.h"
#import "WXExceptionUtils.h"
+#import "WXConfigCenterProtocol.h"
+#import "WXComponent+Layout.h"
@implementation WXSDKEngine
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.h b/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.h
index 1baa20f..651af7c 100644
--- a/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.h
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.h
@@ -20,7 +20,65 @@
#import "WXComponent.h"
#import "WXSDKInstance.h"
#import "WXUtility.h"
+#import "WXLayoutDefine.h"
+#import "WXCoreLayout.h"
-@interface WXComponent (Layout)
+#define FlexUndefined NAN
+//#define USE_FLEX
+
+
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ bool flexIsUndefined(float value);
+#ifdef __cplusplus
+}
+#endif
+@interface WXComponent ()
+{
+ @package
+ /**
+ * Layout
+ */
+//#ifndef USE_FLEX
+ css_node_t *_cssNode;
+//#else
+#ifdef __cplusplus
+ WeexCore::WXCoreLayoutNode *_flexCssNode;
+#endif // __cplusplus
+//#endif //USE_FLEX
+ BOOL _isLayoutDirty;
+ CGRect _calculatedFrame;
+ CGPoint _absolutePosition;
+ WXPositionType _positionType;
+}
+
+//#ifndef USE_FLEX
+/**
+ * @abstract Return the css node used to layout.
+ *
+ * @warning Subclasses must not override this.
+ */
+@property(nonatomic, readonly, assign) css_node_t *cssNode;
+//#else
+#ifdef __cplusplus
+@property(nonatomic, readonly, assign) WeexCore::WXCoreLayoutNode *flexCssNode;
+#endif
+//#endif
+
+@end
+
+@interface WXComponent (Layout)
+//#ifndef USE_FLEX
+//#else
+- (void)_insertChildCssNode:(WXComponent*)subcomponent atIndex:(NSInteger)index;
+- (void)_rmChildCssNode:(WXComponent*)subcomponent;
+- (NSInteger) getActualNodeIndex:(WXComponent*)subcomponent atIndex:(NSInteger) index;
++ (void) setUseFlex:(BOOL) useFlex;
++ (BOOL) isUseFlex;
+//#endif
@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.mm b/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.mm
new file mode 100644
index 0000000..4bdb8f4
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.mm
@@ -0,0 +1,963 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#import "WXComponent+Layout.h"
+#import "WXComponent_internal.h"
+#import "WXTransform.h"
+#import "WXAssert.h"
+#import "WXSDKInstance_private.h"
+#import "WXComponent+BoxShadow.h"
+#import "WXLog.h"
+
+bool flexIsUndefined(float value) {
+ return isnan(value);
+}
+
+static BOOL sUseFlex = TRUE;
+
+
+@implementation WXComponent (Layout)
+
+#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+
+#pragma mark Public
+
+- (void)setNeedsLayout
+{
+ _isLayoutDirty = YES;
+ WXComponent *supercomponent = [self supercomponent];
+ if ([WXComponent isUseFlex]) {
+ //protet nil ptr
+ if (self.flexCssNode) {
+ self.flexCssNode->markDirty();
+ }else{
+ WXLogError(@"flexCssNode is nil");
+ }
+ }
+
+ if(supercomponent){
+ [supercomponent setNeedsLayout];
+ }
+}
+
+- (BOOL)needsLayout
+{
+ return _isLayoutDirty;
+}
+
+- (CGSize (^)(CGSize))measureBlock
+{
+ return nil;
+}
+
+- (void)layoutDidFinish
+{
+ WXAssertMainThread();
+}
+
+#pragma mark Private
+
+- (void)_initCSSNodeWithStyles:(NSDictionary *)styles
+{
+//#ifndef USE_FLEX
+ if (! [WXComponent isUseFlex]) {
+ _cssNode = new_css_node();
+
+ _cssNode->print = cssNodePrint;
+ _cssNode->get_child = cssNodeGetChild;
+ _cssNode->is_dirty = cssNodeIsDirty;
+ if ([self measureBlock]) {
+ _cssNode->measure = cssNodeMeasure;
+ }
+ _cssNode->context = (__bridge void *)self;
+
+ [self _recomputeCSSNodeChildren];
+ [self _fillCSSNode:styles isUpdate:NO];
+
+ // To be in conformity with Android/Web, hopefully remove this in the future.
+ if ([self.ref isEqualToString:WX_SDK_ROOT_REF]) {
+ if (isUndefined(_cssNode->style.dimensions[CSS_HEIGHT]) && self.weexInstance.frame.size.height) {
+ _cssNode->style.dimensions[CSS_HEIGHT] = self.weexInstance.frame.size.height;
+ }
+
+ if (isUndefined(_cssNode->style.dimensions[CSS_WIDTH]) && self.weexInstance.frame.size.width) {
+ _cssNode->style.dimensions[CSS_WIDTH] = self.weexInstance.frame.size.width;
+ }
+ }
+ }
+
+//#else
+ else
+ {
+ _flexCssNode = new WeexCore::WXCoreLayoutNode();
+ if ([self measureBlock]) {
+ _flexCssNode->setMeasureFunc(flexCssNodeMeasure);
+ }
+ _flexCssNode->setContext((__bridge void *)self);
+ [self _recomputeCSSNodeChildren];
+ [self _fillCSSNode:styles isUpdate:NO];
+
+ if ([self.ref isEqualToString:WX_SDK_ROOT_REF]) {
+ if (flexIsUndefined(_flexCssNode->getStyleHeight()) && self.weexInstance.frame.size.height) {
+ _flexCssNode->setStyleHeight(self.weexInstance.frame.size.height);
+ }
+
+ if (flexIsUndefined(_flexCssNode->getStyleWidth()) && self.weexInstance.frame.size.width) {
+ _flexCssNode->setStyleWidth(self.weexInstance.frame.size.width,NO);
+ }
+ }
+ }
+//#endif
+}
+
+- (void)_updateCSSNodeStyles:(NSDictionary *)styles
+{
+ [self _fillCSSNode:styles isUpdate:YES];
+}
+
+-(void)_resetCSSNodeStyles:(NSArray *)styles
+{
+ [self _resetCSSNode:styles];
+}
+
+- (void)_recomputeCSSNodeChildren
+{
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex]) {
+ _cssNode->children_count = (int)[self _childrenCountForLayout];
+ }
+//#else
+
+//#endif
+}
+
+- (NSUInteger)_childrenCountForLayout
+{
+ NSArray *subcomponents = _subcomponents;
+ NSUInteger count = subcomponents.count;
+ for (WXComponent *component in subcomponents) {
+ if (!component->_isNeedJoinLayoutSystem) {
+ count--;
+ }
+ }
+ return (int)(count);
+}
+
+
+- (void)_frameDidCalculated:(BOOL)isChanged
+{
+ WXAssertComponentThread();
+
+ if ([self isViewLoaded] && isChanged && [self isViewFrameSyncWithCalculated]) {
+
+ __weak typeof(self) weakSelf = self;
+ [self.weexInstance.componentManager _addUITask:^{
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ if (strongSelf->_transform && !CATransform3DEqualToTransform(strongSelf.layer.transform, CATransform3DIdentity)) {
+ // From the UIView's frame documentation:
+ // https://developer.apple.com/reference/uikit/uiview#//apple_ref/occ/instp/UIView/frame
+ // Warning : If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
+ // So layer's transform must be reset to CATransform3DIdentity before setFrame, otherwise frame will be incorrect
+ strongSelf.layer.transform = CATransform3DIdentity;
+ }
+
+ if (!CGRectEqualToRect(strongSelf.view.frame,strongSelf.calculatedFrame)) {
+ strongSelf.view.frame = strongSelf.calculatedFrame;
+ strongSelf->_absolutePosition = CGPointMake(NAN, NAN);
+ [strongSelf configBoxShadow:_boxShadow];
+ } else {
+ if (![strongSelf equalBoxShadow:_boxShadow withBoxShadow:_lastBoxShadow]) {
+ [strongSelf configBoxShadow:_boxShadow];
+ }
+ }
+
+ [self _resetNativeBorderRadius];
+
+ if (strongSelf->_transform) {
+ [strongSelf->_transform applyTransformForView:strongSelf.view];
+ }
+
+ if (strongSelf->_backgroundImage) {
+ [strongSelf setGradientLayer];
+ }
+ [strongSelf setNeedsDisplay];
+ }];
+ }
+}
+
+- (void)_calculateFrameWithSuperAbsolutePosition:(CGPoint)superAbsolutePosition
+ gatherDirtyComponents:(NSMutableSet<WXComponent *> *)dirtyComponents
+{
+ WXAssertComponentThread();
+
+//#ifndef USE_FLEX
+ if (![WXComponent isUseFlex])
+ {
+ if (!_cssNode->layout.should_update) {
+ return;
+ }
+ _cssNode->layout.should_update = false;
+ _isLayoutDirty = NO;
+
+ CGRect newFrame = CGRectMake(isnan(WXRoundPixelValue(_cssNode->layout.position[CSS_LEFT]))?0:WXRoundPixelValue(_cssNode->layout.position[CSS_LEFT]),
+ isnan(WXRoundPixelValue(_cssNode->layout.position[CSS_TOP]))?0:WXRoundPixelValue(_cssNode->layout.position[CSS_TOP]),
+ isnan(WXRoundPixelValue(_cssNode->layout.dimensions[CSS_WIDTH]))?0:WXRoundPixelValue(_cssNode->layout.dimensions[CSS_WIDTH]),
+ isnan(WXRoundPixelValue(_cssNode->layout.dimensions[CSS_HEIGHT]))?0:WXRoundPixelValue(_cssNode->layout.dimensions[CSS_HEIGHT]));
+
+ BOOL isFrameChanged = NO;
+ if (!CGRectEqualToRect(newFrame, _calculatedFrame)) {
+ isFrameChanged = YES;
+ _calculatedFrame = newFrame;
+ [dirtyComponents addObject:self];
+ }
+
+ _cssNode->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
+ _cssNode->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
+ _cssNode->layout.position[CSS_LEFT] = 0;
+ _cssNode->layout.position[CSS_TOP] = 0;
+
+ [self _frameDidCalculated:isFrameChanged];
+ NSArray * subcomponents = [_subcomponents copy];
+ for (WXComponent *subcomponent in subcomponents) {
+ [subcomponent _calculateFrameWithSuperAbsolutePosition:superAbsolutePosition gatherDirtyComponents:dirtyComponents];
+ }
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> newFrame ,type:%@,ref:%@, parentRef:%@,size :%@ ,instance:%@",self.type,self.ref,self.supercomponent.ref,NSStringFromCGRect(newFrame),self.weexInstance.instanceId);
+#endif
+ }
+
+//#else
+ else
+ {
+ if (self.flexCssNode->hasNewLayout()) {
+ self.flexCssNode->setHasNewLayout(false);
+ _isLayoutDirty = NO;
+ CGRect newFrame = CGRectMake(
+ isnan(WXRoundPixelValue(_flexCssNode->getLayoutPositionLeft()))?0:WXRoundPixelValue(_flexCssNode->getLayoutPositionLeft())
+ ,isnan(WXRoundPixelValue(_flexCssNode->getLayoutPositionTop()))?0:WXRoundPixelValue(_flexCssNode->getLayoutPositionTop())
+ ,isnan(WXRoundPixelValue(_flexCssNode->getLayoutWidth()))?0:WXRoundPixelValue(_flexCssNode->getLayoutWidth())
+ ,isnan(WXRoundPixelValue(_flexCssNode->getLayoutHeight()))?0:WXRoundPixelValue(_flexCssNode->getLayoutHeight())
+ );
+ BOOL isFrameChanged = NO;
+
+ if (!CGRectEqualToRect(newFrame, _calculatedFrame)) {
+
+ isFrameChanged = YES;
+ _calculatedFrame = newFrame;
+ [dirtyComponents addObject:self];
+ }
+
+ [self _frameDidCalculated:isFrameChanged];
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> newFrame ,type:%@,ref:%@, parentRef:%@,size :%@ ,instance:%@",self.type,self.ref,self.supercomponent.ref,NSStringFromCGRect(newFrame),self.weexInstance.instanceId);
+#endif
+ }
+
+ NSArray * subcomponents = [_subcomponents copy];
+ for (WXComponent *subcomponent in subcomponents) {
+ [subcomponent _calculateFrameWithSuperAbsolutePosition:superAbsolutePosition gatherDirtyComponents:dirtyComponents];
+ }
+ }
+//#endif
+}
+
+- (void)_layoutDidFinish
+{
+ WXAssertMainThread();
+
+ if (_positionType == WXPositionTypeSticky) {
+ [self.ancestorScroller adjustSticky];
+ }
+ [self layoutDidFinish];
+}
+
+//#ifndef USE_FLEX
+
+#define WX_STYLE_FILL_CSS_NODE(key, cssProp, type)\
+do {\
+ id value = styles[@#key];\
+ if (value) {\
+ typeof(_cssNode->style.cssProp) convertedValue = (typeof(_cssNode->style.cssProp))[WXConvert type:value];\
+ _cssNode->style.cssProp = convertedValue;\
+ [self setNeedsLayout];\
+ }\
+} while(0);
+
+#define WX_STYLE_FILL_CSS_NODE_PIXEL(key, cssProp)\
+do {\
+ id value = styles[@#key];\
+ if (value) {\
+ CGFloat pixel = [self WXPixelType:value];\
+ if (isnan(pixel)) {\
+ WXLogError(@"Invalid NaN value for style:%@, ref:%@", @#key, self.ref);\
+ } else {\
+ _cssNode->style.cssProp = pixel;\
+ [self setNeedsLayout];\
+ }\
+ }\
+} while(0);
+
+#define WX_STYLE_FILL_CSS_NODE_ALL_DIRECTION(key, cssProp)\
+do {\
+ WX_STYLE_FILL_CSS_NODE_PIXEL(key, cssProp[CSS_TOP])\
+ WX_STYLE_FILL_CSS_NODE_PIXEL(key, cssProp[CSS_LEFT])\
+ WX_STYLE_FILL_CSS_NODE_PIXEL(key, cssProp[CSS_RIGHT])\
+ WX_STYLE_FILL_CSS_NODE_PIXEL(key, cssProp[CSS_BOTTOM])\
+} while(0);
+
+//#else
+
+#define WX_STYLE_FLEX_NODE_JUDGE_LEGAL(key) styles[key]&&!isnan([WXConvert WXPixelType:styles[key] scaleFactor:self.weexInstance.pixelScaleFactor])
+
+//#endif
+
+- (CGFloat)WXPixelType:(id)value
+{
+ return [WXConvert WXPixelType:value scaleFactor:self.weexInstance.pixelScaleFactor];
+}
+
+- (void)_fillCSSNode:(NSDictionary *)styles isUpdate:(BOOL)isUpdate
+{
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ WX_STYLE_FILL_CSS_NODE(direction, direction, css_direction_t)
+ // flex
+ WX_STYLE_FILL_CSS_NODE(flex, flex, CGFloat)
+ WX_STYLE_FILL_CSS_NODE(flexDirection, flex_direction, css_flex_direction_t)
+ WX_STYLE_FILL_CSS_NODE(alignItems, align_items, css_align_t)
+ WX_STYLE_FILL_CSS_NODE(alignSelf, align_self, css_align_t)
+ WX_STYLE_FILL_CSS_NODE(flexWrap, flex_wrap, css_wrap_type_t)
+ WX_STYLE_FILL_CSS_NODE(justifyContent, justify_content, css_justify_t)
+
+ // position
+ WX_STYLE_FILL_CSS_NODE(position, position_type, css_position_type_t)
+ WX_STYLE_FILL_CSS_NODE_PIXEL(top, position[CSS_TOP])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(left, position[CSS_LEFT])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(right, position[CSS_RIGHT])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(bottom, position[CSS_BOTTOM])
+
+ // dimension
+ WX_STYLE_FILL_CSS_NODE_PIXEL(width, dimensions[CSS_WIDTH])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(height, dimensions[CSS_HEIGHT])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(minWidth, minDimensions[CSS_WIDTH])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(minHeight, minDimensions[CSS_HEIGHT])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(maxWidth, maxDimensions[CSS_WIDTH])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(maxHeight, maxDimensions[CSS_HEIGHT])
+
+ // margin
+ WX_STYLE_FILL_CSS_NODE_ALL_DIRECTION(margin, margin)
+ WX_STYLE_FILL_CSS_NODE_PIXEL(marginTop, margin[CSS_TOP])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(marginLeft, margin[CSS_LEFT])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(marginRight, margin[CSS_RIGHT])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(marginBottom, margin[CSS_BOTTOM])
+
+ // border
+ WX_STYLE_FILL_CSS_NODE_ALL_DIRECTION(borderWidth, border)
+ WX_STYLE_FILL_CSS_NODE_PIXEL(borderTopWidth, border[CSS_TOP])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(borderLeftWidth, border[CSS_LEFT])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(borderRightWidth, border[CSS_RIGHT])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(borderBottomWidth, border[CSS_BOTTOM])
+
+ // padding
+ WX_STYLE_FILL_CSS_NODE_ALL_DIRECTION(padding, padding)
+ WX_STYLE_FILL_CSS_NODE_PIXEL(paddingTop, padding[CSS_TOP])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(paddingLeft, padding[CSS_LEFT])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(paddingRight, padding[CSS_RIGHT])
+ WX_STYLE_FILL_CSS_NODE_PIXEL(paddingBottom, padding[CSS_BOTTOM])
+ }
+
+//#else
+ else
+ {
+ // flex
+ if (styles[@"flex"]) {
+ _flexCssNode->setFlex([WXConvert CGFloat:styles[@"flex"]]);
+ }
+ if (isnan(_flexCssNode->getFlex())) {
+ // to make the default flex value is zero, yoga is nan, maybe this can configured by yoga config
+ _flexCssNode->setFlex(0);
+ }
+
+ if (styles[@"flexDirection"]) {
+ _flexCssNode->setFlexDirection([self fxFlexDirection:styles[@"flexDirection"]],isUpdate);
+ }
+ if (styles[@"alignItems"]) {
+ _flexCssNode->setAlignItems([self fxAlign:styles[@"alignItems"]]);
+ }
+ if (styles[@"alignSelf"]) {
+ _flexCssNode->setAlignSelf([self fxAlignSelf:styles[@"alignSelf"]]);
+ }
+ if (styles[@"flexWrap"]) {
+ _flexCssNode->setFlexWrap([self fxWrap:styles[@"flexWrap"]]);
+ }
+ if (styles[@"justifyContent"]) {
+ _flexCssNode->setJustifyContent([self fxJustify:styles[@"justifyContent"]]);
+ }
+
+ // position
+ if (styles[@"position"]) {
+ _flexCssNode->setStylePositionType([self fxPositionType:styles[@"position"]]);
+ }
+ if (styles[@"top"]) {
+ _flexCssNode->setStylePosition(WeexCore::kPositionEdgeTop,
+ [self judgePropValuePropValue:styles[@"top"] defaultValue:NAN]);
+ }
+ if (styles[@"left"]) {
+ _flexCssNode->setStylePosition(WeexCore::kPositionEdgeLeft,
+ [self judgePropValuePropValue:styles[@"left"] defaultValue:NAN]);
+ }
+ if(styles[@"right"]) {
+ _flexCssNode->setStylePosition(WeexCore::kPositionEdgeRight,
+ [self judgePropValuePropValue:styles[@"right"] defaultValue:NAN]);
+ }
+ if (styles[@"bottom"]) {
+ _flexCssNode->setStylePosition(WeexCore::kPositionEdgeBottom,
+ [self judgePropValuePropValue:styles[@"bottom"] defaultValue:NAN]);
+ }
+
+ // dimension
+ if (styles[@"width"]) {
+ _flexCssNode->setStyleWidth([self judgePropValuePropValue:styles[@"width"] defaultValue:NAN]
+ ,isUpdate);
+ }
+ if (styles[@"height"]) {
+ _flexCssNode->setStyleHeight([self judgePropValuePropValue:styles[@"height"] defaultValue:NAN]);
+ }
+ if (styles[@"minWidth"]) {
+ _flexCssNode->setMinWidth([self judgePropValuePropValue:styles[@"minWidth"] defaultValue:NAN]
+ ,isUpdate);
+ }
+ if (styles[@"minHeight"]) {
+ _flexCssNode->setMinHeight([self judgePropValuePropValue:styles[@"minHeight"] defaultValue:NAN]);
+ }
+ if (styles[@"maxWidth"]) {
+ _flexCssNode->setMaxWidth([self judgePropValuePropValue:styles[@"maxWidth"] defaultValue:NAN]
+ ,isUpdate);
+ }
+ if (styles[@"maxHeight"]) {
+ _flexCssNode->setMaxHeight([self judgePropValuePropValue:styles[@"maxHeight"] defaultValue:NAN]);
+ }
+
+ // margin
+ if (styles[@"margin"]) {
+ _flexCssNode->setMargin(WeexCore::kMarginALL,
+ [self judgePropValuePropValue:styles[@"margin"] defaultValue:0]);
+ }
+ if (styles[@"marginTop"]) {
+ _flexCssNode->setMargin(WeexCore::kMarginTop,
+ [self judgePropValuePropValue:styles[@"marginTop"] defaultValue:0]);
+ }
+ if (styles[@"marginBottom"]) {
+ _flexCssNode->setMargin(WeexCore::kMarginBottom,
+ [self judgePropValuePropValue:styles[@"marginBottom"] defaultValue:0]);
+ }
+ if (styles[@"marginRight"]) {
+ _flexCssNode->setMargin(WeexCore::kMarginRight,
+ [self judgePropValuePropValue:styles[@"marginRight"] defaultValue:0]);
+ }
+ if (styles[@"marginLeft"]) {
+ _flexCssNode->setMargin(WeexCore::kMarginLeft,
+ [self judgePropValuePropValue:styles[@"marginLeft"] defaultValue:0]);
+ }
+
+ // border
+ if (styles[@"borderWidth"]) {
+ _flexCssNode->setBorderWidth(WeexCore::kBorderWidthALL,
+ [self judgePropValuePropValue:styles[@"borderWidth"] defaultValue:0]);
+ }
+ if (styles[@"borderTopWidth"]) {
+ _flexCssNode->setBorderWidth(WeexCore::kBorderWidthTop,
+ [self judgePropValuePropValue:styles[@"borderTopWidth"] defaultValue:0]);
+ }
+
+ if (styles[@"borderLeftWidth"]) {
+ _flexCssNode->setBorderWidth(WeexCore::kBorderWidthLeft,
+ [self judgePropValuePropValue:styles[@"borderLeftWidth"] defaultValue:0]);
+ }
+
+ if (styles[@"borderBottomWidth"]) {
+ _flexCssNode->setBorderWidth(WeexCore::kBorderWidthBottom,
+ [self judgePropValuePropValue:styles[@"borderBottomWidth"] defaultValue:0]);
+ }
+ if (styles[@"borderRightWidth"]) {
+ _flexCssNode->setBorderWidth(WeexCore::kBorderWidthRight,
+ [self judgePropValuePropValue:styles[@"borderRightWidth"] defaultValue:0]);
+ }
+
+ // padding
+ if (styles[@"padding"]) {
+ _flexCssNode->setPadding(WeexCore::kPaddingALL,
+ [self judgePropValuePropValue:styles[@"padding"] defaultValue:0]);
+ }
+ if (styles[@"paddingTop"]) {
+ _flexCssNode->setPadding(WeexCore::kPaddingTop,
+ [self judgePropValuePropValue:styles[@"paddingTop"] defaultValue:0]);
+ }
+ if (styles[@"paddingLeft"]) {
+ _flexCssNode->setPadding(WeexCore::kPaddingLeft,
+ [self judgePropValuePropValue:styles[@"paddingLeft"] defaultValue:0]);
+ }
+ if (styles[@"paddingBottom"]) {
+ _flexCssNode->setPadding(WeexCore::kPaddingBottom,
+ [self judgePropValuePropValue:styles[@"paddingBottom"] defaultValue:0]);
+ }
+ if (styles[@"paddingRight"]) {
+ _flexCssNode->setPadding(WeexCore::kPaddingRight,
+ [self judgePropValuePropValue:styles[@"paddingRight"] defaultValue:0]);
+ }
+
+ [self setNeedsLayout];
+ }
+
+//#endif
+}
+
+-(CGFloat)judgePropValuePropValue:(NSString *)propValue defaultValue:(CGFloat)defaultValue{
+ CGFloat convertValue = (CGFloat)[WXConvert WXPixelType:propValue scaleFactor:self.weexInstance.pixelScaleFactor];
+ if (!isnan(convertValue)) {
+ return convertValue;
+ }
+ return defaultValue;
+}
+
+//#ifndef USE_FLEX
+
+#define WX_STYLE_RESET_CSS_NODE(key, cssProp, defaultValue)\
+do {\
+ if (styles && [styles containsObject:@#key]) {\
+ _cssNode->style.cssProp = defaultValue;\
+ [self setNeedsLayout];\
+ }\
+} while(0);
+
+#define WX_STYLE_RESET_CSS_NODE_ALL_DIRECTION(key, cssProp, defaultValue)\
+do {\
+ WX_STYLE_RESET_CSS_NODE(key, cssProp[CSS_TOP], defaultValue)\
+ WX_STYLE_RESET_CSS_NODE(key, cssProp[CSS_LEFT], defaultValue)\
+ WX_STYLE_RESET_CSS_NODE(key, cssProp[CSS_RIGHT], defaultValue)\
+ WX_STYLE_RESET_CSS_NODE(key, cssProp[CSS_BOTTOM], defaultValue)\
+} while(0);
+
+//#else
+
+#define WX_FLEX_STYLE_RESET_CSS_NODE(key, defaultValue)\
+do {\
+ WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_KEY(key,key,defaultValue)\
+} while(0);
+
+#define WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_KEY(judgeKey, propKey, defaultValue)\
+do {\
+ if (styles && [styles containsObject:@#judgeKey]) {\
+ NSMutableDictionary *resetStyleDic = [[NSMutableDictionary alloc] init];\
+ [resetStyleDic setValue:defaultValue forKey:@#propKey];\
+ [self _updateCSSNodeStyles:resetStyleDic];\
+ [self setNeedsLayout];\
+ }\
+} while(0);
+
+#define WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_DIRECTION_KEY(judgeKey, propTopKey,propLeftKey,propRightKey,propBottomKey, defaultValue)\
+do {\
+ if (styles && [styles containsObject:@#judgeKey]) {\
+ NSMutableDictionary *resetStyleDic = [[NSMutableDictionary alloc] init];\
+ [resetStyleDic setValue:defaultValue forKey:@#propTopKey];\
+ [resetStyleDic setValue:defaultValue forKey:@#propLeftKey];\
+ [resetStyleDic setValue:defaultValue forKey:@#propRightKey];\
+ [resetStyleDic setValue:defaultValue forKey:@#propBottomKey];\
+ [self _updateCSSNodeStyles:resetStyleDic];\
+ [self setNeedsLayout];\
+ }\
+} while(0);
+
+//#endif
+
+
+- (void)_resetCSSNode:(NSArray *)styles
+{
+//#ifndef USE_FLEX
+ if(![WXComponent isUseFlex])
+ {
+ WX_STYLE_RESET_CSS_NODE(direction, direction, CSS_DIRECTION_LTR)
+ // flex
+ WX_STYLE_RESET_CSS_NODE(flex, flex, 0.0)
+ WX_STYLE_RESET_CSS_NODE(flexDirection, flex_direction, CSS_FLEX_DIRECTION_COLUMN)
+ WX_STYLE_RESET_CSS_NODE(alignItems, align_items, CSS_ALIGN_STRETCH)
+ WX_STYLE_RESET_CSS_NODE(alignSelf, align_self, CSS_ALIGN_AUTO)
+ WX_STYLE_RESET_CSS_NODE(flexWrap, flex_wrap, CSS_NOWRAP)
+ WX_STYLE_RESET_CSS_NODE(justifyContent, justify_content, CSS_JUSTIFY_FLEX_START)
+
+ // position
+ WX_STYLE_RESET_CSS_NODE(position, position_type, CSS_POSITION_RELATIVE)
+ WX_STYLE_RESET_CSS_NODE(top, position[CSS_TOP], CSS_UNDEFINED)
+ WX_STYLE_RESET_CSS_NODE(left, position[CSS_LEFT], CSS_UNDEFINED)
+ WX_STYLE_RESET_CSS_NODE(right, position[CSS_RIGHT], CSS_UNDEFINED)
+ WX_STYLE_RESET_CSS_NODE(bottom, position[CSS_BOTTOM], CSS_UNDEFINED)
+
+ // dimension
+ WX_STYLE_RESET_CSS_NODE(width, dimensions[CSS_WIDTH], CSS_UNDEFINED)
+ WX_STYLE_RESET_CSS_NODE(height, dimensions[CSS_HEIGHT], CSS_UNDEFINED)
+ WX_STYLE_RESET_CSS_NODE(minWidth, minDimensions[CSS_WIDTH], CSS_UNDEFINED)
+ WX_STYLE_RESET_CSS_NODE(minHeight, minDimensions[CSS_HEIGHT], CSS_UNDEFINED)
+ WX_STYLE_RESET_CSS_NODE(maxWidth, maxDimensions[CSS_WIDTH], CSS_UNDEFINED)
+ WX_STYLE_RESET_CSS_NODE(maxHeight, maxDimensions[CSS_HEIGHT], CSS_UNDEFINED)
+
+ // margin
+ WX_STYLE_RESET_CSS_NODE_ALL_DIRECTION(margin, margin, 0.0)
+ WX_STYLE_RESET_CSS_NODE(marginTop, margin[CSS_TOP], 0.0)
+ WX_STYLE_RESET_CSS_NODE(marginLeft, margin[CSS_LEFT], 0.0)
+ WX_STYLE_RESET_CSS_NODE(marginRight, margin[CSS_RIGHT], 0.0)
+ WX_STYLE_RESET_CSS_NODE(marginBottom, margin[CSS_BOTTOM], 0.0)
+
+ // border
+ WX_STYLE_RESET_CSS_NODE_ALL_DIRECTION(borderWidth, border, 0.0)
+ WX_STYLE_RESET_CSS_NODE(borderTopWidth, border[CSS_TOP], 0.0)
+ WX_STYLE_RESET_CSS_NODE(borderLeftWidth, border[CSS_LEFT], 0.0)
+ WX_STYLE_RESET_CSS_NODE(borderRightWidth, border[CSS_RIGHT], 0.0)
+ WX_STYLE_RESET_CSS_NODE(borderBottomWidth, border[CSS_BOTTOM], 0.0)
+
+ // padding
+ WX_STYLE_RESET_CSS_NODE_ALL_DIRECTION(padding, padding, 0.0)
+ WX_STYLE_RESET_CSS_NODE(paddingTop, padding[CSS_TOP], 0.0)
+ WX_STYLE_RESET_CSS_NODE(paddingLeft, padding[CSS_LEFT], 0.0)
+ WX_STYLE_RESET_CSS_NODE(paddingRight, padding[CSS_RIGHT], 0.0)
+ WX_STYLE_RESET_CSS_NODE(paddingBottom, padding[CSS_BOTTOM], 0.0)
+ }
+
+//#else
+ else
+ {
+ if (styles.count<=0) {
+ return;
+ }
+
+ WX_FLEX_STYLE_RESET_CSS_NODE(flex, @0.0)
+ WX_FLEX_STYLE_RESET_CSS_NODE(flexDirection, @(CSS_FLEX_DIRECTION_COLUMN))
+ WX_FLEX_STYLE_RESET_CSS_NODE(alignItems, @(CSS_ALIGN_STRETCH))
+ WX_FLEX_STYLE_RESET_CSS_NODE(alignSelf, @(CSS_ALIGN_AUTO))
+ WX_FLEX_STYLE_RESET_CSS_NODE(flexWrap, @(CSS_NOWRAP))
+ WX_FLEX_STYLE_RESET_CSS_NODE(justifyContent, @(CSS_JUSTIFY_FLEX_START))
+
+ // position
+ WX_FLEX_STYLE_RESET_CSS_NODE(position, @(CSS_POSITION_RELATIVE))
+ WX_FLEX_STYLE_RESET_CSS_NODE(top, @(CSS_UNDEFINED))
+ WX_FLEX_STYLE_RESET_CSS_NODE(left, @(CSS_UNDEFINED))
+ WX_FLEX_STYLE_RESET_CSS_NODE(right, @(CSS_UNDEFINED))
+ WX_FLEX_STYLE_RESET_CSS_NODE(bottom, @(CSS_UNDEFINED))
+
+ // dimension
+ WX_FLEX_STYLE_RESET_CSS_NODE(width, @(CSS_UNDEFINED))
+ WX_FLEX_STYLE_RESET_CSS_NODE(height, @(CSS_UNDEFINED))
+ WX_FLEX_STYLE_RESET_CSS_NODE(minWidth, @(CSS_UNDEFINED))
+ WX_FLEX_STYLE_RESET_CSS_NODE(minHeight, @(CSS_UNDEFINED))
+ WX_FLEX_STYLE_RESET_CSS_NODE(maxWidth, @(CSS_UNDEFINED))
+ WX_FLEX_STYLE_RESET_CSS_NODE(maxHeight, @(CSS_UNDEFINED))
+
+ // margin
+ WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_DIRECTION_KEY(margin
+ ,marginTop
+ ,marginLeft
+ ,marginRight
+ ,marginBottom
+ , @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(marginTop, @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(marginLeft, @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(marginRight, @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(marginBottom, @(0.0))
+
+ // border
+ WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_DIRECTION_KEY(borderWidth
+ , borderTopWidth
+ , borderLeftWidth
+ , borderRightWidth
+ , borderBottomWidth
+ , @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(borderTopWidth, @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(borderLeftWidth, @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(borderRightWidth, @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(borderBottomWidth, @(0.0))
+
+ // padding
+ WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_DIRECTION_KEY(padding
+ , paddingTop
+ , paddingLeft
+ , paddingRight
+ , paddingBottom
+ , @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(paddingTop, @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(paddingLeft, @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(paddingRight, @(0.0))
+ WX_FLEX_STYLE_RESET_CSS_NODE(paddingBottom, @(0.0))
+ }
+
+
+//#endif
+}
+
+#pragma mark CSS Node Override
+
+//#ifndef USE_FLEX
+#if defined __cplusplus
+extern "C" {
+#endif
+static void cssNodePrint(void *context)
+{
+ WXComponent *component = (__bridge WXComponent *)context;
+ // TODO:
+ printf("%s:%s ", component.ref.UTF8String, component->_type.UTF8String);
+}
+
+static css_node_t * cssNodeGetChild(void *context, int i)
+{
+ WXComponent *component = (__bridge WXComponent *)context;
+ NSArray *subcomponents = component->_subcomponents;
+ for (int j = 0; j <= i && j < subcomponents.count; j++) {
+ WXComponent *child = subcomponents[j];
+ if (!child->_isNeedJoinLayoutSystem) {
+ i++;
+ }
+ }
+
+ if(i >= 0 && i < subcomponents.count){
+ WXComponent *child = subcomponents[i];
+// WXLogInfo(@"FlexLayout -- P:%@ -> C:%@",component,(__bridge WXComponent *)child->_cssNode->context);
+ return child->_cssNode;
+ }
+
+ WXAssert(NO, @"Can not find component:%@'s css node child at index: %ld, totalCount:%ld", component, i, subcomponents.count);
+ return NULL;
+}
+
+static bool cssNodeIsDirty(void *context)
+{
+ WXAssertComponentThread();
+
+ WXComponent *component = (__bridge WXComponent *)context;
+ BOOL needsLayout = [component needsLayout];
+
+ return needsLayout;
+}
+
+static css_dim_t cssNodeMeasure(void *context, float width, css_measure_mode_t widthMode, float height, css_measure_mode_t heightMode)
+{
+ WXComponent *component = (__bridge WXComponent *)context;
+ CGSize (^measureBlock)(CGSize) = [component measureBlock];
+
+ if (!measureBlock) {
+ return (css_dim_t){NAN, NAN};
+ }
+
+ CGSize constrainedSize = CGSizeMake(width, height);
+ CGSize resultSize = measureBlock(constrainedSize);
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> measureblock %@, resultSize:%@",
+ component.type,
+ NSStringFromCGSize(resultSize)
+ );
+#endif
+
+ return (css_dim_t){(float)resultSize.width, (float)resultSize.height};
+}
+#if defined __cplusplus
+};
+#endif
+
+//#else
+
+static WeexCore::WXCoreSize flexCssNodeMeasure(WeexCore::WXCoreLayoutNode *node, float width, WeexCore::MeasureMode widthMeasureMode,float height, WeexCore::MeasureMode heightMeasureMode){
+
+ if (node->getContext() == nullptr) { //为空
+ return WeexCore::WXCoreSize();
+ }
+ WXComponent *component = (__bridge WXComponent *)(node->getContext());
+
+ if (![component respondsToSelector:@selector(measureBlock)]) {
+ return WeexCore::WXCoreSize();
+ }
+
+ CGSize (^measureBlock)(CGSize) = [component measureBlock];
+
+ if (!measureBlock) {
+ return WeexCore::WXCoreSize();
+ }
+
+ CGSize constrainedSize = CGSizeMake(width, height);
+ CGSize resultSize = measureBlock(constrainedSize);
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> measureblock %@, resultSize:%@",
+ component.type,
+ NSStringFromCGSize(resultSize)
+ );
+#endif
+ WeexCore::WXCoreSize size;
+ size.height=(float)resultSize.height;
+ size.width=(float)resultSize.width;
+ return size;
+}
+
+-(WeexCore::WXCorePositionType)fxPositionType:(id)value
+{
+ if([value isKindOfClass:[NSString class]]){
+ if ([value isEqualToString:@"absolute"]) {
+ return WeexCore::kAbsolute;
+ } else if ([value isEqualToString:@"relative"]) {
+ return WeexCore::kRelative;
+ } else if ([value isEqualToString:@"fixed"]) {
+ return WeexCore::kFixed;
+ } else if ([value isEqualToString:@"sticky"]) {
+ return WeexCore::kRelative;
+ }
+ }
+ return WeexCore::kRelative;
+}
+
+- (WeexCore::WXCoreFlexDirection)fxFlexDirection:(id)value
+{
+ if([value isKindOfClass:[NSString class]]){
+ if ([value isEqualToString:@"column"]) {
+ return WeexCore::kFlexDirectionColumn;
+ } else if ([value isEqualToString:@"column-reverse"]) {
+ return WeexCore::kFlexDirectionColumnReverse;
+ } else if ([value isEqualToString:@"row"]) {
+ return WeexCore::kFlexDirectionRow;
+ } else if ([value isEqualToString:@"row-reverse"]) {
+ return WeexCore::kFlexDirectionRowReverse;
+ }
+ }
+ return WeexCore::kFlexDirectionColumn;
+}
+
+//TODO
+- (WeexCore::WXCoreAlignItems)fxAlign:(id)value
+{
+ if([value isKindOfClass:[NSString class]]){
+ if ([value isEqualToString:@"stretch"]) {
+ return WeexCore::kAlignItemsStretch;
+ } else if ([value isEqualToString:@"flex-start"]) {
+ return WeexCore::kAlignItemsFlexStart;
+ } else if ([value isEqualToString:@"flex-end"]) {
+ return WeexCore::kAlignItemsFlexEnd;
+ } else if ([value isEqualToString:@"center"]) {
+ return WeexCore::kAlignItemsCenter;
+ //return WXCoreFlexLayout::WXCore_AlignItems_Center;
+ } else if ([value isEqualToString:@"auto"]) {
+// return YGAlignAuto;
+ } else if ([value isEqualToString:@"baseline"]) {
+// return YGAlignBaseline;
+ }
+ }
+
+ return WeexCore::kAlignItemsStretch;
+}
+
+- (WeexCore::WXCoreAlignSelf)fxAlignSelf:(id)value
+{
+ if([value isKindOfClass:[NSString class]]){
+ if ([value isEqualToString:@"stretch"]) {
+ return WeexCore::kAlignSelfStretch;
+ } else if ([value isEqualToString:@"flex-start"]) {
+ return WeexCore::kAlignSelfFlexStart;
+ } else if ([value isEqualToString:@"flex-end"]) {
+ return WeexCore::kAlignSelfFlexEnd;
+ } else if ([value isEqualToString:@"center"]) {
+ return WeexCore::kAlignSelfCenter;
+ } else if ([value isEqualToString:@"auto"]) {
+ return WeexCore::kAlignSelfAuto;
+ } else if ([value isEqualToString:@"baseline"]) {
+ // return YGAlignBaseline;
+ }
+ }
+
+ return WeexCore::kAlignSelfStretch;
+}
+
+- (WeexCore::WXCoreFlexWrap)fxWrap:(id)value
+{
+ if([value isKindOfClass:[NSString class]]) {
+ if ([value isEqualToString:@"nowrap"]) {
+ return WeexCore::kNoWrap;
+ } else if ([value isEqualToString:@"wrap"]) {
+ return WeexCore::kWrap;
+ } else if ([value isEqualToString:@"wrap-reverse"]) {
+ return WeexCore::kWrapReverse;
+ }
+ }
+ return WeexCore::kNoWrap;
+}
+
+- (WeexCore::WXCoreJustifyContent)fxJustify:(id)value
+{
+ if([value isKindOfClass:[NSString class]]){
+ if ([value isEqualToString:@"flex-start"]) {
+ return WeexCore::kJustifyFlexStart;
+ } else if ([value isEqualToString:@"center"]) {
+ return WeexCore::kJustifyCenter;
+ } else if ([value isEqualToString:@"flex-end"]) {
+ return WeexCore::kJustifyFlexEnd;
+ } else if ([value isEqualToString:@"space-between"]) {
+ return WeexCore::kJustifySpaceBetween;
+ } else if ([value isEqualToString:@"space-around"]) {
+ return WeexCore::kJustifySpaceAround;
+ }
+ }
+ return WeexCore::kJustifyFlexStart;
+}
+
+
+- (NSInteger) getActualNodeIndex:(WXComponent*)subcomponent atIndex:(NSInteger) index
+{
+ NSInteger actualIndex = 0; //实际除去不需要布局的subComponent,此时所在的正确位置
+ for (WXComponent *child in _subcomponents) {
+ if ([child.ref isEqualToString:subcomponent.ref]) {
+ break;
+ }
+ if (child->_isNeedJoinLayoutSystem) {
+ actualIndex ++;
+ }
+ }
+ return actualIndex;
+}
+
+- (void)_insertChildCssNode:(WXComponent*)subcomponent atIndex:(NSInteger)index
+{
+ self.flexCssNode->addChildAt(subcomponent.flexCssNode, (uint32_t)index);
+}
+
+- (void)_rmChildCssNode:(WXComponent *)subcomponent
+{
+ self.flexCssNode->removeChild(subcomponent->_flexCssNode);
+#ifdef DEBUG
+ WXLogDebug(@"flexLayout -> ref:%@ ,flexCssNode->removeChild ,childRef:%@",self.ref,subcomponent.ref);
+#endif
+}
+
+
++ (void) setUseFlex:(BOOL)useFlex
+{
+ sUseFlex =useFlex;
+}
+
+
++ (BOOL) isUseFlex
+{
+ return sUseFlex;
+}
+
+//#endif
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Layout/WXCoreFlexEnum.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXCoreFlexEnum.h b/ios/sdk/WeexSDK/Sources/Layout/WXCoreFlexEnum.h
new file mode 100644
index 0000000..26fe8a3
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXCoreFlexEnum.h
@@ -0,0 +1,107 @@
+#ifdef __cplusplus
+
+#ifndef WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUM_H
+#define WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUM_H
+
+namespace WeexCore {
+
+ /**
+ * MainAxis direction
+ */
+ enum WXCoreFlexDirection {
+ kFlexDirectionColumn,
+ kFlexDirectionColumnReverse,
+ kFlexDirectionRow,
+ kFlexDirectionRowReverse,
+ };
+
+ /**
+ * Controls the position of the element on the MainAxis
+ */
+ enum WXCoreJustifyContent {
+ kJustifyFlexStart,
+ kJustifyCenter,
+ kJustifyFlexEnd,
+ kJustifySpaceBetween,
+ kJustifySpaceAround,
+ };
+
+ /**
+ * Controls the position of the element on the CrossAxis and whether Stretch
+ */
+ enum WXCoreAlignItems {
+ kAlignItemsFlexStart,
+ kAlignItemsCenter,
+ kAlignItemsFlexEnd,
+ kAlignItemsStretch,
+ };
+
+ /**
+ * Controls the count of flexlines
+ */
+ enum WXCoreFlexWrap {
+ kNoWrap,
+ kWrap,
+ kWrapReverse,
+ };
+
+ /**
+ * The align-self will overrides the align-items specified by the Flex container.
+ * The two attributes have the same range of values.
+ */
+ enum WXCoreAlignSelf {
+ kAlignSelfAuto = -1,
+ kAlignSelfFlexStart = kAlignItemsFlexStart,
+ kAlignSelfCenter = kAlignItemsCenter,
+ kAlignSelfFlexEnd = kAlignItemsFlexEnd,
+ kAlignSelfStretch = kAlignItemsStretch,
+ };
+
+ enum WXCorePositionType {
+ kRelative,
+ kAbsolute,
+ kFixed,
+ kSticky = kRelative
+ };
+
+ enum WXCorePositionEdge {
+ kPositionEdgeTop,
+ kPositionEdgeBottom,
+ kPositionEdgeLeft,
+ kPositionEdgeRight,
+ };
+
+ enum WXCoreMarginEdge {
+ kMarginALL,
+ kMarginTop,
+ kMarginBottom,
+ kMarginLeft,
+ kMarginRight,
+ };
+
+ enum WXCorePaddingEdge {
+ kPaddingALL,
+ kPaddingTop,
+ kPaddingBottom,
+ kPaddingLeft,
+ kPaddingRight,
+ };
+
+ enum WXCoreBorderWidthEdge {
+ kBorderWidthALL,
+ kBorderWidthTop,
+ kBorderWidthBottom,
+ kBorderWidthLeft,
+ kBorderWidthRight,
+ };
+
+ enum WXCoreEdge{
+ kTop,
+ kRight,
+ kBottom,
+ kLeft,
+ };
+
+}
+#endif //WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUM_H
+#endif
\ No newline at end of file