Appear event is used by react-navigation to properly dispatch focus. It is important that appear is dispatched after dismissed event. The reverse order of actions would result in getting react-navigation stack in a weird state.
It is relatively streightforward to implement onAppear event on iOS where we hook into didAppear callback from UIViewController. It gets dispatched in the right moment, that is when the transition is fully over.
On Android however it is much more tricky. There is no standard way to be notified from the fragment level that fragment transition finished. One way that is frequently recommended is to override Fragment.onCreateAnimation. However, this only works when custom transitions are provided (e.g. if we set the transition to use fade animation). As we want the platform native transition to be run by default we had to look for other ways. The current approach relies on fragment container's callbacks startViewTransition and endViewTransition, with the latter being triggered once the animation is over. We also need to take into account that a good starting point for the transition is when we call commit on fragment transaction. We use these two methods to determine if the fragment is instantiated (onCreate) within a running transaction and if so we schedule event dispatch at the moment when endViewTransition is called.
Another change this commit introduces on the Android side is that we no longer rely on show/hide for replacing fragments on stack and we now use add/remove transaction methods. Due to this change we had to make our fragments reusable and make onCreateView indempotent.
The previous algorithm was buggy and did not handle the case when multiple VCs are being dismissed. Unfortunately I couldn't find a reliable way that'd allow for reshuffling modally presented VCs (inserting or deleting VCs not from the top) and so I added a special check that'd throw in the case someone attemted to do this.
This fixes the problem when navbar settings update from ones that have header items (e.g. custom title object) to a config without such items. In such a case we'd set navbar items in the first go and never reset those items.
This change fixes the problem when there are changes being made to the stack while the hosting activity is in paused state (e.g. an activity-based dialog from google play services). In such a case it is not allowed to do any changes which can affect fragment manager's state (e.g. updating backstack). For other changes we use `commitAllowingStatLoss` to indicate we are not interested in fragment manager handling their restoration and hence we can perform most operations. Unfortunately installing back handler is not allowed without state change and we need to wait until the fragment host is resumed before we install it.
This change fixes the issue of handling transparent status bar. In such a case the navbar should became slightly bigger in order to fill the status bar space that now belongs to the window. In order for that to happen we use `setFitsSystemWindows` on main screen layout, on app bar layout and on toolbar (the toolbar is the view that gets bigger in the end).
Note that this currently only work if we use Android's native mechanism for changingthe status bar behavior and not using `StatusBar` module from RN. It is because `StausBar` module actually strips top insets from the event that gets delivered to the views. For the time being you can try the following to change status bar:
```
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
```
Note that we are also disabling app bar's scroll behavior. We wasn't utilising this yet, and it cause some weird errors on hot reload because scrolling behavior actually sets `firsSystemWindows` flag on the screen view which ended up consuming insets before appbar would get them.
This change fixes two issues related to stack nesting on Android.
First one being the fact that root and nested fragments were relying on the same fragment manager as opposed to using nested fragment manager structure (via getChildFragmentManager). This resulted in an unprodictable behavior when displaying the top screen. This commit changes this behavior by correctly returning child fragment manager or root fragment manager depending on the containers hierarchy.
Second issue was related to back stack handling and resulted in always the root fragment manager interacting. Instead we want the bottommost active stack to handle back presses. This has been addressed by adding `setPrimaryNavigationFragment` when creating back stack entries.
When some of config props change or new header items are added we need to perform a header update. This diff adds logic to trigger updating header props when that happens.
There was an issue with back stack listener being triggered after reload caused by the fact we weren't cleaning up stack manager's back stack on reload. As a result after reload listener would get triggered with and empty stack first and only then with a back stack with 1 item. Consequence of the first trigger was that we'd call dismiss and move back from the top screen which wasn't a desirable behavior.
After #184 we no longer were acconting for the size of navigation bar when laying out screens on the stack. This was causing elements to be drawn under a non-translucent bar unless SafeAreaView's been used. As this seem not to be desirable in most of the cases (there is no way of seeing items rendered underneath non-translucent header) this change brings back the previous behavior. Instead of using manual method of calculating top inset as it's been done before we rely on edgesForExtendedLayout property of the view controller.
When more than one screen is initially mounted we were only installing the top navigator with UINavController. This was cauing the back-handling logic to not work properly.
This is a similar fix to the one already merged in #215 but for Android this time. We change default screen container to layout its direct children without using flexbox. This is to follow the same pattern as with native stack container.
Before this change we'd rely on new androidx OnBackPressedCallback mechanism. It turns out not to work well in a few cases. The biggest problem with it was that when registered it'd never allow for the hw back button press to fallback to a system default behaviour and instead would always "still" that event. After several attempts of trying to make it work I decided to revert back to a FragmentManager's default solution.
This change adds an ability to use FragmentManager's back stack API. There are also a few problems with it we need to workaround though. One of the problem is the fact that we can not modify back stack history. What we do because of that is that we try to keep at most one item on the back stack and reset it each time our native stack updates. In order for that to work we create a fake transaction that hides and shows the same screen that displays on top. Thanks to that when hw back is pressed the built in transaction rollback logic does nothing to the UI and allows us to handle back navigation using back stack change listener.
For native stack we introduced a new way of layouting screen controllers. We no longer rely on flexbox to layout them so that we can use native stack to provide a correct dimensions and safe area paddings. Unfortunately this change broke the old screen container. With this commit we are adapting layout change to old screen container that will now layout its direct children on the native side by making them take up the whole space of the container.
iOS navbar largeTitle setting allows the navbar to be automatically collapsed when scrolling. For that to work iOS expects scrollable component to be rendered in the screen view hierarchy as a first children (or just to be looked up on the ancestor path using first children when navigating down). On top of that in RN we should use contentInsetAdjustmentBehavior="automatic" for SV's contentInsets to adjust as expected when the header scrolls and to allow for the SV background to be present below the navigation bar.
This works pretty well w/o any changes. However when testing I disovered one issue with the navigation bar rendering in a collapsed state on the first render. After that you could've scroll down to reveal large title bar but the initial state would be collapsed. As on iOS it is expected for large title enabled screens to initially render in the revealed state a workaround was required. It turned out that the header rendered in collapsed state because at the moment when navigation controller is created is has an empty list of view controllers. The list is empty because react first creates nnavigation controller and only then adds children to it. It turned out the fix was to populate viewControllers with a single item at first, then when controllers list is replaced with the children provided from react the header renders properly in the revealed state.
This change makes it possible for header customization options to use appearence API introduces in iOS 13. Thanks to this the default header setting will respect header translucency. Thanks to the appearence API we no longer need to set so many properties in setAnimatedConfig.
- `@available` should not be used with other conditions (not sure if it breaks anything but it does trigger warnings in xcode)
- missing runtime check before using `modalInPresentation`, kept the precompiler instruction so it still builds on older xcodes.
A few bugs fixed with android native stack in this commit:
- fixed a problem with views not resizing when keyboard appears, the bug was due to onMeasure forwardin getHeight which was the old height of the container instead of the changed one
- fixed a problem with back button behavior not being consistent. Now back button is controlled by 'gestureEnabled' to emulate iOS behavior where there is no hw back button but you can swipe back instead.
- added compatibility for "contained" modal styles - for now we always render "containted" fragments on Android, plan to add a way to render in dialogs in the future
There was a bug in PR that introduces enableScreens method to replace useScreens. The bug was that the method did not end up being exported (we use module.exports and not export syntax). On top of that I'm adding a deprecation warning to useScreens method as it interferes with some react hooks tooling.
Supporting fade in for modals can be done by using `modalTransitionStyle = UIModalTransitionStyleCrossDissolve`. For other type of animations we leave this as the default value.
Tested via `@react-navigation/native-stack` with `{ animation: 'fade', presentation: 'transparentModal' }`
* Fix non-default modal presentation styles
Using alternative modal presentation styles like `transparentModal` is not working. This is because accessing `presentationController` before setting the `modalPresentationStyle` causes the presentation controller to be created and cannot be changed afterwards. This is documented https://developer.apple.com/documentation/uikit/uiviewcontroller/1621426-presentationcontroller?language=objc.
Yes very cool API
Tested via @react-navigation/native-stack using presentation: 'transparentModal'.
* Update RNSScreen.m