You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by GitBox <gi...@apache.org> on 2018/10/19 12:33:55 UTC

[GitHub] Hanks10100 closed pull request #1166: [WEEX-352][iOS] Allow developer customize Refresh control by implement new refresh protocol WXPullRefreshProtocol.

Hanks10100 closed pull request #1166:  [WEEX-352][iOS] Allow developer customize Refresh control by implement new refresh protocol WXPullRefreshProtocol.
URL: https://github.com/apache/incubator-weex/pull/1166
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/examples/vue/components/scroller.vue b/examples/vue/components/scroller.vue
index 8336ea0eea..ed672a8249 100644
--- a/examples/vue/components/scroller.vue
+++ b/examples/vue/components/scroller.vue
@@ -1,7 +1,7 @@
 <template>
   <scroller class="list" append="tree" ref="scroller">
-    <refresh class="refresh-view" :display="refresh_display" @refresh="onrefresh" @pullingdown="pullingdown">
-      <img id="roate" ref="roate" src="http://gw.alicdn.com/bao/uploaded/TB1xDrVNFXXXXbEXFXXXXXXXXXX-48-48.png" style="width: 50px;height: 50px;"></img>
+    <refresh class="refresh-view" :display="refresh_display" @refresh="onrefresh" @pullingdown="pullingdown" :finalAnimationDuration="myduration">
+      <!-- <image id="roate" ref="roate" src="http://gw.alicdn.com/bao/uploaded/TB1xDrVNFXXXXbEXFXXXXXXXXXX-48-48.png" style="width: 50px;height: 50px;"></image> -->
     </refresh>
     <div v-for="(sec, i) in sections" :key="i" class="section">
       <div class="header">
@@ -21,6 +21,7 @@
   .refresh-view {
     height: 120px;
     width: 750px;
+    background-color: #c0c0c0;
     display: -ms-flex;
     display: -webkit-flex;
     display: flex;
@@ -74,6 +75,7 @@
 </style>
 
 <script>
+var duration = 2500
   const dom = weex.requireModule('dom')
   console.log(dom)
   module.exports = {
@@ -86,9 +88,13 @@
       onrefresh: function(e) {
         var self = this;
         self.refresh_display = 'show';
+        self.myduration = duration;
         setTimeout(function () {
+          setTimeout(function(){
           self.refresh_display = 'hide';
-        }, 3000)
+          },duration);
+          self.refresh_display = 'endRefresh';
+        }, duration)
       },
       onloading: function(e) {
         var self = this;
@@ -107,6 +113,7 @@
       return {
         refresh_display: 'hide',
         loading_display: 'hide',
+        myduration:0,
         sections: [
           {
             title: 'Header 1',
diff --git a/ios/playground/WeexDemo.xcodeproj/project.pbxproj b/ios/playground/WeexDemo.xcodeproj/project.pbxproj
index 9721ca7c98..e71d39c14b 100644
--- a/ios/playground/WeexDemo.xcodeproj/project.pbxproj
+++ b/ios/playground/WeexDemo.xcodeproj/project.pbxproj
@@ -38,6 +38,7 @@
 		8A0B5EFFF75BF82EA481983D /* libPods-WeexUITestDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E48C20F443AA337D1FE97622 /* libPods-WeexUITestDemo.a */; };
 		C43CDA031F1C6E01005A6B03 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C43CDA021F1C6E01005A6B03 /* libz.tbd */; };
 		C47B78D21F299E27001D3B0C /* WXExtendCallNativeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C47B78D11F299E27001D3B0C /* WXExtendCallNativeTest.m */; };
+		D04712C920A1A277008FB048 /* WXPullRefreshDefaultImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = D04712C820A1A277008FB048 /* WXPullRefreshDefaultImpl.m */; };
 		DC15A3C7200C505C009C8977 /* WXTitleBarModule.m in Sources */ = {isa = PBXBuildFile; fileRef = DC15A3C6200C505C009C8977 /* WXTitleBarModule.m */; };
 		DC15A3D0200E30FC009C8977 /* WXNavigationHandlerImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = DC15A3CE200E30FC009C8977 /* WXNavigationHandlerImpl.m */; };
 		DC20B8E61ECADA2500845F39 /* WXConfigCenterDefaultImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = DC20B8E51ECADA2500845F39 /* WXConfigCenterDefaultImpl.m */; };
