Browse Source

Refactor for easier use

Refactor for easier use
M了个J 11 years ago
parent
commit
6c21d97943

+ 6 - 0
MJRefreshExample/MJRefreshExample.xcodeproj/project.pbxproj

@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		2D1D8FB21935C0CD0019D689 /* UIScrollView+MJRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D1D8FB11935C0CD0019D689 /* UIScrollView+MJRefresh.m */; };
 		2D9CAF6B192FAD750011F500 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D9CAF6A192FAD750011F500 /* Foundation.framework */; };
 		2D9CAF6D192FAD750011F500 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D9CAF6C192FAD750011F500 /* CoreGraphics.framework */; };
 		2D9CAF6F192FAD750011F500 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D9CAF6E192FAD750011F500 /* UIKit.framework */; };
@@ -42,6 +43,8 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
+		2D1D8FB01935C0CD0019D689 /* UIScrollView+MJRefresh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+MJRefresh.h"; sourceTree = "<group>"; };
+		2D1D8FB11935C0CD0019D689 /* UIScrollView+MJRefresh.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+MJRefresh.m"; sourceTree = "<group>"; };
 		2D9CAF67192FAD750011F500 /* MJRefreshExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MJRefreshExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		2D9CAF6A192FAD750011F500 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
 		2D9CAF6C192FAD750011F500 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
@@ -230,6 +233,8 @@
 				2D9CAFB8192FAD9F0011F500 /* MJRefreshFooterView.m */,
 				2D9CAFB9192FAD9F0011F500 /* MJRefreshHeaderView.h */,
 				2D9CAFBA192FAD9F0011F500 /* MJRefreshHeaderView.m */,
+				2D1D8FB01935C0CD0019D689 /* UIScrollView+MJRefresh.h */,
+				2D1D8FB11935C0CD0019D689 /* UIScrollView+MJRefresh.m */,
 			);
 			path = MJRefresh;
 			sourceTree = "<group>";
@@ -340,6 +345,7 @@
 				2D9CAFC6192FAD9F0011F500 /* MJRefreshHeaderView.m in Sources */,
 				2D9CAFBB192FAD9F0011F500 /* MJCollectionViewController.m in Sources */,
 				2D9CAFC1192FAD9F0011F500 /* MJAppDelegate.m in Sources */,
+				2D1D8FB21935C0CD0019D689 /* UIScrollView+MJRefresh.m in Sources */,
 				2D9CAFC3192FAD9F0011F500 /* MJRefreshBaseView.m in Sources */,
 				2D9CAFC0192FAD9F0011F500 /* main.m in Sources */,
 				2D9CAFBC192FAD9F0011F500 /* MJNavigationController.m in Sources */,

+ 1 - 1
MJRefreshExample/MJRefreshExample.xcodeproj/project.xcworkspace/xcshareddata/MJRefreshExample.xccheckout

@@ -5,7 +5,7 @@
 	<key>IDESourceControlProjectFavoriteDictionaryKey</key>
 	<false/>
 	<key>IDESourceControlProjectIdentifier</key>
-	<string>52E93CEA-1330-47E4-BB31-02C59CC92E7E</string>
+	<string>4FB91294-5501-4785-B4F0-10D209FA2C34</string>
 	<key>IDESourceControlProjectName</key>
 	<string>MJRefreshExample</string>
 	<key>IDESourceControlProjectOriginsDictionary</key>

BIN
MJRefreshExample/MJRefreshExample.xcodeproj/project.xcworkspace/xcuserdata/mj.xcuserdatad/UserInterfaceState.xcuserstate


+ 17 - 0
MJRefreshExample/MJRefreshExample.xcodeproj/xcuserdata/mj.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Bucket
+   type = "1"
+   version = "2.0">
+   <Breakpoints>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "Yes"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            scope = "0"
+            stopOnStyle = "0">
+         </BreakpointContent>
+      </BreakpointProxy>
+   </Breakpoints>
+</Bucket>

+ 45 - 80
MJRefreshExample/MJRefreshExample/Classes/Controller/MJCollectionViewController.m

@@ -18,12 +18,7 @@
 #import "MJCollectionViewController.h"
 #import "MJRefresh.h"
 
-@interface MJCollectionViewController () <MJRefreshBaseViewDelegate>
-/**
- *  刷新控件
- */
-@property (weak, nonatomic) MJRefreshFooterView *footer;
-@property (weak, nonatomic) MJRefreshHeaderView *header;
+@interface MJCollectionViewController ()
 /**
  *  存放假数据
  */
@@ -71,27 +66,8 @@ - (void)viewDidLoad
     [self setupCollectionView];
     
     // 2.集成刷新控件
