react-native-screens 3.10.2 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +9 -7
  3. package/android/build.gradle +1 -0
  4. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +28 -11
  5. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +1 -1
  6. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +64 -33
  7. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +9 -31
  8. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +0 -30
  9. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +12 -5
  10. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +10 -0
  11. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +72 -11
  12. package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +18 -1
  13. package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +7 -2
  14. package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +29 -2
  15. package/android/src/main/res/anim/rns_default_enter_in.xml +18 -0
  16. package/android/src/main/res/anim/rns_default_enter_out.xml +19 -0
  17. package/android/src/main/res/anim/rns_default_exit_in.xml +17 -0
  18. package/android/src/main/res/anim/rns_default_exit_out.xml +18 -0
  19. package/android/src/main/res/anim/rns_fade_in.xml +7 -0
  20. package/android/src/main/res/anim/rns_fade_out.xml +7 -0
  21. package/android/src/main/res/anim/rns_no_animation_20.xml +6 -0
  22. package/createNativeStackNavigator/README.md +12 -0
  23. package/ios/RNSScreen.h +10 -0
  24. package/ios/RNSScreen.m +34 -0
  25. package/ios/RNSScreenContainer.m +5 -0
  26. package/ios/RNSScreenStack.m +22 -7
  27. package/ios/RNSScreenStackAnimator.m +45 -14
  28. package/ios/RNSScreenStackHeaderConfig.m +4 -1
  29. package/ios/RNSScreenWindowTraits.h +1 -0
  30. package/ios/RNSScreenWindowTraits.m +20 -0
  31. package/ios/UIViewController+RNScreens.m +10 -0
  32. package/lib/commonjs/native-stack/views/NativeStackView.js +33 -4
  33. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  34. package/lib/module/native-stack/views/NativeStackView.js +33 -4
  35. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  36. package/lib/typescript/native-stack/types.d.ts +34 -0
  37. package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +1 -1
  38. package/lib/typescript/reanimated/ReanimatedScreen.d.ts +1 -1
  39. package/lib/typescript/types.d.ts +60 -5
  40. package/native-stack/README.md +39 -3
  41. package/package.json +1 -1
  42. package/reanimated/package.json +6 -0
  43. package/src/native-stack/types.tsx +34 -0
  44. package/src/native-stack/views/NativeStackView.tsx +33 -4
  45. package/src/types.tsx +60 -5
@@ -65,7 +65,9 @@ class SearchBarManager : ViewGroupManager<SearchBarView>() {
65
65
 
66
66
  @ReactProp(name = "placeholder")
67
67
  fun setPlaceholder(view: SearchBarView, placeholder: String?) {
68
- view.placeholder = placeholder
68
+ if (placeholder != null) {
69
+ view.placeholder = placeholder
70
+ }
69
71
  }
70
72
 
71
73
  @ReactProp(name = "textColor", customType = "Color")
@@ -73,6 +75,21 @@ class SearchBarManager : ViewGroupManager<SearchBarView>() {
73
75
  view.textColor = color
74
76
  }
75
77
 
78
+ @ReactProp(name = "headerIconColor", customType = "Color")
79
+ fun setHeaderIconColor(view: SearchBarView, color: Int?) {
80
+ view.headerIconColor = color
81
+ }
82
+
83
+ @ReactProp(name = "hintTextColor", customType = "Color")
84
+ fun setHintTextColor(view: SearchBarView, color: Int?) {
85
+ view.hintTextColor = color
86
+ }
87
+
88
+ @ReactProp(name = "shouldShowHintSearchIcon")
89
+ fun setShouldShowHintSearchIcon(view: SearchBarView, shouldShowHintSearchIcon: Boolean?) {
90
+ view.shouldShowHintSearchIcon = shouldShowHintSearchIcon ?: true
91
+ }
92
+
76
93
  override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? {
77
94
  return MapBuilder.builder<String, Any>()
78
95
  .put("onChangeText", MapBuilder.of("registrationName", "onChangeText"))
@@ -15,9 +15,12 @@ class SearchBarView(reactContext: ReactContext?) : ReactViewGroup(reactContext)
15
15
  var autoCapitalize: SearchBarAutoCapitalize = SearchBarAutoCapitalize.NONE
16
16
  var textColor: Int? = null
17
17
  var tintColor: Int? = null
18
- var placeholder: String? = null
18
+ var headerIconColor: Int? = null
19
+ var hintTextColor: Int? = null
20
+ var placeholder: String = ""
19
21
  var shouldOverrideBackButton: Boolean = true
20
22
  var autoFocus: Boolean = false
23
+ var shouldShowHintSearchIcon: Boolean = true
21
24
 
22
25
  private var mSearchViewFormatter: SearchViewFormatter? = null
23
26
 
@@ -45,9 +48,11 @@ class SearchBarView(reactContext: ReactContext?) : ReactViewGroup(reactContext)
45
48
  }
46
49
 
47
50
  searchView.inputType = inputType.toAndroidInputType(autoCapitalize)
48
- searchView.queryHint = placeholder
49
51
  mSearchViewFormatter?.setTextColor(textColor)
50
52
  mSearchViewFormatter?.setTintColor(tintColor)
53
+ mSearchViewFormatter?.setHeaderIconColor(headerIconColor)
54
+ mSearchViewFormatter?.setHintTextColor(hintTextColor)
55
+ mSearchViewFormatter?.setPlaceholder(placeholder, shouldShowHintSearchIcon)
51
56
  searchView.overrideBackAction = shouldOverrideBackButton
52
57
  }
