iOS-實現(xiàn)映客首頁TabBar和滑動隱藏NavBar和TabBar

之前在做直播的時候边锁,參照了映客App茅坛,發(fā)現(xiàn)其首頁的效果還挺不錯贡蓖,在網上找了一下相關仿映客App代碼和博客彻桃,大部分都是說如何播放直播流和推流邻眷,對于UI這塊甚少肆饶,所以我自己花了點時間研究了一下映客的首頁UI效果驯镊。

我們來看看最終效果


滑動隱藏NavBar和TabBar效果

從效果圖上可以看出,映客首頁主要分兩部分笼蛛,一部分是實現(xiàn)沒有文字而且中間按鈕突出的TabBar,另一部分是顯示滑動ScrollView隱藏和顯示NavBarTabBar。我們來慢慢看领追。

一、TabBar實現(xiàn)

首先舔亭,我們看下實現(xiàn)后的效果订雾。


映客底部TabBar效果

這里我們可以使用系統(tǒng)的TabBar來實現(xiàn)該效果洼哎。

關于如何設置系統(tǒng)的TabBar噩峦,這里就不贅述了识补,可以看到我項目的代碼凭涂。我們來看重點部分。

1. 提出問題:

  1. 如何實現(xiàn)中間的突出按鈕
  2. 中間突出按鈕超出TabBar部分是如何響應點擊的
  3. 如何實現(xiàn)TabBarItem圖片居中且不帶文字
(1)中間突出按鈕

要實現(xiàn)中間突出的按鈕白翻,直接使用系統(tǒng)TabBar還是不行滤馍,需要用一個取巧的方法巢株,通過KVC的方式(使用KVC可以修改readonly屬性阁苞,不過不能濫用哦)使用自定義的TabBar替換系統(tǒng)UITabBar悼沿。

//創(chuàng)建自己的tabbar糟趾,然后用KVC將自己的tabbar和系統(tǒng)的tabBar替換下
HKTabBar *tabbar = [[HKTabBar alloc] init];
//KVC實質是修改了系統(tǒng)的_tabBar
[self setValue:tabbar forKeyPath:@"tabBar"];

替換了系統(tǒng)UITabBar后义郑,就需要實現(xiàn)中間按鈕了非驮。我們在自定義TabBar中添加UIButton院尔,作為中間按鈕邀摆。

1.在HKTabBarinitWithFrame:方法中,初始化中間按鈕

//設置中間按鈕圖片和尺寸
UIButton *centerBtn = [[UIButton alloc] init];
[centerBtn setBackgroundImage:[UIImage imageNamed:@"tab_launch"] forState:UIControlStateNormal];
[centerBtn setBackgroundImage:[UIImage imageNamed:@"tab_launch"] forState:UIControlStateHighlighted];
//這里button的size是根據(jù)需要設置的中間圖片來的
centerBtn.size = centerBtn.currentBackgroundImage.size;
[centerBtn addTarget:self action:@selector(centerBtnDidClick) forControlEvents:UIControlEventTouchUpInside];
self.centerBtn = centerBtn;
[self addSubview:centerBtn];

2.在layoutSubviews中設置中間按鈕和其他Item位置
由于系統(tǒng)ItemUITabBarButton類型例获,自定義中間按鈕為UIButton榨汤,所以可以根據(jù)Item類型來區(qū)分是自定義按鈕還是系統(tǒng)Item收壕,再調整每個Item的位置蜜宪。這里系統(tǒng)UITabBarButton寬度為TabBar寬度減去中間按鈕寬度的一半。

//系統(tǒng)自帶的按鈕類型是UITabBarButton澳窑,找出這些類型的按鈕,然后重新排布位置颅湘,空出中間的位置
Class class = NSClassFromString(@"UITabBarButton");

self.centerBtn.centerX = self.centerX;
//調整中間按鈕的中線點Y值
self.centerBtn.centerY = (self.height - (self.centerBtn.height - self.height)) * 0.5;

