Demo下载
本篇文章我们来介绍 runtime 的方法交换. 这个功能也是 runtime 里面用的非常多的, 是一个必须掌握的技能.
我们将以一个示例来讲解
在实际开发需求中, 我们都会用到 tableView. 那么在有数据和无数据的情况下,显示不同. 无数据的情况下需要显示无数据信息, 无网络状态下要显示无网络.
实现的方法千千万万. 这里讲解如何用 runtime 来优雅的实现 tableView 无数据状态的切换
那么我们这里的构思是如果tableView 能够像 UITextField一样设置一个 placeholder. 没有数据的时候自动显示. 那我们只需要对 placeholder 进行一个设置就可以了.
思路:
tableView 的刷新 都要调用 [tableview reloadData]
那么我们对 reloadData 方法做一个交换. 在这里检测当前 tableView 页面是否有数据, 没有则显示空数据页面, 有则正常显示.
主要用到C的 API 如下,使用很简单, 主要以代码为主.
//获取方法
class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
//方法交换
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
主要代码如下:
//
// UITableView+placeHolder.m
// test2
//
// Created by liuyaozong on 2021/8/15.
//
#import "UITableView+placeHolder.h"
#import <objc/runtime.h>
#import <objc/message.h>
@interface UITableView ()
@end
@implementation UITableView (placeHolder)
// 每个对象实例化的时候都户调用 load. 所以我们在 load 里面进行方法交换. 利用dispatch_once_t 保证只交换一次
+(void)load
{
//保证只交换一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//原来的方法
Method orginMethod = class_getInstanceMethod(self, @selector(reloadData));
//现在的方法
Method currentMethod = class_getInstanceMethod(self, @selector(currentReloadData));
//方法交换
method_exchangeImplementations(orginMethod, currentMethod);
});
}
-(void)currentReloadData {
//调用之前的方法 这个时候已经交换了,下面的代码实际上调用的是 reloadData
[self currentReloadData];
//设置 placeholder
[self setDefalutView];
}
//设置 placeHolder 获取 section 和 row 进行一个判断.
-(void)setDefalutView {
id<UITableViewDataSource> dataSource = self.dataSource;
NSInteger section = 0;
if ([dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) {
section = [dataSource numberOfSectionsInTableView:self];
} else {
section = 1;
}
NSInteger rows = 0;
for (NSInteger i = 0; i < section; i++) {
NSInteger num = [dataSource tableView: self numberOfRowsInSection:section];
if (num != 0) {
rows = num;
}
}
//如果是空的则添加
if (!rows) {
if (self.placeHolder && ![self.placeHolder isDescendantOfView:self]) {
[self addSubview:self.placeHolder];
}
self.placeHolder.hidden = NO;
self.placeHolder.frame = self.bounds;
} else {
if (self.placeHolder) {
self.placeHolder.hidden = YES;
}
}
}
NSInteger num = 999;
//添加属性
-(UIView *)placeHolder
{
return objc_getAssociatedObject(self, &num);
}
-(void)setPlaceHolder:(UIView *)placeHolder
{
objc_setAssociatedObject(self, &num, placeHolder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
//
// UITableView+placeHolder.h
// test2
//
// Created by liuyaozong on 2021/8/15.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UITableView (placeHolder)
@property (strong,nonatomic) UIView *placeHolder;
@end
NS_ASSUME_NONNULL_END
实际使用
//
// ViewController.m
// test2
//
// Created by liuyaozong on 2021/8/15.
//
#import "ViewController.h"
#import "UITableView+placeHolder.h"
@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
@property (strong,nonatomic) UITableView *tableView;
@property (strong,nonatomic) UIButton *changeStatusBtn;
@property (strong,nonatomic) NSMutableArray *dataList;
@end
@implementation ViewController
-(UIButton *)changeStatusBtn
{
if (!_changeStatusBtn) {
_changeStatusBtn = [[UIButton alloc] init];
[_changeStatusBtn setTitle:@"切换状态" forState:UIControlStateNormal];
[_changeStatusBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[_changeStatusBtn addTarget:self action:@selector(clickChange) forControlEvents:UIControlEventTouchUpInside];
}
return _changeStatusBtn;
}
-(NSMutableArray *)dataList
{
if (!_dataList) {
_dataList = [NSMutableArray array];
}
return _dataList;
}
-(UITableView *)tableView
{
if (!_tableView) {
_tableView = [[UITableView alloc] init];
_tableView.delegate = self;
_tableView.dataSource = self;
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"UITableViewCell"];
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
UILabel *label = [[UILabel alloc] init];
label.text = @"暂无数据";
label.textColor = [UIColor redColor];
label.textAlignment = NSTextAlignmentCenter;
_tableView.placeHolder = label;
}
return _tableView;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.view addSubview:self.tableView];
[self.view addSubview:self.changeStatusBtn];
dispatch_async(dispatch_get_main_queue(), ^{
self.tableView.frame = self.view.bounds;
self.changeStatusBtn.frame = CGRectMake(100, 100, 100, 100);
[self.tableView reloadData];
});
}
-(void)clickChange
{
if (self.dataList.count == 0) {
NSArray *arr = @[@"1",@"1",@"1",@"1",@"1"];
[self.dataList addObjectsFromArray:arr];
} else {
[self.dataList removeAllObjects];
}
[self.tableView reloadData];
}
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
cell.textLabel.text = [NSString stringWithFormat:@"%ld",indexPath.row];
return cell;
}
- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataList.count;
}
@end
网友评论