參考大神的demo
大神的文章中并沒有提及思想积仗,我就把做完這個(gè)demo的思想分享一下扳剿!
先上效果圖:
<br />
原理使用runtime修改teableView.tableHeaderView
<br />
1、首先先創(chuàng)建一個(gè)類別:
NS_CLASS_AVAILABLE_IOS(2_0) @interface UITableView : UIScrollView <NSCoding>
從上面可以知道,tableview繼承ScrollView,所以我們創(chuàng)建的是ScrollView的類別膘盖。
<br />
2、為類別添加屬性
頭視圖是圖片尽狠,所以我們需要定義一個(gè)Image屬性
頭視圖的高度應(yīng)該提供給用戶修改衔憨,所以要頂一個(gè)height屬性
/**
* 圖片
*/
@property (nonatomic,strong) UIImage * pq_headerScaleImage;
/**
* 圖片高
*/
@property (nonatomic,assign) CGFloat pq_headerScaleImageHeight;
- 這里要注意的是:這里我們的類別,從機(jī)制上面來講是不允許這樣子添加屬性的袄膏,這樣子添加的屬性的set以及get方法需要重寫践图。
這里就有兩種方案: - 1:可以通過static來完成,但是會(huì)一直貯存在內(nèi)存中沉馆。
- 2:可以使用runtime來完成码党。
<br />
3、重寫set get方法
//設(shè)置圖片高度的方法
- (void)setPq_headerScaleImageHeight:(CGFloat)pq_headerScaleImageHeight{
objc_setAssociatedObject(self, PQ_HeaderScaleImageHeightKey, @(pq_headerScaleImageHeight), OBJC_ASSOCIATION_COPY_NONATOMIC);
}
//判斷取到的是不是0斥黑,如果是0揖盘,返回默認(rèn)值200,否則返回真實(shí)高度
- (CGFloat)pq_headerScaleImageHeight{
CGFloat value = [objc_getAssociatedObject(self, PQ_HeaderScaleImageHeightKey) floatValue];
return (value == 0)?200:value;
}
//圖片set方法
- (void)setPq_headerScaleImage:(UIImage *)pq_headerScaleImage{
//調(diào)用懶加載方法
self.pq_headerImageView.image = pq_headerScaleImage;
}
//圖片get方法
- (UIImage *)pq_headerScaleImage{
return self.pq_headerImageView.image;
}
<br />
如果你細(xì)心你會(huì)看到在設(shè)置圖片的方法中:
self.pq_headerImageView.image = pq_headerScaleImage;
<br />
所以還要有一個(gè)imageView锌奴,這里使用的是懶加載
- (UIImageView *)pq_headerImageView{
UIImageView *imageView = objc_getAssociatedObject(self, PQ_HeaderScaleImageViewKey);
if (!imageView) {
imageView = [[UIImageView alloc]init];
imageView.clipsToBounds = YES;
imageView.contentMode = UIViewContentModeScaleAspectFill;
[self insertSubview:imageView atIndex:0];
objc_setAssociatedObject(self, PQ_HeaderScaleImageViewKey, imageView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return imageView;
}
4兽狭、通過上面的懶方法,我們現(xiàn)在有了一個(gè)ImageView鹿蜀,接下來我們應(yīng)該對(duì)imageView做一些處理了
- (void)setUpHeaderImageViewFrame{
self.pq_headerImageView.frame = CGRectMake(0, 0, self.bounds.size.width, self.pq_headerScaleImageHeight);
}
- (void)setUpHeaderImageView{
//
[self setUpHeaderImageViewFrame];
if (self.pq_isInitial == NO) {
[self addObserver:self forKeyPath:PQKeyPath(self, contentOffset) options:NSKeyValueObservingOptionNew context:nil];
[self pq_setIsInitial:YES];
}
}
<br />
上面提供了兩個(gè)方法:
- 1.一個(gè)用來設(shè)置imageView的高度
- 2.一個(gè)用來設(shè)置KVO箕慧,對(duì)于什么時(shí)候要設(shè)置KVO,要如何去設(shè)置茴恰、以及設(shè)置的次數(shù)和什么時(shí)候銷毀颠焦,需要用到一個(gè)變量去進(jìn)行判斷,所以有了第五步
<br />
5往枣、設(shè)置標(biāo)志位伐庭,用于判斷何時(shí)添加KVO以及防止多次添加
//初始化判斷 set方法
- (void)pq_setIsInitial:(BOOL)pq_isInitial{
objc_setAssociatedObject(self, PQ_isInitialKey , @(pq_isInitial), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//初始化判斷 get方法
- (BOOL)pq_isInitial{
return [objc_getAssociatedObject(self, PQ_isInitialKey) boolValue];
}
<br />
然后我們現(xiàn)在寫的基本上差不多了粉渠,最后我們要交互系統(tǒng)的方法實(shí)現(xiàn),改成調(diào)用我們的方法去實(shí)現(xiàn)
<br />
6圾另、使用runtime交互方法:交互原理霸株,在我之前的一篇文章有提及到
+ (void)load{
[self pq_swiaaleInstanceMethodWithSelector:@selector(setTableHeaderView:) selB:@selector(pq_setTableHeaderView:)];
}
- (void)pq_setTableHeaderView:(UIView *)tableHeaderView{
//如果不是tableView 就返回
if (![self isKindOfClass:[UITableView class]]) return;
//調(diào)用系統(tǒng)的方法
[self pq_setTableHeaderView:tableHeaderView];
//設(shè)置頭視圖
UITableView *tableView = (UITableView *)self;
self.pq_headerScaleImageHeight = tableView.tableHeaderView.frame.size.height;
}
<br />
7、在主程序中調(diào)用
self.myTableView.pq_headerScaleImage = [UIImage imageNamed:@"222"];
// 設(shè)置tableView頭部視圖集乔,必須設(shè)置頭部視圖背景顏色為clearColor,否則會(huì)被擋住
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 200)];
headerView.backgroundColor = [UIColor clearColor];
self.myTableView.tableHeaderView = headerView;
8淳衙、把源碼奉上:
<br />
.h
#import <UIKit/UIKit.h>
@interface UIScrollView (PQHeaderScaleImage)
/**
* 圖片
*/
@property (nonatomic,strong) UIImage * pq_headerScaleImage;
/**
* 圖片高
*/
@property (nonatomic,assign) CGFloat pq_headerScaleImageHeight;
@end
<br />
.m
#import "UIScrollView+PQHeaderScaleImage.h"
#import <objc/message.h>
#define PQKeyPath(objc,keyPath) @(((void)objc.keyPath,#keyPath))
@interface NSObject (PQHeaderScaleImage)
/**
* 替換類方法
*
* @param selA 原來的方法
* @param selB 要替換的方法
*/
+ (void)pq_swizzleClassMethodWithSelector:(SEL)selA selB:(SEL)selB;
/**
* 替換對(duì)象方法
*
* @param selA 原來的方法
* @param selB 要替換的方法
*/
+ (void)pq_swiaaleInstanceMethodWithSelector:(SEL)selA selB:(SEL)selB;
@end
@implementation NSObject (PQHeaderScaleImage)
+ (void)pq_swizzleClassMethodWithSelector:(SEL)selA selB:(SEL)selB{
//獲取原來的方法
Method m1 = class_getClassMethod(self, selA);
//要替換的方法
Method m2 = class_getClassMethod(self, selB);
//判斷能否加入
//
BOOL isAdd = class_addMethod(self, selA, method_getImplementation(m2), method_getTypeEncoding(m2));
//不能加入,就直接替換 能加入我們就不管了
if (!isAdd) {
method_exchangeImplementations(m1, m2);
}
}
//和上面類似
+ (void)pq_swiaaleInstanceMethodWithSelector:(SEL)selA selB:(SEL)selB{
Method m1 = class_getInstanceMethod(self , selA);
Method m2 = class_getInstanceMethod(self, selB);
BOOL isAdd = class_addMethod(self, selA, method_getImplementation(m2), method_getTypeEncoding(m2));
if (!isAdd) {
method_exchangeImplementations(m1, m2);
}
}
@end
@implementation UIScrollView (PQHeaderScaleImage)
static char * const PQ_HeaderScaleImageViewKey = "PQ_HeaderScaleImageViewKey";
static char * const PQ_HeaderScaleImageHeightKey = "PQ_HeaderScaleImageHeightKey";
static char * const PQ_isInitialKey = "isInitialKey";
+ (void)load{
[self pq_swiaaleInstanceMethodWithSelector:@selector(setTableHeaderView:) selB:@selector(pq_setTableHeaderView:)];
}
- (void)pq_setTableHeaderView:(UIView *)tableHeaderView{
//如果不是tableView 就返回
if (![self isKindOfClass:[UITableView class]]) return;
//調(diào)用系統(tǒng)的方法
[self pq_setTableHeaderView:tableHeaderView];
//設(shè)置頭視圖
UITableView *tableView = (UITableView *)self;
self.pq_headerScaleImageHeight = tableView.tableHeaderView.frame.size.height;
}
//初始化判斷 set方法
- (void)pq_setIsInitial:(BOOL)pq_isInitial{
objc_setAssociatedObject(self, PQ_isInitialKey , @(pq_isInitial), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//初始化判斷 get方法
- (BOOL)pq_isInitial{
return [objc_getAssociatedObject(self, PQ_isInitialKey) boolValue];
}
//圖片set方法
- (void)setPq_headerScaleImage:(UIImage *)pq_headerScaleImage{
//調(diào)用懶加載方法
self.pq_headerImageView.image = pq_headerScaleImage;
//設(shè)置頭視圖
[self setUpHeaderImageView];
}
//圖片get方法
- (UIImage *)pq_headerScaleImage{
return self.pq_headerImageView.image;
}
//設(shè)置圖片高度的方法
- (void)setPq_headerScaleImageHeight:(CGFloat)pq_headerScaleImageHeight{
objc_setAssociatedObject(self, PQ_HeaderScaleImageHeightKey, @(pq_headerScaleImageHeight), OBJC_ASSOCIATION_COPY_NONATOMIC);
//設(shè)置頭視圖高度
[self setUpHeaderImageViewFrame];
}
//判斷取到的是不是0饺著,如果是0,返回默認(rèn)值200肠牲,否則返回真實(shí)高度
- (CGFloat)pq_headerScaleImageHeight{
CGFloat value = [objc_getAssociatedObject(self, PQ_HeaderScaleImageHeightKey) floatValue];
return (value == 0)?200:value;
}
- (UIImageView *)pq_headerImageView{
UIImageView *imageView = objc_getAssociatedObject(self, PQ_HeaderScaleImageViewKey);
if (!imageView) {
imageView = [[UIImageView alloc]init];
imageView.clipsToBounds = YES;
imageView.contentMode = UIViewContentModeScaleAspectFill;
[self insertSubview:imageView atIndex:0];
objc_setAssociatedObject(self, PQ_HeaderScaleImageViewKey, imageView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return imageView;
}
- (void)setUpHeaderImageViewFrame{
self.pq_headerImageView.frame = CGRectMake(0, 0, self.bounds.size.width, self.pq_headerScaleImageHeight);
}
- (void)setUpHeaderImageView{
//
[self setUpHeaderImageViewFrame];
if (self.pq_isInitial == NO) {
[self addObserver:self forKeyPath:PQKeyPath(self, contentOffset) options:NSKeyValueObservingOptionNew context:nil];
[self pq_setIsInitial:YES];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
// 獲取當(dāng)前偏移量
CGFloat offsetY = self.contentOffset.y;
if (offsetY < 0) {
self.pq_headerImageView.frame = CGRectMake(offsetY, offsetY, self.bounds.size.width - offsetY * 2, self.pq_headerScaleImageHeight - offsetY);
} else {
self.pq_headerImageView.frame = CGRectMake(0, 0, self.bounds.size.width, self.pq_headerScaleImageHeight);
}
}
- (void)dealloc
{
if (self.pq_isInitial) { // 初始化過幼衰,就表示有監(jiān)聽contentOffset屬性,才需要移除
[self removeObserver:self forKeyPath:PQKeyPath(self, contentOffset)];
}
}
@end