iOS 12.1上的UITabBar项目向后跳跃导航

iOS 12.1上的UITabBar项目向后跳跃导航,ios,swift,uitabbar,Ios,Swift,Uitabbar,我有一个iOS应用程序,主屏幕上有UITabBarController,导航到一个细节屏幕,隐藏UITabBarController,设置为hidesBottomBarWhenPushed=true 返回主屏幕时,UITabBarController会执行一个奇怪的“跳转”,如图所示: 只有在iOS 12.1上才会发生这种情况,而不是在12.0或11.x上 看起来像是iOS 12.1的bug,因为我注意到其他应用程序,比如FB Messenger,也有这种行为,但我想知道,有什么解决办法吗?我

我有一个iOS应用程序,主屏幕上有
UITabBarController
,导航到一个细节屏幕,隐藏
UITabBarController
,设置为
hidesBottomBarWhenPushed=true

返回主屏幕时,
UITabBarController
会执行一个奇怪的“跳转”,如图所示:

只有在iOS 12.1上才会发生这种情况,而不是在12.0或11.x上

看起来像是iOS 12.1的bug,因为我注意到其他应用程序,比如FB Messenger,也有这种行为,但我想知道,有什么解决办法吗?

我想这是苹果的bug 但您可以尝试将其作为热修复程序:只需使用以下代码为tabBar创建一个类:

import UIKit

class FixedTabBar: UITabBar {

    var itemFrames = [CGRect]()
    var tabBarItems = [UIView]()


    override func layoutSubviews() {
        super.layoutSubviews()

        if itemFrames.isEmpty, let UITabBarButtonClass = NSClassFromString("UITabBarButton") as? NSObject.Type {
            tabBarItems = subviews.filter({$0.isKind(of: UITabBarButtonClass)})
            tabBarItems.forEach({itemFrames.append($0.frame)})
        }

        if !itemFrames.isEmpty, !tabBarItems.isEmpty, itemFrames.count == items?.count {
            tabBarItems.enumerated().forEach({$0.element.frame = itemFrames[$0.offset]})
        }
    }
}
导入UIKit
阿巴尔延伸{
打开覆盖函数布局子视图(){
super.layoutSubviews()
如果让UITabBarButtonClass=NSClassFromString(“UITabBarButton”)作为?NSObject.Type{
let subItems=self.subviews.filter({return$0.isKind(of:UITabBarButtonClass)})
如果subItems.count>0{
让tmpWidth=UIScreen.main.bounds.width/CGFloat(subItems.count)
对于子项中的(索引,项)。枚举(){
item.frame=CGRect(x:CGFloat(索引)*tmpWidth,y:0,宽度:tmpWidth,高度:item.bounds.height)
}
}
}
}
打开覆盖函数hitTest(uPoint:CGPoint,带有事件:UIEvent?->UIView{
如果让视图:uitabar=super.hitTest(点,带:事件)作为?uitabar{
对于视图中的项。子视图{

如果point.x>=item.frame.origin.x&&point.x有两种方法可以解决此问题, 首先,在UITabBarController中,设置isTranslucent=false,如下所示:

[[UITabBar appearance] setTranslucent:NO];
第二,如果第一个解决方案无法解决您的问题,请尝试以下方法:

这是Objective-C代码

// .h
@interface CYLTabBar : UITabBar
@end 

// .m
#import "CYLTabBar.h"

CG_INLINE BOOL
OverrideImplementation(Class targetClass, SEL targetSelector, id (^implementationBlock)(Class originClass, SEL originCMD, IMP originIMP)) {
   Method originMethod = class_getInstanceMethod(targetClass, targetSelector);
   if (!originMethod) {
       return NO;
   }
   IMP originIMP = method_getImplementation(originMethod);
   method_setImplementation(originMethod, imp_implementationWithBlock(implementationBlock(targetClass, targetSelector, originIMP)));
   return YES;
}
@implementation CYLTabBar

+ (void)load {

   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       if (@available(iOS 12.1, *)) {
           OverrideImplementation(NSClassFromString(@"UITabBarButton"), @selector(setFrame:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP originIMP) {
               return ^(UIView *selfObject, CGRect firstArgv) {

                   if ([selfObject isKindOfClass:originClass]) {

                       if (!CGRectIsEmpty(selfObject.frame) && CGRectIsEmpty(firstArgv)) {
                           return;
                       }
                   }

                   // call super
                   void (*originSelectorIMP)(id, SEL, CGRect);
                   originSelectorIMP = (void (*)(id, SEL, CGRect))originIMP;
                   originSelectorIMP(selfObject, originCMD, firstArgv);
               };
           });
       }
   });
}
@end

更多信息:

这里有一个解决方案,可以处理添加或删除的旋转和选项卡栏项:

class FixedTabBar: UITabBar {

    var buttonFrames: [CGRect] = []
    var size: CGSize = .zero

    override func layoutSubviews() {
        super.layoutSubviews()

        if UIDevice.current.systemVersion >= "12.1" {
            let buttons = subviews.filter {
                String(describing: type(of: $0)).hasSuffix("Button")
            }
            if buttonFrames.count == buttons.count, size == bounds.size {
                zip(buttons, buttonFrames).forEach { $0.0.frame = $0.1 }
            } else {
                buttonFrames = buttons.map { $0.frame }
                size = bounds.size
            }
        }
    }
}
谢谢你的想法, 我刚刚将c内联函数改为OC static方法,因为我不会过多地使用这个
重写实现

static CGFloat const kIPhoneXTabbarButtonErrorHeight = 33;
static CGFloat const kIPhoneXTabbarButtonHeight = 48;


@implementation FixedTabBar


typedef void(^NewTabBarButtonFrameSetter)(UIView *, CGRect);
typedef NewTabBarButtonFrameSetter (^ImpBlock)(Class originClass, SEL originCMD, IMP originIMP);


+ (BOOL)overrideImplementationWithTargetClass:(Class)targetClass targetSelector:(SEL)targetSelector implementBlock:(ImpBlock)implementationBlock {
    Method originMethod = class_getInstanceMethod(targetClass, targetSelector);
    if (!originMethod) {
        return NO;
    }
    IMP originIMP = method_getImplementation(originMethod);
    method_setImplementation(originMethod, imp_implementationWithBlock(implementationBlock(targetClass, targetSelector, originIMP)));
    return YES;
}


+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (@available(iOS 12.1, *)) {
            [self overrideImplementationWithTargetClass:NSClassFromString(@"UITabBarButton")
                                         targetSelector:@selector(setFrame:)
                                         implementBlock:^NewTabBarButtonFrameSetter(__unsafe_unretained Class originClass, SEL originCMD, IMP originIMP) {
                return ^(UIView *selfObject, CGRect firstArgv) {
                    if ([selfObject isKindOfClass:originClass]) {
                        if (!CGRectIsEmpty(selfObject.frame) && CGRectIsEmpty(firstArgv)) {
                            return;
                        }
                        if (firstArgv.size.height == kIPhoneXTabbarButtonErrorHeight) {
                            firstArgv.size.height = kIPhoneXTabbarButtonHeight;
                        }
                    }
                    void (*originSelectorIMP)(id, SEL, CGRect);
                    originSelectorIMP = (void (*)(id, SEL, CGRect))originIMP;
                    originSelectorIMP(selfObject, originCMD, firstArgv);
                };
            }];
        }
    });
}

