react-native icon indicating copy to clipboard operation
react-native copied to clipboard

RN Alert button sometimes renders in the upper-left corner of the screen (iOS)

Open Willham12 opened this issue 1 year ago • 8 comments

Description

Previous issue: https://github.com/facebook/react-native/issues/33889

RN Alert button appears on the left top corner.

Steps to reproduce

function openURL(url: string, error: string) {
	Linking.openURL(url).catch(() => showAlert(`${error} ${url}`));
}

function showAlert(
	title: string,
	message?: string,
	buttons?: AlertButton[],
	cancelable = true,
	onDismiss?: () => void
) {
	Alert.alert(title, message, buttons, {
		cancelable: cancelable,
		onDismiss: onDismiss,
		userInterfaceStyle: 'light',
	});

React Native Version

0.74.3

Affected Platforms

Runtime - iOS

Output of npx react-native info

info Fetching system and libraries information...
System:
  OS: macOS 14.5
  CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
  Memory: 11.83 GB / 64.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 18.19.0
    path: ~/.nvm/versions/node/v18.19.0/bin/node
  Yarn:
    version: 4.2.2
    path: ~/.nvm/versions/node/v18.19.0/bin/yarn
  npm:
    version: 10.2.3
    path: ~/.nvm/versions/node/v18.19.0/bin/npm
  Watchman:
    version: 2024.06.10.00
    path: /usr/local/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /usr/local/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.5
      - iOS 17.5
      - macOS 14.5
      - tvOS 17.5
      - visionOS 1.2
      - watchOS 10.5
  Android SDK:
    API Levels:
      - "33"
      - "34"
    Build Tools:
      - 33.0.0
      - 33.0.1
      - 34.0.0
      - 35.0.0
    System Images:
      - android-33 | Google APIs Intel x86_64 Atom
      - android-34 | Google APIs Intel x86_64 Atom
      - android-34 | Google Play Intel x86_64 Atom
    Android NDK: Not Found
IDEs:
  Android Studio: 2024.1 AI-241.18034.62.2411.12071903
  Xcode:
    version: 15.4/15F31d
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.11
    path: /usr/local/bin/javac
  Ruby:
    version: 3.3.3
    path: /usr/local/opt/ruby/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.3.1
    wanted: 18.3.1
  react-native:
    installed: 0.74.3
    wanted: 0.74.3
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Stacktrace or Logs

No crash

Reproducer

https://snack.expo.dev/PLgXKRrgtZIRIjiqFxkR-

Screenshots and Videos

https://github.com/facebook/react-native/issues/33889

Willham12 avatar Jul 16 '24 07:07 Willham12

Still happening in 0.75.2 Any updates on this?

rafaelctavares avatar Sep 05 '24 18:09 rafaelctavares

What is the last version that hadn't this problem? I'm considering downgrading for a temporary fix.

bernborgess avatar Sep 09 '24 16:09 bernborgess

This is the current comparison of Alert.alert("Test"); on android vs ios:

Android iOS
wpp image

Not how the "OK" is showing in the upper left corner.

bernborgess avatar Sep 09 '24 16:09 bernborgess

We are also facing this issue with [email protected]

native iOS apps also have this issue when trying to present a modal on top of an existing alert

https://stackoverflow.com/questions/27028983/uialertcontroller-is-moved-to-buggy-position-at-top-of-screen-when-it-calls-pre

possible solution: https://stackoverflow.com/a/28731393

satheeshwaran avatar Sep 13 '24 09:09 satheeshwaran

Looking at Xcode logs there is a UILayoutConstraint breaking due to which the alert is misplaced on the screen.

2024-09-16 15:03:45.398080+0530 wagering[1676:706712] [LayoutConstraints] Unable to simultaneously satisfy constraints.
	Probably at least one of the constraints in the following list is one you don't want. 
	Try this: 
		(1) look at each constraint and try to figure out which you don't expect; 
		(2) find the code that added the unwanted constraint or constraints and fix it. 
	(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x2836b3b60 h=-&- v=-&- UITransitionView:0x10ac406f0.minY == 0   (active, names: '|':UIWindow:0x10ac39500 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x2836b07d0 h=-&- v=-&- V:[UITransitionView:0x10ac406f0]-(0)-|   (active, names: '|':UIWindow:0x10ac39500 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x2836b3840 h=--- v=--- UIWindow:0x10ac39500.height == 0   (active)>",
    "<NSLayoutConstraint:0x283686b20 _UIKeyboardLayoutAlignmentView:0x10ae22e90.bottom == UITransitionView:0x10ac406f0.bottom   (active)>",
    "<NSLayoutConstraint:0x2836c5ae0 _UIAlertControllerPhoneTVMacView:0x11102f800'New version available! '.bottom <= UIView:0x10ae46d00.bottom - 8   (active)>",
    "<NSLayoutConstraint:0x2836c7520 V:|-(0)-[UIView:0x10ae46d00]   (active, names: '|':UITransitionView:0x10ac406f0 )>",
    "<NSLayoutConstraint:0x2836c6120 UIView:0x10ae46d00.bottom == _UIKeyboardLayoutAlignmentView:0x10ae22e90.top   (active)>",
    "<NSLayoutConstraint:0x2836c4cd0 _UIAlertControllerPhoneTVMacView:0x11102f800'New version available! '.top >= UIView:0x10ae46d00.top + 47   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x2836c5ae0 _UIAlertControllerPhoneTVMacView:0x11102f800'New version available! '.bottom <= UIView:0x10ae46d00.bottom - 8   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2024-09-16 15:03:46.216950+0530 wagering[1676:706712] [LayoutConstraints] Unable to simultaneously satisfy constraints.
	Probably at least one of the constraints in the following list is one you don't want. 
	Try this: 
		(1) look at each constraint and try to figure out which you don't expect; 
		(2) find the code that added the unwanted constraint or constraints and fix it. 
	(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x2836b3b60 h=-&- v=-&- UITransitionView:0x10ac406f0.minY == 0   (active, names: '|':UIWindow:0x10ac39500 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x2836b07d0 h=-&- v=-&- V:[UITransitionView:0x10ac406f0]-(0)-|   (active, names: '|':UIWindow:0x10ac39500 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x2836b3840 h=--- v=--- UIWindow:0x10ac39500.height == 0   (active)>",
    "<NSLayoutConstraint:0x283686b20 _UIKeyboardLayoutAlignmentView:0x10ae22e90.bottom == UITransitionView:0x10ac406f0.bottom   (active)>",
    "<NSLayoutConstraint:0x2836c4500 _UIAlertControllerPhoneTVMacView:0x11102f800'New version available! '.centerY == UILayoutGuide:0x282e81420'UIViewSafeAreaLayoutGuide'.centerY   (active)>",
    "<NSLayoutConstraint:0x2836c7520 V:|-(0)-[UIView:0x10ae46d00]   (active, names: '|':UITransitionView:0x10ac406f0 )>",
    "<NSLayoutConstraint:0x2836c6120 UIView:0x10ae46d00.bottom == _UIKeyboardLayoutAlignmentView:0x10ae22e90.top   (active)>",
    "<NSLayoutConstraint:0x2836c4cd0 _UIAlertControllerPhoneTVMacView:0x11102f800'New version available! '.top >= UIView:0x10ae46d00.top + 47   (active)>",
    "<NSLayoutConstraint:0x2836c68f0 'UIViewSafeAreaLayoutGuide-bottom' V:[UILayoutGuide:0x282e81420'UIViewSafeAreaLayoutGuide']-(0)-|   (active, names: '|':UIView:0x10ae46d00 )>",
    "<NSLayoutConstraint:0x2836c76b0 'UIViewSafeAreaLayoutGuide-top' V:|-(47)-[UILayoutGuide:0x282e81420'UIViewSafeAreaLayoutGuide']   (active, names: '|':UIView:0x10ae46d00 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x2836c68f0 'UIViewSafeAreaLayoutGuide-bottom' V:[UILayoutGuide:0x282e81420'UIViewSafeAreaLayoutGuide']-(0)-|   (active, names: '|':UIView:0x10ae46d00 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

satheeshwaran avatar Sep 16 '24 11:09 satheeshwaran

Looking at Xcode logs there is a UILayoutConstraint breaking due to which the alert is misplaced on the screen.

2024-09-16 15:03:45.398080+0530 wagering[1676:706712] [LayoutConstraints] Unable to simultaneously satisfy constraints.
	Probably at least one of the constraints in the following list is one you don't want. 
	Try this: 
		(1) look at each constraint and try to figure out which you don't expect; 
		(2) find the code that added the unwanted constraint or constraints and fix it. 
	(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x2836b3b60 h=-&- v=-&- UITransitionView:0x10ac406f0.minY == 0   (active, names: '|':UIWindow:0x10ac39500 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x2836b07d0 h=-&- v=-&- V:[UITransitionView:0x10ac406f0]-(0)-|   (active, names: '|':UIWindow:0x10ac39500 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x2836b3840 h=--- v=--- UIWindow:0x10ac39500.height == 0   (active)>",
    "<NSLayoutConstraint:0x283686b20 _UIKeyboardLayoutAlignmentView:0x10ae22e90.bottom == UITransitionView:0x10ac406f0.bottom   (active)>",
    "<NSLayoutConstraint:0x2836c5ae0 _UIAlertControllerPhoneTVMacView:0x11102f800'New version available! '.bottom <= UIView:0x10ae46d00.bottom - 8   (active)>",
    "<NSLayoutConstraint:0x2836c7520 V:|-(0)-[UIView:0x10ae46d00]   (active, names: '|':UITransitionView:0x10ac406f0 )>",
    "<NSLayoutConstraint:0x2836c6120 UIView:0x10ae46d00.bottom == _UIKeyboardLayoutAlignmentView:0x10ae22e90.top   (active)>",
    "<NSLayoutConstraint:0x2836c4cd0 _UIAlertControllerPhoneTVMacView:0x11102f800'New version available! '.top >= UIView:0x10ae46d00.top + 47   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x2836c5ae0 _UIAlertControllerPhoneTVMacView:0x11102f800'New version available! '.bottom <= UIView:0x10ae46d00.bottom - 8   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2024-09-16 15:03:46.216950+0530 wagering[1676:706712] [LayoutConstraints] Unable to simultaneously satisfy constraints.
	Probably at least one of the constraints in the following list is one you don't want. 
	Try this: 
		(1) look at each constraint and try to figure out which you don't expect; 
		(2) find the code that added the unwanted constraint or constraints and fix it. 
	(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x2836b3b60 h=-&- v=-&- UITransitionView:0x10ac406f0.minY == 0   (active, names: '|':UIWindow:0x10ac39500 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x2836b07d0 h=-&- v=-&- V:[UITransitionView:0x10ac406f0]-(0)-|   (active, names: '|':UIWindow:0x10ac39500 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x2836b3840 h=--- v=--- UIWindow:0x10ac39500.height == 0   (active)>",
    "<NSLayoutConstraint:0x283686b20 _UIKeyboardLayoutAlignmentView:0x10ae22e90.bottom == UITransitionView:0x10ac406f0.bottom   (active)>",
    "<NSLayoutConstraint:0x2836c4500 _UIAlertControllerPhoneTVMacView:0x11102f800'New version available! '.centerY == UILayoutGuide:0x282e81420'UIViewSafeAreaLayoutGuide'.centerY   (active)>",
    "<NSLayoutConstraint:0x2836c7520 V:|-(0)-[UIView:0x10ae46d00]   (active, names: '|':UITransitionView:0x10ac406f0 )>",
    "<NSLayoutConstraint:0x2836c6120 UIView:0x10ae46d00.bottom == _UIKeyboardLayoutAlignmentView:0x10ae22e90.top   (active)>",
    "<NSLayoutConstraint:0x2836c4cd0 _UIAlertControllerPhoneTVMacView:0x11102f800'New version available! '.top >= UIView:0x10ae46d00.top + 47   (active)>",
    "<NSLayoutConstraint:0x2836c68f0 'UIViewSafeAreaLayoutGuide-bottom' V:[UILayoutGuide:0x282e81420'UIViewSafeAreaLayoutGuide']-(0)-|   (active, names: '|':UIView:0x10ae46d00 )>",
    "<NSLayoutConstraint:0x2836c76b0 'UIViewSafeAreaLayoutGuide-top' V:|-(47)-[UILayoutGuide:0x282e81420'UIViewSafeAreaLayoutGuide']   (active, names: '|':UIView:0x10ae46d00 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x2836c68f0 'UIViewSafeAreaLayoutGuide-bottom' V:[UILayoutGuide:0x282e81420'UIViewSafeAreaLayoutGuide']-(0)-|   (active, names: '|':UIView:0x10ae46d00 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

Did you find out how to reproduce it in the React Native API of the ui?

bernborgess avatar Sep 17 '24 12:09 bernborgess

One way to replicate this is to show multiple alerts and move the app foreground ->background->foreground numerous times.

satheeshwaran avatar Sep 18 '24 12:09 satheeshwaran

I can reproduce it when firing alert when app in the background(not killed)

dovhanrg avatar Sep 18 '24 12:09 dovhanrg

Same goes for modal. I am using modal from React-native. My modal is a global modal in App.ts. Modal is controlled by ref. Only on ios

new_arch enabled, react-native: 0.75.4

https://github.com/user-attachments/assets/2d4373d0-f198-4be7-8d78-75bba59d587a

hasanalicansu avatar Nov 11 '24 08:11 hasanalicansu

The issue is still present for Modal and Alert components in Android as well on version 0.76.1 with newArch enabled and disabled.

remacr avatar Nov 13 '24 17:11 remacr

I noticed it happens for me whenever I am mirroring my phone for a presentation. Once I turn off mirroring my iPhone it is displayed in the center.

faljabi avatar Nov 13 '24 18:11 faljabi

The issue is still present for Modal and Alert components in Android as well on version 0.76.1 with newArch enabled and disabled.

Can confirm, this is still occurring on 0.76.3 also. I had a global modal which is completely unusable now.

Tencryn avatar Nov 25 '24 21:11 Tencryn

I'm experiencing this issue as well on version 0.74.3. Any updates?

AlbertoCabreraJr avatar Jan 24 '25 14:01 AlbertoCabreraJr

encountering this issue on both android and ios, modal and alerts components, we're getting reports about it but i was unable to consistently reproduce it react-native: 0.76.7 new architecture enabled

RodSarhan avatar Mar 03 '25 15:03 RodSarhan

I made this patch to force the UIAlertController to the center of the window at all times. After this fix the misalignment has not completely gone away but the frequency has reduced. Someone else can try the patch or work on top of this.

react-native+0.74.1.patch

satheeshwaran avatar Mar 24 '25 11:03 satheeshwaran

I can reproduce it when firing alert when app in the background(not killed)

Yes, when I try this, it quite consistently pops up in the middle of the screen the first time, but if click OK to close it, and then go to the background again to create an alert, it almost always ends up in the upper left corner. This is on iOS. It is still a problem in RN 0.77.2.

Alatius avatar Apr 15 '25 14:04 Alatius

Any Updates?

KyeAtkinson avatar Apr 16 '25 10:04 KyeAtkinson

Bumping once again but my temporary solution to this as the issue is replicable by creating an Alert whilst the app is in the background state (Not Closed).

My Solution was to create an queuing system for Alerts when the AppState is not in the background they are then shown.

KyeAtkinson avatar Apr 30 '25 18:04 KyeAtkinson

Bumping once again but my temporary solution to this as the issue is replicable by creating an Alert whilst the app is in the background state (Not Closed).

My Solution was to create an queuing system for Alerts when the AppState is not in the background they are then shown.

Can you elaborate on that? How do you detect the situation that triggers the corner render?

bernborgess avatar May 01 '25 11:05 bernborgess

Bumping once again but my temporary solution to this as the issue is replicable by creating an Alert whilst the app is in the background state (Not Closed). My Solution was to create an queuing system for Alerts when the AppState is not in the background they are then shown.

Can you elaborate on that? How do you detect the situation that triggers the corner render?

This issue persists when an Alert is called when the app is in the background state. I've created this hook for Alerts to get added to a queue if the app is not 'active'.

Once active it will show the Alerts from the queue. It may have a few quirks but resolves the issue till a permanent fix is implemented in RN

import { useCallback, useEffect, useRef } from "react";
import { useTranslation } from "react-i18next";
import { Alert, AlertButton, AppState, AppStateStatus } from "react-native";

const alertQueue: UseShowAlertProps[] = [];
let currentAppState: AppStateStatus = "active";

AppState.addEventListener("change", (nextState) => {
  currentAppState = nextState;
  if (nextState === "active") {
    showNextAlert();
  }
});

function showNextAlert() {
  if (alertQueue.length === 0) return;

  const nextAlert = alertQueue.shift();
  if (nextAlert) {
    showNativeAlert(nextAlert);
  }
}

function showNativeAlert({ title = "", message, buttons }: UseShowAlertProps) {
  Alert.alert(title, message, buttons);
}

/**
 * @description This hook should be used in the instance that an Alert may need to be shown whilst the app is in the background.
 */
export function useAlert() {
  const { t } = useTranslation();
  const isMountedRef = useRef(true);

  useEffect(() => {
    return () => {
      isMountedRef.current = false;
    };
  }, []);

  const showAlert = useCallback(
    function ({ title, message, buttons, ...rest }: UseShowAlertProps) {
      // Make sure all button actions dismiss the modal or use default OK button
      const resolvedButtons: Button[] = buttons
        ? buttons.map((button) => ({
            ...button,
            onPress: () => {
              button.onPress?.();
              showNextAlert(); // Go to next alert
            },
          }))
        : [
            {
              text: t("OK_TEXT"),
              onPress: showNextAlert,
            },
          ];

      const alertProps: UseShowAlertProps = {
        title: title || "",
        message,
        buttons: resolvedButtons,
        ...rest,
      };

      if (currentAppState === "active") {
        showNativeAlert(alertProps);
      } else {
        alertQueue.push(alertProps);
      }
    },
    [t],
  );

  return { showAlert };
}`

KyeAtkinson avatar May 01 '25 12:05 KyeAtkinson

Bumping once again but my temporary solution to this as the issue is replicable by creating an Alert whilst the app is in the background state (Not Closed). My Solution was to create an queuing system for Alerts when the AppState is not in the background they are then shown.

Can you elaborate on that? How do you detect the situation that triggers the corner render?

This issue persists when an Alert is called when the app is in the background state. I've created this hook for Alerts to get added to a queue if the app is not 'active'.

Once active it will show the Alerts from the queue. It may have a few quirks but resolves the issue till a permanent fix is implemented in RN

...

In my case the issue is very flaky. It starts when I preview a camera image and ask for confirmation, but refreshing the app made the error go away, and I couldn't reproduce it again. At no time the app was put into background.

bernborgess avatar May 01 '25 12:05 bernborgess

This issue is still happening on RN 0.81

RodSarhan avatar Oct 01 '25 12:10 RodSarhan

This issue is still happening on RN 0.79

AhmadZFinstreet avatar Oct 08 '25 07:10 AhmadZFinstreet