NSInteger btnIndex = 0;
for (UIView *btn in self.subviews) {//遍歷tabbar的子控件
    if ([btn isKindOfClass:class]) {//如果是系統(tǒng)的UITabBarButton,那么就調整子控件位置悲立,空出中間位置
        //按鈕寬度為TabBar寬度減去中間按鈕寬度的一半
        btn.width = (self.width - self.centerBtn.width) * 0.5;
        //中間按鈕前的寬度薪夕,這里就3個按鈕原献,中間按鈕Index為1
        if (btnIndex < 1) {
            btn.x = btn.width * btnIndex;
        } else { //中間按鈕后的寬度
            btn.x = btn.width * btnIndex + self.centerBtn.width;
        }
        
        btnIndex++;
        //如果是索引是0(從0開始的),直接讓索引++倔撞,目的就是讓消息按鈕的位置向右移動鄙陡,空出來中間按鈕的位置
        if (btnIndex == 0) {
            btnIndex++;
        }
    }
}

[self bringSubviewToFront:self.centerBtn];

到這里趁矾,中間按鈕就實現(xiàn)好了毫捣,但是如何讓超出TabBar部分(即紅色框部分)響應點擊事件呢培漏?

TabBar中間按鈕突出位置

(2)超出TabBar部分響應點擊

按照系統(tǒng)默認處理方式,超出TabBar部分侧甫,是不會響應點擊事件的(不信的可以自己試試哦)。要響應點擊事件冷冗,這里就需要重寫UIViewhitTest:方法(該方法可以決定點擊事件的響應者蒿辙,關于hitTest說明思灌,可以參見iOS-使用hitTest控制點擊事件的響應對象)了泰偿。

//重寫hitTest方法,去監(jiān)聽中間按鈕的點擊攒发,目的是為了讓凸出的部分點擊也有反應
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    
    //判斷當前手指是否點擊到中間按鈕上烟阐,如果是蜒茄,則響應按鈕點擊檀葛,其他則系統(tǒng)處理
    //首先判斷當前View是否被隱藏了屿聋,隱藏了就不需要處理了
    if (self.isHidden == NO) {
        
        //將當前tabbar的觸摸點轉換坐標系润讥,轉換到中間按鈕的身上楚殿,生成一個新的點
        CGPoint newP = [self convertPoint:point toView:self.centerBtn];
        
        //判斷如果這個新的點是在中間按鈕身上砌溺,那么處理點擊事件最合適的view就是中間按鈕
        if ( [self.centerBtn pointInside:newP withEvent:event]) {
            return self.centerBtn;
        }
    }
    
    return [super hitTest:point withEvent:event];
}

處理完突出部分规伐,就剩下不帶文字的Item了猖闪。

(3) TabBar中Item圖片居中且不帶文字

有的同學可能就會說了萧朝,要不帶文字,不設置tabBarItemtitle不就好了竖配。但是title這個NavBar的標題也是要用的进胯,所以還是必須要設置。

那要怎么辦呢诸衔?其實很簡單就缆,要實現(xiàn)該效果竭宰,以下代碼就夠了

//設置圖片居中切揭,這里的4.5廓旬,根據(jù)實際中間按鈕圖片大小來決定
Vc.tabBarItem.imageInsets = UIEdgeInsetsMake(4.5, 0, -4.5, 0);
//設置不顯示文字嗤谚,將title的位置設置成無限遠巩步,就看不到了
Vc.tabBarItem.titlePositionAdjustment = UIOffsetMake(0, MAXFLOAT);

到這里终畅,TabBar的實現(xiàn)就結束了离福,下面我們來看看如何實現(xiàn)隱藏和顯示NavBarTabBar妖爷。

二絮识、隱藏和顯示NavBar和TabBar實現(xiàn)

首先次舌,我們來看看效果


滑動隱藏NavBar和TabBar效果

1. 提出問題:

  1. 如何移動NavBarTabBar
  2. 如何控制NavBarTabBar移動距離
  3. 如何控制使ScrollView移動的同時其顯示的區(qū)域正確
  4. 如何在手指滑動距離較小時,收起或者展開NavBarTabBar
  5. 如何在Push到其他頁面逐沙,再Pop回來后酱吝,NavBarTabBar顯示正確