53
58
  }
@@ -3,6 +3,8 @@ package com.swmansion.rnscreens
3
3
  import android.graphics.drawable.Drawable
4
4
  import android.view.View
5
5
  import android.widget.EditText
6
+ import android.widget.ImageView
7
+ import androidx.appcompat.R
6
8
  import androidx.appcompat.widget.SearchView
7
9
 
8
10
  class SearchViewFormatter(var searchView: SearchView) {
@@ -10,9 +12,13 @@ class SearchViewFormatter(var searchView: SearchView) {
10
12
  private var mDefaultTintBackground: Drawable? = null
11
13
 
12
14
  private val searchEditText
13
- get() = searchView.findViewById<View>(androidx.appcompat.R.id.search_src_text) as? EditText
15
+ get() = searchView.findViewById<View>(R.id.search_src_text) as? EditText
14
16
  private val searchTextPlate
15
- get() = searchView.findViewById<View>(androidx.appcompat.R.id.search_plate)
17
+ get() = searchView.findViewById<View>(R.id.search_plate)
18
+ private val searchIcon
19
+ get() = searchView.findViewById<ImageView>(R.id.search_button)
20
+ private val searchCloseIcon
21
+ get() = searchView.findViewById<ImageView>(R.id.search_close_btn)
16
22
 
17
23
  fun setTextColor(textColor: Int?) {
18
24
  val currentDefaultTextColor = mDefaultTextColor
@@ -37,4 +43,25 @@ class SearchViewFormatter(var searchView: SearchView) {
37
43
  searchTextPlate.background = currentDefaultTintColor
38
44
  }
39
45
  }
46
+
47
+ fun setHeaderIconColor(headerIconColor: Int?) {
48
+ headerIconColor?.let {
49
+ searchIcon.setColorFilter(it)
50
+ searchCloseIcon.setColorFilter(it)
51
+ }
52
+ }
53
+
54
+ fun setHintTextColor(hintTextColor: Int?) {
55
+ hintTextColor?.let {
56
+ searchEditText?.setHintTextColor(it)
57
+ }
58
+ }
59
+
60
+ fun setPlaceholder(placeholder: String, shouldShowHintSearchIcon: Boolean) {
61
+ if (shouldShowHintSearchIcon) {
62
+ searchView.queryHint = placeholder
63
+ } else {
64
+ searchEditText?.hint = placeholder
65
+ }
66
+ }
40
67
  }
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <set xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <alpha
4
+ android:interpolator="@android:interpolator/accelerate_decelerate"
5
+ android:fromAlpha="0"
6
+ android:toAlpha="1.0"
7
+ android:startOffset="100"
8
+ android:duration="100"/>
9
+ <scale
10
+ android:interpolator="@android:interpolator/accelerate_decelerate"
11
+ android:fromXScale="0.85"
12
+ android:toXScale="1"
13
+ android:fromYScale="0.85"
14
+ android:toYScale="1"
15
+ android:pivotX="50%"
16
+ android:pivotY="50%"
17
+ android:duration="200"/>
18
+ </set>
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <set xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <alpha
4
+ android:fromAlpha="1"
5
+ android:toAlpha="0.4"
6
+ android:startOffset="100"
7
+ android:duration="100"
8
+ android:interpolator="@android:interpolator/accelerate_decelerate" />
9
+
10
+ <scale
11
+ android:interpolator="@android:interpolator/accelerate_decelerate"
12
+ android:fromXScale="1"
13
+ android:toXScale="1.15"
14
+ android:fromYScale="1"
15
+ android:toYScale="1.15"
16
+ android:pivotX="50%"
17
+ android:pivotY="50%"
18
+ android:duration="200"/>
19
+ </set>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <set xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:shareInterpolator="false">
4
+ <alpha
5
+ android:fromAlpha="0.0"
6
+ android:toAlpha="1"
7
+ android:startOffset="50"
8
+ android:duration="100"/>
9
+ <scale
10
+ android:fromXScale="1.15"
11
+ android:toXScale="1"
12
+ android:fromYScale="1.15"
13
+ android:toYScale="1"
14
+ android:pivotX="50%"
15
+ android:pivotY="50%"
16
+ android:duration="200"/>
17
+ </set>
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <set xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:shareInterpolator="false"
4
+ android:zAdjustment="top">
5
+ <alpha
6
+ android:fromAlpha="1"
7
+ android:toAlpha="0.0"
8
+ android:startOffset="50"
9
+ android:duration="100"/>
10
+ <scale
11
+ android:fromXScale="1"
12
+ android:toXScale="0.85"
13
+ android:fromYScale="1"
14
+ android:toYScale="0.85"
15
+ android:pivotX="50%"
16
+ android:pivotY="50%"
17
+ android:duration="200"/>
18
+ </set>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--Duration taken from debugging source code-->
3
+ <alpha xmlns:android="http://schemas.android.com/apk/res/android"
4
+ android:fromAlpha="0.0"
5
+ android:toAlpha="1.0"
6
+ android:duration="150"
7
+ /> <!--Remember to change duration in the corresponding xml when modifying it-->
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--Duration taken from debugging source code-->
3
+ <alpha xmlns:android="http://schemas.android.com/apk/res/android"
4
+ android:fromAlpha="1.0"
5
+ android:toAlpha="0.0"
6
+ android:duration="150"
7
+ /> <!--Remember to change duration in the corresponding xml when modifying it-->
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <alpha xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:fromAlpha="1.0"
4
+ android:toAlpha="1.0"
5
+ android:duration="20"
6
+ /> <!-- non-zero duration ensures transition events are triggered correctly -->
@@ -464,6 +464,18 @@ Defaults to an empty string.
464
464
 
465
465
  The search field text color.
466
466
 
467
+ #### `hintTextColor`
468
+
469
+ The search hint text color. (Android only)
470
+
471
+ #### `headerIconColor`
472
+
473
+ The search and close icon color shown in the header. (Android only)
474
+
475
+ #### `shouldShowHintSearchIcon`
476
+
477
+ Show the search hint icon when search bar is focused. (Android only)
478
+
467
479
  ### Helpers
468
480
 
469
481
  The stack navigator adds the following methods to the navigation prop:
package/ios/RNSScreen.h CHANGED
@@ -29,6 +29,11 @@ typedef NS_ENUM(NSInteger, RNSScreenReplaceAnimation) {
29
29
  RNSScreenReplaceAnimationPush,
30
30
  };
31
31
 
32
+ typedef NS_ENUM(NSInteger, RNSScreenSwipeDirection) {
33
+ RNSScreenSwipeDirectionVertical,
34
+ RNSScreenSwipeDirectionHorizontal,
35
+ };
36
+
32
37
  typedef NS_ENUM(NSInteger, RNSActivityState) {
33
38
  RNSActivityStateInactive = 0,
34
39
  RNSActivityStateTransitioningOrBelowTop = 1,
@@ -47,6 +52,7 @@ typedef NS_ENUM(NSInteger, RNSWindowTrait) {
47
52
  RNSWindowTraitAnimation,
48
53
  RNSWindowTraitHidden,
49
54
  RNSWindowTraitOrientation,
55
+ RNSWindowTraitHomeIndicatorHidden,
50
56
  };
51
57
 
52
58
  @interface RCTConvert (RNSScreen)
@@ -91,19 +97,23 @@ typedef NS_ENUM(NSInteger, RNSWindowTrait) {
91
97
  @property (nonatomic) RNSScreenStackAnimation stackAnimation;
92
98
  @property (nonatomic) RNSScreenStackPresentation stackPresentation;
93
99
  @property (nonatomic) RNSScreenReplaceAnimation replaceAnimation;
100
+ @property (nonatomic) RNSScreenSwipeDirection swipeDirection;
94
101
  @property (nonatomic) BOOL preventNativeDismiss;
95
102
  @property (nonatomic) BOOL hasOrientationSet;
96
103
  @property (nonatomic) BOOL hasStatusBarStyleSet;
97
104
  @property (nonatomic) BOOL hasStatusBarAnimationSet;
98
105
  @property (nonatomic) BOOL hasStatusBarHiddenSet;
106
+ @property (nonatomic) BOOL hasHomeIndicatorHiddenSet;
99
107
  @property (nonatomic) BOOL customAnimationOnSwipe;
100
108
  @property (nonatomic) BOOL fullScreenSwipeEnabled;
109
+ @property (nonatomic, retain) NSNumber *transitionDuration;
101
110
 
102
111
  #if !TARGET_OS_TV
103
112
  @property (nonatomic) RNSStatusBarStyle statusBarStyle;
104
113
  @property (nonatomic) UIStatusBarAnimation statusBarAnimation;
105
114
  @property (nonatomic) BOOL statusBarHidden;
106
115
  @property (nonatomic) UIInterfaceOrientationMask screenOrientation;
116
+ @property (nonatomic) BOOL homeIndicatorHidden;
107
117
  #endif
108
118
 
109
119
  - (void)notifyFinishTransitioning;
package/ios/RNSScreen.m CHANGED
@@ -34,6 +34,7 @@
34
34
  _hasStatusBarAnimationSet = NO;
35
35
  _hasStatusBarHiddenSet = NO;
36
36
  _hasOrientationSet = NO;
37
+ _hasHomeIndicatorHiddenSet = NO;
37
38
  }
38
39
 
39
40
  return self;
@@ -206,6 +207,13 @@
206
207
  _screenOrientation = screenOrientation;
207
208
  [RNSScreenWindowTraits enforceDesiredDeviceOrientation];
208
209
  }
210
+
211
+ - (void)setHomeIndicatorHidden:(BOOL)homeIndicatorHidden
212
+ {
213
+ _hasHomeIndicatorHiddenSet = YES;
214
+ _homeIndicatorHidden = homeIndicatorHidden;
215
+ [RNSScreenWindowTraits updateHomeIndicatorAutoHidden];
216
+ }
209
217
  #endif
210
218
 
211
219
  - (UIView *)reactSuperview
@@ -436,6 +444,17 @@
436
444
  return UIInterfaceOrientationMaskAllButUpsideDown;
437
445
  }
