فهرست منبع

1.修改RedAnt Mobile查询界面上拉加载和下拉刷新。

Pen Li 8 سال پیش
والد
کامیت
5e413533b1

+ 56 - 0
RedAnt Mobile/RedAnt Mobile.xcodeproj/project.pbxproj

@@ -13,6 +13,11 @@
 		4221EF9D1FA81D5B0026E1EC /* RAQueryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4221EF9C1FA81D5B0026E1EC /* RAQueryViewController.m */; };
 		4221EF9F1FA81E150026E1EC /* RAPredefQuery.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4221EF9E1FA81E150026E1EC /* RAPredefQuery.storyboard */; };
 		4221EFA11FA81E580026E1EC /* RAQuery.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4221EFA01FA81E580026E1EC /* RAQuery.storyboard */; };
+		4221EFB21FA8599A0026E1EC /* JLRefreshHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 4221EFAB1FA853660026E1EC /* JLRefreshHeader.m */; };
+		4221EFB31FA8599A0026E1EC /* JLRefreshBasis.m in Sources */ = {isa = PBXBuildFile; fileRef = 4221EFAD1FA853660026E1EC /* JLRefreshBasis.m */; };
+		4221EFB41FA8599A0026E1EC /* UIScrollView+JLRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = 4221EFAF1FA853660026E1EC /* UIScrollView+JLRefresh.m */; };
+		4221EFB51FA8599A0026E1EC /* UIView+JLExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 4221EFB11FA853660026E1EC /* UIView+JLExtension.m */; };
+		4221EFB61FA859B80026E1EC /* JLRefreshFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4221EFA81FA853660026E1EC /* JLRefreshFooter.m */; };
 		42DFAE6B1F9B2B73009AFCCC /* Phone_Action_Cell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 42DFAE6A1F9B2B73009AFCCC /* Phone_Action_Cell.xib */; };
 		42DFAE6D1F9B2BE4009AFCCC /* Phone_Edit_Cell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 42DFAE6C1F9B2BE4009AFCCC /* Phone_Edit_Cell.xib */; };
 		42DFAE6F1F9B2C18009AFCCC /* Phone_Enum_Cell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 42DFAE6E1F9B2C18009AFCCC /* Phone_Enum_Cell.xib */; };
