react-native-screens 3.10.0 → 3.11.1

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 (46) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +9 -7
  3. package/android/build.gradle +1 -2
  4. package/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +1 -1
  5. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +28 -11
  6. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +1 -1
  7. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +64 -33
  8. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +9 -31
  9. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +3 -32
  10. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +12 -5
  11. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +10 -0
  12. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +72 -11
  13. package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +18 -1
  14. package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +7 -2
  15. package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +29 -2
  16. package/android/src/main/res/anim/rns_default_enter_in.xml +18 -0
  17. package/android/src/main/res/anim/rns_default_enter_out.xml +19 -0
  18. package/android/src/main/res/anim/rns_default_exit_in.xml +17 -0
  19. package/android/src/main/res/anim/rns_default_exit_out.xml +18 -0
  20. package/android/src/main/res/anim/rns_fade_in.xml +7 -0
  21. package/android/src/main/res/anim/rns_fade_out.xml +7 -0
  22. package/android/src/main/res/anim/rns_no_animation_20.xml +6 -0
  23. package/createNativeStackNavigator/README.md +21 -33
  24. package/ios/RNSScreen.h +10 -0
  25. package/ios/RNSScreen.m +34 -0
  26. package/ios/RNSScreenContainer.m +5 -0
  27. package/ios/RNSScreenStack.m +22 -7
  28. package/ios/RNSScreenStackAnimator.m +45 -14
  29. package/ios/RNSScreenStackHeaderConfig.m +4 -1
  30. package/ios/RNSScreenWindowTraits.h +1 -0
  31. package/ios/RNSScreenWindowTraits.m +20 -0
  32. package/ios/UIViewController+RNScreens.m +10 -0
  33. package/lib/commonjs/native-stack/views/NativeStackView.js +33 -4
  34. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  35. package/lib/module/native-stack/views/NativeStackView.js +33 -4
  36. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  37. package/lib/typescript/native-stack/types.d.ts +34 -0
  38. package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +1 -1
  39. package/lib/typescript/reanimated/ReanimatedScreen.d.ts +1 -1
  40. package/lib/typescript/types.d.ts +60 -5
  41. package/native-stack/README.md +39 -3
  42. package/package.json +1 -1
  43. package/reanimated/package.json +6 -0
  44. package/src/native-stack/types.tsx +34 -0
  45. package/src/native-stack/views/NativeStackView.tsx +33 -4
  46. package/src/types.tsx +60 -5
@@ -6,11 +6,15 @@ import android.annotation.SuppressLint
6
6
  import android.annotation.TargetApi
7
7
  import android.app.Activity
8
8
  import android.content.pm.ActivityInfo
9
+ import android.graphics.Color
9
10
  import android.os.Build
10
11
  import android.view.View
11
12
  import android.view.ViewParent
12
13
  import android.view.WindowManager
13
14
  import androidx.core.view.ViewCompat
15
+ import androidx.core.view.WindowCompat
16
+ import androidx.core.view.WindowInsetsCompat
17
+ import androidx.core.view.WindowInsetsControllerCompat
14
18
  import com.facebook.react.bridge.GuardedRunnable
15
19
  import com.facebook.react.bridge.ReactContext
16
20
  import com.facebook.react.bridge.UiThreadUtil
