美文网首页
iOS开发--iOS 12.1系统tabBar按钮偏移错位问题

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

作者: 青苹果园 | 来源:发表于2018-11-29 15:56 被阅读229次

    现在我们使用的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的代理方法中。

    // MARK: - <UIApplicationDelegate>
        
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            // ...
    
            // 处理iOS 12.1系统tabBar图标偏移错位问题
            UIView.swizzledTabBarButtonFrame()
    
            return true
        }
    

    如果有任何疑问,欢迎留言或私信交流。

    相关文章

      网友评论

          本文标题:iOS开发--iOS 12.1系统tabBar按钮偏移错位问题

          本文链接:https://www.haomeiwen.com/subject/wlcfcqtx.html