先上效果圖:

代碼地址:https://github.com/huisaziru/BossHireDemo
最近正在找工作,然后用Boss直聘這個(gè)軟件試了下,發(fā)現(xiàn)招聘詳情頁面上拉挺有意思的,很容易被迷惑;上拉時(shí),拉到底再往上拉時(shí)能看到首頁的view,而且滾動(dòng)條好像是在下面那個(gè)view上;
一開始以為上面那個(gè)只是一個(gè)tableview添加在首頁的那個(gè)view上,后面發(fā)現(xiàn)下面那個(gè)tabbarcontroller縮小了,并且返回的時(shí)候,和dissmiss一樣,基本確定是首頁present出來這個(gè)詳情頁;
分析上拉返回實(shí)現(xiàn)
詳情頁有一個(gè)tableview,tableview需要加在scrollview上,scrollview是橫向滾動(dòng),用來獲取其他招聘詳情;
要實(shí)現(xiàn)這個(gè)效果,以下條件可以滿足:
- scrollview在拖動(dòng)的時(shí)候是透明,不拖動(dòng)時(shí)為不透明,因?yàn)樾枰獧M向滾動(dòng)
- tableview什么時(shí)候都透明,拉到底再往上拉的時(shí)候,如果不透明,就會(huì)出現(xiàn)tableview的背景色,就擋住了下面;
- menuview是和tableview同一級(jí),一開始是不透明的,需要一個(gè)containerview包含它們,然后containerview加在scrollview上;因?yàn)閠ableview動(dòng)的時(shí)候menuview不動(dòng),拉到底再往上拉時(shí)候才跟著動(dòng);所以menuview不在tableview上;
看上去這些就能實(shí)現(xiàn)這個(gè)功能,但是有個(gè)細(xì)節(jié)很重要,就是滾動(dòng)條滾到menuview下面的時(shí)候,只有滾動(dòng)條透過menuview
當(dāng)時(shí)看到這個(gè)效果的時(shí)候有點(diǎn)懵,為什么只有滾動(dòng)條可以透過menuview;按理講要透明的話,應(yīng)該全部都會(huì)透過去,看得到下面;這個(gè)是最難的地方;
后面想到了局部透明,隨著往上拉,menuview慢慢的從上往下透明,剛好讓進(jìn)來的滾動(dòng)條透出來,但是有個(gè)問題:透明出來的部分還是會(huì)把下面的東西透出來,不單單只透明滾動(dòng)條;
這時(shí)我想到找一個(gè)背景view放在menuview局部透明位置的底下,讓上面的透明區(qū)域被下面擋?。贿@里注意不能是menuview大小,要不然擋住了下面,但是這時(shí)會(huì)將滾動(dòng)條擋住,真是糾結(jié);
現(xiàn)在的問題是:底下的那個(gè)背景view怎么不擋住滾動(dòng)條?
分析:滾動(dòng)條是在tableview 上面的,所以可以從tableview入手,將背景view加到tableview底部:contentsize底部下面--因?yàn)橹挥欣降走@個(gè)背景view才能出來;
驗(yàn)證一下:當(dāng)拉倒底,再往上拉時(shí),menuview慢慢透出上面,這時(shí)候背景view慢慢出來,剛好擋住透明部分,這時(shí)滾動(dòng)條被menuview可以透過;到這里基本宣告成功!
menuview透出來
還有一個(gè)點(diǎn)沒說:就是menuview怎么慢慢透出來,我想的辦法是,menuview底下有一個(gè)白色不透明的maskview,上面就是放按鈕的containerView,這個(gè)view的背景顏色帶透明;
- 初始狀態(tài):上面透明部分被下面maskview擋住
- 拉到底往上拉時(shí):maskview的y慢慢變大,高變成maskview.height - y,這樣就能將上面的containerView慢慢透出來
- 當(dāng)maskview的y等于maskview.height時(shí),等于全透了,這時(shí)tableview的背景view剛好全出來了,完全擋住menu view;
我發(fā)現(xiàn)Boss直聘有一個(gè)bug,正是這個(gè)bug論證了我的猜想;當(dāng)拉到底時(shí),再往上拉一段距離,這時(shí)快速拉下來,神奇的現(xiàn)象就發(fā)生了:menuview已經(jīng)不在底下了,最重要的是,它上部分是透明的,可以透過看到tableview的字;這個(gè)bug很好解決,就是突然下來導(dǎo)致的,沒有過渡,只需在當(dāng)前offset沒有到底的時(shí),將menuview的坐標(biāo)復(fù)原就可以;
到這里終于是搞定了?。?!
以下是上面主要的實(shí)現(xiàn)代碼
背景view:這里要注意的是,因?yàn)閎oss直聘的詳情頁支持scrollview切換,這里只用一個(gè)tableview,當(dāng)數(shù)據(jù)變化時(shí),contentsize就會(huì)變,這時(shí)候需要更新背景view的frame,這里用kvo檢測(cè)tableview的content size就可以實(shí)現(xiàn)這個(gè);
//tableFooterBackGroundView作用:當(dāng)tableview拉到底,再往上拉時(shí),menuview 慢慢透出來的部分,如果底下沒有view,會(huì)看到下面,所以添加此view,位置在tableview底部下面
self.tableFooterBackGroundView = [[UIView alloc] initWithFrame:CGRectMake(0, self.tableView.contentSize.height, frame.size.width, MenuHeight)];
self.tableFooterBackGroundView.backgroundColor = [UIColor whiteColor];
[self.tableView addSubview:self.tableFooterBackGroundView];
// 對(duì)tableView的contentSize 進(jìn)行kvo,因?yàn)槊看握?qǐng)求的數(shù)據(jù)不一樣,conentSize不一樣,更新tableFootBackGroundView的top
[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
menuview:
/** 菜單view,和tableview同一級(jí)*/
- (UIView *)createMenuView:(CGRect)frame {
UIView *containerView = [[UIView alloc] initWithFrame:frame];
//遮罩view的作用:tableview上升時(shí),遮罩view慢慢變小,讓menuview透出來 tableFooterBackGroundView慢慢出來,剛好擋住了透明的部分,滾動(dòng)條在tableview之上,所以可以透過menuview
self.contentMenuMaskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
self.contentMenuMaskView.backgroundColor = [UIColor whiteColor];
[containerView addSubview:self.contentMenuMaskView];
UIView *menuView = [[[NSBundle mainBundle] loadNibNamed:@"MenuView" owner:self options:nil] firstObject];
menuView.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
menuView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8];
[containerView addSubview:menuView];
return containerView;
}
滾動(dòng)處理:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentSize.height > 0 && scrollView.tag == 0) {//拉到底
if (scrollView.contentOffset.y + scrollView.height > scrollView.contentSize.height) {
self.scrollView.backgroundColor = [UIColor clearColor];
CGFloat dy = scrollView.contentOffset.y + scrollView.height - scrollView.contentSize.height;
self.contentMenuView.y = self.view.height - self.contentMenuView.height - dy;
if (dy <= self.contentMenuView.height) {
self.contentMenuMaskView.y = dy;
self.contentMenuMaskView.height = self.contentMenuView.height - dy;
} else {
//防止menuview上升一部分后突然往上拉,上升高度大于menuview的高度,height還沒變成0,所以需要設(shè)成0;
if (self.contentMenuMaskView.height != 0) {
self.contentMenuMaskView.height = 0;
}
}
} else {
self.scrollView.backgroundColor = [UIColor groupTableViewBackgroundColor];
//這個(gè)是boss直聘的bug
//防止從底下往上拉時(shí),突然一下下來,因?yàn)闆]有過渡,所以contentMenuView的坐標(biāo)還在上面,所以需要重置下
CGFloat originContentMenuY = self.view.height - self.contentMenuView.height;
if (self.contentMenuView.y != originContentMenuY) {
self.contentMenuView.y = originContentMenuY;
self.contentMenuMaskView.y = 0;
self.contentMenuMaskView.height = self.contentMenuView.height;
}
}
}
}
這個(gè)demo還有一些比如
-
UIViewControllerAnimatedTransitioning的用法,用來定制轉(zhuǎn)場(chǎng)效果,實(shí)現(xiàn)demo中縮小tabbarcontroller的效果; - 還有一些keyframe動(dòng)畫,組合動(dòng)畫的應(yīng)用;
- 還有scrollview的用法,只用一個(gè)tableview進(jìn)行切換;
以上這些就不在這里講了,可以直接看代碼,注釋很詳細(xì);
如果覺得寫的還可以,幫忙star一下,謝謝~