@@ -112,6 +117,16 @@
 		4221EF9C1FA81D5B0026E1EC /* RAQueryViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RAQueryViewController.m; sourceTree = "<group>"; };
 		4221EF9E1FA81E150026E1EC /* RAPredefQuery.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = RAPredefQuery.storyboard; sourceTree = "<group>"; };
 		4221EFA01FA81E580026E1EC /* RAQuery.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = RAQuery.storyboard; sourceTree = "<group>"; };
+		4221EFA71FA853660026E1EC /* JLRefreshFooter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JLRefreshFooter.h; sourceTree = "<group>"; };
+		4221EFA81FA853660026E1EC /* JLRefreshFooter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JLRefreshFooter.m; sourceTree = "<group>"; };
+		4221EFAA1FA853660026E1EC /* JLRefreshHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JLRefreshHeader.h; sourceTree = "<group>"; };
+		4221EFAB1FA853660026E1EC /* JLRefreshHeader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JLRefreshHeader.m; sourceTree = "<group>"; };
+		4221EFAC1FA853660026E1EC /* JLRefreshBasis.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JLRefreshBasis.h; sourceTree = "<group>"; };
+		4221EFAD1FA853660026E1EC /* JLRefreshBasis.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JLRefreshBasis.m; sourceTree = "<group>"; };
+		4221EFAE1FA853660026E1EC /* UIScrollView+JLRefresh.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+JLRefresh.h"; sourceTree = "<group>"; };
+		4221EFAF1FA853660026E1EC /* UIScrollView+JLRefresh.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+JLRefresh.m"; sourceTree = "<group>"; };
+		4221EFB01FA853660026E1EC /* UIView+JLExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIView+JLExtension.h"; sourceTree = "<group>"; };
+		4221EFB11FA853660026E1EC /* UIView+JLExtension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+JLExtension.m"; sourceTree = "<group>"; };
 		42DFAE6A1F9B2B73009AFCCC /* Phone_Action_Cell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Phone_Action_Cell.xib; sourceTree = "<group>"; };
 		42DFAE6C1F9B2BE4009AFCCC /* Phone_Edit_Cell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Phone_Edit_Cell.xib; sourceTree = "<group>"; };
 		42DFAE6E1F9B2C18009AFCCC /* Phone_Enum_Cell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Phone_Enum_Cell.xib; sourceTree = "<group>"; };
@@ -294,6 +309,39 @@
 			name = Mode;
 			sourceTree = "<group>";
 		};
+		4221EFA51FA853660026E1EC /* Refresh */ = {
+			isa = PBXGroup;
+			children = (
+				4221EFA61FA853660026E1EC /* Footer */,
+				4221EFA91FA853660026E1EC /* Header */,
+				4221EFAC1FA853660026E1EC /* JLRefreshBasis.h */,
+				4221EFAD1FA853660026E1EC /* JLRefreshBasis.m */,
+				4221EFAE1FA853660026E1EC /* UIScrollView+JLRefresh.h */,
+				4221EFAF1FA853660026E1EC /* UIScrollView+JLRefresh.m */,
+				4221EFB01FA853660026E1EC /* UIView+JLExtension.h */,
+				4221EFB11FA853660026E1EC /* UIView+JLExtension.m */,
+			);
+			path = Refresh;
+			sourceTree = "<group>";
+		};
+		4221EFA61FA853660026E1EC /* Footer */ = {
+			isa = PBXGroup;
+			children = (
+				4221EFA71FA853660026E1EC /* JLRefreshFooter.h */,
+				4221EFA81FA853660026E1EC /* JLRefreshFooter.m */,
+			);
+			path = Footer;
+			sourceTree = "<group>";
+		};
+		4221EFA91FA853660026E1EC /* Header */ = {
+			isa = PBXGroup;
+			children = (
+				4221EFAA1FA853660026E1EC /* JLRefreshHeader.h */,
+				4221EFAB1FA853660026E1EC /* JLRefreshHeader.m */,
+			);
+			path = Header;
+			sourceTree = "<group>";
+		};
 		42DFAE601F9B2A01009AFCCC /* ActionCell */ = {
 			isa = PBXGroup;
 			children = (
@@ -469,6 +517,7 @@
 				71CB70B21FA1CCBC009FDCB7 /* fake data */,
 				71F3A9BA1F5F8E89001036C8 /* result */,
 				42F2EB3B1FA80EB300BC6468 /* Model */,
+				4221EFA51FA853660026E1EC /* Refresh */,
 				71F3A9A31F5F8E22001036C8 /* AppDelegate.h */,
 				71F3A9A41F5F8E22001036C8 /* AppDelegate.m */,
 				71F3A9A91F5F8E22001036C8 /* Main.storyboard */,
@@ -787,6 +836,11 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				4221EFB61FA859B80026E1EC /* JLRefreshFooter.m in Sources */,
+				4221EFB21FA8599A0026E1EC /* JLRefreshHeader.m in Sources */,
+				4221EFB31FA8599A0026E1EC /* JLRefreshBasis.m in Sources */,
+				4221EFB41FA8599A0026E1EC /* UIScrollView+JLRefresh.m in Sources */,
+				4221EFB51FA8599A0026E1EC /* UIView+JLExtension.m in Sources */,
 				71F3AA3E1F612369001036C8 /* CommonEditorCellSignature.m in Sources */,
 				71F3AA441F612369001036C8 /* EnumSelectViewController.m in Sources */,
 				71F3A9E31F5FA531001036C8 /* JLKeyboardListener.m in Sources */,
@@ -987,6 +1041,7 @@
 				DEVELOPMENT_TEAM = HXWLAA5YN5;
 				INFOPLIST_FILE = "RedAnt Mobile/Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				ONLY_ACTIVE_ARCH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = "usai.redant.RedAnt-Mobile";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 			};
@@ -999,6 +1054,7 @@
 				DEVELOPMENT_TEAM = HXWLAA5YN5;
 				INFOPLIST_FILE = "RedAnt Mobile/Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				ONLY_ACTIVE_ARCH = NO;
 				PRODUCT_BUNDLE_IDENTIFIER = "usai.redant.RedAnt-Mobile";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 			};

BIN
RedAnt Mobile/RedAnt Mobile.xcodeproj/project.xcworkspace/xcuserdata/macmini1.xcuserdatad/UserInterfaceState.xcuserstate


+ 20 - 0
RedAnt Mobile/RedAnt Mobile/Refresh/Footer/JLRefreshFooter.h

@@ -0,0 +1,20 @@
+//
+//  JLRefreshFooter.h
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/7.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "JLRefreshBasis.h"
+
+@interface JLRefreshFooter : JLRefreshBasis
+
+@property (nonatomic,assign) BOOL showDefaultTips;
+
+- (void)noMoreData;
+- (void)setRefreshTitle:(NSString *)title forState:(JLRefreshState)state;
+- (NSString *)refreshTitleForState:(JLRefreshState)state;
+- (void)showTip;
+
+@end

+ 277 - 0
RedAnt Mobile/RedAnt Mobile/Refresh/Footer/JLRefreshFooter.m

@@ -0,0 +1,277 @@
+//
+//  JLRefreshFooter.m
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/7.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "JLRefreshFooter.h"
+
+@interface JLRefreshFooter ()
+
+@property (nonatomic,strong) NSMutableDictionary *stateTitle;
+@property (nonatomic,strong) UILabel *refreshTitleLabel;
+
+@end
+
+@implementation JLRefreshFooter
+
+#pragma mark - SubView
+
+- (UILabel *)refreshTitleLabel {
+    if (!_refreshTitleLabel) {
+        _refreshTitleLabel = [[UILabel alloc] init];
+        _refreshTitleLabel.textAlignment = NSTextAlignmentCenter;
+        _refreshTitleLabel.font = [UIFont systemFontOfSize:14.0f];
+        _refreshTitleLabel.hidden = YES;
+    }
+    return _refreshTitleLabel;
+}
+
+#pragma mark - Private
+
+- (BOOL)contentSizeIsOutOfScrollViewBounds {
+    // top + content > height
+    return self.scrollView.jl_insetTop + self.scrollView.jl_ContentHeight > self.scrollView.jl_height;
+}
+
+- (NSMutableDictionary *)stateTitle {
+    if (!_stateTitle) {
+        _stateTitle = [@{
+                         @(JLRefreshStateIdle)          : @"will load More",
+                         @(JLRefreshStatePulling)       : @"will load More",
+                         @(JLRefreshStateWillRefresh)   : @"loosen to load more",
+                         @(JLRefreshStateRefreshing)    : @"loading...",
+                         @(JLRefreshStateNoMore)        : @"no more"
+                         } mutableCopy];
+    }
+    return _stateTitle;
+}
+
+- (void)setRefreshTitleForState:(JLRefreshState)state {
+    self.refreshTitleLabel.text = [self refreshTitleForState:state];
+    [self setNeedsLayout];
+}
+
+
+#pragma mark - Override
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    
+    
+    [self.refreshTitleLabel sizeToFit];
+    CGRect frame = self.refreshTitleLabel.bounds;
+    CGFloat Y = (CGRectGetHeight(self.bounds) - CGRectGetHeight(frame)) * 0.5;
+    CGFloat X = (CGRectGetWidth(self.bounds) - CGRectGetWidth(frame)) * 0.5;
+    self.refreshTitleLabel.jl_y = Y;
+    self.refreshTitleLabel.jl_x = X;
+    
+    [self addSubview:self.refreshTitleLabel];
+    
+}
+
+- (void)willMoveToSuperview:(UIView *)newSuperview {
+    
+    [super willMoveToSuperview:newSuperview];
+    
+    if (newSuperview) { // add
+        
+        self.scrollView.jl_insetBottom += self.jl_height;
+        
+        self.jl_y = self.scrollView.jl_ContentHeight;
+    
+    } else { // remove
+        
+        self.scrollView.jl_insetBottom -= self.jl_height;
+        
+    }
+    
+}
+
+- (void)scrollViewContentSizeDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+    [super scrollViewContentSizeDidChange:change];
+    
+    self.jl_y = self.scrollView.jl_ContentHeight;
+}
+
+- (void)scrollViewContentOffsetDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+    [super scrollViewContentOffsetDidChange:change];
+
+    if (self.state == JLRefreshStateRefreshing) {
+        return;
+    }
+    
+    CGFloat offsetY = self.scrollView.jl_offsetY + self.scrollView.jl_height;
+    CGFloat startY = self.scrollView.jl_insetBottom + self.scrollView.jl_ContentHeight - self.jl_height;
+    CGFloat idle2WillRefresh = startY + self.jl_height;
+    
+    if ([self contentSizeIsOutOfScrollViewBounds]) {
+        
+        
+        if (offsetY < startY) {
+            return;
+        }
+       
+        
+        if (self.scrollView.isDragging) {
+            
+            if (self.state == JLRefreshStateIdle) { // 1
+                
+                self.state = JLRefreshStatePulling; // 2
+                
+            } else if (self.state == JLRefreshStatePulling && offsetY < startY) {
+                
+                self.state = JLRefreshStateIdle;
+                
+            } else if (self.state == JLRefreshStatePulling && offsetY >= idle2WillRefresh) { // 完全显示出来才刷新
+                
+                self.state = JLRefreshStateWillRefresh; // 4
+                
+            }
+            
+           
+            
+        } else {
+            
+            // 停止拖动的减速过程
+            if (self.state != JLRefreshStateRefreshing && offsetY >= idle2WillRefresh) {
+                self.state = JLRefreshStateRefreshing;
+            }
+            
+        }
+        
+        float percentage = (offsetY - startY) / self.jl_height;
+        if (percentage < 0) {
+            percentage = 0;
+        }
+        self.pullPercentage = percentage;
+        [self showTip];
+        if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(jl_pullRefresh:state:percentage:)]) {
+            [self.refreshDelegate jl_pullRefresh:self state:self.state percentage:self.pullPercentage];
+        }
+        
+    } else {
+        
+        CGPoint old = [change[@"old"] CGPointValue];
+        CGPoint new = [change[@"new"] CGPointValue];
+        
+        if (self.scrollView.jl_insetTop == self.scrollViewOriginInsetTop && new.y > -self.scrollViewOriginInsetTop && old.y < new.y) { // 上拉,在未超出height的范围内拖动都视为刷新
+            
+            if (self.state == JLRefreshStateRefreshing) return;
+            
+            if (self.scrollView.isDragging) {
+                self.state = JLRefreshStateWillRefresh;
+                self.pullPercentage = 1;
+                [self showTip];
+                if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(jl_pullRefresh:state:percentage:)]) {
+                    [self.refreshDelegate jl_pullRefresh:self state:self.state percentage:self.pullPercentage];
+                }
+            }
+            
+        }
+
+        
+    }
+    
+    
+    
+    
+}
+
+- (void)scrollViewPanGestureStateDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+    [super scrollViewPanGestureStateDidChange:change];
+    
+    if (self.pan.state == UIGestureRecognizerStateEnded) {
+        
+        if (self.state == JLRefreshStateWillRefresh) {
+            self.state = JLRefreshStateRefreshing;
+        }
+        
+        
+    }
+    
+}
+
+#pragma mark - Setter
+
+- (void)setState:(JLRefreshState)state {
+    
+    JLRefreshState oldState = self.state;
+    
+    if (state == oldState) {
+        return;
+    }
+    
+    super.state = state;
+    
+     __weak typeof(self) weakself = self;
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [weakself setRefreshTitleForState:state];
+    });
+
+    
+    
+    if (state == JLRefreshStateIdle || state == JLRefreshStateNoMore) {
+        
+        if (oldState != JLRefreshStateRefreshing) {
+            return;
+        }
+        
+        if (weakself.refreshDelegate && [weakself.refreshDelegate respondsToSelector:@selector(jl_endRefresh:)]) {
+            
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [weakself.refreshDelegate jl_endRefresh:weakself];
+            });
+            
+        }
+        
+    } else if (state == JLRefreshStatePulling) {
+        
+        
+    } else if (state == JLRefreshStateWillRefresh) {
+        
+        
+    } else if (state == JLRefreshStateRefreshing) {
+        
+        if (weakself.refreshDelegate && [weakself.refreshDelegate respondsToSelector:@selector(jl_beginRefresh:)]) {
+            
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [weakself.refreshDelegate jl_beginRefresh:weakself];
+            });
+            
+        }
+        
+    }
+    
+}
+
+#pragma mark - Public
+
+- (void)noMoreData {
+    self.state = JLRefreshStateNoMore;
+}
+
+- (void)showTip {
+    self.refreshTitleLabel.hidden = NO;
+}
+
+- (void)setRefreshTitle:(NSString *)title forState:(JLRefreshState)state {
+    if (title == nil) {
+        return;
+    }
+    
+    [self.stateTitle setObject:title forKey:@(state)];
+}
+
+- (NSString *)refreshTitleForState:(JLRefreshState)state {
+    return [self.stateTitle objectForKey:@(state)];
+}
+
+
+
+@end

