現(xiàn)在主流app里面經(jīng)常需要有tableView上滑出現(xiàn)懸浮條樣式的設(shè)計(jì),寶寶看了下appleStore里面專題推薦用的scrollView 也有類似的懸浮效果,只不過它是下拉懸浮,而主流的是上滑懸??!最終實(shí)現(xiàn)效果如以下gif圖。



其實(shí)一看上去 表以為是兩種不同的方案,其實(shí)兩種思路方式完全一致,本來核心代碼就一行,那么要修改的代碼豈不是半行,確實(shí)如此哦,寶寶已笑瘋??????,好了啦,開個(gè)玩笑,進(jìn)入正題!
先別敲代碼,想想懸浮條實(shí)現(xiàn)思路
-
上滑懸浮條樣式的思路
當(dāng)你看到本文第一張的動(dòng)圖的時(shí)候你想到了什么,對,沒錯(cuò),上滑的時(shí)候高度在減小最后會(huì)減小到一個(gè)固定值,下滑的時(shí)候高度在增加最后也會(huì)增加到一個(gè)固定值,那么簡單點(diǎn)描述就是懸浮條原點(diǎn)不變,讓其高度減小,在下面代碼里第二種實(shí)現(xiàn)方式就是這樣;其實(shí)我實(shí)現(xiàn)的是讓懸浮條的原點(diǎn)變化,高度不變,也是一樣的效果。
-
appStore下拉懸浮條樣式的思路
有了第一種思路,會(huì)發(fā)現(xiàn)下拉懸浮條的過程與上面驚人的相似,上滑的時(shí)候高度在減小,只不過最后會(huì)減為0,下滑的時(shí)候高度在增加最后也會(huì)增加到一個(gè)固定值。
-
技術(shù)實(shí)現(xiàn)
通過KVO監(jiān)聽tableView的contentOffset屬性值或者在scrollView的代理方法中監(jiān)聽contentOffset的值來讓懸浮條的原點(diǎn)變化,高度不變,或者原點(diǎn)不變,高度變化!
將自定義的headerView放在tabView 的上面,即headerView和tableView為平級關(guān)系,都添加到viewController的view上,然后設(shè)置tableView的contentInset為headerView的值,在tableView滑動(dòng)的時(shí)候,動(dòng)態(tài)改變view的位置或者大小,使這個(gè)headerView看起來就像是有了懸浮功能的tableView.tableHeaderView。 -
核心代碼
寶寶真的沒有騙你們,真的只要一行,只要你看得懂就行??????,因?yàn)槲野盐逍衖f語句和成一行了,表問我為什么!
_yellowView.frame = offset.y < -64 ? (offset.y <= -200 ? CGRectMake(0, 0, 375, 200) : CGRectMake(0, -offset.y - 200, 375, 200)) : CGRectMake(0, 64 - 200, 375, 200);
代碼實(shí)現(xiàn)
- (void)viewDidLoad {
[super viewDidLoad];
//加載tableView到self.view上
UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 375, 667) style:UITableViewStylePlain];
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
tableView.dataSource = self;
tableView.contentInset = UIEdgeInsetsMake(200, 0, 0, 0);
[self.view addSubview:tableView];
//利用KVO監(jiān)聽tableView的contentOffset的屬性值,從而動(dòng)態(tài)改變懸浮條yellowView的frame值
[tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
//加載yellowView作為懸浮條視圖到self.view上,因此tableView和yellowView是同一級關(guān)系,黃色視圖在tableView的上面
TouchView *yellowView = [[TouchView alloc] initWithFrame:CGRectMake(0, 0, 375, 200)];
yellowView.backgroundColor = [UIColor yellowColor];
//將alpha設(shè)置成為0.7的透明度是為了看得更清楚底下的tableView隨時(shí)滾動(dòng)的位置
yellowView.alpha = 0.7;
[self.view addSubview:yellowView];
//設(shè)置成全局的實(shí)例變量是為了在監(jiān)聽方法中可以改變懸浮條yellowView的frame值
_yellowView = yellowView;
}
#pragma mark - KVO監(jiān)聽tableView的contentOffset的屬性值變化
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
CGPoint offset = [change[NSKeyValueChangeNewKey] CGPointValue];
//64為懸浮條最終停留的高度
if (offset.y < -64) {
//小于-64則有兩種情況,第一種小于-64是當(dāng)懸浮條一進(jìn)來時(shí)候,由于contentInset.top被我們調(diào)成了懸浮條的高度200,
因此offset.y初始值是-200,當(dāng)用戶上滑懸浮條的時(shí)候,會(huì)在 -200 <= offset.y <= -64 范圍內(nèi)波動(dòng),我們需要
調(diào)整懸浮條的Y坐標(biāo)為-offset.y - 200,如- (-180) - 200 = -20 ,代表tableView向上偏移了20各點(diǎn),我們改變其原點(diǎn),
造成懸浮條懸停的假象;第二種小于-64是當(dāng)懸浮條一進(jìn)來時(shí)候,offset.y初始值是-200,當(dāng)用戶下滑懸浮條的時(shí)候,offset.y會(huì)負(fù)的越多,
在 offset.y < -200 范圍內(nèi)波動(dòng),我們需要調(diào)整懸浮條的Y坐標(biāo)為固定值200,不讓其變大變小即可??!搞定
//第一種方案,改變懸浮條的Y坐標(biāo),而不改變其高度,這也是本文核心代碼?。。。? _yellowView.frame = offset.y <= -200 ? CGRectMake(0, 0, 375, 200) : CGRectMake(0, -offset.y - 200, 375, 200);
//第二種方案,改變懸浮條的高度,而不改變其原點(diǎn)
//_yellowView.frame = offset.y < -200 ? CGRectMake(0, 0, 375, 200) : CGRectMake(0, 0, 375, -offset.y);
//下面這行代碼沒必要添加,我看到網(wǎng)上很多人加了這句,實(shí)屬?zèng)]必要,tableView滾動(dòng)本來就靠contentOffset,contentInset只是添加額外的滾動(dòng)區(qū)域的
//_tableView.contentInset = offset.y > -200 ? UIEdgeInsetsMake(-offset.y, 0, 0, 0) :UIEdgeInsetsMake(200, 0, 0, 0);
}else {
//大于等于-64就只有一種情況了,當(dāng)?shù)竭_(dá)臨界值的時(shí)候就會(huì) 懸停64的最小高度,該高度自己隨便寫啦,然后appStore那個(gè)效果是上滑到臨界值后,高度依然減小,
那么需要調(diào)整懸浮條的Y坐標(biāo)為-offset.y - 200,offset.y會(huì)越來越 大于-64,導(dǎo)致_yellowView.frame.origin.y負(fù)的越多,越來偏離屏幕原點(diǎn),
正好滿足需求看不見懸浮條啦!
//appStore下拉懸浮條效果只需要改這行代碼讓上滑的時(shí)候原點(diǎn)超過其高度200,這樣就看不到懸浮條了
//_yellowView.frame = CGRectMake(0, -offset.y - 200, 375, 200);;
//第一種方案,改變懸浮條的Y坐標(biāo)為懸停最小值64 - 200,這樣在用戶界面上就只顯示64的高度,而不改變其高度
_yellowView.frame = CGRectMake(0, 64 - 200, 375, 200);
//第二種方案,改變懸浮條的高度為最小值64,而不改變其原點(diǎn)
//_yellowView.frame = CGRectMake(0, 0, 375, 64);
//下面這行代碼沒必要添加,我看到網(wǎng)上很多人加了這句,實(shí)屬?zèng)]必要,tableView滾動(dòng)本來就靠contentOffset,contentInset只是添加額外的滾動(dòng)區(qū)域的
//_tableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
}
}
自定義TouchView
如果有另外需求比如要是拖動(dòng)黃色視圖,tableView要是也能滾動(dòng)的話,那么就需要自己攔截點(diǎn)擊事件??!
@implementation TouchView
//這兩個(gè)方法隨便寫一個(gè)即可,為的是攔截響應(yīng)鏈,不讓其捕獲到觸摸事件,這樣用戶手指點(diǎn)擊上面的黃色視圖,window分發(fā)觸摸事件的時(shí)候,會(huì)認(rèn)為用戶點(diǎn)擊的那個(gè)點(diǎn)最遠(yuǎn)的響應(yīng)視圖是 tableView,因?yàn)閠ableView在黃色視圖的下方,這樣,當(dāng)滑動(dòng)黃色視圖的時(shí)候,tableView也跟著一起滾動(dòng)了啦?。?!
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return NO;
}
//先調(diào)用hitTest:withEvent:此方法,再調(diào)用pointInside:withEvent:
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *view = [super hitTest:point withEvent:event];
if ([view isKindOfClass:[self class]]) {
return nil;
}
return nil;
}
@end