UITabBar 点击刷新功能
我们都知道 UITabBar 只能添加 UITabBarItem,而 UITabBarItem 是继承自 NSObject 的,但是我们可以发现 UITabBarItem 内部有一个 _view 属性与私有类 UITabBarButton 关联着,而这个 UITabBarButton 正是我们在 UITabBar 上看到的一个个按钮对象。
经过调试,发现 UITabBarButton 的 UIControlEventTouchUpInside 事件与 UITabBar 的 _buttonUp: 方法关联着,所以我们只需要通过 runtime hook 该方法即可拦截到 UITabBarButton 的点击事件。
这里提醒一下,虽然 UITabBarButton 继承自 UIControl,但是对于是否选中的标记,UITabBarButton 并没有采用 UIControl 的 selected 属性,而是在内部用了一个私有变量 _selected 来标记。
附上 Demo
特性
- 不依赖 UITabBarControllerDelegate,通过对 
UITabBarItem进行扩展来实现 - UITabBar 支持刷新动画 ,默认关闭动画
 - UITabBar 支持自定义刷新动画,需遵守
XPTabBarRefreshViewAnimating协议,默认动画是UIActivityIndicatorView - 支持 iPhone/iPad,适配横竖屏
 
效果演示

代码使用演示
// 根据项目实际场景获取UITabBarItem
UITabBarItem *tabBarItem =  self.tabBarItem; //self.navigationController.tabBarItem;
// 启用刷新动画
tabBarItem.enabledRefreshAnimation = YES;
// 自定义动画
tabBarItem.refreshView = [[CustomRefreshView alloc] init];
// 监听刷新回调
[tabBarItem setRefreshBlock:^(UITabBar *tabBar, UITabBarItem *tabBarItem) {
    // 发送网络请求刷新数据
    NSLog(@"Refresh");
    // 当网络请求完毕时记得调用 `stopRefresh` 方法
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       [tabBarItem stopRefresh];
    });
}];
关于 UITabBarItem 的获取说明
- 结构1
 
UITabBarController
│
├── UIViewController
│
├── UIViewController
│
└── UIViewController
- 结构2
 
UINavigationController
│
└── UITabBarController
    │
    ├── UIViewController
    │
    ├── UIViewController
    │
    └── UIViewController
对于上图的结构1、2,直接使用 UIViewController.tabBarItem 即可。
- 结构3
 
UITabBarController
│
└── UINavigationController
    │
    ├── UIViewController
    │
    ├── UIViewController
    │
    └── UIViewController
对于上图的结构3,使用 UIViewController.navigationController.tabBarItem 即可。
源码
- .h 文件
 
#import <UIKit/UIKit.h>
@protocol XPTabBarRefreshViewAnimating;
typedef void(^XPTabBarRefreshBlock)(UITabBar *tabBar, UITabBarItem *tabBarItem);
NS_CLASS_AVAILABLE_IOS(8_0) @interface UITabBarItem (XPTabBarRefresh)
/// 刷新回调
@property (nonatomic, copy) XPTabBarRefreshBlock refreshBlock;
/// 刷新动画视图, 默认`UIActivityIndicatorView`
@property (nonatomic, strong) UIView<XPTabBarRefreshViewAnimating> *refreshView;
/// 是否启用刷新动画, 默认`NO`
@property (nonatomic, assign, getter=isEnabledRefreshAnimation) BOOL enabledRefreshAnimation;
/// 停止刷新(请在`refreshBlock`回调中调用该方法)
- (void)stopRefresh;
@end
NS_CLASS_AVAILABLE_IOS(8_0) @interface UITabBar (XPTabBarRefresh)
@end
@protocol XPTabBarRefreshViewAnimating <NSObject>
/// 开始刷新动画
- (void)startRefreshAnimating;
/// 结束刷新动画
- (void)stopRefreshAnimating;
@end
- .m 文件
 