@@ -21,6 +25,7 @@ object ScreenWindowTraits {
21
25
  // https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.java
22
26
  private var mDidSetOrientation = false
23
27
  private var mDidSetStatusBarAppearance = false
28
+ private var mDidSetNavigationBarAppearance = false
24
29
  private var mDefaultStatusBarColor: Int? = null
25
30
  internal fun applyDidSetOrientation() {
26
31
  mDidSetOrientation = true
@@ -30,6 +35,10 @@ object ScreenWindowTraits {
30
35
  mDidSetStatusBarAppearance = true
31
36
  }
32
37
 
38
+ internal fun applyDidSetNavigationBarAppearance() {
39
+ mDidSetNavigationBarAppearance = true
40
+ }
41
+
33
42
  internal fun setOrientation(screen: Screen, activity: Activity?) {
34
43
  if (activity == null) {
35
44
  return
@@ -72,23 +81,21 @@ object ScreenWindowTraits {
72
81
  }
73
82
 
74
83
  internal fun setStyle(screen: Screen, activity: Activity?, context: ReactContext?) {
75
- if (activity == null || context == null) {
84
+ if (activity == null || context == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
76
85
  return
77
86
  }
78
87
  val screenForStyle = findScreenForTrait(screen, WindowTraits.STYLE)
79
88
  val style = screenForStyle?.statusBarStyle ?: "light"
80
89
 
81
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
82
- UiThreadUtil.runOnUiThread {
83
- val decorView = activity.window.decorView
84
- var systemUiVisibilityFlags = decorView.systemUiVisibility
85
- systemUiVisibilityFlags = if ("dark" == style) {
86
- systemUiVisibilityFlags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
87
- } else {
88
- systemUiVisibilityFlags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
89
- }
90
- decorView.systemUiVisibility = systemUiVisibilityFlags
90
+ UiThreadUtil.runOnUiThread {
91
+ val decorView = activity.window.decorView
92
+ var systemUiVisibilityFlags = decorView.systemUiVisibility
93
+ systemUiVisibilityFlags = if ("dark" == style) {
94
+ systemUiVisibilityFlags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
95
+ } else {
96
+ systemUiVisibilityFlags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
91
97
  }
98
+ decorView.systemUiVisibility = systemUiVisibilityFlags
92
99
  }
93
100
  }
94
101
 
@@ -144,6 +151,48 @@ object ScreenWindowTraits {
144
151
  }
145
152
  }
146
153
 
154
+ // Methods concerning navigationBar management were taken from `react-native-navigation`'s repo:
155
+ // https://github.com/wix/react-native-navigation/blob/9bb70d81700692141a2c505c081c2d86c7f9c66e/lib/android/app/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt
156
+ internal fun setNavigationBarColor(screen: Screen, activity: Activity?) {
157
+ if (activity == null) {
158
+ return
159
+ }
160
+
161
+ val window = activity.window
162
+
163
+ val screenForNavBarColor = findScreenForTrait(screen, WindowTraits.NAVIGATION_BAR_COLOR)
164
+ val color = screenForNavBarColor?.navigationBarColor ?: window.navigationBarColor
165
+
166
+ WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightNavigationBars =
167
+ isColorLight(color)
168
+ window.navigationBarColor = color
169
+ }
170
+
171
+ internal fun setNavigationBarHidden(screen: Screen, activity: Activity?) {
172
+ if (activity == null) {
173
+ return
174
+ }
175
+
176
+ val window = activity.window
177
+
178
+ val screenForNavBarHidden = findScreenForTrait(screen, WindowTraits.NAVIGATION_BAR_HIDDEN)
179
+ val hidden = screenForNavBarHidden?.isNavigationBarHidden ?: false
180
+
181
+ WindowCompat.setDecorFitsSystemWindows(window, hidden)
182
+ if (hidden) {
183
+ WindowInsetsControllerCompat(window, window.decorView).let { controller ->
184
+ controller.hide(WindowInsetsCompat.Type.navigationBars())
185
+ controller.systemBarsBehavior =
186
+ WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
187
+ }
188
+ } else {
189
+ WindowInsetsControllerCompat(
190
+ window,
191
+ window.decorView
192
+ ).show(WindowInsetsCompat.Type.navigationBars())
193
+ }
194
+ }
195
+
147
196
  internal fun trySetWindowTraits(screen: Screen, activity: Activity?, context: ReactContext?) {
148
197
  if (mDidSetOrientation) {
149
198
  setOrientation(screen, activity)
@@ -154,6 +203,10 @@ object ScreenWindowTraits {
154
203
  setTranslucent(screen, activity, context)
155
204
  setHidden(screen, activity)
156
205
  }
206
+ if (mDidSetNavigationBarAppearance) {
207
+ setNavigationBarColor(screen, activity)
208
+ setNavigationBarHidden(screen, activity)
209
+ }
157
210
  }
158
211
 
159
212
  private fun findScreenForTrait(screen: Screen, trait: WindowTraits): Screen? {
@@ -211,6 +264,14 @@ object ScreenWindowTraits {
211
264
  WindowTraits.TRANSLUCENT -> screen.isStatusBarTranslucent != null
212
265
  WindowTraits.HIDDEN -> screen.isStatusBarHidden != null
213
266
  WindowTraits.ANIMATED -> screen.isStatusBarAnimated != null
267
+ WindowTraits.NAVIGATION_BAR_COLOR -> screen.navigationBarColor != null
268
+ WindowTraits.NAVIGATION_BAR_HIDDEN -> screen.isNavigationBarHidden != null
214
269
  }
215
270
  }
271
+
272
+ private fun isColorLight(color: Int): Boolean {
273
+ val darkness: Double =
274
+ 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255
275
+ return darkness < 0.5
276
+ }
216
277
  }
@@ -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 -->
@@ -360,10 +360,12 @@ Defaults to `auto`.
360
360
 
361
361
  Sets the translucency of the status bar (similar to the `StatusBar` component). Defaults to `false`.
362
362
 
363
- ### Search bar
363
+ ### Search bar (iOS only)
364
364
 
365
365
  The search bar is just a `searchBar` property that can be specified in the navigator's `defaultNavigationOptions` prop or an individual screen's `navigationOptions`. Search bars are rarely static so normally it is controlled by passing an object to `searchBar` navigation option in the component's body.
366
366
 
367
+ Search bar is only supported on iOS.
368
+
367
369
  Example:
368
370
 
369
371
  ```js
@@ -388,11 +390,7 @@ Possible values:
388
390
  - `sentences`
389
391
  - `characters`
390
392
 
391
- Defaults to `sentences` on iOS and `'none'` on Android.
392
-
393
- #### `autoFocus` (Android only)
394
-
395
- When set to `true` focuses search bar automatically when screen is appearing. Default value is `false`.
393
+ Defaults to `sentences`.
396
394
 
397
395
  #### `barTintColor`
398
396
 
@@ -400,37 +398,23 @@ The search field background color.
400
398
 
401
399
  By default bar tint color is translucent.
402
400
 
403
- #### `cancelButtonText` (iOS only)
401
+ #### `cancelButtonText`
404
402
 
405
403
  The text to be used instead of default `Cancel` button text.
406
404
 
407
- #### `disableBackButtonOverride` (Android only)
408
-
409
- Default behavior is to prevent screen from going back when search bar is open (`disableBackButtonOverride: false`). If you don't want this to happen set `disableBackButtonOverride` to `true`
410
-
411
- #### `hideNavigationBar` (iOS only)
405
+ #### `hideNavigationBar`
412
406
 
413
407
  Boolean indicating whether to hide the navigation bar during searching.
414
408
 
415
409
  Defaults to `true`.
416
410
 
417
- #### `hideWhenScrolling` (iOS only)
411
+ #### `hideWhenScrolling`
418
412
 
419
413
  Boolean indicating whether to hide the search bar when scrolling.
420
414
 
421
415
  Defaults to `true`.
422
416
 
423
- #### `inputType` (Android only)
424
-
425
- This prop is used to change type of the input and keyboard. Default value is `'text'`.
426
-
427
- All values:
428
- - `'text'` - normal text input
429
- - `'number'` - number input
430
- - `'email'` - email input
431
- - `'phone'` - phone input
432
-
433
- #### `obscureBackground` (iOS only)
417
+ #### `obscureBackground`
434
418
 
435
419
  Boolean indicating whether to obscure the underlying content with semi-transparent overlay.
436
420
 
@@ -440,7 +424,7 @@ Defaults to `true`.
440
424
 
441
425
  A callback that gets called when search bar has lost focus.
442
426
 
443
- #### `onCancelButtonPress` (iOS only)
427
+ #### `onCancelButtonPress`
444
428
 
445
429
  A callback that gets called when the cancel button is pressed.
446
430
 
@@ -462,18 +446,10 @@ static navigationOptions = ({navigation}) => {
462
446
  };
463
447
  ```
464
448
 
465
- #### `onClose` (Android only)
466
-
467
- A callback that gets called when search bar is closing
468
-
469
449
  #### `onFocus`
470
450
 
471
451
  A callback that gets called when search bar has received focus.
472
452
 
473
- #### `onOpen` (Android only)
474
-
475
- A callback that gets called when search bar is expanding
476
-
477
453
  #### `onSearchButtonPress`
478
454
 
479
455
  A callback that gets called when the search button is pressed. It receives the current text value of the search bar.
@@ -488,6 +464,18 @@ Defaults to an empty string.
488
464
 
489
465
  The search field text color.
490
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
+
491
479
  ### Helpers
492
480
 
493
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
+ RNSScreenSwipeDirectionHorizontal,
34
+ RNSScreenSwipeDirectionVertical,
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);