Custom TopBar button (react component) never resizes to content width under Fabric on iOS 26 and resets to 44pt after any button rebuild

未关闭
#8,339 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
48/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
冷清
技术栈
objective-c, react-native
领域
mobile

调研方向

从 RCTFabricSurface.mm 开始,将其固有尺寸处理与 RCTSurface.mm 进行比较,然后通过 RNNReactButtonView.mm 跟踪 delegate 路径。阅读 RNNUIBarButtonItem.mm 和 RNNButtonsPresenter.setButtons:,以了解约束创建和按钮重建。完成的标准是:由内容决定的宽度在 Fabric 下正常工作,在内容变化时更新,并且在重复重新应用 TopBar 后仍保持稳定。

由索引模型根据 Issue 内容生成。

描述

platform: iOS type: accepted/bug
What happened?

On iOS 26 with the TopBar (RNNReactButtonView/RNNUIBarButtonItem's initWithCustomView: code path, gated behind if (@available(iOS 26.0, *))), a custom React component used as a topBar.rightButtons[].component never grows past the reserved 44pt minimum slot, even though the button is clearly designed to support it (see the kMinBarButtonSlotSize / "grow width once when React reports intrinsic size" comments in RNNUIBarButtonItem.mm and RNNReactButtonView.mm).

Image

Tracked this down to two separate, compounding bugs in the current implementation:

  1. The width-growth mechanism never fires under Fabric (New Architecture).
    The whole feature depends on -surface:didChangeIntrinsicSize: being called on RNNReactButtonView's delegate. That callback is only ever invoked by the legacy, non-Fabric RCTSurface (see RCTSurface.mm, setIntrinsicSize: → [delegate surface:self didChangeIntrinsicSize:...], driven by rootShadowView:didChangeIntrinsicSize:). Under the New Architecture, RCTFabricSurface declares the same intrinsicSize property (inherited from RCTSurfaceProtocol) but never sets it and never calls the delegate method at all — there is no matching code path in RCTFabricSurface.mm. As a result, RNNReactButtonView.intrinsicSize/measuredSurfaceContentSize is always {0,0} and the button's width constraint is never updated from its initial 44pt value, regardless of how wide the mounted React content actually is.

  2. Even after working around (1), the button snaps back to 44pt the next time TopBar buttons are rebuilt (e.g. reselecting a bottom tab, or any mergeOptions/appear-triggered re-application of topBar.rightButtons). RNNButtonsPresenter.setButtons: calls RNNButtonBuilder.build: → initWithCustomView: and constructs a brand new RNNUIBarButtonItem every time buttons are applied, even though the underlying RNNReactButtonView (looked up via RNNReactComponentRegistry.createComponentIfNotExists:) is reused (it's keyed by componentId in a map and only replaced/removed when actually different). initWithCustomView: unconditionally creates new UILayoutPriorityRequired width/height constraints on that reused view every single time, without ever deactivating the constraints created by the previous RNNUIBarButtonItem instance. This leaves multiple conflicting required-priority width constraints active on the same view (e.g. one pinned at the previously-grown width, a fresh one reset to 44pt) — Auto Layout can't satisfy both, and in practice the newly-added 44pt constraint wins, visibly snapping the button back down.

What was the expected behaviour?

A custom React component used as a TopBar button should size itself to its actual rendered content width (bounded below by the ~44pt minimum tap-target size), and should keep tracking that width correctly:

  • On initial mount, based on the component's real rendered content, not just the reserved 44pt placeholder.
  • On subsequent content changes within the same mounted component (e.g. a counter/badge whose text gets longer or shorter).
  • Consistently across repeated TopBar button re-applications (e.g. switching bottom tabs away and back), without the width getting reset or fighting with stale constraints from a previous RNNUIBarButtonItem instance.
Was it tested on latest react-native-navigation?
  • I have tested this issue on the latest react-native-navigation release and it still reproduces.
Help us reproduce this issue!

Reproduction steps

  1. Clone this public minimal reproduction repo:
    https://github.com/matyi/RNNGlassRepro

  2. Run the app and open the screen that uses a custom React component in topBar.rightButtons.

  3. Observe that the button renders variable-width content, but its width stays constrained and the content gets clipped instead of expanding to fit.

  4. As a workaround, manually measure the mounted view hierarchy and pass the actual size through handleIntrinsicSizeChange:. With that fallback in place, the button grows correctly on first mount.

  5. Put the screen inside a bottomTabs layout.

  6. Let the button grow, then switch to another tab and back, or trigger any flow that causes topBar.rightButtons to be re-applied, such as a mergeOptions call in componentDidAppear.

  7. Observe that the button width resets to the minimum size and clips the content again, even though the button content itself has not changed.

In what environment did this happen?
  • React Native Navigation version: 8.8.11
  • React Native version: 0.81.x (or even the lates 0.86.x)
  • Has Fabric (React Native's new rendering system) enabled: yes
  • Node version: v22.19.0
  • Device model: reproduced on iOS 26.1 simulator, also iphone 15 ios 26.5 real device
  • iOS version: 26.1, 26.5
主要语言
MDX
星标
13.2k
派生
2.6k
平均合并
2 天 10 小时
30 天内合并 PR
6

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

wix/react-native-navigation 的其他 Issue

查看 wix/react-native-navigation 的全部 Issue

相似的 Issue

更多 Mobile Dev Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。