@@ -105,6 +106,8 @@
 		C43CDA021F1C6E01005A6B03 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
 		C47B78D01F299E27001D3B0C /* WXExtendCallNativeTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXExtendCallNativeTest.h; sourceTree = "<group>"; };
 		C47B78D11F299E27001D3B0C /* WXExtendCallNativeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXExtendCallNativeTest.m; sourceTree = "<group>"; };
+		D04712C720A1A277008FB048 /* WXPullRefreshDefaultImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WXPullRefreshDefaultImpl.h; path = extend/handler/WXPullRefreshDefaultImpl.h; sourceTree = "<group>"; };
+		D04712C820A1A277008FB048 /* WXPullRefreshDefaultImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = WXPullRefreshDefaultImpl.m; path = extend/handler/WXPullRefreshDefaultImpl.m; sourceTree = "<group>"; };
 		DC15A3C5200C505C009C8977 /* WXTitleBarModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WXTitleBarModule.h; path = extend/module/WXTitleBarModule.h; sourceTree = "<group>"; };
 		DC15A3C6200C505C009C8977 /* WXTitleBarModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = WXTitleBarModule.m; path = extend/module/WXTitleBarModule.m; sourceTree = "<group>"; };
 		DC15A3CE200E30FC009C8977 /* WXNavigationHandlerImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXNavigationHandlerImpl.m; sourceTree = "<group>"; };
@@ -336,6 +339,8 @@
 				DC20B8E51ECADA2500845F39 /* WXConfigCenterDefaultImpl.m */,
 				DCABAFFC1D029753001C8592 /* WXImgLoaderDefaultImpl.h */,
 				DCABAFFD1D029753001C8592 /* WXImgLoaderDefaultImpl.m */,
+				D04712C720A1A277008FB048 /* WXPullRefreshDefaultImpl.h */,
+				D04712C820A1A277008FB048 /* WXPullRefreshDefaultImpl.m */,
 			);
 			name = handler;
 			sourceTree = "<group>";
@@ -627,6 +632,7 @@
 				2AE88A2C1C8544E6003329DE /* WXScannerVC.m in Sources */,
 				775BEE861C1E8ECC008D1629 /* WXDemoViewController.m in Sources */,
 				DC5B53691E8CED9400E02125 /* WXScannerHistoryVC.m in Sources */,
+				D04712C920A1A277008FB048 /* WXPullRefreshDefaultImpl.m in Sources */,
 				74CC79EB1C2B9E4700829368 /* UIViewController+WXDemoNaviBar.m in Sources */,
 				DCABB0011D02975E001C8592 /* WXEventModule.m in Sources */,
 				7478481E1E0CD4910044500D /* WXSyncTestModule.m in Sources */,
diff --git a/ios/playground/WeexDemo/AppDelegate.m b/ios/playground/WeexDemo/AppDelegate.m
index 7687f47a65..ec4091c921 100644
--- a/ios/playground/WeexDemo/AppDelegate.m
+++ b/ios/playground/WeexDemo/AppDelegate.m
@@ -38,6 +38,7 @@
 #import "WXNavigationHandlerImpl.h"
 //#import "WXAnalyzerCenter.h"
 
+#import "WXPullRefreshDefaultImpl.h"
 
 #ifdef DEBUG
 #import "DebugAnalyzer.h"
@@ -123,7 +124,8 @@ - (void)initWeexSDK
     [WXSDKEngine registerHandler:[WXEventModule new] withProtocol:@protocol(WXEventModuleProtocol)];
     [WXSDKEngine registerHandler:[WXConfigCenterDefaultImpl new] withProtocol:@protocol(WXConfigCenterProtocol)];
     [WXSDKEngine registerHandler:[WXNavigationHandlerImpl new] withProtocol:@protocol(WXNavigationProtocol)];