首先务热,我們要解決最基本的問題崎岂,如何讓NavBarTabBar移動

(1)移動NavBar和TabBar

移動的話,其實很簡單,只需要改變他們的Y坐標即可濒憋。

//這里的self就是NavBar或者TabBar
CGRect viewFrame = self.frame;
viewFrame.origin.y = newOffsetY;
self.frame = viewFrame;
(2)控制NavBar和TabBar移動距離

移動距離,就要取決于ScrollView的相對移動距離了条辟,即相對之前contentOffset.y滑動了多少本姥。

在計算相對移動距離之前婚惫,我們需要獲取上次滑動ScrollViewcontentOffset.y辰妙,我們可以在- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView中獲取上次滑動ScrollViewcontentOffset.y,即_previousOffsetY粗井,

_previousOffsetY = scrollView.contentOffset.y;

之后實現(xiàn)ScrollView的委托方法- (void)scrollViewDidScroll:(UIScrollView *)scrollView,在其中監(jiān)聽ScrollView的移動距離浇衬,從而計算相對移動距離deltaY

CGFloat deltaY = scrollView.contentOffset.y - _previousOffsetY;

在得到相對移動距離后,我們就需要分別控制NavBarTabBar的移動距離了絮姆。
這里蚁阳,我們可以實現(xiàn)一個分類來專門控制他們的移動螺捐。

要注意的是:當相對距離超出應移動范圍時定血,需要對其校正
那么灾票,我們必須先知道NavBarTabBar坐標的上下限铝条,即其展開和收起時的Y坐標班缰,以下代碼openOffsetY為展開的Y坐標埠忘,closeOffsetY為收起的Y坐標莹妒。