-    [self setupRefresh];
-}
-
-/**
- *  集成刷新控件
- */
-- (void)setupRefresh
-{
-    // 1.下拉刷新
-    MJRefreshHeaderView *header = [MJRefreshHeaderView header];
-    header.scrollView = self.collectionView;
-    header.delegate = self;
-#warning 自动刷新(一进入程序就下拉刷新)
-    [header beginRefreshing];
-    self.header = header;
-    
-    // 2.上拉加载更多
-    MJRefreshFooterView *footer = [MJRefreshFooterView footer];
-    footer.scrollView = self.collectionView;
-    footer.delegate = self;
-    self.footer = footer;
+    [self addHeader];
+    [self addFooter];
 }
 
 /**
@@ -104,68 +80,57 @@ - (void)setupCollectionView
     [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:MJCollectionViewCellIdentifier];
 }
 
-/**
- 为了保证内部不泄露,在dealloc中释放占用的内存
- */
-- (void)dealloc
+- (void)addHeader
 {
-    NSLog(@"MJCollectionViewController--dealloc---");
-    [self.header free];
-    [self.footer free];
-}
-
-#pragma mark - 刷新控件的代理方法
-#pragma mark 开始进入刷新状态
-- (void)refreshViewBeginRefreshing:(MJRefreshBaseView *)refreshView
-{
-    NSLog(@"%@----开始进入刷新状态", refreshView.class);
-    
-    // 1.添加假数据
-    for (int i = 0; i<5; i++) {
-        if ([refreshView isKindOfClass:[MJRefreshHeaderView class]]) { // 下拉刷新
-            [self.fakeColors insertObject:MJRandomColor atIndex:0];
-        } else { // 上拉加载更多
-            [self.fakeColors addObject:MJRandomColor];
+    __unsafe_unretained typeof(self) vc = self;
+    // 添加下拉刷新头部控件
+    [self.collectionView addHeaderWithCallback:^{
+        // 进入刷新状态就会回调这个Block
+        
+        // 增加5条假数据
+        for (int i = 0; i<5; i++) {
+            [vc.fakeColors insertObject:MJRandomColor atIndex:0];
         }
-    }
+        
+        // 模拟延迟加载数据,因此2秒后才调用)
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+            [vc.collectionView reloadData];
+            // 结束刷新
+            [vc.collectionView headerEndRefreshing];
+        });
+    }];
     
-    // 2.2秒后刷新表格UI
-    [self performSelector:@selector(doneWithView:) withObject:refreshView afterDelay:2.0];
-}
-
-#pragma mark 刷新完毕
-- (void)refreshViewEndRefreshing:(MJRefreshBaseView *)refreshView
-{
-    NSLog(@"%@----刷新完毕", refreshView.class);
+#warning 自动刷新(一进入程序就下拉刷新)
+    [self.collectionView headerBeginRefreshing];
 }
 
-- (void)doneWithView:(MJRefreshBaseView *)refreshView
+- (void)addFooter
 {
-    // 刷新表格
-    [self.collectionView reloadData];
-    
-    // (最好在刷新表格后调用)调用endRefreshing可以结束刷新状态
-    [refreshView endRefreshing];
+    __unsafe_unretained typeof(self) vc = self;
+    // 添加上拉刷新尾部控件
+    [self.collectionView addFooterWithCallback:^{
+        // 进入刷新状态就会回调这个Block
+        
+        // 增加5条假数据
+        for (int i = 0; i<5; i++) {
+            [vc.fakeColors addObject:MJRandomColor];
+        }
+        
+        // 模拟延迟加载数据,因此2秒后才调用)
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+            [vc.collectionView reloadData];
+            // 结束刷新
+            [vc.collectionView footerEndRefreshing];
+        });
+    }];
 }
 
-#pragma mark 监听刷新状态的改变
-- (void)refreshView:(MJRefreshBaseView *)refreshView stateChange:(MJRefreshState)state
+/**
+ 为了保证内部不泄露,在dealloc中释放占用的内存
+ */
+- (void)dealloc
 {
-    switch (state) {
-        case MJRefreshStateNormal:
-            NSLog(@"%@----切换到:普通状态", refreshView.class);
-            break;
-            
-        case MJRefreshStatePulling:
-            NSLog(@"%@----切换到:松开即可刷新的状态", refreshView.class);
-            break;
-            
-        case MJRefreshStateRefreshing:
-            NSLog(@"%@----切换到:正在刷新状态", refreshView.class);
-            break;
-        default:
-            break;
-    }
+    NSLog(@"MJCollectionViewController--dealloc---");
 }
 
 #pragma mark - collection数据源代理

+ 40 - 77
MJRefreshExample/MJRefreshExample/Classes/Controller/MJTableViewController.m

@@ -19,11 +19,6 @@
 #define MJRandomData [NSString stringWithFormat:@"随机数据---%d", arc4random_uniform(1000000)]
 
 @interface MJTableViewController ()