+ 18 - 0
RedAnt Mobile/RedAnt Mobile/Refresh/Header/JLRefreshHeader.h

@@ -0,0 +1,18 @@
+//
+//  JLRefreshHeader.h
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/6.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "JLRefreshBasis.h"
+
+@interface JLRefreshHeader : JLRefreshBasis
+
+
+- (void)setRefreshTitle:(NSString *)title forState:(JLRefreshState)state;
+
+- (NSString *)refreshTitleForState:(JLRefreshState)state;
+
+@end

+ 219 - 0
RedAnt Mobile/RedAnt Mobile/Refresh/Header/JLRefreshHeader.m

@@ -0,0 +1,219 @@
+//
+//  JLRefreshHeader.m
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/6.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "JLRefreshHeader.h"
+
+@interface JLRefreshHeader ()
+
+@property (nonatomic,strong) NSMutableDictionary *stateTitle;
+
+@property (nonatomic,strong) UILabel *refreshTitleLabel;
+
+@end
+
+@implementation JLRefreshHeader
+
+#pragma mark - private
+
+- (NSMutableDictionary *)stateTitle {
+    if (!_stateTitle) {
+        _stateTitle = [@{
+                         @(JLRefreshStateIdle)          : @"pull to refresh",
+                         @(JLRefreshStatePulling)       : @"pull to refresh",
+                         @(JLRefreshStateWillRefresh)   : @"loosen to refresh",
+                         @(JLRefreshStateRefreshing)    : @"loading..."
+                         } mutableCopy];
+    }
+    return _stateTitle;
+}
+
+- (void)setRefreshTitleForState:(JLRefreshState)state {
+    self.refreshTitleLabel.text = [self refreshTitleForState:state];
+    [self setNeedsLayout];
+}
+
+#pragma mark - override
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    
+    self.jl_y = -self.jl_height;
+    
+    [self.refreshTitleLabel sizeToFit];
+    CGRect frame = self.refreshTitleLabel.bounds;
+    CGFloat Y = (CGRectGetHeight(self.bounds) - CGRectGetHeight(frame)) * 0.5;
+    CGFloat X = (CGRectGetWidth(self.bounds) - CGRectGetWidth(frame)) * 0.5;
+    self.refreshTitleLabel.jl_y = Y;
+    self.refreshTitleLabel.jl_x = X;
+    
+    [self addSubview:self.refreshTitleLabel];
+}
+
+- (void)scrollViewContentOffsetDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    // 正在刷新
+    if (self.state == JLRefreshStateRefreshing) {
+        
+        // 解决SectionHeader停留
+        CGFloat insetTop = -self.scrollView.jl_offsetY > self.scrollViewOriginInsetTop ? -self.scrollView.jl_offsetY : self.scrollViewOriginInsetTop;
+        insetTop = insetTop > self.jl_height + self.scrollViewOriginInsetTop ? self.jl_height + self.scrollViewOriginInsetTop : insetTop;
+        self.scrollView.jl_insetTop = insetTop;
+        
+        return;
+    }
+    
+    CGFloat offsetY = self.scrollView.jl_offsetY;
+    CGFloat startY = -self.scrollView.jl_insetTop; // refresh 开始显示的临界点
+    
+    if (offsetY > startY) {
+        return;
+    }
+    
+    CGFloat idle2willRefreshY = startY - self.jl_height; // 正常状态到即将刷新状态临界点
+    
+    if (self.scrollView.isDragging) {
+        
+        if (self.state == JLRefreshStateIdle && offsetY < startY) {
+            
+            self.state = JLRefreshStatePulling;
+            
+        } else if (self.state == JLRefreshStatePulling && offsetY <= idle2willRefreshY) {
+            
+            self.state = JLRefreshStateWillRefresh;
+            
+        } else if (self.state == JLRefreshStatePulling && offsetY > startY) {
+            
+            self.state = JLRefreshStateIdle;
+            
+        } else if (self.state == JLRefreshStateWillRefresh && offsetY > idle2willRefreshY) {
+            
+            self.state = JLRefreshStatePulling;
+        
+        }
+        
+        float percentage = (startY - offsetY) / self.jl_height;
+        if (percentage <= 0) {
+            percentage = 0;
+        }
+        self.pullPercentage = percentage;
+        
+        if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(jl_pullRefresh:state:percentage:)]) {
+            [self.refreshDelegate jl_pullRefresh:self state:self.state percentage:self.pullPercentage];
+        }
+        
+    }
+
+}
+
+- (void)scrollViewPanGestureStateDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    if (self.pan.state == UIGestureRecognizerStateEnded && self.state == JLRefreshStateWillRefresh) {
+        // 手松开开始刷新
+        self.state = JLRefreshStateRefreshing;
+    }
+}
+
+- (void)setState:(JLRefreshState)state {
+    JLRefreshState oldState = self.state;
+    
+    if (state == oldState) {
+        return;
+    }
+    
+    super.state = state;
+    
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [self setRefreshTitleForState:state];
+    });
+    
+    if (state == JLRefreshStateIdle) {
+
+        // 恢复原状态
+        if (oldState != JLRefreshStateRefreshing) {
+            return;
+        }
+        
+        __weak typeof(self) weakself = self;
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [UIView animateWithDuration:JLRefreshDuration animations:^{
+                
+                weakself.scrollView.jl_insetTop = weakself.scrollViewOriginInsetTop;
+                
+            } completion:^(BOOL finished) {
+                
+                if (weakself.refreshDelegate && [weakself.refreshDelegate respondsToSelector:@selector(jl_endRefresh:)]) {
+                    
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [weakself.refreshDelegate jl_endRefresh:weakself];
+                    });
+                    
+                }
+                
+            }];
+        });
+
+        
+        
+        
+    } else if (state == JLRefreshStatePulling) {
+        // 下拉刷新
+        
+    } else if (state == JLRefreshStateWillRefresh) {
+        // 松开刷新
+        
+    } else if (state == JLRefreshStateRefreshing) {
+        // 正在刷新
+        __weak typeof(self) weakself = self;
+        dispatch_async(dispatch_get_main_queue(), ^{
+            
+            [UIView animateWithDuration:JLRefreshDuration animations:^{
+                
+                weakself.scrollView.jl_insetTop = weakself.jl_height;
+                
+            } completion:^(BOOL finished) {
+                
+                if (weakself.refreshDelegate && [weakself.refreshDelegate respondsToSelector:@selector(jl_beginRefresh:)]) {
+                    
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [weakself.refreshDelegate jl_beginRefresh:weakself];
+                    });
+                    
+                }
+                
+            }];
+            
+        });
+
+    }
+}
+
+#pragma mark - Public
+
+- (void)setRefreshTitle:(NSString *)title forState:(JLRefreshState)state {
+    if (title == nil) {
+        return;
+    }
+    
+    [self.stateTitle setObject:title forKey:@(state)];
+}
+
+- (NSString *)refreshTitleForState:(JLRefreshState)state {
+    return [self.stateTitle objectForKey:@(state)];
+}
+
+#pragma mark - SubView
+
+- (UILabel *)refreshTitleLabel {
+    if (!_refreshTitleLabel) {
+        _refreshTitleLabel = [[UILabel alloc] init];
+        _refreshTitleLabel.textAlignment = NSTextAlignmentCenter;
+        _refreshTitleLabel.font = [UIFont systemFontOfSize:14.0f];
+    }
+    return _refreshTitleLabel;
+}
+
+
+@end

