react-native
react-native copied to clipboard
RN Alert button sometimes renders in the upper-left corner of the screen (iOS)
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
Still happening in 0.75.2 Any updates on this?
What is the last version that hadn't this problem? I'm considering downgrading for a temporary fix.
This is the current comparison of Alert.alert("Test"); on android vs ios:
| Android | iOS |
|---|---|
Not how the "OK" is showing in the upper left corner.
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
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.
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?
One way to replicate this is to show multiple alerts and move the app foreground ->background->foreground numerous times.
I can reproduce it when firing alert when app in the background(not killed)
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
The issue is still present for Modal and Alert components in Android as well on version 0.76.1 with newArch enabled and disabled.
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.
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.
I'm experiencing this issue as well on version 0.74.3. Any updates?
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
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.
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.
Any Updates?
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.
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?
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 };
}`
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.
This issue is still happening on RN 0.81
This issue is still happening on RN 0.79