-/**
- *  刷新控件
- */
-@property (weak, nonatomic) MJRefreshFooterView *footer;
-@property (weak, nonatomic) MJRefreshHeaderView *header;
 /**
  *  存放假数据
  */
@@ -55,11 +50,7 @@ - (void)viewDidLoad
     [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:MJTableViewCellIdentifier];
     
     // 2.集成刷新控件
-    // 2.1.下拉刷新
-    [self addHeader];
-    
-    // 2.2.上拉加载更多
-    [self addFooter];
+    [self setupRefresh];
 }
 
 /**
@@ -68,85 +59,57 @@ - (void)viewDidLoad
 - (void)dealloc
 {
     NSLog(@"MJTableViewController--dealloc---");
-    [self.header free];
-    [self.footer free];
 }
 
-- (void)addFooter
+
+
+/**
+ *  集成刷新控件
+ */
+- (void)setupRefresh
 {
-    MJRefreshFooterView *footer = [MJRefreshFooterView footer];
-    footer.scrollView = self.tableView;
+    // 1.下拉刷新(进入刷新状态就会调用self的headerRereshing)
+    [self.tableView addHeaderWithTarget:self action:@selector(headerRereshing)];
+#warning 自动刷新(一进入程序就下拉刷新)
+    [self.tableView headerBeginRefreshing];
     
-    __unsafe_unretained MJTableViewController *vc = self;
-    // 进入刷新状态就会调用beginRefreshingBlock
-    footer.beginRefreshingBlock = ^(MJRefreshBaseView *refreshView) {
-        // 增加5条假数据
-        for (int i = 0; i<5; i++) {
-            [vc.fakeData addObject:MJRandomData];
-        }
-        
-        // 模拟延迟加载数据,因此2秒后才调用)
-        // 这里的refreshView其实就是footer
-        [vc performSelector:@selector(doneWithView:) withObject:refreshView afterDelay:2.0];
-        
-        NSLog(@"%@----开始进入刷新状态", refreshView.class);
-    };
-    self.footer = footer;
+    // 2.上拉加载更多(进入刷新状态就会调用self的footerRereshing)
+    [self.tableView addFooterWithTarget:self action:@selector(footerRereshing)];
 }
 
-- (void)addHeader
+#pragma mark 开始进入刷新状态
+- (void)headerRereshing
 {
-    MJRefreshHeaderView *header = [MJRefreshHeaderView header];
-    header.scrollView = self.tableView;
+    // 1.添加假数据
+    for (int i = 0; i<5; i++) {
+        [self.fakeData insertObject:MJRandomData atIndex:0];
+    }
     
-    __unsafe_unretained MJTableViewController *vc = self;
-    header.beginRefreshingBlock = ^(MJRefreshBaseView *refreshView) {
-        // 进入刷新状态就会回调这个Block
-        
-        // 增加5条假数据
-        for (int i = 0; i<5; i++) {
-            [vc.fakeData insertObject:MJRandomData atIndex:0];
-        }
-        
-        // 模拟延迟加载数据,因此2秒后才调用)
-        // 这里的refreshView其实就是header
-        [vc performSelector:@selector(doneWithView:) withObject:refreshView afterDelay:2.0];
+    // 2.2秒后刷新表格UI
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        // 刷新表格
+        [self.tableView reloadData];
         
-        NSLog(@"%@----开始进入刷新状态", refreshView.class);
-    };
-    header.endStateChangeBlock = ^(MJRefreshBaseView *refreshView) {
-        // 刷新完毕就会回调这个Block
-        NSLog(@"%@----刷新完毕", refreshView.class);
-    };
-    header.refreshStateChangeBlock = ^(MJRefreshBaseView *refreshView, MJRefreshState state) {
-        // 控件的刷新状态切换了就会调用这个block
-        switch (state) {
-            case MJRefreshStateNormal:
-                NSLog(@"%@----切换到:普通状态", refreshView.class);
-                break;
-                
-            case MJRefreshStatePulling:
-                NSLog(@"%@----切换到:松开即可刷新的状态", refreshView.class);
-                break;
-                
-            case MJRefreshStateRefreshing:
-                NSLog(@"%@----切换到:正在刷新状态", refreshView.class);
-                break;
-            default:
-                break;
-        }
-    };
-#warning 自动刷新(一进入程序就下拉刷新)
-    [header beginRefreshing];
-    self.header = header;
+        // (最好在刷新表格后调用)调用endRefreshing可以结束刷新状态
+        [self.tableView headerEndRefreshing];
+    });
 }
 
-- (void)doneWithView:(MJRefreshBaseView *)refreshView
+- (void)footerRereshing
 {
-    // 刷新表格
-    [self.tableView reloadData];
-    // (最好在刷新表格后调用)调用endRefreshing可以结束刷新状态
-    [refreshView endRefreshing];
+    // 1.添加假数据
+    for (int i = 0; i<5; i++) {
+        [self.fakeData addObject:MJRandomData];
+    }
+    
+    // 2.2秒后刷新表格UI
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        // 刷新表格
+        [self.tableView reloadData];
+        
+        // (最好在刷新表格后调用)调用endRefreshing可以结束刷新状态
+        [self.tableView footerEndRefreshing];
+    });
 }
 
 #pragma mark - Table view data source