+ 65 - 0
RedAnt Mobile/RedAnt Mobile/Refresh/JLRefreshBasis.h

@@ -0,0 +1,65 @@
+//
+//  JLRefresh.h
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "UIScrollView+JLRefresh.h"
+#import "UIView+JLExtension.h"
+
+typedef enum {
+    /**空闲状态*/
+    JLRefreshStateIdle = 1 << 0,
+    /**下拉状态*/
+    JLRefreshStatePulling = 1 << 1,
+    /**即将刷新,还未松开*/
+    JLRefreshStateWillRefresh = 1 << 2,
+    /**刷新状态,松开*/
+    JLRefreshStateRefreshing = 1 << 3,
+    /**没有更多数据*/
+    JLRefreshStateNoMore = 1 << 4
+    
+} JLRefreshState;
+
+UIKIT_EXTERN NSTimeInterval const JLRefreshDuration;
+UIKIT_EXTERN CGFloat const JLRefreshHeight;
+UIKIT_EXTERN NSString *const JLRefreshKeyPathContentOffset;
+UIKIT_EXTERN NSString *const JLRefreshKeyPathContentSize;
+UIKIT_EXTERN NSString *const JLRefreshKeyPathContentInset;
+UIKIT_EXTERN NSString *const JLRefreshKeyPathState;
+
+@class JLRefreshBasis;
+@protocol JLRefreshDelegate <NSObject>
+
+- (void)jl_pullRefresh:(JLRefreshBasis *)refresh state:(JLRefreshState)state percentage:(float)percentage;//percentage's range [0,max)
+
+- (void)jl_beginRefresh:(JLRefreshBasis *)refresh;
+
+- (void)jl_endRefresh:(JLRefreshBasis *)refresh;
+
+@end
+
+@interface JLRefreshBasis : UIView
+
+@property (nonatomic,assign) JLRefreshState state;
+@property (nonatomic,assign) float pullPercentage;
+@property (nonatomic,weak,readonly) UIScrollView *scrollView;
+@property (nonatomic,assign,readonly) CGFloat scrollViewOriginInsetTop; ///<header只管Top
+@property (nonatomic,assign,readonly) CGFloat scrollViewOriginInsetBottom; ///<footer只管Bottom
+@property (nonatomic,strong,readonly) UIPanGestureRecognizer *pan;
+@property (nonatomic,weak) id<JLRefreshDelegate> refreshDelegate;
+@property (nonatomic,assign) float pullPercentate;
+
+- (void)endRefresh;
+
+- (void)prepareInterface;
+
+// 需要子类实现
+- (void)scrollViewContentSizeDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change;
+- (void)scrollViewContentOffsetDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change;
+- (void)scrollViewPanGestureStateDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change;
+
+@end