@end

在您的
UITabBarController
中,设置
isTranslucent=false

以下是swift代码

extension UIApplication {
open override var next: UIResponder? {
    // Called before applicationDidFinishLaunching
    SwizzlingHelper.enableInjection()
    return super.next
}
}

类SwizzlingHelper{

static func enableInjection() {
    DispatchQueue.once(token: "com.SwizzlingInjection") {
        //what to need inject
        UITabbarButtonInjection.inject()
    }
}
更多信息

我面临着完全相同的问题,应用程序的架构是每个选项卡一个导航控制器。我发现解决这个问题的最简单的非黑客方法是将
uitabarcontroller
放在
UINavigationController
内,然后移除单个
UINavigationController
s

之前:

                   -> UINavigationController -> UIViewController
                   -> UINavigationController -> UIViewController
UITabBarController -> UINavigationController -> UIViewController
                   -> UINavigationController -> UIViewController
                   -> UINavigationController -> UIViewController
                                             -> UIViewController
                                             -> UIViewController
UINavigationController -> UITabBarController -> UIViewController
                                             -> UIViewController
                                             -> UIViewController
之后:

                   -> UINavigationController -> UIViewController
                   -> UINavigationController -> UIViewController
UITabBarController -> UINavigationController -> UIViewController
                   -> UINavigationController -> UIViewController
                   -> UINavigationController -> UIViewController
                                             -> UIViewController
                                             -> UIViewController
UINavigationController -> UITabBarController -> UIViewController
                                             -> UIViewController
                                             -> UIViewController
通过使用外部
UINavigationController
,将视图控制器推到导航堆栈上时,不需要隐藏
uitabar

注意事项:

                   -> UINavigationController -> UIViewController
                   -> UINavigationController -> UIViewController
UITabBarController -> UINavigationController -> UIViewController
                   -> UINavigationController -> UIViewController
                   -> UINavigationController -> UIViewController
                                             -> UIViewController
                                             -> UIViewController
UINavigationController -> UITabBarController -> UIViewController
                                             -> UIViewController
                                             -> UIViewController
到目前为止,我发现的唯一问题是,在每个
UIViewController
上设置标题或右/左栏按钮项没有相同的效果。为了解决这个问题,我在可见的
UIViewController
发生更改时,通过
uiabbarcontrollerdelegate
应用了更改

func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
    guard let topItem = self.navigationController?.navigationBar.topItem else { return }
    precondition(self.navigationController == viewController.navigationController, "Navigation controllers do not match. The following changes might result in unexpected behaviour.")
    topItem.title = viewController.title
    topItem.titleView = viewController.navigationItem.titleView
    topItem.leftBarButtonItem = viewController.navigationItem.leftBarButtonItem
    topItem.rightBarButtonItem = viewController.navigationItem.rightBarButtonItem
}

请注意,我添加了一个
预处理失败
,以捕获导航架构被修改时出现的任何情况

苹果现在已经在iOS 12.1.1中修复了这一问题

如果您仍然想保持选项卡栏半透明,则需要从
UITabBar
中进行子类化,并覆盖属性
安全区域插入

class MyTabBar: UITabBar {

private var safeInsets = UIEdgeInsets.zero

@available(iOS 11.0, *)
override var safeAreaInsets: UIEdgeInsets {
    set {
        if newValue != UIEdgeInsets.zero {
            safeInsets = newValue
        }
    }
    get {
        return safeInsets
    }
} 
}

这样做的目的是不允许系统将
插入设置为零,这样选项卡栏就不会跳转。

您可以通过以下方式覆盖
-(UIEdgeInsets)安全区域插入方法,用于一些iOS 12子版本:

- (UIEdgeInsets)safeAreaInsets {
    UIEdgeInsets insets = [super safeAreaInsets];
    CGFloat h = CGRectGetHeight(self.frame);
    if (insets.bottom >= h) {
        insets.bottom = [self.window safeAreaInsets].bottom;
    }
    return insets;
}
在我的例子(iOS 12.1.4)中,我发现这种奇怪的glitchy行为是由使用
.modalPresentationStyle=.fullScreen


在将他们的presentationStyle更新为
。全屏
后,故障消失了。

我想知道为什么还没有发布(或者可能我错过了!)。它甚至出现在whatsApp上。新的iOS 12.1.1(目前作为测试版发布)已经解决了这一问题好主意,我想尝试一下,但使用自定义的
UITabBar
UITabBarController
并不是那么容易…现在尝试一下,似乎很有希望,只是无法处理方向change@IgorKulman我没有用不同的方向测试它…我会试着在weekend@user10589608我收回它,在6S上工作,但在XS Max上不工作,在XS Max上,UI看起来非常不亮,在将设备旋转到景观时也会不亮。您应该提到,您的代码引用自UITabBarController中的『github.com/QMUI/QMUI_iOS/issues/410』,
[self.tabBar setTranslucent:NO]
很有效!谢谢。如果您想保持选项卡栏半透明,这很难解决问题。这很有效,但如果您想保持选项卡半透明,请尝试我的解决方案:为什么会发生这种情况,以及为什么要将选项卡栏设置为半透明来解决它?现在我遇到了tableView底部插入的问题:(这只是iOS 12中的一个bug,还是以前也存在过?我在12.1.0中遇到过这个bug,然后将我的iPhone更新为12.1.2,所以这个bug现在消失了。但是我想知道我应该从所有以前的iOS版本中限制它,还是只限于12.1.0版本。thanx!@SerjRubens这个bug是在iOS 12中引入的,在iOS 12.1.1中修复的。我不知道该怎么办。)你的意思是限制以前iOS版本的bug啊,好吧,这就是我想知道的,谢谢:)p.s.限制mb不是一个好词,但我添加了如下条件以避免以前版本的bug:tabBar.isTranslucent=ProcessInfo().IsoperatingSystemMatleast(OperatingSystemVersion(majorVersion:12,minorVersion:1,patchVersion:1))。所以现在我可以更改它,只要你说它在版本中还可以