+ 15 - 22
MJRefreshExample/MJRefreshExample/MJRefresh/MJRefresh.h

@@ -1,34 +1,27 @@
 
-#import "MJRefreshFooterView.h"
-#import "MJRefreshHeaderView.h"
+#import "UIScrollView+MJRefresh.h"
 
 /**
  MJ友情提示:
  1. 添加头部控件的方法
- MJRefreshHeaderView *header = [MJRefreshHeaderView header];
- header.scrollView = self.collectionView; // 或者tableView
+ [self.tableView addHeaderWithTarget:self action:@selector(headerRereshing)];
+ 或者
+ [self.tableView addHeaderWithCallback:^{ }];
  
  2. 添加尾部控件的方法
- MJRefreshFooterView *footer = [MJRefreshFooterView footer];
- footer.scrollView = self.collectionView; // 或者tableView
+ [self.tableView addFooterWithTarget:self action:@selector(footerRereshing)];
+ 或者
+ [self.tableView addFooterWithCallback:^{ }];
  
- 3. 监听刷新控件的状态有2种方式:
- * 设置delegate,通过代理方法监听(参考MJCollectionViewController.m)
- * 设置block,通过block回调监听(参考MJTableViewController.m)
+ 3. 可以在MJRefreshConst.h和MJRefreshConst.m文件中自定义显示的文字内容和文字颜色
  
- 4. 可以在MJRefreshConst.h和MJRefreshConst.m文件中自定义显示的文字内容和文字颜色
+ 4. 本框架兼容iOS6\iOS7,iPhone\iPad横竖屏
  
- 5. 本框架兼容iOS6\iOS7,iPhone\iPad横竖屏
+ 5.自动进入刷新状态
+ 1> [self.tableView headerBeginRefreshing];
+ 2> [self.tableView footerBeginRefreshing];
  
- 6.为了保证内部不泄露,最好在控制器的dealloc中释放占用的内存
-    - (void)dealloc
-    {
-        [_header free];
-        [_footer free];
-    }
- 
- 7.自动刷新:调用beginRefreshing可以自动进入下拉刷新状态
- 
- 8.结束刷新
- 1> endRefreshing
+ 6.结束刷新
+ 1> [self.tableView headerEndRefreshing];
+ 2> [self.tableView footerEndRefreshing];
 */

+ 3 - 54
MJRefreshExample/MJRefreshExample/MJRefresh/MJRefreshBaseView.h

@@ -23,37 +23,6 @@ typedef enum {
     MJRefreshViewTypeFooter = 1 // 尾部控件
 } MJRefreshViewType;
 
-#pragma mark - 回调的Block定义
-typedef void (^BeginRefreshingBlock)(MJRefreshBaseView *refreshView);
-typedef void (^EndRefreshingBlock)(MJRefreshBaseView *refreshView);
-typedef void (^RefreshStateChangeBlock)(MJRefreshBaseView *refreshView, MJRefreshState state);
-
-/**
- 代理的协议定义
- */
-@protocol MJRefreshBaseViewDelegate <NSObject>
-@optional
-/**
- *  开始进入刷新状态就会调用
- *
- *  @param refreshView 刷新控件
- */
-- (void)refreshViewBeginRefreshing:(MJRefreshBaseView *)refreshView;
-/**
- *  刷新完毕就会调用
- *
- *  @param refreshView 刷新控件
- */
-- (void)refreshViewEndRefreshing:(MJRefreshBaseView *)refreshView;
-/**
- *  刷新状态变更就会调用
- *
- *  @param refreshView 刷新控件
- *  @param state       刷新控件的状态
- */
-- (void)refreshView:(MJRefreshBaseView *)refreshView stateChange:(MJRefreshState)state;
-@end
-
 /**
  类的声明
  */