+ 142 - 0
RedAnt Mobile/RedAnt Mobile/Refresh/JLRefreshBasis.m

@@ -0,0 +1,142 @@
+//
+//  JLRefresh.m
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "JLRefreshBasis.h"
+
+NSTimeInterval const JLRefreshDuration = 0.25;
+CGFloat const JLRefreshHeight = 64;
+NSString *const JLRefreshKeyPathContentOffset = @"contentOffset";
+NSString *const JLRefreshKeyPathContentSize = @"contentSize";
+NSString *const JLRefreshKeyPathContentInset = @"contentInset";
+NSString *const JLRefreshKeyPathState = @"state";
+
+@interface JLRefreshBasis ()
+{
+    __weak UIScrollView *_scrollView;
+    UIPanGestureRecognizer *_pan;
+}
+
+@property (nonatomic,assign) CGFloat scrollViewOriginInsetTop;
+@property (nonatomic,assign) CGFloat scrollViewOriginInsetBottom;
+
+@end
+
+@implementation JLRefreshBasis
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    if (self = [super initWithFrame:frame]) {
+        [self prepareInterface];
+        self.state = JLRefreshStateIdle;
+    }
+    return self;
+}
+
+- (void)prepareInterface {
+    self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+    self.backgroundColor = [UIColor clearColor];
+    self.jl_height = JLRefreshHeight;
+}
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    self.jl_width = CGRectGetWidth(self.superview.frame);
+}
+
+- (void)dealloc {
+    [self resignObserver];
+}
+
+/**添加观察者*/
+- (void)registObserver {
+    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
+    
+    [_scrollView addObserver:self forKeyPath:JLRefreshKeyPathContentOffset options:options context:nil];
+    [_scrollView addObserver:self forKeyPath:JLRefreshKeyPathContentSize options:options context:nil];
+    [_scrollView addObserver:self forKeyPath:JLRefreshKeyPathState options:options context:nil];
+    _pan = _scrollView.panGestureRecognizer;
+    [_pan addObserver:self forKeyPath:JLRefreshKeyPathState options:options context:nil];
+    
+}
+
+/**移除观察者*/
+- (void)resignObserver {
+    
+    [_scrollView removeObserver:self forKeyPath:JLRefreshKeyPathContentOffset];
+    [_scrollView removeObserver:self forKeyPath:JLRefreshKeyPathContentSize];
+    [_scrollView removeObserver:self forKeyPath:JLRefreshKeyPathState];
+    [_pan removeObserver:self forKeyPath:JLRefreshKeyPathState];
+    _pan = nil;
+}
+
+- (void)willMoveToSuperview:(UIView *)newSuperview {
+    
+    [self resignObserver];
+    // 父控件不是ScrollView,不做设置
+    if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) {
+        _scrollView = nil;
+        return;
+    }
+
+    _scrollView = (UIScrollView *)newSuperview;
+    _scrollView.alwaysBounceVertical = YES; // 永远支持垂直弹簧效果
+    
+    self.state = JLRefreshStateIdle;
+        
+    self.scrollViewOriginInsetTop = _scrollView.jl_insetTop;
+    self.scrollViewOriginInsetBottom = _scrollView.jl_insetBottom;
+    
+    [self registObserver];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
+    
+    if (!self.isUserInteractionEnabled) {
+        return;
+    }
+    
+    if (keyPath == JLRefreshKeyPathContentSize) {
+        [self scrollViewContentSizeDidChange:change];
+    }
+    
+    if (self.hidden) {
+        return;
+    }
+    
+    if (keyPath == JLRefreshKeyPathContentOffset) {
+        [self scrollViewContentOffsetDidChange:change];
+    }
+    
+    
+    if (keyPath == JLRefreshKeyPathState) {
+        [self scrollViewPanGestureStateDidChange:change];
+    }
+    
+}
+
+- (void)scrollViewContentSizeDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+}
+
+- (void)scrollViewContentOffsetDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+        
+}
+
+- (void)scrollViewPanGestureStateDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+}
+
+- (void)setState:(JLRefreshState)state {
+    _state = state;
+}
+
+- (void)endRefresh {
+    
+    self.state = JLRefreshStateIdle;
+}
+
+@end

