iOS 12.1系统tabBar按钮偏移错位问题

在iOS12.1系统中,使用UINavigationController推送UIViewController并隐藏TabBar时,手势返回会出现TabBarButton布局错误。解决方案包括:不修改tabBar透明度或添加背景图片,以及利用runtime交换UITabBarButton的setFrame方法来过滤错误设置。文中提供了OC和Swift两种代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

现在我们使用的APP框架,大部分都是 UINavigationController + UITabBarController的模式。如果使用的是默认的UITabBar磨砂效果,而且在pushViewController时,设置了hidesBottomBarWhenPushed属性为true ,则在iOS 12.1系统,使用手势返回的时候,就会触发UITabBarButton 被设置frame.size 为 (0, 0)的错误布局, 导致的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
        }
    }
}

方案一:修改tabBarisTranslucent透明度(不推荐)或添加背景图片

1、可以通过修改tabBar的透明度达到我们想要的效果:

 

if #available(iOS 12.1, *) {
  UITabBar.appearance().isTranslucent = false
} else {
  UITabBar.appearance().isTranslucent = true
}

但是这个会引入其它的问题:

  • (1)tabBar想保留原来的磨砂效果;
  • (2)会影响tableView的布局,底部间距多了tabBar的高度;

所以这种方式pass

2、可以统一给tabBar添加背景图片,则不会有这个问题。

方案二:使用runtime过滤UITabBarButtonframe设置(推荐)

为了达到既不修改原先的设计又达到解决这个问题,我这边通过交换UITabBarButtonsetFrame方法,过滤一些错误的大小设置,来达到我们的目的,这里提供了OCSwift两个版本的代码,如下:

  • OC

添加UIView的分类,并在+load方法中,交换UITabBarButtonsetFrame方法,过滤错误的大小

 

#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,静态变量默认用dispatch_once初始化,可以替代dispatch_once的实现

 

import UIKit

extension UIView {
    
    /// Swift4 不支持dispatch_once,静态变量默认用dispatch_once初始化,可以替代dispatch_once的实现
    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方法,所以需要手动去触发,我这里直接放到UIApplicationDelegate的代理方法中。

 

转载链接:https://sg.jianshu.io/p/32f7fe7fccee

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值