438
446
 
447
+ - (UIViewController *)childViewControllerForHomeIndicatorAutoHidden
448
+ {
449
+ UIViewController *vc = [self findChildVCForConfigAndTrait:RNSWindowTraitHomeIndicatorHidden includingModals:YES];
450
+ return vc == self ? nil : vc;
451
+ }
452
+
453
+ - (BOOL)prefersHomeIndicatorAutoHidden
454
+ {
455
+ return ((RNSScreenView *)self.view).homeIndicatorHidden;
456
+ }
457
+
439
458
  // if the returned vc is a child, it means that it can provide config;
440
459
  // if the returned vc is self, it means that there is no child for config and self has config to provide,
441
460
  // so we return self which results in asking self for preferredStatusBarStyle/Animation etc.;
@@ -495,6 +514,9 @@
495
514
  case RNSWindowTraitOrientation: {
496
515
  return ((RNSScreenView *)self.view).hasOrientationSet;
497
516
  }
517
+ case RNSWindowTraitHomeIndicatorHidden: {
518
+ return ((RNSScreenView *)self.view).hasHomeIndicatorHiddenSet;
519
+ }
498
520
  default: {
499
521
  RCTLogError(@"Unknown trait passed: %d", (int)trait);
500
522
  }
@@ -771,6 +793,8 @@ RCT_EXPORT_VIEW_PROPERTY(preventNativeDismiss, BOOL)
771
793
  RCT_EXPORT_VIEW_PROPERTY(replaceAnimation, RNSScreenReplaceAnimation)