+ 27 - 0
RedAnt Mobile/RedAnt Mobile/Refresh/UIScrollView+JLRefresh.h

@@ -0,0 +1,27 @@
+//
+//  UIScrollView+JLRefresh.h
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class JLRefreshBasis;
+
+@interface UIScrollView (JLRefresh)
+
+@property (nonatomic,strong) JLRefreshBasis *jl_header;
+
+@property (nonatomic,strong) JLRefreshBasis *jl_footer;
+
+@property (nonatomic,assign) CGFloat jl_offsetY;
+
+@property (nonatomic,assign) CGFloat jl_insetTop;
+
+@property (nonatomic,assign) CGFloat jl_insetBottom;
+
+@property (nonatomic,assign) CGFloat jl_ContentHeight;
+
+@end

+ 92 - 0
RedAnt Mobile/RedAnt Mobile/Refresh/UIScrollView+JLRefresh.m

@@ -0,0 +1,92 @@
+//
+//  UIScrollView+JLRefresh.m
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "UIScrollView+JLRefresh.h"
+#import "JLRefreshBasis.h"
+#import <objc/runtime.h>
+
+@implementation UIScrollView (JLRefresh)
+
+static const char JLHeaderKey = '\a';
+
+- (void)setJl_header:(JLRefreshBasis *)jl_header {
+    
+    if (jl_header) {
+        [self.jl_header removeFromSuperview];
+        [self insertSubview:jl_header atIndex:0];
+        
+        [self willChangeValueForKey:@"jl_header"];
+        objc_setAssociatedObject(self, &JLHeaderKey, jl_header, OBJC_ASSOCIATION_ASSIGN);
+        [self didChangeValueForKey:@"jl_header"];
+    }
+    
+}
+
+- (JLRefreshBasis *)jl_header {
+    return objc_getAssociatedObject(self, &JLHeaderKey);
+}
+
+static const char JLFooterKey = '\b';
+
+- (void)setJl_footer:(JLRefreshBasis *)jl_footer {
+    
+    if (jl_footer) {
+        [self.jl_footer removeFromSuperview];
+        [self insertSubview:jl_footer atIndex:0];
+        
+        [self willChangeValueForKey:@"jl_footer"];
+        objc_setAssociatedObject(self, &JLFooterKey, jl_footer, OBJC_ASSOCIATION_ASSIGN);
+        [self didChangeValueForKey:@"jl_footer"];
+    }
+}
+
+- (JLRefreshBasis *)jl_footer {
+    return objc_getAssociatedObject(self, &JLFooterKey);
+}
+
+- (void)setJl_offsetY:(CGFloat)jl_offsetY {
+    CGPoint contentOff = self.contentOffset;
+    contentOff.y = jl_offsetY;
+    self.contentOffset = contentOff;
+}
+
+- (CGFloat)jl_offsetY {
+    return self.contentOffset.y;
+}
+
+- (void)setJl_insetTop:(CGFloat)jl_insetTop {
+    UIEdgeInsets inset = self.contentInset;
+    inset.top = jl_insetTop;
+    self.contentInset = inset;
+}
+
+- (CGFloat)jl_insetTop {
+    return self.contentInset.top;
+}
+
+- (void)setJl_insetBottom:(CGFloat)jl_insetBottom {
+    UIEdgeInsets inset = self.contentInset;
+    inset.bottom = jl_insetBottom;
+    self.contentInset = inset;
+}
+
+- (CGFloat)jl_insetBottom {
+    return self.contentInset.bottom;
+}
+
+- (void)setJl_ContentHeight:(CGFloat)jl_ContentHeight {
+    CGSize contentSize = self.contentSize;
+    contentSize.height = jl_ContentHeight;
+    self.contentSize = contentSize;
+}
+
+- (CGFloat)jl_ContentHeight {
+    return self.contentSize.height;
+}
+
+@end

