Tencent / QMUI_iOS

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

iOS 14,iPadOS,对 UIView 使用 qmui_layoutSubviewsBlock,对一个设置了 inputAccessoryView 的输入框聚焦会出现死循环 #1247

Closed jiasongs closed 2 years ago

jiasongs commented 3 years ago

Bug 表现 iPadOS,开启悬浮键盘的情况下,UIView使用qmui_layoutSubviewsBlock,会造成循环调用

截图

图片 图片

「_UIPopoverStandardChromeView」还有几个系统私有类一直不停的循环调用layoutSubviews

如何重现

  1. 重要:开启iPad悬浮键盘模式
  2. UITextView设置self.textView.inputAccessoryView = UIView.new;
  3. 「UIView」(注意不能是子类,只能是UIView类)使用qmui_layoutSubviewsBlock,例如:self.view.qmui_layoutSubviewsBlock = ^(__kindof UIView *view) {};

此DEMO中,QDTextViewController的58行做了测试代码,运行DEMO,进入QDTextViewController,点击输入框即可复现 QMUIDemoTest.zip

其他信息

jiasongs commented 3 years ago
图片

经过反复测试得知,只要不在+ (void)load里hook@selector(layoutSubviews)就会出现上述问题,反之则不会 QMUIDemoTest2.zip

MoLice commented 2 years ago

根据 @ziecho 的分析,系统的 UIView 在 UIViewCommonInitWithFrame 时会判断当前的 layoutSubviews 是否为最初的 IMP,根据判断结果走不同的布局策略。而如果在 UIView 保存了最初的 IMP 后,业务代码再去 hook -[UIView layoutSubviews],就会导致某些系统 View 内部的判断认为 IMP 变了,走了预期之外的逻辑。通常这种情况不会导致什么问题,但在 issue 描述的这种场景里会出现死循环。

issue 的补充里说了“如果在 +load 里 hook 就不会出现问题”,是因为 +load 的时机比第一次触发 UIViewCommonInitWithFrame 还要早,所以 UIViewCommonInitWithFrame 里保存的就是你已经 hook 后的 IMP,就不影响原本的判断逻辑。

对于这个场景,我们暂时针对性地修复了,会跟随新版本发布,请关注。

MoLice commented 2 years ago

已发布 4.4.1 修复该问题。