772
794
  RCT_EXPORT_VIEW_PROPERTY(stackPresentation, RNSScreenStackPresentation)
773
795
  RCT_EXPORT_VIEW_PROPERTY(stackAnimation, RNSScreenStackAnimation)
796
+ RCT_EXPORT_VIEW_PROPERTY(swipeDirection, RNSScreenSwipeDirection)
797
+ RCT_EXPORT_VIEW_PROPERTY(transitionDuration, NSNumber)
774
798
 
775
799
  RCT_EXPORT_VIEW_PROPERTY(onAppear, RCTDirectEventBlock);
776
800
  RCT_EXPORT_VIEW_PROPERTY(onDisappear, RCTDirectEventBlock);
@@ -785,6 +809,7 @@ RCT_EXPORT_VIEW_PROPERTY(screenOrientation, UIInterfaceOrientationMask)
785
809
  RCT_EXPORT_VIEW_PROPERTY(statusBarAnimation, UIStatusBarAnimation)
786
810
  RCT_EXPORT_VIEW_PROPERTY(statusBarHidden, BOOL)
787
811
  RCT_EXPORT_VIEW_PROPERTY(statusBarStyle, RNSStatusBarStyle)
812
+ RCT_EXPORT_VIEW_PROPERTY(homeIndicatorHidden, BOOL)
788
813
  #endif
