Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display any view on top of native-stack's modal #525

Closed
Strate opened this issue May 26, 2020 · 52 comments · Fixed by #1096
Closed

Display any view on top of native-stack's modal #525

Strate opened this issue May 26, 2020 · 52 comments · Fixed by #1096
Labels
Area: Modal Issue related with iOS modal stack presentation

Comments

@Strate
Copy link

Strate commented May 26, 2020

Hello, thank you for this awesome library.

Is there any possible solution to display any view on top of visible stack's modal?

I realized, that react-native-modal is not visible over stack's modal, and also there is no way to display any view on top of screen. For example, I need to display in-app-notification on top of all content, or sometime display modal loader spinner - both of them are not appears when stack's modal is visible.

This is related to ios only, android works fine.

@friedolinfoerder
Copy link

friedolinfoerder commented May 26, 2020

I've got the same issue. iOS-Modal issue only.

@arberkryeziu
Copy link

same issue

@hirbod
Copy link
Contributor

hirbod commented May 27, 2020

This issue is known and also reported by me. You can't even toggle the element inspector on the modal. They managed to get EXPOs Dev Menu finally to overlay the modal, but not sure about any other view. The modal is currently the most top element that nothing can overlap. I also had issues with in-app-notifications and worked around displaying them inside the modal when modal was open - but yeah, there should be another method for it. Maybe we need to wrap a provider for that native-modal and let it mount there - dunno if thats even possible, just thinking.

P:S the modal from import { Modal } from "react-native" actually can overlap it. My app uses this too

@Strate
Copy link
Author

Strate commented May 27, 2020

@hirbod how Modal from react-native can overlap? I've tested it for a while, but modal just invisible and dismisses almost right after mounted.

@hirbod
Copy link
Contributor

hirbod commented May 27, 2020

@Strate https://streamable.com/mtsbmh here, from my app

@Strate
Copy link
Author

Strate commented May 27, 2020

@hirbod wow, awesome! This is looks like Shared Element Transition, but with Modal?

@hirbod
Copy link
Contributor

hirbod commented May 27, 2020

Yes, tooked me a crazy amount of time and is not smooth on android :D
But it works with react-native modal in a react-native-screens modal :D
Hopefully we will have natie-shared-element support for v5 stack at some point :)

@Strate
Copy link
Author

Strate commented May 27, 2020

@hirbod could you please share some code for Modal from react-native? Which presentation used for display over screen's modal? Because my modal still invisible :(

@hirbod
Copy link
Contributor

hirbod commented Jun 2, 2020

@Strate its nothing special.

import { Modal } from "react-native";
....
...
... 
renderFullscreen = () => {
return (
      <Modal
        transparent
        visible={fullscreen}
        onShow={this.handleModalShow}
        onRequestClose={this.close}
      />
)};

I think the important thing is that Modal isn't rendered on mount. My modal gets rendered when my Image receives an onPress. I have an if-statement around my Modal in my code.

And my renderFullScreen returns the Modal and unmounts again in my close function. My onPress sets the state fullscreen to true and renders the modal

    return (
      <View>
        <TouchableWithoutFeedback onPress={....}>
          <View>
            <ImageCustom />
          </View>
        </TouchableWithoutFeedback>
        {fullscreen && this.renderFullscreen()}
      </View>
    );

@a-eid
Copy link

a-eid commented Aug 3, 2020

I've the same issue, I'm trying to display a loader but can't draw over the 'formsheet' modal.

<>
  <NavigationContainer>
    <Stack.Navigator screenOptions={{ stackPresentation: 'formSheet' }} .... >
  </NavigationContainer> 
  <View style={{
   ...Stylesheet.absoluteFillObject,
   zIndex: 1000, 
   backgroundColor: 'rgba(0,0,0,0.4)'
  }}/> /* <<< */
</>

@WoLewicki WoLewicki added the Area: Modal Issue related with iOS modal stack presentation label Aug 13, 2020
@WoLewicki
Copy link
Member

Can you provide a repo with the thing you want to achieve so I could work on it?

@a-eid
Copy link

a-eid commented Aug 26, 2020

