Tencent / QMUI_iOS

QMUI iOS——致力于提高项目 UI 开发效率的解决方案
http://qmuiteam.com/ios
Other
7.05k stars 1.37k forks source link

AutomaticCustomNavigationBarTransitionStyle 在某些场景的 bug #1325

Closed MoLice closed 2 years ago

MoLice commented 2 years ago

Bug 表现

  1. 以 QD 为例,项目配置表开启 AutomaticCustomNavigationBarTransitionStyle = YES
  2. VC1 里实现以下代码:

    static UIImage *bg;
    - (UIImage *)qmui_navigationBarBackgroundImage {
        if (!bg) {
            bg = [UIImage qmui_imageWithColor:UIColor.blackColor]; // 黑色
        }
        return bg;
    }
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        [self.view setNeedsLayout];
        [self.view layoutIfNeeded];
    }
  3. VC2 里实现以下代码:
    - (UIImage *)qmui_navigationBarBackgroundImage {
        UIViewController *vc = self.qmui_previousViewController;
        UIImage *bg = [((id<QMUINavigationControllerDelegate>)vc) qmui_navigationBarBackgroundImage];
        if (!bg) {
            return NavBarBackgroundImage; //  获取不到前一个界面时,使用当前 App 默认导航栏背景图(蓝色)
        }
        return bg;
    }
  4. 从 VC1 push 进 VC2,然后再 pop 回来时,可观察到导航栏突变。

录屏

Bug 现场的界面截图,或者 Xcode 控制台的错误信息截图,有问题的代码截图

预期的表现

按以上实现代码的语义,VC2 的导航栏希望保持与 VC1 的导航栏样式一致,但在 pop 过程中该意图无法正确满足。

其他信息

MoLice commented 2 years ago

问题分析

当开启 AutomaticCustomNavigationBarTransitionStyle 功能时,以 pop 转场为例,QMUI 在以下几个时机里会试图通过 qmui_navigationBarBackgroundImage 等系列接口去询问 pop 前后两个界面的导航栏样式,并通过一些方式来对比,如果发现前后样式不一致,则会把系统导航栏的 backgroundView 隐藏,并在前后两个 VC.view 上各自添加一条假的 UINavigationBar 来模拟真实导航栏效果:

  1. willPop VC2 时,会尝试给 VC2 添加假 bar。
  2. VC1、VC2 的 viewWillLayoutSubviews 里,会尝试给 VC1、VC2 添加假 bar。

一般情况下,但 VC2 pop 到 VC1 时,VC1 的 viewWillLayoutSubviews 是不会被触发的。但如果按 issue 里的代码写法,VC1 在 viewWillAppear: 时主动触发自己的 layout,则整个流程会变成这样:

  1. willPop VC2 时,UINavigationController 还没把 VC2 从 viewControllers 内移除掉,所以此时 VC2 能获取到 qmui_previousViewController,并返回 qmui_previousViewController 的背景图,于是 QMUI 认为两张图是相同的,不触发假 bar 逻辑。
  2. VC1 主动触发自身的 viewWillLayoutSubviews,于是 QMUI 重新走 AutomaticCustomNavigationBarTransitionStyle 的逻辑,当询问到 VC2 时,由于 VC2 已经从 UINavigationController 里被移除掉了,所以 VC2 获取到的 qmui_previousViewController 为空,于是返回默认的蓝色图,QMUI 认为蓝色图和黑色图不一致,所以触发假 bar 效果,将 UINavigationBar.backgroundView 隐藏,并只给 VC1 添加了一条假 bar。于是就出现视频里的表现:pop 过程中 VC2 的导航栏空了,但 VC1 还有——因为此时 VC1 是假 bar。

这里的问题关键在于:

  1. qmui_previousViewController 对时机有要求,并非每次调用都能获取到正确的值。
  2. AutomaticCustomNavigationBarTransitionStyle 的实现只按照最理想的情况来写,没有考虑到随时可能被触发。
MoLice commented 2 years ago

已发布 4.4.0 修复该问题。