789
814
 
790
815
  - (UIView *)view
@@ -835,6 +860,15 @@ RCT_ENUM_CONVERTER(
835
860
  RNSScreenReplaceAnimationPop,
836
861
  integerValue)
837
862
 
863
+ RCT_ENUM_CONVERTER(
864
+ RNSScreenSwipeDirection,
865
+ (@{
866
+ @"vertical" : @(RNSScreenSwipeDirectionVertical),
867
+ @"horizontal" : @(RNSScreenSwipeDirectionHorizontal),
868
+ }),
869
+ RNSScreenSwipeDirectionHorizontal,
870
+ integerValue)
871
+
838
872
  #if !TARGET_OS_TV
839
873
  RCT_ENUM_CONVERTER(
840
874
  RNSStatusBarStyle,
@@ -23,6 +23,11 @@
23
23
  {
24
24
  return [self findActiveChildVC].supportedInterfaceOrientations;
25
25
  }
26
+
27
+ - (UIViewController *)childViewControllerForHomeIndicatorAutoHidden
28
+ {
29
+ return [self findActiveChildVC];
30
+ }
26
31
  #endif
27
32
 
28
33
  - (UIViewController *)findActiveChildVC
@@ -45,6 +45,11 @@
45
45
  {
46
46
  return [self topViewController].supportedInterfaceOrientations;
47
47
  }
48
+
49
+ - (UIViewController *)childViewControllerForHomeIndicatorAutoHidden
50
+ {
51
+ return [self topViewController];
52
+ }
48
53
  #endif
49
54
 
50
55
  @end
@@ -645,13 +650,23 @@
645
650
 
646
651
  - (void)handleSwipe:(UIPanGestureRecognizer *)gestureRecognizer
647
652
  {
648
- float translation = [gestureRecognizer translationInView:gestureRecognizer.view].x;
649
- float velocity = [gestureRecognizer velocityInView:gestureRecognizer.view].x;
650
- float distance = gestureRecognizer.view.bounds.size.width;
651
- BOOL isRTL = _controller.view.semanticContentAttribute == UISemanticContentAttributeForceRightToLeft;
652
- if (isRTL) {
653
- translation = -translation;
654
- velocity = -velocity;
653
+ RNSScreenView *topScreen = (RNSScreenView *)_controller.viewControllers.lastObject.view;
654
+ float translation;
655
+ float velocity;
656
+ float distance;
657
+ if (topScreen.swipeDirection == RNSScreenSwipeDirectionVertical) {
658
+ translation = [gestureRecognizer translationInView:gestureRecognizer.view].y;
659
+ velocity = [gestureRecognizer velocityInView:gestureRecognizer.view].y;
660
+ distance = gestureRecognizer.view.bounds.size.height;
661
+ } else {
662
+ translation = [gestureRecognizer translationInView:gestureRecognizer.view].x;
663
+ velocity = [gestureRecognizer velocityInView:gestureRecognizer.view].x;
664
+ distance = gestureRecognizer.view.bounds.size.width;
665
+ BOOL isRTL = _controller.view.semanticContentAttribute == UISemanticContentAttributeForceRightToLeft;
666
+ if (isRTL) {
667
+ translation = -translation;
668
+ velocity = -velocity;
669
+ }
655
670
  }
656
671
 
657
672
  float transitionProgress = (translation / distance);
@@ -2,6 +2,13 @@
2
2
  #import "RNSScreen.h"
3
3
  #import "RNSScreenStack.h"
4
4
 
5
+ // proportions to default transition duration
6
+ static const float RNSSlideOpenTransitionDurationProportion = 1;
7
+ static const float RNSFadeOpenTransitionDurationProportion = 0.2 / 0.35;
8
+ static const float RNSSlideCloseTransitionDurationProportion = 0.25 / 0.35;
9
+ static const float RNSFadeCloseTransitionDurationProportion = 0.15 / 0.35;
10
+ static const float RNSFadeCloseDelayTransitionDurationProportion = 0.1 / 0.35;
11
+
5
12
  @implementation RNSScreenStackAnimator {
6
13
  UINavigationControllerOperation _operation;
7
14
  NSTimeInterval _transitionDuration;
@@ -11,7 +18,7 @@
11
18
  {
12
19
  if (self = [super init]) {
13
20
  _operation = operation;
14
- _transitionDuration = 0.35; // default duration
21
+ _transitionDuration = 0.35; // default duration in seconds
15
22
  }
16
23
  return self;
17
24
  }
@@ -32,6 +39,12 @@
32
39
  if (screen != nil && screen.stackAnimation == RNSScreenStackAnimationNone) {
33
40
  return 0;
34
41
  }
42
+
43
+ if (screen != nil && screen.transitionDuration != nil && [screen.transitionDuration floatValue] >= 0) {
44
+ float durationInSeconds = [screen.transitionDuration floatValue] / 1000.0;
45
+ return durationInSeconds;
46
+ }
47
+
35
48
  return _transitionDuration;
36
49
  }
37
50
 
@@ -184,14 +197,30 @@
184
197
  } else if (_operation == UINavigationControllerOperationPop) {
185
198
  toViewController.view.transform = CGAffineTransformIdentity;
186
199
  [[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view];
187
- [UIView animateWithDuration:[self transitionDuration:transitionContext]
188
- animations:^{
189
- toViewController.view.transform = CGAffineTransformIdentity;
190
- fromViewController.view.transform = topBottomTransform;
191
- }
192
- completion:^(BOOL finished) {
193
- [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
194
- }];
200
+
201
+ void (^animationBlock)(void) = ^{
202
+ toViewController.view.transform = CGAffineTransformIdentity;
203
+ fromViewController.view.transform = topBottomTransform;
204
+ };
205
+ void (^completionBlock)(BOOL) = ^(BOOL finished) {
206
+ if (transitionContext.transitionWasCancelled) {
207
+ toViewController.view.transform = CGAffineTransformIdentity;
208
+ }
209
+ [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
210
+ };
211
+
212
+ if (!transitionContext.isInteractive) {
213
+ [UIView animateWithDuration:[self transitionDuration:transitionContext]
214
+ animations:animationBlock
215
+ completion:completionBlock];
216
+ } else {
217
+ // we don't want the EaseInOut option when swiping to dismiss the view, it is the same in default animation option
218
+ [UIView animateWithDuration:[self transitionDuration:transitionContext]
219
+ delay:0.0
220
+ options:UIViewAnimationOptionCurveLinear
221
+ animations:animationBlock
222
+ completion:completionBlock];
223
+ }
195
224
  }
196
225
  }
197
226
 
@@ -202,6 +231,8 @@
202
231
  CGAffineTransform topBottomTransform =
203
232
  CGAffineTransformMakeTranslation(0, 0.08 * transitionContext.containerView.bounds.size.height);
204
233
 
234
+ const float transitionDuration = [self transitionDuration:transitionContext];
235
+
205
236
  if (_operation == UINavigationControllerOperationPush) {
206
237
  toViewController.view.transform = topBottomTransform;
207
238
  toViewController.view.alpha = 0.0;
@@ -209,7 +240,7 @@
209
240
 
210
241
  // Android Nougat open animation
211
242
  // http://aosp.opersys.com/xref/android-7.1.2_r37/xref/frameworks/base/core/res/res/anim/activity_open_enter.xml
212
- [UIView animateWithDuration:0.35
243
+ [UIView animateWithDuration:transitionDuration * RNSSlideOpenTransitionDurationProportion // defaults to 0.35 s
213
244
  delay:0
214
245
  options:UIViewAnimationOptionCurveEaseOut
215
246
  animations:^{
@@ -220,7 +251,7 @@
220
251
  fromViewController.view.transform = CGAffineTransformIdentity;
221
252
  [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
222
253
  }];
223
- [UIView animateWithDuration:0.2
254
+ [UIView animateWithDuration:transitionDuration * RNSFadeOpenTransitionDurationProportion // defaults to 0.2 s
224
255
  delay:0
225
256
  options:UIViewAnimationOptionCurveEaseOut
226
257
  animations:^{
@@ -234,7 +265,7 @@
234
265
 
235
266
  // Android Nougat exit animation
236
267
  // http://aosp.opersys.com/xref/android-7.1.2_r37/xref/frameworks/base/core/res/res/anim/activity_close_exit.xml
237
- [UIView animateWithDuration:0.25
268
+ [UIView animateWithDuration:transitionDuration * RNSSlideCloseTransitionDurationProportion // defaults to 0.25 s
238
269
  delay:0
239
270
  options:UIViewAnimationOptionCurveEaseIn
240
271
  animations:^{
@@ -244,8 +275,8 @@
244
275
  completion:^(BOOL finished) {
245
276
  [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
246
277
  }];
247
- [UIView animateWithDuration:0.15
248
- delay:0.1
278
+ [UIView animateWithDuration:transitionDuration * RNSFadeCloseTransitionDurationProportion // defaults to 0.15 s
279
+ delay:transitionDuration * RNSFadeCloseDelayTransitionDurationProportion // defaults to 0.1 s
249
280
  options:UIViewAnimationOptionCurveLinear
250
281
  animations:^{
251
282
  fromViewController.view.alpha = 0.0;
@@ -152,9 +152,12 @@
152
152
  nextVC = nav.topViewController;
153
153
  }
154
154
 
155
+ // we want updates sent to the VC below modal too since it is also visible
156
+ BOOL isPresentingVC = vc.presentedViewController == nextVC;
157
+
155
158
  BOOL isInFullScreenModal = nav == nil && _screenView.stackPresentation == RNSScreenStackPresentationFullScreenModal;
156
159
  // if nav is nil, it means we can be in a fullScreen modal, so there is no nextVC, but we still want to update
157
- if (vc != nil && (nextVC == vc || isInFullScreenModal)) {
160
+ if (vc != nil && (nextVC == vc || isInFullScreenModal || isPresentingVC)) {
158
161
  [RNSScreenStackHeaderConfig updateViewController:self.screenView.controller withConfig:self animated:YES];
159
162
  }
160
163
  }
@@ -9,6 +9,7 @@
9
9
  #endif
10
10
  + (void)updateStatusBarAppearance;
11
11
  + (void)enforceDesiredDeviceOrientation;
12
+ + (void)updateHomeIndicatorAutoHidden;
12
13
 
13
14
  #if !TARGET_OS_TV
14
15
  + (UIStatusBarStyle)statusBarStyleForRNSStatusBarStyle:(RNSStatusBarStyle)statusBarStyle;
@@ -42,6 +42,25 @@
42
42
  #endif
43
43
  }
44
44
 
45
+ + (void)updateHomeIndicatorAutoHidden
46
+ {
47
+ #if !TARGET_OS_TV
48
+
49
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
50
+ __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
51
+ if (@available(iOS 13, *)) {
52
+ UIWindow *firstWindow = [[[UIApplication sharedApplication] windows] firstObject];
53
+ if (firstWindow != nil) {
54
+ [[firstWindow rootViewController] setNeedsUpdateOfHomeIndicatorAutoHidden];
55
+ }
56
+ } else
57
+ #endif
58
+ {
59
+ [UIApplication.sharedApplication.keyWindow.rootViewController setNeedsUpdateOfHomeIndicatorAutoHidden];
60
+ }
61
+ #endif
62
+ }
63
+
45
64
  #if !TARGET_OS_TV
46
65
  + (UIStatusBarStyle)statusBarStyleForRNSStatusBarStyle:(RNSStatusBarStyle)statusBarStyle
47
66
  {
@@ -165,6 +184,7 @@
165
184
  {
166
185
  [RNSScreenWindowTraits updateStatusBarAppearance];
167
186
  [RNSScreenWindowTraits enforceDesiredDeviceOrientation];
187
+ [RNSScreenWindowTraits updateHomeIndicatorAutoHidden];
168
188
  }
169
189
 
170
190
  #if !TARGET_OS_TV
@@ -31,6 +31,12 @@
31
31
  return childVC ? childVC.supportedInterfaceOrientations : [self reactNativeScreensSupportedInterfaceOrientations];
32
32
  }
33
33
 
34
+ - (UIViewController *)reactNativeScreensChildViewControllerForHomeIndicatorAutoHidden
35
+ {
36
+ UIViewController *childVC = [self findChildRNScreensViewController];
37
+ return childVC ?: [self reactNativeScreensChildViewControllerForHomeIndicatorAutoHidden];
38
+ }
39
+
34
40
  - (UIViewController *)findChildRNScreensViewController
35
41
  {
36
42
  UIViewController *lastViewController = [[self childViewControllers] lastObject];
@@ -61,6 +67,10 @@
61
67
  method_exchangeImplementations(
62
68
  class_getInstanceMethod(uiVCClass, @selector(supportedInterfaceOrientations)),
63
69
  class_getInstanceMethod(uiVCClass, @selector(reactNativeScreensSupportedInterfaceOrientations)));
70
+
71
+ method_exchangeImplementations(
72
+ class_getInstanceMethod(uiVCClass, @selector(childViewControllerForHomeIndicatorAutoHidden)),
73
+ class_getInstanceMethod(uiVCClass, @selector(reactNativeScreensChildViewControllerForHomeIndicatorAutoHidden)));
64
74
  });
65
75
  }
66
76
  #endif