先上效果圖:
代碼地址:https://github.com/huisaziru/BossHireDemo
最近正在找工作,然后用Boss直聘這個軟件試了下,發現招聘詳情頁面上拉挺有意思的,很容易被迷惑;上拉時,拉到底再往上拉時能看到首頁的view,而且滾動條好像是在下面那個view上;
一開始以為上面那個只是一個tableview添加在首頁的那個view上,后面發現下面那個tabbarcontroller縮小了,并且返回的時候,和dissmiss一樣,基本確定是首頁present出來這個詳情頁;
分析上拉返回實現
詳情頁有一個tableview,tableview需要加在scrollview上,scrollview是橫向滾動,用來獲取其他招聘詳情;
要實現這個效果,以下條件可以滿足:
- scrollview在拖動的時候是透明,不拖動時為不透明,因為需要橫向滾動
- tableview什么時候都透明,拉到底再往上拉的時候,如果不透明,就會出現tableview的背景色,就擋住了下面;
- menuview是和tableview同一級,一開始是不透明的,需要一個containerview包含它們,然后containerview加在scrollview上;因為tableview動的時候menuview不動,拉到底再往上拉時候才跟著動;所以menuview不在tableview上;
看上去這些就能實現這個功能,但是有個細節很重要,就是滾動條滾到menuview下面的時候,只有滾動條透過menuview
當時看到這個效果的時候有點懵,為什么只有滾動條可以透過menuview;按理講要透明的話,應該全部都會透過去,看得到下面;這個是最難的地方;
后面想到了局部透明,隨著往上拉,menuview慢慢的從上往下透明,剛好讓進來的滾動條透出來,但是有個問題:透明出來的部分還是會把下面的東西透出來,不單單只透明滾動條;
這時我想到找一個背景view放在menuview局部透明位置的底下,讓上面的透明區域被下面擋?。贿@里注意不能是menuview大小,要不然擋住了下面,但是這時會將滾動條擋住,真是糾結;
現在的問題是:底下的那個背景view怎么不擋住滾動條?
分析:滾動條是在tableview 上面的,所以可以從tableview入手,將背景view加到tableview底部:contentsize底部下面--因為只有拉到底這個背景view才能出來;
驗證一下:當拉倒底,再往上拉時,menuview慢慢透出上面,這時候背景view慢慢出來,剛好擋住透明部分,這時滾動條被menuview可以透過;到這里基本宣告成功!
menuview透出來
還有一個點沒說:就是menuview怎么慢慢透出來,我想的辦法是,menuview底下有一個白色不透明的maskview,上面就是放按鈕的containerView,這個view的背景顏色帶透明;
- 初始狀態:上面透明部分被下面maskview擋住
- 拉到底往上拉時:maskview的y慢慢變大,高變成maskview.height - y,這樣就能將上面的containerView慢慢透出來
- 當maskview的y等于maskview.height時,等于全透了,這時tableview的背景view剛好全出來了,完全擋住menu view;
我發現Boss直聘有一個bug,正是這個bug論證了我的猜想;當拉到底時,再往上拉一段距離,這時快速拉下來,神奇的現象就發生了:menuview已經不在底下了,最重要的是,它上部分是透明的,可以透過看到tableview的字;這個bug很好解決,就是突然下來導致的,沒有過渡,只需在當前offset沒有到底的時,將menuview的坐標復原就可以;
到這里終于是搞定了!!!
以下是上面主要的實現代碼
背景view:這里要注意的是,因為boss直聘的詳情頁支持scrollview切換,這里只用一個tableview,當數據變化時,contentsize就會變,這時候需要更新背景view的frame,這里用kvo檢測tableview的content size就可以實現這個;
//tableFooterBackGroundView作用:當tableview拉到底,再往上拉時,menuview 慢慢透出來的部分,如果底下沒有view,會看到下面,所以添加此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];
// 對tableView的contentSize 進行kvo,因為每次請求的數據不一樣,conentSize不一樣,更新tableFootBackGroundView的top
[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
menuview:
/** 菜單view,和tableview同一級*/
- (UIView *)createMenuView:(CGRect)frame {
UIView *containerView = [[UIView alloc] initWithFrame:frame];
//遮罩view的作用:tableview上升時,遮罩view慢慢變小,讓menuview透出來 tableFooterBackGroundView慢慢出來,剛好擋住了透明的部分,滾動條在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;
}
滾動處理:
- (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,所以需要設成0;
if (self.contentMenuMaskView.height != 0) {
self.contentMenuMaskView.height = 0;
}
}
} else {
self.scrollView.backgroundColor = [UIColor groupTableViewBackgroundColor];
//這個是boss直聘的bug
//防止從底下往上拉時,突然一下下來,因為沒有過渡,所以contentMenuView的坐標還在上面,所以需要重置下
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;
}
}
}
}
這個demo還有一些比如
-
UIViewControllerAnimatedTransitioning
的用法,用來定制轉場效果,實現demo中縮小tabbarcontroller的效果; - 還有一些keyframe動畫,組合動畫的應用;
- 還有scrollview的用法,只用一個tableview進行切換;
以上這些就不在這里講了,可以直接看代碼,注釋很詳細;
如果覺得寫的還可以,幫忙star一下,謝謝~