@@ -75,24 +44,9 @@ typedef void (^RefreshStateChangeBlock)(MJRefreshBaseView *refreshView, MJRefres
 @property (nonatomic, weak, readonly) UIActivityIndicatorView *activityView;
 @property (nonatomic, assign, readonly) UIEdgeInsets scrollViewInitInset;
 
-#pragma mark - 监听相关
-/**
- *  开始进入刷新状态就会调用
- */
-@property (nonatomic, copy) BeginRefreshingBlock beginRefreshingBlock;
-/**
- *  刷新完毕就会调用
- */
-@property (nonatomic, copy) RefreshStateChangeBlock refreshStateChangeBlock;
-/**
- *  刷新状态变更就会调用
- */
-@property (nonatomic, copy) EndRefreshingBlock endStateChangeBlock;
-
-/**
- *  代理
- */
-@property (nonatomic, weak) id<MJRefreshBaseViewDelegate> delegate;
+#pragma mark - 回调
+@property (weak, nonatomic) id beginRefreshingTaget;
+@property (assign, nonatomic) SEL beginRefreshingAction;
 
 #pragma mark - 刷新相关
 /**
@@ -108,11 +62,6 @@ typedef void (^RefreshStateChangeBlock)(MJRefreshBaseView *refreshView, MJRefres
  */
 - (void)endRefreshing;
 
-/**
- *  结束使用、释放资源
- */
-- (void)free;
-
 #pragma mark - 交给子类去实现 和 调用
 @property (assign, nonatomic) MJRefreshState state;
 - (int)totalDataCountInScrollView;

+ 13 - 50
MJRefreshExample/MJRefreshExample/MJRefresh/MJRefreshBaseView.m

@@ -8,6 +8,7 @@
 
 #import "MJRefreshBaseView.h"
 #import "MJRefreshConst.h"
+#import <objc/message.h>
 
 @interface  MJRefreshBaseView()
 @property (assign, nonatomic) BOOL hasInitInset;
@@ -140,14 +141,9 @@ - (void)setBounds:(CGRect)bounds
 */
 - (void)setScrollView:(UIScrollView *)scrollView
 {
-    // 移除之前的监听器
-    [self.scrollView removeObserver:self forKeyPath:MJRefreshContentOffset context:nil];
-    // 监听contentOffset
-    [scrollView addObserver:self forKeyPath:MJRefreshContentOffset options:NSKeyValueObservingOptionNew context:nil];
-    
+    [scrollView addSubview:self];
     // 设置scrollView
     _scrollView = scrollView;
-    [scrollView addSubview:self];
 }
 
 #pragma mark 监听UIScrollView的contentOffset属性
@@ -168,37 +164,18 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
         if (self.state == MJRefreshStatePulling && offsetY <= validOffsetY) {
             // 转为普通状态
             self.state = MJRefreshStateNormal;
-            [self notifyStateChange];
         } else if (self.state == MJRefreshStateNormal && offsetY > validOffsetY) {
             // 转为即将刷新状态
             self.state = MJRefreshStatePulling;
-            [self notifyStateChange];
         }
     } else { // 即将刷新 && 手松开
         if (self.state == MJRefreshStatePulling) {
             // 开始刷新
             self.state = MJRefreshStateRefreshing;
-            [self notifyStateChange];
         }
     }
 }
 
-/**
- *  通知状态改变
- */
-- (void)notifyStateChange
-{
-    // 通知代理
-    if ([self.delegate respondsToSelector:@selector(refreshView:stateChange:)]) {
-        [self.delegate refreshView:self stateChange:self.state];
-    }
-    
-    // 回调
-    if (self.refreshStateChangeBlock) {
-        self.refreshStateChangeBlock(self, self.state);
-    }
-}
-
 #pragma mark 设置状态
 - (void)setState:(MJRefreshState)state
 {
@@ -220,15 +197,7 @@ - (void)setState:(MJRefreshState)state
             
             // 说明是刚刷新完毕 回到 普通状态的
             if (MJRefreshStateRefreshing == self.state) {
-                // 通知代理
-                if ([self.delegate respondsToSelector:@selector(refreshViewEndRefreshing:)]) {
-                    [self.delegate refreshViewEndRefreshing:self];
-                }
                 
-                // 回调
-                if (self.endStateChangeBlock) {
-                    self.endStateChangeBlock(self);
-                }
             }
             
 			break;
@@ -243,14 +212,8 @@ - (void)setState:(MJRefreshState)state
 			self.arrowImage.hidden = YES;
             self.arrowImage.transform = CGAffineTransformIdentity;
             
-            // 通知代理
-            if ([self.delegate respondsToSelector:@selector(refreshViewBeginRefreshing:)]) {
-                [self.delegate refreshViewBeginRefreshing:self];
-            }
-            
-            // 回调
-            if (self.beginRefreshingBlock) {
-                self.beginRefreshingBlock(self);
+            if ([self.beginRefreshingTaget respondsToSelector:self.beginRefreshingAction]) {
+                objc_msgSend(self.beginRefreshingTaget, self.beginRefreshingAction, self);
             }
 			break;
         default:
@@ -289,16 +252,16 @@ - (void)endRefreshing
 #pragma mark - 随便实现
 - (CGFloat)validY { return 0;}
 - (MJRefreshViewType)viewType {return MJRefreshViewTypeHeader;}
-- (void)free
-{
-    [self.scrollView removeObserver:self forKeyPath:MJRefreshContentOffset];
-}
-- (void)removeFromSuperview
+
+- (void)willMoveToSuperview:(UIView *)newSuperview
 {
-    MJLog(@"removeFromSuperview");
-//    [self free];
-    self.scrollView = nil;
-    [super removeFromSuperview];
+    if (self.superview) { // 旧的父控件
+        [self.superview removeObserver:self forKeyPath:MJRefreshContentOffset context:nil];
+    }
+    
+    if (newSuperview) { // 新的父控件
+        [newSuperview addObserver:self forKeyPath:MJRefreshContentOffset options:NSKeyValueObservingOptionNew context:nil];
+    }
 }
 
 - (int)totalDataCountInScrollView

+ 23 - 0
MJRefreshExample/MJRefreshExample/MJRefresh/MJRefreshFooterView.h

@@ -7,7 +7,30 @@
 //  上拉加载更多
 
 #import "MJRefreshBaseView.h"
+@class MJRefreshFooterView;
+
+/**
+ 代理的协议定义
+ */
+@protocol MJRefreshFooterViewDelegate <NSObject>
+@optional
+/**
+ *  开始进入刷新状态就会调用
+ *
+ *  @param refreshFooterView 刷新控件
+ */
+- (void)refreshFooterViewBeginRefreshing:(MJRefreshFooterView *)refreshFooterView;
+@end
 
 @interface MJRefreshFooterView : MJRefreshBaseView
 + (instancetype)footer;
+/**
+ *  代理
+ */
+@property (weak, nonatomic) id<MJRefreshFooterViewDelegate> delegate;
+
+/**
+ *  开始进入刷新状态就会调用
+ */
+@property (nonatomic, copy) void (^beginRefreshingCallback)(MJRefreshFooterView *refreshFooterView);
 @end

+ 25 - 20
MJRefreshExample/MJRefreshExample/MJRefresh/MJRefreshFooterView.m

@@ -40,19 +40,27 @@ - (void)setFrame:(CGRect)frame
     }
 }
 
