一、需求背景
現(xiàn)在越來越多的應(yīng)用追求個(gè)性化的頁(yè)面風(fēng)格,類似下圖的UI設(shè)計(jì),原生的tabbar已經(jīng)不能滿足應(yīng)用的需求,那么我們就需要對(duì)tabbar 進(jìn)行自定義處理了。

那么如何才能達(dá)到這種設(shè)計(jì)需求呢
1、自定義一個(gè)類繼承至 UITabBar
@interface BaseTabbar : UITabBar
2、重寫B(tài)aseTabbar layoutSubviews方法
- (void)layoutSubviews {
[super layoutSubviews];
TSUser *user = [TSUser currentUser];
TSNationConfigModel *config = [TSNationConfigModel currentConfig];
if (config.enabled_sns && config.enabled_sns_user_content && user.user_level == 20) {
CGFloat index = 0;
//每個(gè)item的寬高
CGFloat w = self.width / (self.subviews.count+1);
CGFloat h = self.height;
//設(shè)置中間 按鈕 publishButton的frame(-40 設(shè)置發(fā)布按鈕向上偏移量 )
self.publishButton.frame = CGRectMake(2 * w, -40 , w, h+50);
for (UIView *button in self.subviews) {
if ([button isKindOfClass:NSClassFromString(@"UITabBarButton")]) {
//跳過中間 按鈕
if (index == 2) index = 3;
button.frame = CGRectMake(index * w, 0, w, h);
index ++;
}
}
}
}
3、到此自定義UITabBar布局已經(jīng)完成 ,接下來最關(guān)鍵一步要即將上演, 那就是 通過KVC 將UITabBarController控制器中自帶的的tabBar 替換成 我們自定義完成的BaseTabbar。
BaseTabbar *customTabBar = [[BaseTabbar alloc]init]
//使用KVC重設(shè)tabBar
[self setValue:customTabBar forKey:@"tabBar"];
至此 自定義的Tabbar已經(jīng)創(chuàng)建成功,快去看看是不是已經(jīng)是符合產(chǎn)品需求的布局吧,大老爺你以為到此就結(jié)束了嗎,當(dāng)然還有請(qǐng)繼續(xù)往下看。。。
二、中間按鈕 超出 UITabBar(見圖一 按鈕3部分)不響應(yīng)事件問題處理
父視圖是圖中藍(lán)色框大小,中間按鈕凸起部分在視圖外,在這種情況下如果我們不做任何處理,點(diǎn)擊圖中紅色區(qū)域是無法被響應(yīng)的。
響應(yīng)鏈在這里就不過多介紹了,我們主要介紹事件分發(fā)時(shí)是如何找到最合適的事件處理者。
這里需要了解兩個(gè)方法:
// 判斷點(diǎn)擊區(qū)域是否在當(dāng)前視圖范圍內(nèi)
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
// 最適合處理事件的試圖
View- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法一用于判斷點(diǎn)擊區(qū)域是否在當(dāng)前視圖范圍內(nèi)
方法二用于尋找最適合的View
我們以上圖的例子進(jìn)行說明,暫時(shí)稱呼它為tabbar,其視圖范圍為圖中1區(qū)域,中間凸起部分button在視圖外的部分為2區(qū)域,兩者重合部分為3區(qū)域。
在點(diǎn)擊1、2、3區(qū)域時(shí)都會(huì)先調(diào)用tabbar的- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event,只不過返回值不同,結(jié)果不同,我們?cè)敿?xì)來看看會(huì)發(fā)生什么。
1.1、點(diǎn)擊區(qū)域 -> 1
tabbar的方法一返回YES,接著會(huì)去遍歷tabbar上所有子控件并調(diào)用子控件中的方法一,也就是會(huì)調(diào)用button的方法一,由于button不在1區(qū)域內(nèi),所以button的方法一返回NO,并且button的方法二返回null,然后調(diào)用tabbar的方法二并返回self即tabbar本身處理點(diǎn)擊事件。

1.2、點(diǎn)擊區(qū)域 -> 2
tabbar的方法一返回YES,接著會(huì)去遍歷tabbar上所有子控件并調(diào)用子控件中的方法一,也就是會(huì)調(diào)用button的方法一,所以button的方法一返回YES,并且button的方法二返回本身,然后調(diào)用tabbar的方法二并返回button,最終由button處理點(diǎn)擊事件。

1.3、點(diǎn)擊區(qū)域 -> 3
我們?cè)谧远xtabbar時(shí),一般有兩種方式:
1、將自定義的tabbar添加到原來的tabbar上。
2、利用KVC替換掉原來的tabbar。
方式一
事件分發(fā)是由下往上進(jìn)行分發(fā),在調(diào)用系統(tǒng)tabbar方法一的時(shí)候,返回值為NO,事件不會(huì)繼續(xù)向上分發(fā),即不會(huì)調(diào)用自定義的tabbar的那兩個(gè)方法,由系統(tǒng)的tabbar成為事件的接收者。此時(shí)無法實(shí)現(xiàn)我們的需求。
方式二
我們知道最終的接收者實(shí)際上是通過tabbar的方法二的返回值決定的,所以我們只需要修改自定義tabbar的方法二的實(shí)現(xiàn)即可。
// 其中self.plusButton為中間凸起按鈕
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint newPoint = [self convertPoint:point toView:self.plusButton];
//tabbar 是否隱藏,隱藏了就不需要考慮點(diǎn)擊了(tabbar 不展示的二級(jí)頁(yè)面需要處理,否則二級(jí)頁(yè)面將會(huì)觸發(fā) plusButton按鈕的點(diǎn)擊事件)
if (self.hidden) {
return [super hitTest:point withEvent:event];
}else{
// 判斷是否屬于按鈕范圍
if ([self.plusButton pointInside:newPoint withEvent:event]) {
return self.plusButton;
} else {
return [super hitTest:point withEvent:event];
}
}
}
三、總結(jié)
在做類似這種UI時(shí),需要將自定義tabbar通過KVC替換掉原有tabbar,并重寫- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event返回合適的View去處理事件。
文章部分內(nèi)容借鑒鏈接:(http://www.itdecent.cn/p/dd39c49e7931)
文章持續(xù)更新中、希望對(duì)各位有所幫助、有問題可留言 大家共同學(xué)習(xí).