//NavBar
//kStatusBarHeight為狀態(tài)欄高度
openOffsetY = kStatusBarHeight;
closeOffsetY = -CGRectGetHeight(self.frame;
//TabBar
//kScreenHeight為屏幕寬度,hk_extraDistance為中間按鈕突出的距離
openOffsetY = kScreenHeight - CGRectGetHeight(self.frame);
closeOffsetY = kScreenHeight + self.hk_extraDistance;

以下坐標都代表Y坐標鉴腻,我們這里只做豎直方向移動
知道可以移動的范圍后爽哎,就可以根據(jù)相對移動距離deltaY計算移動后的坐標了课锌。
對于NavBar渺贤,計算后的坐標(即當前坐標減去deltaY)癣亚,要大于收起的坐標小于展開的坐標述雾。
對于TabBar,計算后的坐標(即即當前坐標加上deltaY)唆缴,要大于展開的坐標小于收起的坐標面徽。
這里畫畫圖會好理解一些趟紊。

//NavBar最終要移動的Y坐標
newOffsetY = CGRectGetMinY(self.frame) - deltaY;
newOffsetY = MAX(closeOffsetY, MIN(openOffsetY, newOffsetY));
//TabBar最終要移動的Y坐標
newOffsetY = CGRectGetMinY(self.frame) + deltaY;
newOffsetY = MIN(closeOffsetY, MAX(openOffsetY, newOffsetY));   

之后,就只要將NavBarTabBar移動到指定坐標即可

CGRect viewFrame = self.frame;
viewFrame.origin.y = newOffsetY;
self.frame = viewFrame;

我們再來看看ScrollView是怎么控制移動的铛嘱。

(3)控制使ScrollView移動的同時其顯示的區(qū)域正確

細心的童鞋可能會發(fā)現(xiàn)墨吓,當NavBar收起或者展開的過程中帖烘,ScrollView是跟著一起移動的蚓让,即ScrollView本身并沒滑動窄瘟,而是Y坐標在改變蹄葱。那如何實現(xiàn)呢图云?

這里竣况,我們可以改變ScrollViewcontentInset來滿足我們的需求情萤,相對于改變ScrollViewframe要方便很多哦筋岛。

我們要根據(jù)NavBarTabBar移動后的坐標睁宰,改變ScrollViewcontentInsettopbottom孝赫。
topNavBarMaxY寒锚,就是當前Y坐標加上本身的高度刹前。
bottomTabBar突出的距離喇喉,即屏幕高度減去其Y坐標大于0的部分拣技。
這里要注意:ScrollViewscrollIndicatorInsets同時也需要更新,不然Indicator顯示就有問題了邪驮。

CGFloat navBarMaxY = CGRectGetMaxY(self.navigationController.navigationBar.frame);
CGFloat tabBarMinY = CGRectGetMinY(self.tabBarController.tabBar.frame);
UIEdgeInsets scrollViewInset = self.tableView.contentInset;
scrollViewInset.top = navBarMaxY;
scrollViewInset.bottom = MAX(0, kScreenHeight - tabBarMinY);
self.tableView.contentInset = scrollViewInset;
self.tableView.scrollIndicatorInsets = scrollViewInset;
(4)在手指滑動距離較小時沮榜,收起或者展開NavBar和TabBar

細心的童鞋可能會發(fā)現(xiàn)蟆融,映客在滑動距離比較小的時候型酥,有的時候NavBarTabBar會彈回來萍歉,有的時候會收起枪孩。這個是怎么做的呢蔑舞?

這個就需要在停止滑動的時候處理攻询,我們可以在ScrollView的委托方法- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate中處理钧栖。
在滑動時,要判斷當前NavBar或者TabBar滑動距離啃奴,是否滑到到最大坐標(最大坐標減去最小坐標)的一半依溯。如果沒有滑動黎炉,則收起拜隧,反之展開。這里可能比較繞雀费,我們看看代碼盏袄。

//判斷當前`NavBar`是展開還是收起
- (BOOL)hk_shouldOpen {
    CGFloat viewY = CGRectGetMinY(self.frame);
    //[self hk_openOffsetY]為展開的Y坐標辕羽,[self hk_closeOffsetY] 為收起的Y坐標
    CGFloat viewMinY = [self hk_openOffsetY];
    viewMinY = [self hk_closeOffsetY] + ([self hk_openOffsetY] - [self hk_closeOffsetY]) * 0.5;
    
    if (viewY <= viewMinY) {
        return NO;
    }
    return YES;
}

當知道是展開還是收起后绰寞,就可以進行滑動了滤钱。這里我們做一個簡單動畫件缸,使滑動看起來自然一些他炊,這里除了需要改變ScrollViewcontentInset,還需要改變其contentOffset舌胶,因為NavBarTabBar移動了幔嫂,ScrollView也要跟著一起移動履恩。

[UIView animateWithDuration:0.2 animations:^{
    
    CGFloat navBarOffsetY = 0;
    if (opening) {
        //navBarOffsetY為NavBar從當前位置到展開滑動的距離
        navBarOffsetY = [self.navigationController.navigationBar hk_open];
        [self.tabBarController.tabBar hk_open];
    } else {
        //navBarOffsetY為NavBar從當前位置到收起滑動的距離
        navBarOffsetY = [self.navigationController.navigationBar hk_close];
        [self.tabBarController.tabBar hk_close];
    }
    //更新TableView的contentInset
    [self updateScrollViewInset];
    //根據(jù)NavBar的偏移量來滑動TableView
    CGPoint contentOffset = self.tableView.contentOffset;
    contentOffset.y += navBarOffsetY;
    self.tableView.contentOffset = contentOffset;
}];
(5)在Push到其他頁面,再Pop回來后绽昏,NavBar和TabBar顯示正確

Push到其他頁面之前全谤,必須把NavBarTabBar都展開认然,不然在收起的狀態(tài)Push到其他頁面卷员,NavBarTabBar都不見了削饵。
這里就需要在- (void)viewWillDisappear:(BOOL)animated中將NavBarTabBar都展開葵孤。

還有一個地方需要注意:
Push到其他頁面的時候尤仍,如果此時ScrollViewcontentInset不為(0,0,0,0)時宰啦,系統(tǒng)會自動將其置為(0,0,0,0),這樣在Push后师抄,還會走到之前頁面ScrollViewscrollViewDidScroll:方法中辆布,會導致NavBar消失锋玲。對于這種情況惭蹂,就不應該讓其繼續(xù)走我們處理展開和收取NavBarTabBar的流程盾碗。
我們可以通過以下代碼控制,當該UIViewController不是當前顯示的UIViewController時,就不往下走了。

//在push到其他頁面時候谬盐,還是會走該方法,這個時候不應該繼續(xù)執(zhí)行
if (!(self.isViewLoaded && self.view.window != nil)) {
   return;
}

到這里砸烦,映客首頁的效果就實現(xiàn)好了幢痘!

Demo項目

該Demo項目地址:https://github.com/HustHank/YingKeHomeDemo

封裝的滑動隱藏NavBar和TabBar開源控件

項目地址:https://github.com/HustHank/HKScrollingNavAndTabBar

如果覺得該文章對你有用颜说,請幫忙點贊或者Star我的GitHub項目门粪,謝謝!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拟蜻,隨后出現(xiàn)的幾起案子瞭郑,更是在濱河造成了極大的恐慌屈张,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件场绿,死亡現(xiàn)場離奇詭異焰盗,居然都是意外死亡熬拒,警方通過查閱死者的電腦和手機蛀序,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門徐裸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人檬姥,你說我怎么就攤上這事健民”蹋” “怎么了崇堵?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長赏廓。 經常有香客問我幔摸,道長既忆,這世上最難降的妖魔是什么跃脊? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上彰亥,老公的妹妹穿的比我還像新娘任斋。我一直安慰自己废酷,他們只是感情好澈蟆,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寥闪,像睡著了一般疲憋。 火紅的嫁衣襯著肌膚如雪缚柳。 梳的紋絲不亂的頭發(fā)上喂击,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音谐檀,去河邊找鬼桐猬。 笑死溃肪,一個胖子當著我的面吹牛惫撰,可吹牛的內容都是我干的厨钻。 我是一名探鬼主播夯膀,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼涂佃!你這毒婦竟也來了辜荠?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤午笛,失蹤者是張志新(化名)和其女友劉穎药磺,沒想到半個月后癌佩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體我碟,經...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡矫俺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了催烘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖咽袜,靈堂內的尸體忽然破棺而出询刹,到底是詐尸還是另有隱情凹联,我是刑警寧澤蔽挠,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站杠巡,受9級特大地震影響氢拥,放射性物質發(fā)生泄漏。R本人自食惡果不足惜厘线,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耳璧。 院中可真熱鬧旨枯,春花似錦攀隔、人聲如沸昆汹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捅彻。三九已至沟饥,卻和暖如春贤旷,著一層夾襖步出監(jiān)牢的瞬間幼驶,已是汗流浹背盅藻。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工勃蜘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缭贡,地道東北人阳惹。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像纲岭,于是被迫代替她去往敵國和親荒勇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評論 25 707
  • 美好天元錳這是我們的生活區(qū)跨蟹,很美的一片綠花滴地窗轩,【圖片】【圖片】【圖片】【圖片】【圖片】【圖片】【圖片】
    王思華閱讀 271評論 0 0
  • random quote Random Quote "說什么呢" ——張大俠 New ...
    放風箏的小小馬閱讀 387評論 0 0
  • 在任何銷售工作中舌缤,如果哪方先報價就意味著哪方被動,所以在實際工作中玻墅,在顧客尚未作出購買決定前澳厢,都不要急于亮出底牌赏酥,...
    零叫獸閱讀 980評論 0 1
  • 倚坐冬季邊緣摸屠,輕啜一口雪酒季二,夢里回旋期盼胯舷,冬去春來桑嘶。古詩吟詠逃顶,春江水暖鴨先知以政。我似乎也嗅到了春的味道妙蔗。 花開一季眉反,...
    戀上輕狂書生閱讀 666評論 0 1