+ 22 - 0
RedAnt Mobile/RedAnt Mobile/Refresh/UIView+JLExtension.h

@@ -0,0 +1,22 @@
+//
+//  UIView+Extension.h
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface UIView (JLExtension)
+
+#pragma mark - Frame
+
+@property (nonatomic,assign) CGFloat jl_x;
+@property (nonatomic,assign) CGFloat jl_y;
+@property (nonatomic,assign) CGFloat jl_height;
+@property (nonatomic,assign) CGFloat jl_width;
+@property (nonatomic,assign) CGFloat jl_right;
+@property (nonatomic,assign) CGFloat jl_bottom;
+
+@end

+ 71 - 0
RedAnt Mobile/RedAnt Mobile/Refresh/UIView+JLExtension.m

@@ -0,0 +1,71 @@
+//
+//  UIView+Extension.m
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "UIView+JLExtension.h"
+
+@implementation UIView (JLExtension)
+
+#pragma mark - Frame
+
+- (void)setJl_x:(CGFloat)jl_x {
+    CGRect frame = self.frame;
+    frame.origin.x = jl_x;
+    self.frame = frame;
+}
+
+- (CGFloat)jl_x {
+    return self.frame.origin.x;
+}
+
+- (void)setJl_y:(CGFloat)jl_y {
+    CGRect frame = self.frame;
+    frame.origin.y = jl_y;
+    self.frame = frame;
+}
+
+- (CGFloat)jl_y {
+    return self.frame.origin.y;
+}
+
+- (void)setJl_width:(CGFloat)jl_width {
+    CGRect frame = self.frame;
+    frame.size.width = jl_width;
+    self.frame = frame;
+}
+
+- (CGFloat)jl_width {
+    return self.frame.size.width;
+}
+
+- (void)setJl_height:(CGFloat)jl_height {
+    CGRect frame = self.frame;
+    frame.size.height = jl_height;
+    self.frame = frame;
+}
+
+- (CGFloat)jl_height {
+    return self.frame.size.height;
+}
+
+- (void)setJl_right:(CGFloat)jl_right {
+    self.jl_x = jl_right - self.jl_width;
+}
+
+- (CGFloat)jl_right {
+    return self.jl_x + self.jl_width;
+}
+
+- (void)setJl_bottom:(CGFloat)jl_bottom {
+    self.jl_y = jl_bottom - self.jl_height;
+}
+
+- (CGFloat)jl_bottom {
+    return self.jl_y + self.jl_height;
+}
+
+@end

+ 118 - 13
RedAnt Mobile/RedAnt Mobile/ResultViewController.m

@@ -11,10 +11,12 @@
 #import "FullyShowViewController.h"
 #import "RANetwork.h"
 #import "RAUtils.h"
+#import "JLRefreshHeader.h"
+#import "JLRefreshFooter.h"
 
 static const int delta = 25;
 
-@interface ResultViewController ()
+@interface ResultViewController () <JLRefreshDelegate>
 
 @property (nonatomic,assign) NSInteger offset;
 @property (nonatomic,strong) NSDictionary *params;
@@ -68,16 +70,17 @@ static const int delta = 25;
 //    self.content_data = [[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil] mutableCopy];
     
     