+- (void)willMoveToSuperview:(UIView *)newSuperview
+{
+    [super willMoveToSuperview:newSuperview];
+    
+    if (self.superview) { // 旧的父控件
+        [self.superview removeObserver:self forKeyPath:MJRefreshContentSize context:nil];
+    }
+    
+    if (newSuperview) { // 新的父控件
+        [newSuperview addObserver:self forKeyPath:MJRefreshContentSize options:NSKeyValueObservingOptionNew context:nil];
+    }
+}
+
 #pragma mark - UIScrollView相关
 #pragma mark 重写设置ScrollView
 - (void)setScrollView:(UIScrollView *)scrollView
 {
-    // 1.移除以前的监听器
-    [self.scrollView removeObserver:self forKeyPath:MJRefreshContentSize context:nil];
-    // 2.监听contentSize
-    [scrollView addObserver:self forKeyPath:MJRefreshContentSize options:NSKeyValueObservingOptionNew context:nil];
-    
-    // 3.父类的方法
+    // 父类的方法
     [super setScrollView:scrollView];
     
-    // 4.重新调整frame
+    // 重新调整frame
     [self adjustFrame];
 }
 
@@ -149,6 +157,16 @@ - (void)setState:(MJRefreshState)state
                 inset.bottom = bottom;
                 self.scrollView.contentInset = inset;
             }];
+            
+            // 通知代理
+            if ([self.delegate respondsToSelector:@selector(refreshFooterViewBeginRefreshing:)]) {
+                [self.delegate refreshFooterViewBeginRefreshing:self];
+            }
+            
+            // 回调
+            if (self.beginRefreshingCallback) {
+                self.beginRefreshingCallback(self);
+            }
 			break;
         }
             
@@ -157,13 +175,6 @@ - (void)setState:(MJRefreshState)state
 	}
 }
 
-//- (void)endRefreshingWithoutIdle
-//{
-//    _withoutIdle = YES;
-//    [self endRefreshing];
-//    _withoutIdle = NO;
-//}
-
 #pragma mark 获得scrollView的内容 超出 view 的高度
 - (CGFloat)contentBreakView
 {
@@ -188,10 +199,4 @@ - (int)viewType
 {
     return MJRefreshViewTypeFooter;
 }
-
-- (void)free
-{
-    [super free];
-    [self.scrollView removeObserver:self forKeyPath:MJRefreshContentSize];
-}
 @end

+ 23 - 0
MJRefreshExample/MJRefreshExample/MJRefresh/MJRefreshHeaderView.h

@@ -7,7 +7,30 @@
 //  下拉刷新
 
 #import "MJRefreshBaseView.h"
+@class MJRefreshHeaderView;
+
+/**
+ 代理的协议定义
+ */
+@protocol MJRefreshHeaderViewDelegate <NSObject>
+@optional
+/**
+ *  开始进入刷新状态就会调用
+ *
+ *  @param refreshHeaderView 刷新控件
+ */
+- (void)refreshHeaderViewBeginRefreshing:(MJRefreshHeaderView *)refreshHeaderView;
+@end
 
 @interface MJRefreshHeaderView : MJRefreshBaseView
 + (instancetype)header;
+/**
+ *  代理
+ */
+@property (weak, nonatomic) id<MJRefreshHeaderViewDelegate> delegate;
+
+/**
+ *  开始进入刷新状态就会调用
+ */
+@property (nonatomic, copy) void (^beginRefreshingCallback)(MJRefreshHeaderView *refreshHeaderView);
 @end

+ 10 - 0
MJRefreshExample/MJRefreshExample/MJRefresh/MJRefreshHeaderView.m

@@ -136,6 +136,16 @@ - (void)setState:(MJRefreshState)state
                 // 2.设置滚动位置
                 self.scrollView.contentOffset = CGPointMake(0, - self.scrollViewInitInset.top - MJRefreshViewHeight);
             }];