Hi @WoLewicki
this repo explains the issue ( at least the one I'm having.)

https://github.com/a-eid/react-native-native-stack-global-loader

you can see that when we have a formSheet screen the loader is behind the screen unlike the normal screen. please let me know if you need more context.

I wan't to do another example for toast but it basically is the same idea.

thank you.

@a-eid
Copy link

a-eid commented Aug 26, 2020

it's also worth noting that this happens also with other modals.

@Abhishek12345679
Copy link

also a modal screen (using stackPresentation:modal) does not pop up over a react-native-modal's modal component.

@WoLewicki
Copy link
Member

@a-eid regarding your example: the loader will not load above the modal this way since all the views displayed under the root view of the application will be displayed also under the modal screens. You can spot the same thing while using Modal component from react-native. It is like so because of the native view hierarchy of iOS. Roots of the modals are above the react's root view, so the only way to display something above the modal is to either display it in that modal, or display another modal on the previous modal. In particular, for your example, the flow can be changed somehow like that:

<Stack.Screen
            name="modal-screen"
            options={{
              stackPresentation: 'formSheet',
            }}>
            {() => (
              <GlobalLoader>
                <ModalScreen />
              </GlobalLoader>
            )}
</Stack.Screen>

Then the GlobalLoader component is under the modal screen in the view hierarchy and is displayed on the modal component.

Another option, maybe even better, is to make the GlobalLoader a Screen of the stack and call navigation to it when triggered and goBack when removing it. In order for it to be displayed on the whole screen, you can use stackPresentation: 'transparentModal' which will give you an option to make the component semi-transparent. Does it solve your issue?

@Abhishek12345679 yes, because it messes with the modal stack of native-stack and is not permitted in react-native-screens since they both use view controllers.

@a-eid
Copy link

a-eid commented Aug 27, 2020

@WoLewicki thank you for the reply.

I'm wondering how would you display toasts on a 'formSheet' screens ?
also can you trigger a modal with Animated.Value ?

@WoLewicki
Copy link
Member

About the first question, what is the difference of having formSheet screens for displaying toasts? I think the approach should be the same, the Toast should be a child of the Screen component in order to be visible over the modal screen. You probably cannot use transparentModal then because it disables touches on the screens under it.

About the second question, I am not sure what you mean. If you mean to control the transition of the modal appearing then no, since it is controlled by a native UINavigationController.

@a-eid
Copy link

a-eid commented Aug 27, 2020

global toasts are great when you have a background task ( socket connection or even an api request ). and you wanna notify the user when the task is finish ( success | error ). there is no way to find out where the user is on the app.

global toasts & loaders have a single mount point which is really great for those cases and convenient for almost all other cases.

I'm not really that familiar with IOS but do u think there is a way to do that on ios Natively ?

@WoLewicki
Copy link
Member

You can read more about how certain interactions with user on iOS should be implemented in Apple's HIG. You can look at e.g. Alert Views and then look for libraries in react-native that implement those things. Also, I think it is just not possible to have the listener for such events in one place only when trying to use native iOS modal views since they simply aren't children of RCTRootView. Can I help you more with it?

@a-eid
Copy link

a-eid commented Aug 31, 2020

@WoLewicki thank you very much, I'll be reading more about it.

@WoLewicki
Copy link
Member

Can I close this issue then? @Strate do you any more information?

@Strate
Copy link
Author

Strate commented Sep 2, 2020

@WoLewicki looks like there is still no solution to display any view on top of modal ^(

@WoLewicki
Copy link
Member

@Strate you can do it as long as you are displaying it in the Screen, in which the modal is located or use another modal screen to display it.

@Strate
Copy link
Author

Strate commented Sep 3, 2020

@WoLewicki thats okay for alerts, for example, but not great for toasts :(

This is how toasts looks like when rendered inside Screen which displayed in modal:
image

@sslash
Copy link

sslash commented Dec 9, 2020

I'm having the same issue. I can append my view to UIWindow on the native side, and it will appear on top. But the problem is that I have a ViewController with IBActions etc, and afaik you can't append a ViewController to UIWindow (?). Or do you know if there's another way to natively append a ViewController to the UINavigationController (ie the modal) ?

@Strate
Copy link
Author

Strate commented Dec 10, 2020

@WoLewicki thank you for suggestion!
Do you have any idea how to implement this thing: "You can use your own component that will add a subview to the UIWindow"? Sounds very easy, but very hard for non-iOS developer :(

@WoLewicki
Copy link
Member

Here is the code which adds the monitor to the UIWindow: https://github.com/facebook/react-native/blob/dc80b2dcb52fadec6a573a9dd1824393f8c29fdc/React/CoreModules/RCTPerfMonitor.mm#L336. I think you can make your own native module, which will make a native view and add it straight under the UIWindow. I may add such thing in a free moment and I will post it here then.

@a-eid
Copy link

a-eid commented Apr 26, 2021

@WoLewicki that would be awesome, thank you.

@WoLewicki
Copy link
Member

I added a PoC package where you can render a View under the UIWindow. You can try and play with it: https://github.com/WoLewicki/react-native-window-view.

@a-eid
Copy link

a-eid commented Apr 27, 2021

@WoLewicki I'm testing it rn, do you think you'll keep it as a separate package or will you be adding it to react-native-screens ?

edit:

it's only draggable, it doesn't allow touches, do u think I can try and fix it ?

been trying to fix it but Objc syntax is so weird.

observations:

The modal does display on top of RNWindowView.

if the RNWindowView is displayed last it always comes on top thou

@a-eid
Copy link

a-eid commented Apr 27, 2021

overlay

@WoLewicki
Copy link
Member

do you think you'll keep it as a separate package or will you be adding it to react-native-screens ?

I think it will now be a separate package since react-native-screens is library connected to navigation and this component has nothing to do with it. It may change in the future though.

it's only draggable, it doesn't allow touches, do u think I can try and fix it ?

I fixed it in 0.3.0. I also added option to make it non-draggable with draggable prop.

The modal does display on top of RNWindowView.
if the RNWindowView is displayed last it always comes on top thou

Yes, because the modal and RNNWindowView are both attached to the UIWindow, so their visibility is determined by their attaching order, just like you saw here: #525 (comment). For now what you can do is detect when the modal is being pushed and do a hide-show sequence when it comes up, something similar to what you posted above. I also added another option to make the view always visible by subscribing to subview updates of the UIWindow in your AppDelegate.m (see changes here: WoLewicki/react-native-window-view@1cf924e#diff-572a1fa43eaec8dc052d345a050635916f8bdeb379f1ab00367368dffa947e45R14)

@a-eid
Copy link

a-eid commented Apr 28, 2021

@WoLewicki now Touchables work inside of the windowView now, great work thank you.

@nandorojo
Copy link

I'm a bit new to this thread, and I'm not exactly sure how the iOS Window works.

@WoLewicki does the package you linked to solve the problem of rendering toasts on top of any modal? I have some UI providers (bottom sheet menu, toast notifications) at the root of my app, and I'd like them to render on top of any content.

@a-eid
Copy link

a-eid commented May 23, 2021

@nandorojo the package work similar to Perf Monitor window, which does display in front on any modal as long as it was displayed last. it goes behind the modal if the modal is displayed after it.

@nandorojo
Copy link

I see, you mean last in the component tree, right? As in, if I render this after my native stack, it should be fine.

Can treat this component as a sort of a Portal for components at the root of the app that need to render on top of everything?

@a-eid
Copy link

a-eid commented May 23, 2021

I see, you mean last in the component tree, right? As in, if I render this after my native stack, it should be fine.

yes and no.

you see if you render this component then trigger a modal, the modal will display on top of the component.

@nandorojo
Copy link

Oh, so it's based on the time of rendering? As if it were attached to the window imperatively?

@WoLewicki
Copy link
Member

I added an option for the toast to stay on top even after pushing modals after it was rendered (see WoLewicki/react-native-window-view#2 (comment)). This library is kind of a PoC still, so if you see any bugs/inconsistencies, please report them there.

@a-eid
Copy link

a-eid commented May 24, 2021

@WoLewicki thank you so much, I will be testing this lib extensively in a couple of apps that we have, will report and if possible contribute.

@nandorojo
Copy link

I added an option for the toast to stay on top even after pushing modals after it was rendered (see WoLewicki/react-native-window-view#2 (comment)). This library is kind of a PoC still, so if you see any bugs/inconsistencies, please report them there.

@WoLewicki thanks! Is this a prop on the JS side? I don't know my way around native code so I'm not sure exactly what I should be doing.

Also, are pointed events configurable? I'd like to set it to box-none, not sure what the default behavior is. Thanks again.

@WoLewicki
Copy link
Member

It requires adding code in your app's AppDelegate.m as shown in the code of example app. As for pointer events, I have not changed anything about it, so I assume it should work in a default RN way. If not, please submit issues in the repo.

@a-eid
Copy link

a-eid commented Jun 17, 2021

@WoLewicki pointerEvents & style do no work consistently between android & ios.

positional style do work but not all styles, for example backgroundColor does not work.

also pointerEvents doesn't seem to be working.

@a-eid
Copy link

a-eid commented Jun 24, 2021

@WoLewicki

Screen Shot 2021-06-24 at 14 40 13

does having RNViewContainer & RNWindowView means that we have 2 views ?

is that necessary and could that be the issue why pointerEvents is not working properly ?

also is there a reason we are using RCTView & UIView and not one of a kind ?

I tried adding hitTest:withEvent: but it didn't work.

Screen Shot 2021-06-24 at 14 44 49

@WoLewicki
Copy link
Member

@a-eid I think I answered the questions in your issue in the repo, and I think we should keep the discussion there since it is connected to it.

@nandorojo
Copy link

@WoLewicki is it possible that your PoC will make it into the screens package? I find it really necessary to render content arbitrarily on top of a modal from the root of the app.

This includes toasts, menus, portals, alert dialogues, etc.

I'm working on a design system like Radix / HeadlessUI for React Native, and it requires using a portal at the root of the app for those use cases, among others.

It would be great for the API to be like so:

<WindowView>
  {children}
</WindowView>

Where:

  1. pointerEvents defaults to box-none so it doesn't interrupt touches
  2. shown is always true, so there's no need for a prop
  3. draggable is default false or removed altogether
  4. most importantly, the content always stays at on top of the app, regardless of the number of modals or when they were added.

Does this seem realistic? I think it would add a lot to screens. I wish I could help implement it, I just don't know any native code.

Thanks again for all the great work here!

@WoLewicki
Copy link
Member

I think WindowView doesn't belong to the react-native-screens package since it has nothing to do with the platform navigation primitives, and it is the main goal of this package. As for those changes, I think it 4. is already implemented, and the discussion about others should probably be moved to some issue in that repo. I am open to suggestions if you think I am wrong.

@WoLewicki WoLewicki linked a pull request Sep 1, 2021 that will close this issue
9 tasks
@WoLewicki
Copy link
Member

After some discussion, we decided to move WindowView to react-native-screens package. You can check #1096 and test if it is what you need.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Modal Issue related with iOS modal stack presentation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants