UITabBar在iPhoneX等Push时显示错乱问题

问题描述

看图说话吧,用慢动作,看得明显一些


跳一下

先Present,再Push,看到TabBar 跳了一下,真机上有时会不跳,就显示一半的情况。

解决方案

直接上解决方案:
替换 UITabBar 的 setFrame方法

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;
}

static CGFloat const kIPhoneXTabbarHeight = 83;

+ (BOOL)iPhoneX {
    if (@available(iOS 11.0, *)) {
        BOOL result = NO;
        UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
            UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
            if (orientation == UIInterfaceOrientationUnknown || orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
                result = window.safeAreaInsets.top > 0 && window.safeAreaInsets.bottom > 0;
            } else {
                result = window.safeAreaInsets.bottom > 0 && window.safeAreaInsets.left > 0 && window.safeAreaInsets.right > 0;
            }
        }
        return result;
    }
    return NO;
}

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        OverrideImplementation(NSClassFromString(@"UITabBar"), @selector(setFrame:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP originIMP) {
            return ^(UIView *selfObject, CGRect firstArgv) {
                if ([self iPhoneX]) {
                    if (firstArgv.size.height != kIPhoneXTabbarHeight) {
                        firstArgv.size.height = kIPhoneXTabbarHeight;
                    }
                    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];

                    CGFloat y =window.bounds.size.height -kIPhoneXTabbarHeight;
                    if (firstArgv.origin.y != y) {
                        firstArgv.origin.y = y;
                    }
                }

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

(只想找答案的就看这里吧)

分析过程

起初并不知道这一问题重现的步骤,一直以为是偶现的,没注意,后来发现这个问题次数很多,就开始关注了。

重现步骤:

APP里面有UITabBar,每个Tab页里都有一个Nav,并且在这个页面上先Present一个页后,关闭Present的View,再Push到新的页面,就会出现这个现象。
还有几个前提条件:
1、iPhoneX 系列
2、TabBar的translucent属性为 YES,即半透明磨砂状的
3、新的页面的hidesBottomBarWhenPushed 是 YES的,即要TabBar要消失
4、Push时,一定是使用动画效果的(这是个废话)
这么多条件下才能出现,可见这个Bug应该不算大概率的Bug了。

首先,知道了重现的步骤,那就比较一下Present一个View后,TabBar具体发生了什么变化。
从两次的表现来看,我们发现
在异常情况下 Push时 TabBar是到进入页面后,才消失的。
而正常情况下,Push时,TabBar一直跟随第一个页面,感觉这两种情况下TabBar的父窗体都不同的样子。

那就从堆栈看一下,UIView有一个方法 - (void)willMoveToSuperview:(nullable UIView *)newSuperview 可以捕获到父窗体变更,我们用个Category实现一下UITabBar的这个方法,看一下变更时的堆栈
参考代码:

@implementation UITabBar (FixPush)
- (void)willMoveToSuperview:(nullable UIView *)newSuperview{
    NSLog(@"要换了 %@",newSuperview);
    NSLog(@"---%@",[NSThread callStackSymbols]);
}
@end

【正常情况下的 push】

2019-06-20 09:40:19.119639+0800 XXTabBar[10773:55905] 要换了 <UIView: 0x7fb15801d210; frame = (414 0; 414 896); layer = <CALayer: 0x60000197e9a0>>
2019-06-20 09:40:19.122909+0800 XXTabBar[10773:55905] ---(
    0   XXTabBar                            0x0000000100481c63 -[UITabBar(FixPush) willMoveToSuperview:] + 83
    1   UIKitCore                           0x0000000104ab70c2 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 545
    2   UIKitCore                           0x0000000103f20ee1 -[UITabBarController _hideBarWithTransition:isExplicit:duration:] + 1058
    3   UIKitCore                           0x0000000103f564c5 -[UINavigationController _hideOrShowBottomBarIfNeededWithTransition:] + 975
    4   UIKitCore                           0x0000000103f55484 -[UINavigationController pushViewController:transition:forceImmediate:] + 1850
    5   UIKitCore                           0x0000000103f54bac -[UINavigationController pushViewController:animated:] + 681
    6   XXTabBar                            0x0000000100482700 -[FirstViewController gotoNext:] + 208
    7   UIKitCore                           0x00000001045f1204 -[UIApplication sendAction:to:from:forEvent:] + 83
    8   UIKitCore                           0x00
2019-06-20 09:40:19.638141+0800 XXTabBar[10773:55905] 要换了 <UILayoutContainerView: 0x7fb158016410; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x60000197d4c0>>
2019-06-20 09:40:19.640726+0800 XXTabBar[10773:55905] ---(
    0   XXTabBar                            0x0000000100481c63 -[UITabBar(FixPush) willMoveToSuperview:] + 83
    1   UIKitCore                           0x0000000104ab70c2 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 545
    2   UIKitCore                           0x0000000103f34ce8 -[UILayoutContainerView addSubview:] + 75
    3   UIKitCore                           0x0000000103f216fa __65-[UITabBarController _hideBarWithTransition:isExplicit:duration:]_block_invoke.661 + 65
    4   UIKitCore                           0x000000010401ced8 -[_UIViewControllerTransitionCoordinator _applyBlocks:releaseBlocks:] + 294
    5   UIKitCore                           0x0000000104018cfd -[_UIViewControllerTransitionContext _runAlongsideCompletions] + 132
    6   UIKitCore                           0x00000001040189ec -[_UIViewControllerTransitionContext completeTransition:] + 118
    7   UIKitCore                           0x000000010402a686 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke.118 + 877

【异常情况下的 push】

2019-06-20 09:40:08.056567+0800 XXTabBar[10773:55905] 要换了 <UIView: 0x7fb15801d210; frame = (0 0; 414 896); layer = <CALayer: 0x60000197e9a0>>
2019-06-20 09:40:08.060075+0800 XXTabBar[10773:55905] ---(
    0   XXTabBar                            0x0000000100481c63 -[UITabBar(FixPush) willMoveToSuperview:] + 83
    1   UIKitCore                           0x0000000104ab70c2 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 545
    2   UIKitCore                           0x0000000103f20ee1 -[UITabBarController _hideBarWithTransition:isExplicit:duration:] + 1058
    3   UIKitCore                           0x0000000103f564c5 -[UINavigationController _hideOrShowBottomBarIfNeededWithTransition:] + 975
    4   UIKitCore                           0x0000000103f55484 -[UINavigationController pushViewController:transition:forceImmediate:] + 1850
    5   UIKitCore                           0x0000000103f54bac -[UINavigationController pushViewController:animated:] + 681
    6   XXTabBar                            0x0000000100482700 -[FirstViewController gotoNext:] + 208
    7   UIKitCore                           0x00000001045f1204 -[UIApplication sendAction:to:from:forEvent:] + 83
    8   UIKitCore                           0x00
2019-06-20 09:40:08.065555+0800 XXTabBar[10773:55905] 要换了 <UILayoutContainerView: 0x7fb158016410; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x60000197d4c0>>
2019-06-20 09:40:08.067366+0800 XXTabBar[10773:55905] ---(
    0   XXTabBar                            0x0000000100481c63 -[UITabBar(FixPush) willMoveToSuperview:] + 83
    1   UIKitCore                           0x0000000104ab70c2 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 545
    2   UIKitCore                           0x0000000103f34ce8 -[UILayoutContainerView addSubview:] + 75
    3   UIKitCore                           0x0000000103f199fc -[UITabBarController _prepareTabBar] + 324
    4   UIKitCore                           0x0000000103f19f3f -[UITabBarController _layoutContainerView] + 378
    5   UIKitCore                           0x0000000103f194a6 -[UITabBarController __viewWillLayoutSubviews] + 38
    6   UIKitCore                           0x0000000103f3438d -[UILayoutContainerView layoutSubviews] + 217
    7   UIKitCore                           0x0000000104abd9c1 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1417
    8   QuartzCore                          0x000000010602eeae -[CALayer layoutSublayers] + 173

从时间上看,TabBar更换到UILayoutContainerView父窗体, 异常情况下比正常情况下提早了0.5秒,且触发的对象也不同。
本来的想法是阻止这种异常的触发,但尝试了很多次都没有找到合适的办法,
那就只能在改变UITabBar的frame了,这样只能效果上基本接近。

通过替换UITabBar的setFrame方法,捕获到异常时的frame

2019-06-20 15:50:48.973860+0800 XXTabBar[34846:368452] frame {{0, 813}, {414, 83}}
2019-06-20 15:50:48.975635+0800 XXTabBar[34846:368452] frame {{0, 813}, {414, 49}}
2019-06-20 15:50:48.975828+0800 XXTabBar[34846:368452] frame {{0, 847}, {414, 49}}
2019-06-20 15:50:49.504939+0800 XXTabBar[34846:368452] frame {{0, 847}, {414, 49}}

正常时

2019-06-20 15:51:17.453253+0800 XXTabBar[34846:368452] frame {{0, 813}, {414, 83}}
2019-06-20 15:51:17.968698+0800 XXTabBar[34846:368452] frame {{0, 813}, {414, 83}}

看到frame中高度和Top都不对,那就校正吧

if (firstArgv.size.height != kIPhoneXTabbarHeight) {
                        firstArgv.size.height = kIPhoneXTabbarHeight;
                    }
                    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];

                    CGFloat y =window.bounds.size.height -kIPhoneXTabbarHeight;
                    if (firstArgv.origin.y != y) {
                        firstArgv.origin.y = y;
                    }

试一下效果:


修改后效果

github 代码位置 https://github.com/yonglinwang002/UITabBar-FixForPush

好了,先这样吧。

以后有时间,再看一下有没有办法改到和正常情况下一样

参考资料:

  1. tabbar 跳动
  2. iOS12.1 在 pop 后,会引起TabBar布局异常
  3. QMUI_iOS 解决iOS12.1 TabBar问题
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,475评论 4 372
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,744评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,101评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,732评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,141评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,049评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,188评论 2 320
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,965评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,716评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,867评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,341评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,663评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,376评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,200评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,990评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,179评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,979评论 2 279

推荐阅读更多精彩内容