+            
+            // 通知代理
+            if ([self.delegate respondsToSelector:@selector(refreshHeaderViewBeginRefreshing:)]) {
+                [self.delegate refreshHeaderViewBeginRefreshing:self];
+            }
+            
+            // 回调
+            if (self.beginRefreshingCallback) {
+                self.beginRefreshingCallback(self);
+            }
 			break;
         }
             

+ 83 - 0
MJRefreshExample/MJRefreshExample/MJRefresh/UIScrollView+MJRefresh.h

@@ -0,0 +1,83 @@
+//
+//  UIScrollView+MJRefresh.h
+//  MJRefreshExample
+//
+//  Created by MJ Lee on 14-5-28.
+//  Copyright (c) 2014年 itcast. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface UIScrollView (MJRefresh)
+#pragma mark - 下拉刷新
+/**
+ *  添加一个下拉刷新头部控件
+ *
+ *  @param callback 回调
+ */
+- (void)addHeaderWithCallback:(void (^)())callback;
+
+/**
+ *  添加一个下拉刷新头部控件
+ *
+ *  @param target 目标
+ *  @param action 回调方法
+ */
+- (void)addHeaderWithTarget:(id)target action:(SEL)action;
+
+/**
+ *  移除下拉刷新头部控件
+ */
+- (void)removeHeader;
+
+/**
+ *  主动让下拉刷新头部控件进入刷新状态
+ */
+- (void)headerBeginRefreshing;
+
+/**
+ *  让下拉刷新头部控件停止刷新状态
+ */
+- (void)headerEndRefreshing;
+
+/**
+ *  下拉刷新头部控件的可见性
+ */
+@property (nonatomic, assign, getter = isHeaderHidden) BOOL headerHidden;
+
+#pragma mark - 上拉刷新
+/**
+ *  添加一个上拉刷新尾部控件
+ *
+ *  @param callback 回调
+ */
+- (void)addFooterWithCallback:(void (^)())callback;
+
+/**
+ *  添加一个上拉刷新尾部控件
+ *
+ *  @param target 目标
+ *  @param action 回调方法
+ */
+- (void)addFooterWithTarget:(id)target action:(SEL)action;
+
+/**
+ *  移除上拉刷新尾部控件
+ */
+- (void)removeFooter;
+
+/**
+ *  主动让上拉刷新尾部控件进入刷新状态
+ */
+- (void)footerBeginRefreshing;
+
+/**
+ *  让上拉刷新尾部控件停止刷新状态
+ */
+- (void)footerEndRefreshing;
+
+/**
+ *  下拉刷新头部控件的可见性
+ */
+@property (nonatomic, assign, getter = isFooterHidden) BOOL footerHidden;
+@end

+ 211 - 0
MJRefreshExample/MJRefreshExample/MJRefresh/UIScrollView+MJRefresh.m

