投稿文章,作者:Dreamer-Panda(微博)
本篇blog主要講解如何定制TabBarItem的大小,最終實現AppStore各大主流APP TabBarItem超出TabBar的效果。希望對大家有所幫助。
方案一:UIEdgeInsets
適用場景:
適合APP的TabBarItemImage的圖片資源放在本地
圖片超出tabbar的高度,需移動其位置,來進行適應
弊端:
若在本地配置好後,tabbar的圖片就不能改動了,若tabbar的圖片來自服務端,且不停的切換圖片的大小,以上則很難滿足。若有此方面的需求請看方案二。
實現:
[tabbarItem setImageInsets:UIEdgeInsetsMake(<#CGFloat top#>, <#CGFloat left#>, <#CGFloat bottom#>, <#CGFloat right#>)]
注:圖片太大超出tabbar時,系統並不會調整image和title的位置,你需要根據圖片的高度,計算出需要往上移動的高度,然後設置top和bottom屬性即可。切記top = - bottom,否則image將會被拉伸或者被壓縮。
方案二:Runtime
利用runtime的話相對方案一來說要比較復雜一點,但其靈活度比較高,我們能夠根據服務端所給的image來動態的變化TabBarItem的大小,類似像淘寶、京東活動時。思想:主要是利用runtime對UITabBar的layoutSubviews進行重寫,然後調整UITabBarItem的位置。另外,當時在做的APP已經有4-5年的歷史了,一開始打算自已定制tabbar,發現要改動的還是挺多的,於是就放棄了。做之前也看了前輩iOS程序犭袁的CYLTabBarController,從中也學到了不少思路。
實現:
首先我們使用runtime method swizzling交換系統的- (void)layoutSubviews;
使用KVC對系統的UITabBarButton、UITabBarSwappableImageView、UITabBarButtonLabel、_UIBadgeView進行捕獲
拿到控件後我們對其的frame進行計算,判斷當前有沒有超出tabbar的高度,若超出則進行處理
再次利用runtime method swizzling交換系統的- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;使圖片超過後也能接受點擊
代碼:
method swizzling:
static void ExchangedMethod(SEL originalSelector, SEL swizzledSelector, Class class) { Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }
計算frame,並對其重新布局
UIView *tabBarImageView, *tabBarButtonLabel, *tabBarBadgeView; for (UIView *sTabBarItem in childView.subviews) { if ([sTabBarItem isKindOfClass:NSClassFromString(@"UITabBarSwappableImageView")]) { tabBarImageView = sTabBarItem; } else if ([sTabBarItem isKindOfClass:NSClassFromString(@"UITabBarButtonLabel")]) { tabBarButtonLabel = sTabBarItem; } else if ([sTabBarItem isKindOfClass:NSClassFromString(@"_UIBadgeView")]) { tabBarBadgeView = sTabBarItem; } } NSString *tabBarButtonLabelText = ((UILabel *)tabBarButtonLabel).text; CGFloat y = CGRectGetHeight(self.bounds) - (CGRectGetHeight(tabBarButtonLabel.bounds) + CGRectGetHeight(tabBarImageView.bounds)); if (y < 3) { if (!tabBarButtonLabelText.length) { space -= tabBarButtonLabelHeight; } childView.frame = CGRectMake(childView.frame.origin.x, y - space, childView.frame.size.width, childView.frame.size.height - y + space); }
讓圖片超出部分也能響應點擊事件
- (UIView *)s_hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (!self.clipsToBounds && !self.hidden && self.alpha > 0) { UIView *result = [super hitTest:point withEvent:event]; if (result) { return result; } else { for (UIView *subview in self.subviews.reverseObjectEnumerator) { CGPoint subPoint = [subview convertPoint:point fromView:self]; result = [subview hitTest:subPoint withEvent:event]; if (result) { return result; } } } } return nil; }
注意事項
在給tabbar設置圖片的時候一定要設置圖片的renderingMode,否則就會出現下圖中圖片丟失的現象
UITabBarButton被修改frame之後,僅有UITabBarSwappableImageView能夠響應點擊事件,不過我們能夠在UITabBar的- (void)touchesBegan:(NSSet
當適配圖片後不要忘記適配_UIBadgeView的frame
效果圖
正常中間超出
做活動時全部超出
圖片丟失
UIBadgeView
感謝大家花費時間來查看這篇blog,需要下載demo的同學請猛戳GitHub。