react / react/react-native

[iOS] ModalHostViewController not properly dismissed if presenting another VC

オープン
#54,856 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Component: Modal Needs: Triage :mag: Platform: iOS
主要言語
C++
スター
127k
フォーク
25.3k
平均マージ
1日 23時間
マージ済み PR(30日)
4

説明

Description

In our brownfield application, we have a scenario where we can open a login flow from the RN Modal component. The login flow shows an iOS system view controller with a webview. At the same time, the dialog is getting closed. The result of these two actions happening roughly at the same time is that: 1) the login VC gets dismissed, while the RCTModalHostViewController is still present as a top-level VC, but without any UI. It is intercepting all touches and preventing further usage of the app.

Here is the view hierarchy:
Image

I've done some investigation and found that this is due to the following RN code:

- (void)dismissViewController:(UIViewController *)modalViewController
                     animated:(BOOL)animated
                   completion:(void (^)(void))completion
{
  [modalViewController dismissViewControllerAnimated:animated completion:completion];
}

Basically the modal VC is trying to dismiss itself. However, the dismissViewControllerAnimated:completion: method has two modes:

  • When given VC is the top presented on, it dismisses itself from its parent
  • When given VC is presenting another VC, it is dismissing that VC instead, but it stays presented itself.

Relevant docs from iOS API:

The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, UIKit asks the presenting view controller to handle the dismissal.

Fix for that would be to make the code dismiss the same whether modal VC is presenting another VC or not. A simple way would be to replace:

  [modalViewController dismissViewControllerAnimated:animated completion:completion];

with

  [modalViewController.presentingViewController dismissViewControllerAnimated:animated completion:completion];

Note: the reproducer uses react-native-image-picker library to show another VC, but in our brownfield app, we use our VC. The type of VC does not really matter, as the important part is that it's presented on top of the modal VC that will be dismissed before the 2nd VC gets dismissed.

Steps to reproduce
  1. Download & run the reproducer repo: https://github.com/mdjastrzebski/rn-repro-stale-modal-vc-blocks-touches
  2. Press the "Open Modal" button
  3. The modal should open, press "Open Photo Library"
  4. The system Photo Picker should slide in briefly and get dismissed
  5. After you are back to the initial VC you cannot make any more touch actions, as an invisible RCTModalHostViewController is still present (see view hierarchy screenshot)

You may need to repeat this a few times, but you should be able to trigger the issue. In our brownfield app, we have 100% repro, but in the contrived example, it only occurs sometimes.

https://github.com/user-attachments/assets/0b2e63d6-4301-4045-b108-a8bfeccc5b3c

React Native Version

0.83.0

Affected Platforms

Runtime - iOS

Output of npx @react-native-community/cli info
System:
  OS: macOS 26.1
  CPU: (11) arm64 Apple M3 Pro
  Memory: 266.56 MB / 36.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 22.17.1
    path: /Users/mdj/.nvm/versions/node/v22.17.1/bin/node
  Yarn:
    version: 1.22.22
    path: /Users/mdj/.nvm/versions/node/v22.17.1/bin/yarn
  npm:
    version: 10.9.2
    path: /Users/mdj/.nvm/versions/node/v22.17.1/bin/npm
  Watchman:
    version: 2025.07.28.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.16.2
    path: /Users/mdj/.rbenv/shims/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 25.0
      - iOS 26.0
      - macOS 26.0
      - tvOS 26.0
      - visionOS 26.0
      - watchOS 26.0
  Android SDK:
    API Levels:
      - "35"
      - "36"
    Build Tools:
      - 35.0.0
      - 36.0.0
    System Images:
      - android-35 | Google Play ARM 64 v8a
      - android-36 | Google Play ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2025.2 AI-252.25557.131.2521.14432022
  Xcode:
    version: 26.0.1/17A400
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.16
    path: /usr/bin/javac
  Ruby:
    version: 3.4.5
    path: /Users/mdj/.rbenv/shims/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 20.0.0
    wanted: 20.0.0
  react:
    installed: 19.2.0
    wanted: 19.2.0
  react-native:
    installed: 0.83.0
    wanted: 0.83.0
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: true
  newArchEnabled: true
Stacktrace or Logs
n/a
MANDATORY Reproducer

https://github.com/mdjastrzebski/rn-repro-stale-modal-vc-blocks-touches

Screenshots and Videos

No response

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm の dismissViewController: から開始します。リンクされた rn-repro-stale-modal-vc-blocks-touches reproducer を実行し、別の view controller が表示されたときの dismissal path を確認します。photo picker または別の controller が閉じられた後に、modal host が完全に dismiss され、touches をインターセプトしなくなれば完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
ios, objective-c, react-native
領域
mobile-dev
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
静か
明瞭さ
明確に書かれている
初心者へのやさしさ
68/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。