#import "UITabBar+XPTabBarRefresh.h"
#import <objc/runtime.h>
@interface UITabBarItem (XPTabBarRefreshPrivate)
/// 是否正在刷新
@property (nonatomic, assign, getter=isRefreshing) BOOL refreshing;
/// 开始刷新
- (void)startRefresh;
@end
@implementation UITabBarItem (XPTabBarRefreshPrivate)
- (void)startRefresh {
    self.refreshing = YES;
    if (!self.isEnabledRefreshAnimation) {
        return;
    }
    UIView *containerView = (UIView *)[self valueForKey:@"_view"];
    if (containerView == nil || ![containerView isKindOfClass:UIView.class]) {
        return;
    }
    for (UIView *subview in containerView.subviews) {
        subview.hidden = YES;
    }
    UIView<XPTabBarRefreshViewAnimating> *animatingView = self.refreshView;
    [animatingView setUserInteractionEnabled:NO];
    [animatingView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [containerView addSubview:animatingView];
    NSDictionary *views = @{@"view" : animatingView};
    [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:views]];
    [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:views]];
    [animatingView startRefreshAnimating];
}
- (void)stopRefresh {
    self.refreshing = NO;
    if (!self.isEnabledRefreshAnimation) {
        return;
    }
    UIView<XPTabBarRefreshViewAnimating> *animatingView = self.refreshView;
    for (UIView *subview in animatingView.superview.subviews) {
        subview.hidden = NO;
    }
    [animatingView stopRefreshAnimating];
    [animatingView removeFromSuperview];
}
- (void)setRefreshBlock:(XPTabBarRefreshBlock)refreshBlock {
    objc_setAssociatedObject(self, @selector(refreshBlock), refreshBlock, OBJC_ASSOCIATION_COPY);
}
- (XPTabBarRefreshBlock)refreshBlock {
    return objc_getAssociatedObject(self, _cmd);
}
- (void)setRefreshView:(UIView<XPTabBarRefreshViewAnimating> *)refreshView {
    objc_setAssociatedObject(self, @selector(refreshView), refreshView, OBJC_ASSOCIATION_RETAIN);
}
- (UIView<XPTabBarRefreshViewAnimating> *)refreshView {
    UIView<XPTabBarRefreshViewAnimating> *view = objc_getAssociatedObject(self, _cmd);
    if (nil == view) {
        UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        indicator.hidesWhenStopped = YES;
        view = (UIView<XPTabBarRefreshViewAnimating> *)indicator;
        [self setRefreshView:view];
    }
    return view;
}
- (void)setEnabledRefreshAnimation:(BOOL)enabledRefreshAnimation {
    objc_setAssociatedObject(self, @selector(isEnabledRefreshAnimation), @(enabledRefreshAnimation), OBJC_ASSOCIATION_RETAIN);
}
- (BOOL)isEnabledRefreshAnimation {
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setRefreshing:(BOOL)refreshing {
    objc_setAssociatedObject(self, @selector(isRefreshing), @(refreshing), OBJC_ASSOCIATION_RETAIN);
}
- (BOOL)isRefreshing {
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}
@end
#pragma mark -
@implementation UITabBar (XPTabBarRefresh)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        SEL originalSelector = NSSelectorFromString(@"_buttonUp:");
        SEL swizllingSelector = @selector(xp_buttonUp:);
        Method originalMethod = class_getInstanceMethod(self, originalSelector);
        Method swizlledMethod = class_getInstanceMethod(self, swizllingSelector);
        BOOL flag = class_addMethod(self, originalSelector, method_getImplementation(swizlledMethod), method_getTypeEncoding(swizlledMethod));
        if (flag) {
            class_replaceMethod(self, swizllingSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizlledMethod);
        }
    });
}
- (void)xp_buttonUp:(UIControl *)sender {
    BOOL selected = [[sender valueForKey:@"_selected"] boolValue];
    if (selected) {
        UITabBarItem *item = self.selectedItem;
        XPTabBarRefreshBlock refreshBlock = [item refreshBlock];
        if (refreshBlock && !item.isRefreshing) {
            [item startRefresh];
            refreshBlock(self, item);
            return;
        }
    }
    [self xp_buttonUp:sender];
}
@end
#pragma mark -
@interface UIActivityIndicatorView (XPTabBarRefresh)<XPTabBarRefreshViewAnimating>
@end
@implementation UIActivityIndicatorView (XPTabBarRefresh)
- (void)startRefreshAnimating {
    [self startAnimating];
}
- (void)stopRefreshAnimating {
    [self stopAnimating];
}
@end
最后提一下,除了 runtime 之外,也可以通过 UITabBarControllerDelegate 代理实现,可以从 - (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController 和 - (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController 这两个方法下手。
网上随便一搜都是基于 - (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController  方法的实现,这里就不再累赘了。