Tencent / QMUI_iOS

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

automaticCustomNavigationBarTransitionStyle = YES 的效果在 pop 时可能出错 #1253

Closed xixisplit closed 3 years ago

xixisplit commented 3 years ago

Bug 表现 导航栏 automaticCustomNavigationBarTransitionStyle = YES 滑动返回 导航栏过度动画异常

截图 Bug 现场的界面截图,或者 Xcode 控制台的错误信息截图,有问题的代码截图 https://user-images.githubusercontent.com/17351286/119950828-e1a7bf00-bfcd-11eb-92f9-a69a2c0c8577.mp4 如何重现

  1. 实现 2 个控制器.分别默认同一个颜色的导航栏
  2. A 页面打开 B 页面.在 B 页面中.进入页面之后.延时刷新导航栏到其他颜色
  3. 滑动返回.这时候.A 与 B 的过度动画如上方第一个视频
  4. 第二种实现方式
  5. 在控制器中实现以下代码也会出现此问题 - (NSString *)customNavigationBarTransitionKey{ return [self navigationBarBackgroundImage].qmui_averageColor.qmui_hexString; }

具体的BUG 表现逻辑为.默认情况下.A与 B 的导航栏一致 ,然后进入 B 页面.之后.因为 B 导航栏延迟刷新.这时候. A 与 B 的状态栏不一致.这时候就会触发这个 bug

这边附带demo navBugDemo.zip

预期的表现 正常情况下.A 与 B 的导航栏颜色不一致的情况下.滑动过度表现应该如下方视屏. https://user-images.githubusercontent.com/17351286/119951922-0e100b00-bfcf-11eb-8c1d-6d0bddd74d9a.mp4

其他信息

HHMedic commented 3 years ago

请问你这个问题解决了吗?

xixisplit commented 3 years ago

请问你这个问题解决了吗?

不使用自动处理. 在 base 控制器中实现 - (nullable NSString *)customNavigationBarTransitionKey{ return [NSString stringWithFormat:@"%@",self]; } 将当前控制器的信息作为 key 返回.让框架每个控制器都处理过度动画就没问题了

MoLice commented 3 years ago

背景

先明确一下 AutomaticCustomNavigationBarTransitionStyle 这个功能的实现思路。它是当前后两个界面拥有不同的 UINavigationBar 样式时,在 push/pop 过程中,会把系统的 UINavigationBar 的 backgroundView 隐藏,然后创建两个 UINavigationBar 实例,分别复制前后两个界面希望的样式,再把这两个 UINavigationBar 分别添加到前后两个 vc.view 上,从而实现“动画过程中看到两个不同样式的 UINavigationBar”的效果。

这里的关键在“把假 bar 添加到 vc.view”的动作。

Bug 解析

QMUI 当前的实现里,对于 pop 操作(包含手势返回),即将被 pop 走的界面的假 bar,是在 pop 调用时就添加了。而背后即将显示出来的界面,它的假 bar 是在它的 viewWillLayoutSubviews 时添加。在 issue 提供的 Demo 里,在这过程中,即将显示的那个 vc 的 viewWillLayoutSubviews 没有被调用,导致背后 vc 的导航栏是透明的,如视频截图所示:

这是不符合预期的。

review 这个功能的代码发现,QMUI 里并没有代码去主动保证 pop 过程中,即将显示的那个 vc 的 viewWillLayoutSubviews 一定会被调用,纯粹取决于系统默认行为。我们用空项目测试过,标准的 pop 操作确实不会触发 viewWillLayoutSubviews,到这一步可以定性该问题为 QMUI 自身的 bug。

但 QMUI Demo 里又是正常的,经调试后发现:

  1. QMUI Demo 的配置表里,给 UIColorSeparator 设置了值为 UIColor.qd_separatorColor,该颜色是一个 QMUIThemeColor 动态色。
  2. 在 UIView+QMUITheme.m 里,对 UIView 类型会重写 didMoveToWindow 方法,在该方法里刷新 view 的样式(本意是某个 view 添加到界面上后,应该用最新的 theme 的样式去重新渲染它)。
  3. 在该方法里,对于值类型为 QMUIThemeColor 的属性,会重新调用一次它的 setter。
  4. 而在 UIView(QMUI_Border) 里,我们提供了属性 qmui_borderColor 并且为它赋初始值 UIColorSeparator。从第1步里我们可知,在 QMUI Demo 内它的初始值为 QMUIThemeColor 类型,所以当 pop 时,前一个界面的 vc.view 被重新加到界面上,会触发它的 didMoveToWindow(第2步),然后它的 setQmui_borderColor: 方法会被执行,从而触发它的 layoutSubviews,进而触发 vc 的 viewWillLayoutSubviews。

通过以上分析可知,QMUI Demo 没出现 issue 的问题是因为配置表的 UIColorSeparator 类型刚好保证了 pop 时前一个界面的 viewWillLayoutSubviews 一定会被触发,而 issue 里的 Demo,UIColorSeparator 值为默认的 UIColorMake(222, 224, 226),并非动态类型,所以在第3步里就跳过了,于是没人再保证 viewWillLayoutSubviews 会被执行,所以假 bar 的效果出错。

解决方式

QMUI 下个版本将会解决该问题,在此之前你可以手动修改本地的 QMUI,增加红框所示的代码:

MoLice commented 3 years ago

已发布 4.3.0 修复该问题。