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:40:02 UTC
[10/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
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