現(xiàn)在我們使用的APP框架,大部分都是
UINavigationController + UITabBarController的模式。如果使用的是默認的UITabBar磨砂效果,而且在pushViewController時,設(shè)置了hidesBottomBarWhenPushed屬性為true,則在iOS 12.1系統(tǒng),使用手勢返回的時候,就會觸發(fā)UITabBarButton被設(shè)置frame.size為 (0, 0)的錯誤布局, 導(dǎo)致的tabBar上的圖標和文字偏移錯位。

案例
extension UINavigationController {
@objc open func ck_pushViewController(_ viewController: UIViewController, animated: Bool) {
viewController.hidesBottomBarWhenPushed = true
self.ck_pushViewController(viewController, animated: animated)
if self.viewControllers.count == 1 {
viewController.hidesBottomBarWhenPushed = false
}
}
}
方案一:修改tabBar的isTranslucent透明度(不推薦)或添加背景圖片
1、可以通過修改tabBar的透明度達到我們想要的效果:
if #available(iOS 12.1, *) {
UITabBar.appearance().isTranslucent = false
} else {
UITabBar.appearance().isTranslucent = true
}
但是這個會引入其它的問題:
- (1)
tabBar想保留原來的磨砂效果; - (2)會影響
tableView的布局,底部間距多了tabBar的高度;
所以這種方式pass。
2、可以統(tǒng)一給tabBar添加背景圖片,則不會有這個問題。
方案二:使用runtime過濾UITabBarButton的frame設(shè)置(推薦)
為了達到既不修改原先的設(shè)計又達到解決這個問題,我這邊通過交換UITabBarButton的setFrame方法,過濾一些錯誤的大小設(shè)置,來達到我們的目的,這里提供了OC和Swift兩個版本的代碼,如下:
-
OC版
添加
UIView的分類,并在+load方法中,交換UITabBarButton的setFrame方法,過濾錯誤的大小
#import <UIKit/UIKit.h>
@interface UIView (CKTabBarItem)
@end
#import "UIView+CKTabBarItem.h"
#import <objc/runtime.h>
@implementation UIView (CKTabBarItem)
+ (void)load
{
if (@available(iOS 12.1, *)) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = NSClassFromString(@"UITabBarButton");
SEL originalSelector = @selector(setFrame:);
SEL swizzledSelector = @selector(ck_setFrame:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL isSuccess = class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (isSuccess) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
}
- (void)ck_setFrame:(CGRect)frame
{
if (!CGRectIsEmpty(self.frame)) {
if (CGRectIsEmpty(frame)) {
return;
}
frame.size.height = MAX(frame.size.height, 48.0);
}
[self ck_setFrame: frame];
}
@end
-
Swift版
Swift4 不支持dispatch_once,靜態(tài)變量默認用dispatch_once初始化,可以替代dispatch_once的實現(xiàn)
import UIKit
extension UIView {
/// Swift4 不支持dispatch_once,靜態(tài)變量默認用dispatch_once初始化,可以替代dispatch_once的實現(xiàn)
private static let swizzlingTabBarButtonFrame: Void = {
guard #available(iOS 12.1, *) else {
return
}
guard let cls = NSClassFromString("UITabBarButton") else {
return
}
let originalSelector = #selector(setter: UIView.frame)
let swizzledSelector = #selector(UIView.ck_setFrame)
guard let originalMethod = class_getInstanceMethod(cls, originalSelector) else {
return
}
guard let swizzledMethod = class_getInstanceMethod(cls, swizzledSelector) else {
return
}
let isSuccess = class_addMethod(cls,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod))
if (isSuccess) {
class_replaceMethod(cls,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}()
@objc func ck_setFrame(frame: CGRect) {
var newFrame: CGRect = frame
if !self.frame.isEmpty {
guard !newFrame.isEmpty else {
return
}
newFrame.size.height = newFrame.size.height > 48.0 ? newFrame.size.height : 48.0
}
self.ck_setFrame(frame: newFrame)
}
open class func swizzledTabBarButtonFrame() {
UIView.swizzlingTabBarButtonFrame
}
}
Swift因為沒有+load方法,所以需要手動去觸發(fā),我這里直接放到UIApplicationDelegate的代理方法中。
// MARK: - <UIApplicationDelegate>
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// ...
// 處理iOS 12.1系統(tǒng)tabBar圖標偏移錯位問題
UIView.swizzledTabBarButtonFrame()
return true
}
如果有任何疑問,歡迎留言或私信交流。