-    int width=[self.content_layout[@"header"][@"width"] intValue];
-    if(width<self.tableview.frame.size.width)
-        width=self.tableview.frame.size.width;
-    
-    CGRect frame = CGRectMake(self.tableview.frame.origin.x, self.tableview.frame.origin.y, width, self.tableview.frame.size.height);
-    self.tableview.frame=frame;
-    self.scrollview.contentSize=self.tableview.frame.size;
+//    int width=[self.content_layout[@"header"][@"width"] intValue];
+//    if(width<self.tableview.frame.size.width)
+//        width=self.tableview.frame.size.width;
+//
+//    CGRect frame = CGRectMake(self.tableview.frame.origin.x, self.tableview.frame.origin.y, width, self.tableview.frame.size.height);
+//    self.tableview.frame=frame;
+//    self.scrollview.contentSize=self.tableview.frame.size;
     // Do any additional setup after loading the view, typically from a nib.
     
     [self loadContent];
+    [self setupTableRefreshView];
 }
 
 -(void)cellDoubleTapAction:(UIGestureRecognizer*)gestureRecognizer
@@ -360,19 +363,53 @@ static const int delta = 25;
 
 #pragma mark - Public
 
-- (NSInteger)resultItemCount {
-    return [[self.content_data objectForKey:@"count"] integerValue];
-}
-
 - (void)setQueryParams:(NSDictionary *)params {
     self.params = params;
 }
 
+#pragma mark - Private
+
+- (void)setupTableRefreshView {
+    self.tableview.jl_header = [[JLRefreshHeader alloc] init];
+    self.tableview.jl_header.refreshDelegate = self;
+    
+    self.tableview.jl_footer = [[JLRefreshFooter alloc] init];
+    self.tableview.jl_footer.refreshDelegate = self;
+}
+
+- (NSInteger)resultItemCount {
+    return [[self.content_data objectForKey:@"count"] integerValue];
+}
+
 - (void)loadContent {
+    [self loadContentWithOption:0 Complete:nil];
+}
+/**
+ * 0: init load
+ * 1: refresh
+ * 2: load more
+ */
+- (void)loadContentWithOption:(int)option Complete:(void (^)(int result,int count))finish {
     if (self.params) {
+        
+        NSMutableDictionary *dic = self.params.mutableCopy;
+        switch (option) {
+            case 0:
+            case 1: {
+                self.offset = 0;
+            }
+            break;
+            case 2: {
+                self.offset = [self resultItemCount];
+            }
+            break;
+        }
+        [dic setObject:@(self.offset) forKey:@"offset"];
+        [dic setObject:@(delta) forKey:@"limit"];
+        
         __weak typeof(self) weakSelf = self;
         dispatch_async(dispatch_get_global_queue(0, 0), ^{
-            NSDictionary *contentDic = [RANetwork query:self.params.mutableCopy];
+            NSDictionary *contentDic = [RANetwork query:dic];
             NSInteger result = [[contentDic objectForKey:@"result"] integerValue];
             dispatch_async(dispatch_get_main_queue(), ^{
                 if (weakSelf) {
@@ -380,10 +417,16 @@ static const int delta = 25;
                     if (result == RESULT_TRUE) {
                         strongSelf.content_layout = [contentDic objectForKey:@"layout"];
                         strongSelf.content_data = [contentDic objectForKey:@"data"];
+                        [strongSelf updateTableFrame];
                         [strongSelf.tableview reloadData];
                     } else {
                         [RAUtils message_alert:[contentDic objectForKey:@"err_msg"] title:@"Warning" controller:strongSelf];
                     }
+                    
+                    if (finish) {
+                        finish((int)result,(int)[self resultItemCount]);
+                    }
+                    
                 }
                 
             });
@@ -391,4 +434,66 @@ static const int delta = 25;
     }
 }
 
+- (void)updateTableFrame {
+    int width=[self.content_layout[@"header"][@"width"] intValue];
+    if(width<self.tableview.frame.size.width)
+    width=self.tableview.frame.size.width;
+    
+    CGRect frame = CGRectMake(self.tableview.frame.origin.x, self.tableview.frame.origin.y, width, self.tableview.frame.size.height);
+    self.tableview.frame=frame;
+    self.scrollview.contentSize=self.tableview.frame.size;
+}
+
+#pragma mark - RefreshDelegate
+
+- (void)jl_pullRefresh:(JLRefreshBasis *)refresh state:(JLRefreshState)state percentage:(float)percentage {
+    
+    if ([refresh isEqual:self.tableview.jl_header]) {
+       
+    }
+    
+    if (refresh == self.tableview.jl_footer) {
+       
+    }
+    
+}
+
+- (void)jl_endRefresh:(JLRefreshBasis *)refresh {
+    // state == idle
+    
+    // refresh UI
+    [self.tableview reloadData];
+    if (refresh == self.tableview.jl_footer && refresh.state == JLRefreshStateNoMore) {
+        
+    }
+
+}
+
+- (void)jl_beginRefresh:(JLRefreshBasis *)refresh {
+    // state == refreshing
+    // load data
+    
+    // finish loading data
+    int option = 0;
+    if ([refresh isEqual:self.tableview.jl_header]) {
+        option = 1;
+    }
+    if (refresh == self.tableview.jl_footer) {
+        option = 2;
+    }
+    __weak typeof(self) weakSelf = self;
+    [self loadContentWithOption:option Complete:^(int result, int count) {
+        if ([refresh isEqual:weakSelf.tableview.jl_header]) {
+            [weakSelf.tableview.jl_header performSelector:@selector(endRefresh)];
+        }
+        
+        if (refresh == weakSelf.tableview.jl_footer) {
+            [weakSelf.tableview.jl_footer performSelector:@selector(endRefresh)];
+            if (result == 2 && count < delta) {
+                [weakSelf.tableview.jl_footer performSelector:@selector(noMoreData)];
+            }
+        }
+    }];
+}
+
 @end