@@ -0,0 +1,211 @@
+//
+//  UIScrollView+MJRefresh.m
+//  MJRefreshExample
+//
+//  Created by MJ Lee on 14-5-28.
+//  Copyright (c) 2014年 itcast. All rights reserved.
+//
+
+#import "UIScrollView+MJRefresh.h"
+#import "MJRefreshHeaderView.h"
+#import "MJRefreshFooterView.h"
+#import <objc/runtime.h>
+
+@interface UIScrollView()
+@property (weak, nonatomic) MJRefreshHeaderView *header;
+@property (weak, nonatomic) MJRefreshFooterView *footer;
+@end
+
+
+@implementation UIScrollView (MJRefresh)
+
+#pragma mark - 运行时相关
+static char MJRefreshHeaderViewKey;
+static char MJRefreshFooterViewKey;
+
+- (void)setHeader:(MJRefreshHeaderView *)header {
+    [self willChangeValueForKey:@"MJRefreshHeaderViewKey"];
+    objc_setAssociatedObject(self, &MJRefreshHeaderViewKey,
+                             header,
+                             OBJC_ASSOCIATION_ASSIGN);
+    [self didChangeValueForKey:@"MJRefreshHeaderViewKey"];
+}
+
+- (MJRefreshHeaderView *)header {
+    return objc_getAssociatedObject(self, &MJRefreshHeaderViewKey);
+}
+
+- (void)setFooter:(MJRefreshFooterView *)footer {
+    [self willChangeValueForKey:@"MJRefreshFooterViewKey"];
+    objc_setAssociatedObject(self, &MJRefreshFooterViewKey,
+                             footer,
+                             OBJC_ASSOCIATION_ASSIGN);
+    [self didChangeValueForKey:@"MJRefreshFooterViewKey"];
+}
+
+- (MJRefreshFooterView *)footer {
+    return objc_getAssociatedObject(self, &MJRefreshFooterViewKey);
+}
+
+#pragma mark - 下拉刷新
+/**
+ *  添加一个下拉刷新头部控件
+ *
+ *  @param callback 回调
+ */
+- (void)addHeaderWithCallback:(void (^)())callback
+{
+    // 1.创建新的header
+    if (!self.header) {
+        MJRefreshHeaderView *header = [MJRefreshHeaderView header];
+        header.scrollView = self;
+        self.header = header;
+    }
+    
+    // 2.设置block回调
+    self.header.beginRefreshingCallback = ^(MJRefreshHeaderView *refreshHeaderView){
+        if (callback) {
+            callback();
+        }
+    };
+}
+
+/**
+ *  添加一个下拉刷新头部控件
+ *
+ *  @param target 目标
+ *  @param action 回调方法
+ */
+- (void)addHeaderWithTarget:(id)target action:(SEL)action
+{
+    // 1.创建新的header
+    if (!self.header) {
+        MJRefreshHeaderView *header = [MJRefreshHeaderView header];
+        header.scrollView = self;
+        self.header = header;
+    }
+    
+    // 2.设置目标和回调方法
+    self.header.beginRefreshingTaget = target;
+    self.header.beginRefreshingAction = action;
+}
+
+/**
+ *  移除下拉刷新头部控件
+ */
+- (void)removeHeader
+{
+    [self.header removeFromSuperview];
+    self.header = nil;
+}
+
+/**
+ *  主动让下拉刷新头部控件进入刷新状态
+ */
+- (void)headerBeginRefreshing
+{
+    [self.header beginRefreshing];
+}
+
+/**
+ *  让下拉刷新头部控件停止刷新状态
+ */
+- (void)headerEndRefreshing
+{
+    [self.header endRefreshing];
+}
+
+/**
+ *  下拉刷新头部控件的可见性
+ */
+- (void)setHeaderHidden:(BOOL)hidden
+{
+    self.header.hidden = hidden;
+}
+
+- (BOOL)isHeaderHidden
+{
+    return self.header.isHidden;
+}
+
+#pragma mark - 上拉刷新
+/**
+ *  添加一个上拉刷新尾部控件
+ *
+ *  @param callback 回调
+ */
+- (void)addFooterWithCallback:(void (^)())callback
+{
+    // 1.创建新的footer
+    if (!self.footer) {
+        MJRefreshFooterView *footer = [MJRefreshFooterView footer];
+        footer.scrollView = self;
+        self.footer = footer;
+    }
+    
+    // 2.设置block回调
+    self.footer.beginRefreshingCallback = ^(MJRefreshFooterView *refreshFooterView){
+        if (callback) {
+            callback();
+        }
+    };
+}
+
+/**
+ *  添加一个上拉刷新尾部控件
+ *
+ *  @param target 目标
+ *  @param action 回调方法
+ */
+- (void)addFooterWithTarget:(id)target action:(SEL)action
+{
+    // 1.创建新的footer
+    if (!self.footer) {
+        MJRefreshFooterView *footer = [MJRefreshFooterView footer];
+        footer.scrollView = self;
+        self.footer = footer;
+    }
+    
+    // 2.设置目标和回调方法
+    self.footer.beginRefreshingTaget = target;
+    self.footer.beginRefreshingAction = action;
+}
+
+/**
+ *  移除上拉刷新尾部控件
+ */
+- (void)removeFooter
+{
+    [self.footer removeFromSuperview];
+    self.footer = nil;
+}
+
+/**
+ *  主动让上拉刷新尾部控件进入刷新状态
+ */
+- (void)footerBeginRefreshing
+{
+    [self.footer beginRefreshing];
+}
+
+/**
+ *  让上拉刷新尾部控件停止刷新状态
+ */
+- (void)footerEndRefreshing
+{
+    [self.footer endRefreshing];
+}
+
+/**
+ *  下拉刷新头部控件的可见性
+ */
+- (void)setFooterHidden:(BOOL)hidden
+{
+    self.footer.hidden = hidden;
+}
+
+- (BOOL)isFooterHidden
+{
+    return self.footer.isHidden;
+}
+@end