-    
+    [WXSDKEngine registerHandler:[WXPullRefreshDefaultImpl new] withProtocol:@protocol(WXPullRefreshProtocol)];
+
     [WXSDKEngine registerComponent:@"select" withClass:NSClassFromString(@"WXSelectComponent")];
     [WXSDKEngine registerModule:@"event" withClass:[WXEventModule class]];
     [WXSDKEngine registerModule:@"syncTest" withClass:[WXSyncTestModule class]];
diff --git a/ios/playground/WeexDemo/extend/handler/WXPullRefreshDefaultImpl.h b/ios/playground/WeexDemo/extend/handler/WXPullRefreshDefaultImpl.h
new file mode 100644
index 0000000000..97841d7cc1
--- /dev/null
+++ b/ios/playground/WeexDemo/extend/handler/WXPullRefreshDefaultImpl.h
@@ -0,0 +1,25 @@
+/*
+ * 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 <Foundation/Foundation.h>
+#import "WXPullRefreshProtocol.h"
+
+@interface WXPullRefreshDefaultImpl : NSObject <WXPullRefreshProtocol>
+
+@end
diff --git a/ios/playground/WeexDemo/extend/handler/WXPullRefreshDefaultImpl.m b/ios/playground/WeexDemo/extend/handler/WXPullRefreshDefaultImpl.m
new file mode 100644
index 0000000000..3f344b4a6d
--- /dev/null
+++ b/ios/playground/WeexDemo/extend/handler/WXPullRefreshDefaultImpl.m
@@ -0,0 +1,388 @@
+/*
+ * 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 "WXPullRefreshDefaultImpl.h"
+#import <UIKit/UIKit.h>
+#import <WeexSDK/WXUtility.h>
+
+#pragma mark WXRotateImageView
+@interface WXRotateImageView : UIImageView
+@property (assign) CGFloat currentAngle;
+- (void)startRotate;
+@end
+
+#pragma mark WXWaveLayer
+typedef void (^WXWaveLayerOverBlock)();
+@interface WXWaveLayer : CALayer
+@property (nonatomic, assign) CGFloat radius;
+@property (nonatomic, assign) NSTimeInterval animationDuration;
+- (void)startAnimation:(WXWaveLayerOverBlock)block;
+@end
+
+#pragma mark implementation WXPullRefreshDefaultImpl
+@interface WXPullRefreshDefaultImpl ()
+@property (nonatomic, weak) UIView *pullContentView;
+@property (nonatomic, weak) UIView *refreshContentView;
+@property (nonatomic, weak) UIView *finalContentView;
+@property (nonatomic, weak) NSTimer *refreshTimer;
+@property (nonatomic, weak) NSTimer *finalTimer;
+
+@property (nonatomic, strong) UILabel *timeLabel;
+@property (nonatomic, strong) WXWaveLayer *waveLayer;
+@property (nonatomic, strong) WXRotateImageView *imageView;
+@property (nonatomic, strong) UIImageView *pullingIV;
+
+@property (nonatomic, assign) CGRect oldFrame;
+
+@end
+
+@implementation WXPullRefreshDefaultImpl
+- (void)startPulling:(UIView *)inSuperView
+{
+    if (inSuperView == nil) {
+        return;
+    }
+    _oldFrame = inSuperView.frame;
+    UIView *aView = [[UIView alloc] init];
+    aView.backgroundColor = [UIColor greenColor];
+    [inSuperView addSubview:aView];
+    aView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleHeight;
+    [aView setFrame:inSuperView.bounds];
+    _pullContentView = aView;
+
+    UIImageView *imageView = [[UIImageView alloc] init];
+    imageView.contentMode = UIViewContentModeScaleToFill;
+    imageView.image = [UIImage imageNamed:@"weex"];
+    [aView addSubview:imageView];
+    imageView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleHeight;
+    CGRect bounds = inSuperView.bounds;
+    bounds.origin.x = (bounds.size.width - 80)/2;
+    bounds.size.width = 80;
+    imageView.frame = bounds;
+    _pullingIV = imageView;
+}
+
+- (void)pulling:(NSDictionary *)inParams
+{
+    CGFloat newHeight = ((NSNumber *)inParams[@"pullingDistance"]).floatValue;
+    CGFloat scale = [WXUtility defaultPixelScaleFactor];
+    CGRect tempRect = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, newHeight*scale);
+    UIView *superView = [_pullContentView superview];
+    [superView setFrame:tempRect];
+}
+
+- (void)endPulling
+{
+    if (_pullingIV) {
+        [_pullingIV removeFromSuperview];
+        _pullingIV = nil;
+    }
+    if (_pullContentView) {
+        [_pullContentView removeFromSuperview];
+        _pullContentView = nil;
+    }
+}
+
+- (void)startRefreshing:(UIView *)inSuperView
+{
+    if (inSuperView == nil) {
+        return;
+    }
+    [inSuperView setFrame:_oldFrame];
+
+    UIView *aView = [[UIView alloc] init];
+    aView.backgroundColor = [UIColor grayColor];
+    [inSuperView addSubview:aView];
+    aView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleHeight;
+    [aView setFrame:inSuperView.bounds];
+    _refreshContentView = aView;
+
+    _imageView = [[WXRotateImageView alloc] init];
+    _imageView.contentMode = UIViewContentModeScaleAspectFit;
+    _imageView.image = [UIImage imageNamed:@"weex-icon"];
+    [aView addSubview:_imageView];
+    CGRect bounds = inSuperView.bounds;
+    bounds.origin.x = (bounds.size.width - 80)/2;
+    bounds.size.width = 80;
+    _imageView.frame = bounds;
+
+    [_imageView startRotate];
+}
+
+- (void)endRefreshing
+{
+    if (_imageView) {
+        [_imageView stopAnimating];
+        [_imageView removeFromSuperview];
+        _imageView = nil;
+    }
+    if (_refreshContentView) {
+        [_refreshContentView removeFromSuperview];
+        _refreshContentView = nil;
+    }
+}
+
+- (void)startFinalAnimation:(UIView *)inSuperView duration:(CGFloat)inDuration
+{
+    if (inSuperView == nil) {
+        return;
+    }
+    UIView *aView = [[UIView alloc] init];
+    aView.backgroundColor = [UIColor blueColor];
+    aView.clipsToBounds = YES;
+    [inSuperView addSubview:aView];
+    aView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleHeight;
+    [aView setFrame:inSuperView.bounds];
+    _finalContentView = aView;
+    UIView *bView = (UIView *)inSuperView;
+    CGFloat duration = inDuration/1000;
+
+    [self.waveLayer setRadius:[UIScreen mainScreen].bounds.size.width / 2 + 10];
+    self.waveLayer.position = CGPointMake(bView.frame.size.width / 2, bView.frame.size.height / 2);
+    self.waveLayer.animationDuration = duration/4;
+
+    self.timeLabel.frame = CGRectMake(0, (bView.bounds.size.height - 16) / 2, bView.bounds.size.width, 16);
+    [self updateTimeLabelShow:nil];
+    self.timeLabel.alpha = 0.0;
+
+    __weak typeof(self) weakself = self;
+    [self.waveLayer startAnimation:^{
+        [UIView animateWithDuration:duration/2 animations:^{
+            weakself.timeLabel.alpha = 1.0f;
+        }];
+    }];
+}
+
+- (void)endFinalAnimation
+{
+    if (_timeLabel) {
+        [_timeLabel removeFromSuperview];
+        _timeLabel = nil;
+    }
+    if (_waveLayer) {
+        [_waveLayer removeFromSuperlayer];
+        _waveLayer = nil;
+    }
+    if (_finalContentView) {
+        [_finalContentView removeFromSuperview];
+        _finalContentView = nil;
+    }
+}
+
+- (UILabel *)timeLabel {
+
+    if (nil == _timeLabel) {
+
+        _timeLabel = [[UILabel alloc] init];
+        _timeLabel.font = [UIFont systemFontOfSize:14];
+        _timeLabel.textColor = [UIColor whiteColor];
+        _timeLabel.textAlignment = NSTextAlignmentCenter;
+        [_finalContentView addSubview:_timeLabel];
+    }
+
+    return _timeLabel;
+}
+
+- (WXWaveLayer *)waveLayer {
+
+    if (nil == _waveLayer) {
+        _waveLayer = [[WXWaveLayer alloc] init];
+        [_finalContentView.layer addSublayer:_waveLayer];
+    }
+
+    return _waveLayer;
+}
+
+- (void)updateTimeLabelShow:(NSString*)text {
+    if (text) {
+        self.timeLabel.text = text;
+    }
+    else{
+        NSDate *lastUpdatedTime = [NSDate date];
+        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+        formatter.dateFormat = @"HH:mm";
+
+        NSString *time = [formatter stringFromDate:lastUpdatedTime];
+        NSString *updateFormat = @"Last updated: %@";
+        self.timeLabel.text = [NSString stringWithFormat:updateFormat, time];
+    }
+}
+
+@end
+
+#pragma mark implementation WXRotateImageView
+
+@interface WXRotateImageView()
+
+@property (assign, nonatomic) CATransform3D identityTransform;
+
+@property (strong) NSTimer *timer;
+
+@end
+
+@implementation WXRotateImageView
+
+- (id)init {
+
+    self = [super init];
+    if (nil == self) {
+
+        return self;
+    }
+
+    [self _init];
+    return self;
+}
+
+- (id)initWithFrame:(CGRect)frame {
+
+    self = [super initWithFrame:frame];
+    if (nil == self) {
+
+        return self;
+    }
+
+    [self _init];
+    return self;
+}
+
+- (void)awakeFromNib {
+
+    [super awakeFromNib];
+    [self _init];
+}
+
+- (void)_init {
+
+    CATransform3D transform = CATransform3DIdentity;
+    transform.m34 = 1.0 / -500;
+    self.identityTransform = CATransform3DRotate(transform, 0, 0, 0, 1.0);
+}
+
+- (void)dealloc {
+
+    [self finishRotate];
+}
+
+- (void)startRotate {
+
+    [self finishRotate];
+    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
+}
+
+- (void)finishRotate {
+
+    [self.timer invalidate];
+    self.timer = nil;
+}
+
+- (void)onTimer:(NSTimer *)timer {
+
+    self.currentAngle += 0.04 * M_PI;
+    [self applyEffect];
+}
+
+- (void)applyEffect {
+
+    CATransform3D transform = CATransform3DRotate(self.identityTransform, self.currentAngle, 0, 0, 1.0);
+    self.layer.transform = transform;
+}
+
+
+@end
+
+#pragma mark implementation WXWaveLayer
+@interface WXWaveLayer()<CAAnimationDelegate>
+
+@property (nonatomic, strong) CAAnimationGroup *animationGroup;
+
+@property (strong) CAKeyframeAnimation *keyFrameAnimation;
+
+@property (copy) WXWaveLayerOverBlock block;
+
+@end
+
+@implementation WXWaveLayer
+
+- (id)init {
+    self = [super init];
+    if (self) {
+
+        self.contentsScale = [UIScreen mainScreen].scale;
+
+        self.animationDuration = 0.25f;
+        self.backgroundColor = [UIColor clearColor].CGColor;
+    }
+    return self;
+}
+
+- (void)startAnimation:(WXWaveLayerOverBlock)block {
+
+    self.block = block;
+    self.backgroundColor = [UIColor colorWithRed:0xFF/255.0f green:0x00/255.0f blue:0x00/255.0f alpha:1].CGColor;
+    [self setupAnimationGroup];
+    [self addAnimation:self.keyFrameAnimation forKey:@"pulse"];
+}
+
+- (void)stopAnimation {
+
+    [self removeAllAnimations];
+    self.animationGroup = nil;
+}
+
+- (void)reset {
+
+    self.backgroundColor = [UIColor clearColor].CGColor;
+}
+
+- (void)setRadius:(CGFloat)radius {
+
+    _radius = radius;
+
+    CGPoint tempPos = self.position;
+
+    CGFloat diameter = self.radius * 2;
+
+    self.bounds = CGRectMake(0, 0, diameter, diameter);
+    self.cornerRadius = self.radius;
+    self.position = tempPos;
+}
+
+- (void)setupAnimationGroup {
+
+    CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.xy"];
+    scaleAnimation.values = @[@0, @0.3, @0.8, @1];
+    scaleAnimation.keyTimes = @[@0, @0.5, @0.9, @1];
+    scaleAnimation.duration = self.animationDuration;
+    scaleAnimation.delegate = self;
+
+    self.keyFrameAnimation = scaleAnimation;
+}
+
+#pragma mark CAAnimationDelegate
+- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
+
+    [self stopAnimation];
+    if (self.block) {
+
+        self.block();
+    }
+}
+
+@end
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.mm
index f940e3a7bb..0876ea4939 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXRefreshComponent.mm
@@ -23,6 +23,8 @@
 #import "WXComponent_internal.h"
 #import "WXLog.h"
 #import "WXComponent+Layout.h"
+#import "WXPullRefreshProtocol.h"
+#import "WXHandlerFactory.h"
 
 @interface WXRefreshComponent()
 
@@ -31,6 +33,11 @@ @interface WXRefreshComponent()
 @property (nonatomic) BOOL refreshEvent;
 @property (nonatomic) BOOL pullingdownEvent;
 
+@property (nonatomic) BOOL bPulling;
+@property (nonatomic) BOOL bRefreshing;
+@property (nonatomic) BOOL bFinalAnimation;
+@property (nonatomic) CGFloat finalAnimationDuration;
+
 @property (nonatomic, weak) WXLoadingIndicator *indicator;
 
 @end
@@ -52,6 +59,11 @@ - (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDict
                 WXLogError(@"");
             }
         }
+        if (attributes[@"finalAnimationDuration"]) {
+            _finalAnimationDuration = [WXConvert CGFloat:attributes[@"finalAnimationDuration"]];
+        } else {
+            _finalAnimationDuration = 1;
+        }
 //#ifndef USE_FLEX
         if (![WXComponent isUseFlex]) {
           self.cssNode->style.position_type = CSS_POSITION_ABSOLUTE;
@@ -63,6 +75,9 @@ - (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDict
         }
        
 //#endif
+        _bFinalAnimation = NO;
+        _bPulling = NO;
+        _bRefreshing = NO;
     }
     return self;
 }
@@ -90,6 +105,10 @@ - (void)viewWillUnload
     _displayState = NO;
     _refreshEvent = NO;
     _initFinished = NO;
+
+    _bPulling = NO;
+    _bRefreshing = NO;
+    _bFinalAnimation = NO;
 }
 
 - (void)refresh
@@ -100,6 +119,21 @@ - (void)refresh
 #ifdef DEBUG
     WXLogDebug(@"flexLayout -> refreshComponent : refresh ref:%@",self.ref);
 #endif
+    id <WXPullRefreshProtocol> nativeImp = [WXHandlerFactory handlerForProtocol:@protocol(WXPullRefreshProtocol)];
+    if (nativeImp) {
+        if ([nativeImp respondsToSelector:@selector(endPulling)]) {
+            if (_bPulling) {
+                [nativeImp endPulling];
+                _bPulling = NO;
+            }
+        }
+        if ([nativeImp respondsToSelector:@selector(startRefreshing:)]) {
+            if (!_bRefreshing) {
+                [nativeImp startRefreshing:self.view];
+                _bRefreshing = YES;
+            }
+        }
+    }
     [self fireEvent:@"refresh" params:nil];
 }
 
@@ -111,7 +145,15 @@ - (void)pullingdown:(NSDictionary*)param
 #ifdef DEBUG
     WXLogDebug(@"flexLayout -> refreshComponent : pullingdown ,ref:%@",self.ref);
 #endif
-    
+    id <WXPullRefreshProtocol> nativeImp = [WXHandlerFactory handlerForProtocol:@protocol(WXPullRefreshProtocol)];
+    if (nativeImp && [nativeImp respondsToSelector:@selector(startPulling:)]) {
+        if (!_bPulling) {
+            [nativeImp startPulling:self.view];
+            _bPulling = YES;
+        } else if ([nativeImp respondsToSelector:@selector(pulling:)]){
+            [nativeImp pulling:param];
+        }
+    }
     [self fireEvent:@"pullingdown" params:param];
 }
 
@@ -132,11 +174,31 @@ - (void)updateAttributes:(NSDictionary *)attributes
             _displayState = YES;
         } else if ([attributes[@"display"] isEqualToString:@"hide"]) {
             _displayState = NO;
+        } else if ([attributes[@"display"] isEqualToString:@"endRefresh"]) {
+            id <WXPullRefreshProtocol> nativeImp = [WXHandlerFactory handlerForProtocol:@protocol(WXPullRefreshProtocol)];
+            if (nativeImp && [nativeImp respondsToSelector:@selector(endRefreshing)]) {
+                if (_bRefreshing) {
+                    [nativeImp endRefreshing];
+                    _bRefreshing = NO;
+                }
+            }
+            if (nativeImp && [nativeImp respondsToSelector:@selector(startFinalAnimation:duration:)]) {
+                if (!_bFinalAnimation) {
+                    [nativeImp startFinalAnimation:self.view duration:_finalAnimationDuration];
+                    _bFinalAnimation = YES;
+                }
+            }
         } else {
             WXLogError(@"");
         }
         [self setDisplay];
     }
+
+    if (attributes[@"finalAnimationDuration"]) {
+        _finalAnimationDuration = [WXConvert CGFloat:attributes[@"finalAnimationDuration"]];
+    } else {
+        _finalAnimationDuration = 1;
+    }
 }
 
 - (void)addEvent:(NSString *)eventName
@@ -193,7 +255,15 @@ - (void)setDisplay
         }
         [scrollerProtocol setContentOffset:offset animated:YES];
     }
-  
+    if (!_displayState) {
+        id <WXPullRefreshProtocol> nativeImp = [WXHandlerFactory handlerForProtocol:@protocol(WXPullRefreshProtocol)];
+        if (nativeImp && [nativeImp respondsToSelector:@selector(endFinalAnimation)]) {
+            if (_bFinalAnimation) {
+                [nativeImp endFinalAnimation];
+                _bFinalAnimation = NO;
+            }
+        }
+    }
 }
 
 - (BOOL)displayState
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
index 52bb962ba9..3102d472b4 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
@@ -639,7 +639,7 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView
             instance.onScroll(scrollView.contentOffset);
         }
     }
-    
+    BOOL bPulling = NO;
     if (_lastContentOffset.x > scrollView.contentOffset.x) {
         _direction = @"right";
     } else if (_lastContentOffset.x < scrollView.contentOffset.x) {
@@ -649,20 +649,23 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView
         }
     } else if(_lastContentOffset.y > scrollView.contentOffset.y) {
         _direction = @"down";
+        bPulling = YES;
     } 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"
-    }];
+    if (bPulling) {
+        [_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];
diff --git a/ios/sdk/WeexSDK/Sources/Protocol/WXPullRefreshProtocol.h b/ios/sdk/WeexSDK/Sources/Protocol/WXPullRefreshProtocol.h
new file mode 100644
index 0000000000..9516f10e63
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Protocol/WXPullRefreshProtocol.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef WXPullRefreshProtocol_h
+#define WXPullRefreshProtocol_h
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+@protocol WXPullRefreshProtocol <NSObject>
+@optional
+
+/**
+ * @abstract startPulling means when user start dragging, you can add subview to inSuperView to display customized view
+ */
+- (void)startPulling:(UIView *)inSuperView;
+
+/**
+ * @abstract pulling means when is pulling, inParams contains the distance information
+ */
+- (void)pulling:(NSDictionary *)inParams;
+
+/**
+ * @abstract endPulling will be triggered when user stop dragging
+ */
+- (void)endPulling;
+
+/**
+ * @abstract startRefreshing means when user releases dragging and waiting for network response, you can start an animation for loading
+ */
+- (void)startRefreshing:(UIView *)inSuperView;
+
+/**
+ * @abstract endRefreshing will be triggered when network response comes
+ */
+- (void)endRefreshing;
+
+/**
+ * @abstract startFinalAnimation means when network response comes, show an final animation to let user know the refresh state,
+ *  the duration can be customized by js file
+ */
+- (void)startFinalAnimation:(UIView *)inSuperView duration:(CGFloat)inDuration;
+
+/**
+ * @abstract endFinalAnimation will be triggered when final animation finished
+ */
+- (void)endFinalAnimation;
+@end
+
+#endif /* WXPullRefreshProtocol_h */


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services