@onekeyfe/react-native-pager-view 6.7.0-alpha.375 → 6.7.0-alpha.382
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.
- package/README.md +5 -0
- package/android/src/androidTest/AndroidManifest.xml +7 -0
- package/android/src/androidTest/java/com/reactnativepagerview/CollapsiblePagerTabReservationTest.kt +247 -0
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +76 -21
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerNativeHeaders.kt +72 -7
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +27 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerView.kt +50 -6
- package/docs/SPEC.md +101 -1
- package/ios/RNCCollapsiblePagerViewComponentView.mm +124 -12
- package/ios/tests/RNCCollapsiblePagerHitTestTests.mm +178 -0
- package/ios/tests/RNCCollapsiblePagerPressCancellationTests.mm +65 -0
- package/lib/module/CollapsiblePagerView.js +20 -4
- package/lib/module/CollapsiblePagerView.web.js +11 -2
- package/lib/module/CollapsiblePagerViewNativeComponent.ts +13 -0
- package/lib/typescript/src/CollapsiblePagerView.d.ts +6 -1
- package/lib/typescript/src/CollapsiblePagerView.web.d.ts +2 -0
- package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +5 -0
- package/package.json +2 -2
- package/src/CollapsiblePagerView.tsx +68 -4
- package/src/CollapsiblePagerView.web.tsx +19 -2
- package/src/CollapsiblePagerViewNativeComponent.ts +13 -0
|
@@ -239,6 +239,18 @@ class CollapsiblePagerViewManager : ViewGroupManager<CollapsiblePagerHost>(),
|
|
|
239
239
|
view.applyNativeHeaderStyle()
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
@ReactProp(name = "nativeTabBarVariant")
|
|
243
|
+
override fun setNativeTabBarVariant(view: CollapsiblePagerHost?, value: String?) {
|
|
244
|
+
view ?: return
|
|
245
|
+
view.nativeTabBarVariant = if (value == "segmented") "segmented" else "underline"
|
|
246
|
+
view.applyNativeHeaderStyle()
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
@ReactProp(name = "nativeTabBarTrailingReservedWidth", defaultDouble = 0.0)
|
|
250
|
+
override fun setNativeTabBarTrailingReservedWidth(view: CollapsiblePagerHost?, value: Double) {
|
|
251
|
+
view?.nativeTabBarTrailingReservedWidth = value
|
|
252
|
+
}
|
|
253
|
+
|
|
242
254
|
@ReactProp(name = "nativeTabBarContentPaddingHorizontal")
|
|
243
255
|
override fun setNativeTabBarContentPaddingHorizontal(
|
|
244
256
|
view: CollapsiblePagerHost?,
|
|
@@ -249,6 +261,13 @@ class CollapsiblePagerViewManager : ViewGroupManager<CollapsiblePagerHost>(),
|
|
|
249
261
|
view.applyNativeHeaderStyle()
|
|
250
262
|
}
|
|
251
263
|
|
|
264
|
+
@ReactProp(name = "nativeTabBarContentPaddingVertical")
|
|
265
|
+
override fun setNativeTabBarContentPaddingVertical(view: CollapsiblePagerHost?, value: Double) {
|
|
266
|
+
view ?: return
|
|
267
|
+
view.nativeTabBarContentPaddingVertical = if (value.isFinite()) value.coerceAtLeast(0.0) else 0.0
|
|
268
|
+
view.applyNativeHeaderStyle()
|
|
269
|
+
}
|
|
270
|
+
|
|
252
271
|
@ReactProp(name = "nativeTabBarItemSpacing")
|
|
253
272
|
override fun setNativeTabBarItemSpacing(view: CollapsiblePagerHost?, value: Double) {
|
|
254
273
|
view ?: return
|
|
@@ -343,6 +362,14 @@ class CollapsiblePagerViewManager : ViewGroupManager<CollapsiblePagerHost>(),
|
|
|
343
362
|
view?.scrollToTop(target, animated)
|
|
344
363
|
}
|
|
345
364
|
|
|
365
|
+
override fun scrollHeaderBy(view: CollapsiblePagerHost?, delta: Double) {
|
|
366
|
+
view?.scrollHeaderBy(delta)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
override fun scrollHeaderToOffset(view: CollapsiblePagerHost?, offset: Double) {
|
|
370
|
+
view?.scrollHeaderToOffset(offset)
|
|
371
|
+
}
|
|
372
|
+
|
|
346
373
|
override fun setPage(view: CollapsiblePagerHost?, selectedPage: Int) {
|
|
347
374
|
if (view != null && selectedPage in 0 until view.adapter.itemCount) {
|
|
348
375
|
view.beginPageTransition(selectedPage)
|
|
@@ -37,26 +37,50 @@ class NativeScrollerView(context: Context) : FrameLayout(context) {
|
|
|
37
37
|
setMeasuredDimension(MeasureSpec.getSize(widthSpec), MeasureSpec.getSize(heightSpec))
|
|
38
38
|
}
|
|
39
39
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) = Unit
|
|
40
|
+
|
|
41
|
+
override fun startViewTransition(view: View) {
|
|
42
|
+
super.startViewTransition(view)
|
|
43
|
+
if (view === reactChild) transitioningReactChild = view
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override fun endViewTransition(view: View) {
|
|
47
|
+
super.endViewTransition(view)
|
|
48
|
+
if (transitioningReactChild === view) transitioningReactChild = null
|
|
49
|
+
if (retainedReactChild === view) {
|
|
50
|
+
clearRetainedGeometry()
|
|
51
|
+
if (isAttachedToWindow) scheduleLayout()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
40
54
|
}
|
|
41
55
|
private val contentFrame = object : FrameLayout(context) {
|
|
42
56
|
override fun onMeasure(widthSpec: Int, heightSpec: Int) {
|
|
43
57
|
setMeasuredDimension(
|
|
44
|
-
max(MeasureSpec.getSize(widthSpec),
|
|
45
|
-
|
|
58
|
+
max(MeasureSpec.getSize(widthSpec), effectiveContentWidthPx + insetLeft + insetRight),
|
|
59
|
+
effectiveContentHeightPx + contentTop + insetBottom,
|
|
46
60
|
)
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
50
64
|
// Only the native origin layer moves. Fabric owns its React child's layout/transform.
|
|
65
|
+
val contentWidth = effectiveContentWidthPx
|
|
66
|
+
val contentHeight = effectiveContentHeightPx
|
|
51
67
|
contentOriginFrame.measure(
|
|
52
|
-
MeasureSpec.makeMeasureSpec(
|
|
53
|
-
MeasureSpec.makeMeasureSpec(
|
|
68
|
+
MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY),
|
|
69
|
+
MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY),
|
|
54
70
|
)
|
|
55
|
-
contentOriginFrame.layout(insetLeft, contentTop, insetLeft +
|
|
71
|
+
contentOriginFrame.layout(insetLeft, contentTop, insetLeft + contentWidth, contentTop + contentHeight)
|
|
56
72
|
}
|
|
57
73
|
}
|
|
58
74
|
private val pagerInsets = WeakHashMap<View, Int>()
|
|
59
75
|
private var reactChild: View? = null
|
|
76
|
+
private var transitioningReactChild: View? = null
|
|
77
|
+
private var retainedReactChild: View? = null
|
|
78
|
+
private var retainedContentWidthPx = 0
|
|
79
|
+
private var retainedContentHeightPx = 0
|
|
80
|
+
private val effectiveContentWidthPx: Int
|
|
81
|
+
get() = if (reactChild == null && retainedReactChild != null) retainedContentWidthPx else contentWidthPx
|
|
82
|
+
private val effectiveContentHeightPx: Int
|
|
83
|
+
get() = if (reactChild == null && retainedReactChild != null) retainedContentHeightPx else contentHeightPx
|
|
60
84
|
private var fabricState: StateWrapper? = null
|
|
61
85
|
private var lastOriginX: Int? = null
|
|
62
86
|
private var lastOriginY: Int? = null
|
|
@@ -172,6 +196,8 @@ class NativeScrollerView(context: Context) : FrameLayout(context) {
|
|
|
172
196
|
internal fun addReactChild(child: View, index: Int) {
|
|
173
197
|
require(index == 0 && reactChild == null) { "NativeScroller requires one React content view" }
|
|
174
198
|
viewport.stopMotion()
|
|
199
|
+
clearRetainedGeometry()
|
|
200
|
+
transitioningReactChild = null
|
|
175
201
|
reactChild = child
|
|
176
202
|
contentOriginFrame.addView(child)
|
|
177
203
|
scheduleLayout()
|
|
@@ -180,13 +206,29 @@ class NativeScrollerView(context: Context) : FrameLayout(context) {
|
|
|
180
206
|
internal fun removeReactChild() {
|
|
181
207
|
viewport.stopMotion()
|
|
182
208
|
pendingRestore.cancel()
|
|
183
|
-
reactChild?.let
|
|
209
|
+
reactChild?.let { child ->
|
|
210
|
+
clearRetainedGeometry()
|
|
211
|
+
// Fabric removes the child before its disappearing view finishes drawing.
|
|
212
|
+
if (transitioningReactChild === child &&
|
|
213
|
+
contentOriginFrame.width > 0 && contentOriginFrame.height > 0) {
|
|
214
|
+
retainedReactChild = child
|
|
215
|
+
retainedContentWidthPx = contentOriginFrame.width
|
|
216
|
+
retainedContentHeightPx = contentOriginFrame.height
|
|
217
|
+
}
|
|
218
|
+
contentOriginFrame.removeView(child)
|
|
219
|
+
}
|
|
184
220
|
reactChild = null
|
|
185
221
|
contentWidthPx = 0
|
|
186
222
|
contentHeightPx = 0
|
|
187
223
|
scheduleLayout()
|
|
188
224
|
}
|
|
189
225
|
|
|
226
|
+
private fun clearRetainedGeometry() {
|
|
227
|
+
retainedReactChild = null
|
|
228
|
+
retainedContentWidthPx = 0
|
|
229
|
+
retainedContentHeightPx = 0
|
|
230
|
+
}
|
|
231
|
+
|
|
190
232
|
internal fun reactChildCount() = if (reactChild == null) 0 else 1
|
|
191
233
|
internal fun reactChildAt(index: Int): View {
|
|
192
234
|
require(index == 0)
|
|
@@ -381,6 +423,8 @@ class NativeScrollerView(context: Context) : FrameLayout(context) {
|
|
|
381
423
|
lastContentViewportHeight = null
|
|
382
424
|
viewport.stopMotion()
|
|
383
425
|
viewport.setTag(R.id.onekey_native_scroll_coordinator_offset_listener, null)
|
|
426
|
+
transitioningReactChild = null
|
|
427
|
+
clearRetainedGeometry()
|
|
384
428
|
super.onDetachedFromWindow()
|
|
385
429
|
}
|
|
386
430
|
|
package/docs/SPEC.md
CHANGED
|
@@ -74,7 +74,7 @@ the shared host before their old scroll-edge interaction is released.
|
|
|
74
74
|
The measured overlay height is excluded from the collapsible header distance:
|
|
75
75
|
`header` remains collapsible and `stickyHeader` pins immediately below the
|
|
76
76
|
overlay. Selection, cancellation, reuse and disposal rebind or release the
|
|
77
|
-
interaction and restore any prior scroll-edge style. Native scroll/refresh
|
|
77
|
+
interaction and restore any prior scroll-edge style. The existing owner-scoped native sticky-height callback also reaches NativeList hosts, so their React container slots clip below the same fixed viewport region and release it when the owner detaches. Native scroll/refresh
|
|
78
78
|
coordination continues on the main thread without per-frame JavaScript work.
|
|
79
79
|
Refresh retains the pager-owned `overlay + header + sticky` top inset but offsets
|
|
80
80
|
the indicator by `header + sticky`, so its spinner begins below the fixed overlay.
|
|
@@ -171,6 +171,24 @@ intent. Real vertical consumption, a newer valid command, or owner detachment
|
|
|
171
171
|
releases the target. Existing NativeList and NativeScroller public coordinates
|
|
172
172
|
are unchanged. The same page-top/sticky-top destinations are available without smooth-header mode.
|
|
173
173
|
|
|
174
|
+
## Current-page partial header command
|
|
175
|
+
|
|
176
|
+
`scrollHeaderBy(delta: number)` moves the current page's shared collapsible
|
|
177
|
+
header from its displayed offset C to `clamp(C + delta, 0, D)`.
|
|
178
|
+
`scrollHeaderToOffset(offset: number)` moves it to `clamp(offset, 0, D)`
|
|
179
|
+
regardless of intervening native keyboard or user scrolling. D is the latest
|
|
180
|
+
measured collapsible height. Inputs use logical points on iOS and Android and
|
|
181
|
+
CSS pixels on Web. Non-finite values are ignored. The header and current body move immediately
|
|
182
|
+
so a focused input becomes visible before the next keyboard frame. A valid command
|
|
183
|
+
clears only the current page's body reading position B; it never changes
|
|
184
|
+
another page's memory or adds permanent content/insets. It requires the same
|
|
185
|
+
stable attached current owner as the page-top commands and is a safe no-op
|
|
186
|
+
during horizontal transitions, before mount, or after disposal. A newer
|
|
187
|
+
command or real user vertical consumption takes ownership from an older one.
|
|
188
|
+
Dynamic header measurements clamp any in-flight target to the latest D.
|
|
189
|
+
This command allows a focused input inside the shared header to move above a
|
|
190
|
+
keyboard without replacing the pager's sole vertical scroll owner.
|
|
191
|
+
|
|
174
192
|
## Independent NativeScroller migration
|
|
175
193
|
|
|
176
194
|
Status: implemented in source. Both native builds pass. Android focused emulator
|
|
@@ -333,6 +351,32 @@ warnings were observed. Keyboard, multi-touch, physical-device performance and
|
|
|
333
351
|
the full lifecycle/interaction matrix remain unverified. These are local source
|
|
334
352
|
builds; the currently installed npm version does not yet contain this migration.
|
|
335
353
|
|
|
354
|
+
## Android removal-transition geometry retention
|
|
355
|
+
|
|
356
|
+
Status: implemented locally; unreleased. On the Android UI thread, during a
|
|
357
|
+
screen removal transition,
|
|
358
|
+
NativeList and CollapsiblePager layout entry points respect the RecyclerView
|
|
359
|
+
suppression state established by the screen-transition owner. The owner restores
|
|
360
|
+
the original state when its last transition ownership ends. This adds no public
|
|
361
|
+
API and does not change Web or iOS behavior.
|
|
362
|
+
|
|
363
|
+
If Fabric removes the current NativeScroller React child while its origin-frame
|
|
364
|
+
transition remains active, Android retains the already displayed native extent
|
|
365
|
+
for drawing. The current child and raw content dimensions still clear normally;
|
|
366
|
+
only native content measurement and layout use the retained effective extent.
|
|
367
|
+
The matching transition end or detach clears the private child and extent state
|
|
368
|
+
idempotently. Adding a new child cancels prior retention, and a late end for the
|
|
369
|
+
old child cannot alter the new content. Ordinary removal, size events and scroll
|
|
370
|
+
commands keep their existing semantics.
|
|
371
|
+
|
|
372
|
+
Local arm64 DevShell acceptance covered Base, CXO and Overview exit content,
|
|
373
|
+
normal scrolling, RecyclerView ownership and suppression cleanup for the
|
|
374
|
+
observed CXO exit, and NativeScroller raw-zero content dimensions with a
|
|
375
|
+
nonzero displayed origin followed by detach cleanup. Nested-owner and cancel
|
|
376
|
+
paths, Overview rebound/cancel, natural resize and new-child runtime cases,
|
|
377
|
+
and the full performance matrix remain unverified. SDK publication and
|
|
378
|
+
consumer version alignment are separate steps.
|
|
379
|
+
|
|
336
380
|
## iOS vertical-scroll press cancellation
|
|
337
381
|
|
|
338
382
|
Status: implemented in source. A source-identical `node_modules` build was
|
|
@@ -452,6 +496,40 @@ The attached UIKit regression intentionally constrains the real pager pan to two
|
|
|
452
496
|
required touches to prove the no-pan path; it is distinct from natural Home OS
|
|
453
497
|
input, coalesced-event delivery, and physical-device acceptance.
|
|
454
498
|
|
|
499
|
+
## Native segmented tab bar
|
|
500
|
+
|
|
501
|
+
Status: implemented locally. Focused production-class iOS XCTest and Android
|
|
502
|
+
instrumentation checks pass; consuming App interaction acceptance is separate.
|
|
503
|
+
`nativeTabBar.style.variant` accepts `"underline"` (the default) or
|
|
504
|
+
`"segmented"` on iOS and Android. The optional
|
|
505
|
+
`nativeTabBar.style.contentPaddingVertical` is a nonnegative logical inset,
|
|
506
|
+
defaulting to zero. Omitting both fields preserves the existing content-width,
|
|
507
|
+
horizontally scrollable underline bar. Web ignores `nativeTabBar` and retains
|
|
508
|
+
its caller-rendered sticky header.
|
|
509
|
+
|
|
510
|
+
In segmented mode, the existing `height` is the entire tab slot. The track
|
|
511
|
+
fills the space inside `contentPaddingHorizontal` and
|
|
512
|
+
`contentPaddingVertical`, uses `backgroundColor`, and has a fully rounded
|
|
513
|
+
outline. Its visible items divide the track into equal-width segments in their
|
|
514
|
+
current order, including after item insertion, removal, relabeling, or resize.
|
|
515
|
+
The thumb uses `indicatorColor`, is inset two logical points from its segment,
|
|
516
|
+
and is fully rounded. Segment titles use the existing font and active/inactive
|
|
517
|
+
text colors, remain single-line with truncation, and retain their existing
|
|
518
|
+
accessibility labels, test IDs, and native press event keys. The segmented bar
|
|
519
|
+
does not scroll horizontally; `itemSpacing`, `indicatorHeight`, and
|
|
520
|
+
`indicatorBottom` apply only to underline mode. The pager's native fractional
|
|
521
|
+
page progress moves the thumb continuously between equal-width segments and
|
|
522
|
+
interpolates text emphasis without per-frame JavaScript updates. It settles at
|
|
523
|
+
the actual selected page after a completed or cancelled gesture. RTL reverses
|
|
524
|
+
physical segment order without changing logical page keys or indices.
|
|
525
|
+
|
|
526
|
+
Switching variants or removing the native tab bar clears the previous visual
|
|
527
|
+
state. The existing native press command, page callbacks, retention, vertical
|
|
528
|
+
header geometry, and Web behavior remain unchanged. Acceptance requires
|
|
529
|
+
focused native geometry/progress/press checks on iOS and Android, including
|
|
530
|
+
two or three segments, dynamic labels/items, RTL, narrow widths, and variant
|
|
531
|
+
switching. Type forwarding alone does not prove rendered behavior.
|
|
532
|
+
|
|
455
533
|
## Native tab-press animation default
|
|
456
534
|
|
|
457
535
|
Behavior change: previously an omitted `nativeTabPressAnimationEnabled` animated
|
|
@@ -472,6 +550,28 @@ Status: implemented; focused native-wrapper tests cover both directions,
|
|
|
472
550
|
explicit overrides and in-flight/superseded command targets. Home applies this policy to its JS tab bar, with iOS and
|
|
473
551
|
Android simulator recordings verifying distant jumps and adjacent transitions.
|
|
474
552
|
|
|
553
|
+
## Native tab trailing control reservation
|
|
554
|
+
|
|
555
|
+
Status: implemented locally, unreleased. The focused Android host test passed
|
|
556
|
+
(1/1); iOS hit tests had 8 passes and 1 existing skip. iOS companion-app UI
|
|
557
|
+
acceptance remains unverified.
|
|
558
|
+
`nativeTabBar.style.trailingReservedWidth` is an optional logical trailing
|
|
559
|
+
reservation for a sibling React control in the full-width sticky header. It
|
|
560
|
+
defaults to zero. JavaScript normalizes non-finite or negative input to zero;
|
|
561
|
+
native also clamps the reservation to the actual tab viewport width. On Android
|
|
562
|
+
and iOS, only the native tab viewport shrinks from `W` to `W - R`: its frame
|
|
563
|
+
begins at zero in LTR and at `R` in RTL. The sticky React header, its height,
|
|
564
|
+
and page content keep their original full-width geometry. Touches in the
|
|
565
|
+
reserved region reach the sibling control, while native tab hit testing and
|
|
566
|
+
last-tab visibility use the reduced viewport. Changing the reservation updates
|
|
567
|
+
that viewport; removing it or recycling the pager restores the default zero.
|
|
568
|
+
Web does not render the native tab and ignores this style field. For a positive
|
|
569
|
+
reservation, the box-none sticky React control draws above the native tab so
|
|
570
|
+
its inward fade and toolbar hit area remain visible; zero preserves the old
|
|
571
|
+
native-tab foreground order. Acceptance requires focused native layout and hit
|
|
572
|
+
testing for zero, nonzero, reset, and RTL reservations; type forwarding alone
|
|
573
|
+
is insufficient.
|
|
574
|
+
|
|
475
575
|
## Android refresh foreground coordination
|
|
476
576
|
|
|
477
577
|
Status: implemented; Android Home simulator placement verified on 2026-09-28.
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
// OneKey patch: deduplicate structural mutation parents before scanning the page.
|
|
20
20
|
#include <unordered_set>
|
|
21
|
+
#include <cmath>
|
|
21
22
|
|
|
22
23
|
using namespace facebook::react;
|
|
23
24
|
|
|
@@ -382,7 +383,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
382
383
|
@property (nonatomic, copy) RNCCollapsiblePagerNativeTabPressHandler onTabPress;
|
|
383
384
|
- (void)updateItemsJSON:(NSString *)itemsJSON;
|
|
384
385
|
- (void)updateStyleWithHeight:(CGFloat)height
|
|
386
|
+
variant:(NSString *)variant
|
|
385
387
|
contentPaddingHorizontal:(CGFloat)contentPaddingHorizontal
|
|
388
|
+
contentPaddingVertical:(CGFloat)contentPaddingVertical
|
|
386
389
|
itemSpacing:(CGFloat)itemSpacing
|
|
387
390
|
fontSize:(CGFloat)fontSize
|
|
388
391
|
fontFamily:(NSString *)fontFamily
|
|
@@ -399,12 +402,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
399
402
|
|
|
400
403
|
@implementation RNCCollapsiblePagerNativeTabBarView {
|
|
401
404
|
RNCCollapsiblePagerHorizontalScrollView *_scrollView;
|
|
405
|
+
UIView *_trackView;
|
|
402
406
|
UIView *_indicatorView;
|
|
403
407
|
NSArray<NSDictionary *> *_items;
|
|
404
408
|
NSMutableArray<UIButton *> *_buttons;
|
|
405
409
|
NSMutableArray<NSValue *> *_indicatorFrames;
|
|
406
410
|
CGFloat _barHeight;
|
|
407
411
|
CGFloat _contentPaddingHorizontal;
|
|
412
|
+
CGFloat _contentPaddingVertical;
|
|
413
|
+
BOOL _segmented;
|
|
408
414
|
CGFloat _itemSpacing;
|
|
409
415
|
CGFloat _fontSize;
|
|
410
416
|
NSString *_fontFamily;
|
|
@@ -427,6 +433,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
427
433
|
_indicatorFrames = [NSMutableArray new];
|
|
428
434
|
_barHeight = 44;
|
|
429
435
|
_contentPaddingHorizontal = 20;
|
|
436
|
+
_contentPaddingVertical = 0;
|
|
437
|
+
_segmented = NO;
|
|
430
438
|
_itemSpacing = 8;
|
|
431
439
|
_fontSize = 16;
|
|
432
440
|
_activeTextColor = UIColor.labelColor;
|
|
@@ -448,6 +456,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
448
456
|
_scrollView.delegate = self;
|
|
449
457
|
[self addSubview:_scrollView];
|
|
450
458
|
|
|
459
|
+
_trackView = [UIView new];
|
|
460
|
+
_trackView.userInteractionEnabled = NO;
|
|
461
|
+
[_scrollView addSubview:_trackView];
|
|
462
|
+
|
|
451
463
|
_indicatorView = [UIView new];
|
|
452
464
|
_indicatorView.userInteractionEnabled = NO;
|
|
453
465
|
[_scrollView addSubview:_indicatorView];
|
|
@@ -502,7 +514,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
502
514
|
}
|
|
503
515
|
|
|
504
516
|
- (void)updateStyleWithHeight:(CGFloat)height
|
|
517
|
+
variant:(NSString *)variant
|
|
505
518
|
contentPaddingHorizontal:(CGFloat)contentPaddingHorizontal
|
|
519
|
+
contentPaddingVertical:(CGFloat)contentPaddingVertical
|
|
506
520
|
itemSpacing:(CGFloat)itemSpacing
|
|
507
521
|
fontSize:(CGFloat)fontSize
|
|
508
522
|
fontFamily:(NSString *)fontFamily
|
|
@@ -514,19 +528,26 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
514
528
|
indicatorBottom:(CGFloat)indicatorBottom
|
|
515
529
|
{
|
|
516
530
|
_barHeight = MAX(1, height);
|
|
531
|
+
BOOL wasSegmented = _segmented;
|
|
532
|
+
_segmented = [variant isEqualToString:@"segmented"];
|
|
517
533
|
_contentPaddingHorizontal = MAX(0, contentPaddingHorizontal);
|
|
534
|
+
_contentPaddingVertical = MAX(0, contentPaddingVertical);
|
|
518
535
|
_itemSpacing = MAX(0, itemSpacing);
|
|
519
536
|
_fontSize = MAX(1, fontSize);
|
|
520
537
|
_fontFamily = [fontFamily copy];
|
|
521
|
-
self.backgroundColor = backgroundColor ?: UIColor.clearColor;
|
|
538
|
+
self.backgroundColor = _segmented ? UIColor.clearColor : (backgroundColor ?: UIColor.clearColor);
|
|
522
539
|
_scrollView.backgroundColor = self.backgroundColor;
|
|
540
|
+
_scrollView.scrollEnabled = !_segmented;
|
|
541
|
+
if (_segmented && !wasSegmented) [_scrollView setContentOffset:CGPointZero animated:NO];
|
|
542
|
+
_trackView.hidden = !_segmented;
|
|
543
|
+
_trackView.backgroundColor = backgroundColor ?: UIColor.clearColor;
|
|
523
544
|
_activeTextColor = activeTextColor ?: UIColor.labelColor;
|
|
524
545
|
_inactiveTextColor = inactiveTextColor ?: UIColor.secondaryLabelColor;
|
|
525
546
|
_indicatorColor = indicatorColor ?: _activeTextColor;
|
|
526
547
|
_indicatorHeight = MAX(0, indicatorHeight);
|
|
527
548
|
_indicatorBottom = MAX(0, indicatorBottom);
|
|
528
549
|
_indicatorView.backgroundColor = _indicatorColor;
|
|
529
|
-
_indicatorView.layer.cornerRadius = _indicatorHeight / 2;
|
|
550
|
+
_indicatorView.layer.cornerRadius = _segmented ? 0 : _indicatorHeight / 2;
|
|
530
551
|
for (UIButton *button in _buttons) button.titleLabel.font = self.tabFont;
|
|
531
552
|
[self setNeedsLayout];
|
|
532
553
|
}
|
|
@@ -585,6 +606,33 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
585
606
|
{
|
|
586
607
|
[super layoutSubviews];
|
|
587
608
|
_scrollView.frame = self.bounds;
|
|
609
|
+
if (_segmented) {
|
|
610
|
+
CGFloat width = CGRectGetWidth(self.bounds);
|
|
611
|
+
CGFloat height = CGRectGetHeight(self.bounds);
|
|
612
|
+
CGFloat insetX = MIN(_contentPaddingHorizontal, width / 2);
|
|
613
|
+
CGFloat insetY = MIN(_contentPaddingVertical, height / 2);
|
|
614
|
+
CGRect trackFrame = CGRectMake(insetX, insetY,
|
|
615
|
+
MAX(0, width - 2 * insetX), MAX(0, height - 2 * insetY));
|
|
616
|
+
_trackView.frame = trackFrame;
|
|
617
|
+
_trackView.layer.cornerRadius = CGRectGetHeight(trackFrame) / 2;
|
|
618
|
+
_scrollView.contentSize = self.bounds.size;
|
|
619
|
+
[_indicatorFrames removeAllObjects];
|
|
620
|
+
NSUInteger count = _buttons.count;
|
|
621
|
+
[_buttons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger index, BOOL *stop) {
|
|
622
|
+
CGFloat first = CGRectGetMinX(trackFrame) + CGRectGetWidth(trackFrame) * index / count;
|
|
623
|
+
CGFloat last = CGRectGetMinX(trackFrame) + CGRectGetWidth(trackFrame) * (index + 1) / count;
|
|
624
|
+
CGFloat x = self->_leftToRight ? first : CGRectGetMaxX(trackFrame) - (last - CGRectGetMinX(trackFrame));
|
|
625
|
+
CGFloat end = self->_leftToRight ? last : CGRectGetMaxX(trackFrame) - (first - CGRectGetMinX(trackFrame));
|
|
626
|
+
button.frame = CGRectMake(x, CGRectGetMinY(trackFrame), MAX(0, end - x), CGRectGetHeight(trackFrame));
|
|
627
|
+
CGRect thumbFrame = CGRectMake(x + 2, CGRectGetMinY(trackFrame) + 2,
|
|
628
|
+
MAX(0, CGRectGetWidth(button.frame) - 4), MAX(0, CGRectGetHeight(trackFrame) - 4));
|
|
629
|
+
[self->_indicatorFrames addObject:[NSValue valueWithCGRect:thumbFrame]];
|
|
630
|
+
}];
|
|
631
|
+
_indicatorView.layer.cornerRadius = MAX(0, CGRectGetHeight(trackFrame) - 4) / 2;
|
|
632
|
+
[self updatePresentation];
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
_trackView.frame = CGRectZero;
|
|
588
636
|
UIFont *font = self.tabFont;
|
|
589
637
|
NSMutableArray<NSNumber *> *widths = [NSMutableArray arrayWithCapacity:_buttons.count];
|
|
590
638
|
CGFloat itemsWidth = 0;
|
|
@@ -648,10 +696,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
648
696
|
CGRectGetMinX(fromFrame) + (CGRectGetMinX(toFrame) - CGRectGetMinX(fromFrame)) * fraction,
|
|
649
697
|
CGRectGetMinY(fromFrame),
|
|
650
698
|
CGRectGetWidth(fromFrame) + (CGRectGetWidth(toFrame) - CGRectGetWidth(fromFrame)) * fraction,
|
|
651
|
-
_indicatorHeight
|
|
699
|
+
_segmented ? CGRectGetHeight(fromFrame) : _indicatorHeight
|
|
652
700
|
);
|
|
653
701
|
_indicatorView.frame = indicatorFrame;
|
|
654
|
-
_indicatorView.hidden =
|
|
702
|
+
_indicatorView.hidden = CGRectGetHeight(indicatorFrame) <= 0;
|
|
655
703
|
|
|
656
704
|
NSInteger selectedIndex = (NSInteger)round(progress);
|
|
657
705
|
[_buttons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger index, BOOL *stop) {
|
|
@@ -668,7 +716,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
668
716
|
: UIAccessibilityTraitButton;
|
|
669
717
|
}];
|
|
670
718
|
|
|
671
|
-
if (_shouldCenterSelectedItem && !_scrollView.tracking && !_scrollView.dragging &&
|
|
719
|
+
if (_shouldCenterSelectedItem && !_segmented && !_scrollView.tracking && !_scrollView.dragging &&
|
|
672
720
|
!_scrollView.decelerating && CGRectGetWidth(_scrollView.bounds) > 0) {
|
|
673
721
|
CGFloat maximumOffset = MAX(0, _scrollView.contentSize.width - CGRectGetWidth(_scrollView.bounds));
|
|
674
722
|
CGFloat desiredOffset = RNCClamp(
|
|
@@ -1485,6 +1533,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1485
1533
|
- (void)liftSharedHeadersForPagerTransition:(NSString *)reason;
|
|
1486
1534
|
- (void)restoreSharedHeadersToContainer;
|
|
1487
1535
|
- (void)layoutSharedHeaders;
|
|
1536
|
+
- (void)bringReservedStickyControlToFront;
|
|
1488
1537
|
- (void)restorePinnedStickyHeader;
|
|
1489
1538
|
- (void)updatePinnedStickyHeader;
|
|
1490
1539
|
- (UIScrollView *)currentVerticalScrollViewForParentPager;
|
|
@@ -1575,6 +1624,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
1575
1624
|
NSUInteger _pendingTopInsetReboundGeneration;
|
|
1576
1625
|
NSTimer *_topInsetReboundTimer;
|
|
1577
1626
|
CGFloat _nativeTabBarHeight;
|
|
1627
|
+
CGFloat _nativeTabBarTrailingReservedWidth;
|
|
1578
1628
|
CGFloat _nativeSubHeaderHeight;
|
|
1579
1629
|
CGFloat _headerOffset;
|
|
1580
1630
|
BOOL _scrollEnabled;
|
|
@@ -2501,6 +2551,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2501
2551
|
}
|
|
2502
2552
|
[_nativeTabBarView updateAncestorGesturePrecedence];
|
|
2503
2553
|
[_nativeSubHeaderView updateAncestorGesturePrecedence];
|
|
2554
|
+
[self bringReservedStickyControlToFront];
|
|
2504
2555
|
}
|
|
2505
2556
|
|
|
2506
2557
|
- (void)restoreSharedHeadersToContainer
|
|
@@ -2606,10 +2657,11 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2606
2657
|
CGFloat nativeTabBarVisibleHeight = _nativeTabBarView.hidden
|
|
2607
2658
|
? 0
|
|
2608
2659
|
: MIN(_stickyHeaderHeight, _nativeTabBarHeight);
|
|
2660
|
+
CGFloat tabReservedWidth = MIN(width, MAX(0, _nativeTabBarTrailingReservedWidth));
|
|
2609
2661
|
_nativeTabBarView.frame = CGRectMake(
|
|
2610
|
-
0,
|
|
2662
|
+
self.isLtrLayout ? 0 : tabReservedWidth,
|
|
2611
2663
|
stickyChildY,
|
|
2612
|
-
width,
|
|
2664
|
+
width - tabReservedWidth,
|
|
2613
2665
|
nativeTabBarVisibleHeight
|
|
2614
2666
|
);
|
|
2615
2667
|
_nativeSubHeaderView.frame = CGRectMake(
|
|
@@ -2623,9 +2675,26 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2623
2675
|
);
|
|
2624
2676
|
_topOverlayHostView.frame = CGRectMake(0, 0, width, self.effectiveTopOverlayHeight);
|
|
2625
2677
|
_topOverlayView.frame = _topOverlayHostView.bounds;
|
|
2678
|
+
[self bringReservedStickyControlToFront];
|
|
2626
2679
|
[self applyHeaderOffset];
|
|
2627
2680
|
}
|
|
2628
2681
|
|
|
2682
|
+
- (void)bringReservedStickyControlToFront
|
|
2683
|
+
{
|
|
2684
|
+
if (_stickyHeaderView != nil &&
|
|
2685
|
+
_stickyHeaderView.superview == _nativeTabBarView.superview) {
|
|
2686
|
+
if (_nativeTabBarTrailingReservedWidth > 0) {
|
|
2687
|
+
[_stickyHeaderView.superview bringSubviewToFront:_stickyHeaderView];
|
|
2688
|
+
} else {
|
|
2689
|
+
[_nativeTabBarView.superview bringSubviewToFront:_nativeTabBarView];
|
|
2690
|
+
if (!_nativeSubHeaderView.hidden &&
|
|
2691
|
+
_nativeSubHeaderView.superview == _nativeTabBarView.superview) {
|
|
2692
|
+
[_nativeSubHeaderView.superview bringSubviewToFront:_nativeSubHeaderView];
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2629
2698
|
- (void)updatePinnedStickyHeader
|
|
2630
2699
|
{
|
|
2631
2700
|
BOOL attachedToList = _nativeSmoothHeaderScrollEnabled &&
|
|
@@ -2672,8 +2741,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2672
2741
|
CGFloat childY = shouldPin ? 0 : _headerHeight;
|
|
2673
2742
|
CGFloat width = CGRectGetWidth(self.bounds);
|
|
2674
2743
|
CGFloat tabHeight = _nativeTabBarView.hidden ? 0 : MIN(_stickyHeaderHeight, _nativeTabBarHeight);
|
|
2744
|
+
CGFloat tabReservedWidth = MIN(width, MAX(0, _nativeTabBarTrailingReservedWidth));
|
|
2675
2745
|
_stickyHeaderView.frame = CGRectMake(0, childY, width, _stickyHeaderHeight);
|
|
2676
|
-
_nativeTabBarView.frame = CGRectMake(0
|
|
2746
|
+
_nativeTabBarView.frame = CGRectMake(self.isLtrLayout ? 0 : tabReservedWidth,
|
|
2747
|
+
childY, width - tabReservedWidth, tabHeight);
|
|
2677
2748
|
_nativeSubHeaderView.frame = CGRectMake(0, childY + tabHeight, width,
|
|
2678
2749
|
MIN(MAX(0, _stickyHeaderHeight - tabHeight), _nativeSubHeaderHeight));
|
|
2679
2750
|
_pinnedStickyHostView.hidden = !shouldPin;
|
|
@@ -2693,6 +2764,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
2693
2764
|
}
|
|
2694
2765
|
[_nativeTabBarView updateAncestorGesturePrecedence];
|
|
2695
2766
|
[_nativeSubHeaderView updateAncestorGesturePrecedence];
|
|
2767
|
+
[self bringReservedStickyControlToFront];
|
|
2696
2768
|
[self updateTopScrollEdgeForScrollView:_observedScrollView];
|
|
2697
2769
|
[self logHomeVisualGeometry:shouldPin ? @"pin" : @"unpin"
|
|
2698
2770
|
scrollView:_observedScrollView];
|
|
@@ -3088,6 +3160,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3088
3160
|
if (_stickyHeaderView != nil) [_sharedHeaderHostView bringSubviewToFront:_stickyHeaderView];
|
|
3089
3161
|
if (!_nativeTabBarView.hidden) [_sharedHeaderHostView bringSubviewToFront:_nativeTabBarView];
|
|
3090
3162
|
if (!_nativeSubHeaderView.hidden) [_sharedHeaderHostView bringSubviewToFront:_nativeSubHeaderView];
|
|
3163
|
+
[self bringReservedStickyControlToFront];
|
|
3091
3164
|
[_containerView bringSubviewToFront:_sharedHeaderHostView];
|
|
3092
3165
|
if (_stickyHeaderIsPinned) {
|
|
3093
3166
|
[_pinnedStickyHostView.superview bringSubviewToFront:_pinnedStickyHostView];
|
|
@@ -3292,6 +3365,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3292
3365
|
_homeVisualLastLayoutLog = 0;
|
|
3293
3366
|
_homeVisualLastGeometrySignature = nil;
|
|
3294
3367
|
_nativeTabBarHeight = 44;
|
|
3368
|
+
_nativeTabBarTrailingReservedWidth = 0;
|
|
3295
3369
|
_nativeSubHeaderHeight = 74;
|
|
3296
3370
|
_headerOffset = 0;
|
|
3297
3371
|
_currentLogicalOffset = 0;
|
|
@@ -3373,6 +3447,13 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3373
3447
|
_layoutDirection = RCTNSStringFromString(toString(newViewProps.layoutDirection));
|
|
3374
3448
|
[_nativeTabBarView setLeftToRight:self.isLtrLayout];
|
|
3375
3449
|
[_nativeSubHeaderView setLeftToRight:self.isLtrLayout];
|
|
3450
|
+
[self setNeedsLayout];
|
|
3451
|
+
}
|
|
3452
|
+
if (_needsPropsReapply ||
|
|
3453
|
+
oldViewProps.nativeTabBarTrailingReservedWidth != newViewProps.nativeTabBarTrailingReservedWidth) {
|
|
3454
|
+
double reserved = newViewProps.nativeTabBarTrailingReservedWidth;
|
|
3455
|
+
_nativeTabBarTrailingReservedWidth = std::isfinite(reserved) && reserved >= 0 ? reserved : 0;
|
|
3456
|
+
[self setNeedsLayout];
|
|
3376
3457
|
}
|
|
3377
3458
|
BOOL insetsChanged = NO;
|
|
3378
3459
|
if (_needsPropsReapply || oldViewProps.headerHeight != newViewProps.headerHeight) {
|
|
@@ -3453,7 +3534,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3453
3534
|
}
|
|
3454
3535
|
if (_needsPropsReapply ||
|
|
3455
3536
|
oldViewProps.nativeTabBarHeight != newViewProps.nativeTabBarHeight ||
|
|
3537
|
+
oldViewProps.nativeTabBarVariant != newViewProps.nativeTabBarVariant ||
|
|
3456
3538
|
oldViewProps.nativeTabBarContentPaddingHorizontal != newViewProps.nativeTabBarContentPaddingHorizontal ||
|
|
3539
|
+
oldViewProps.nativeTabBarContentPaddingVertical != newViewProps.nativeTabBarContentPaddingVertical ||
|
|
3457
3540
|
oldViewProps.nativeTabBarItemSpacing != newViewProps.nativeTabBarItemSpacing ||
|
|
3458
3541
|
oldViewProps.nativeTabBarFontSize != newViewProps.nativeTabBarFontSize ||
|
|
3459
3542
|
oldViewProps.nativeTabBarFontFamily != newViewProps.nativeTabBarFontFamily ||
|
|
@@ -3470,7 +3553,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3470
3553
|
NSString *fontFamily = RCTNSStringFromString(newViewProps.nativeTabBarFontFamily);
|
|
3471
3554
|
[_nativeTabBarView
|
|
3472
3555
|
updateStyleWithHeight:_nativeTabBarHeight
|
|
3556
|
+
variant:RCTNSStringFromString(newViewProps.nativeTabBarVariant)
|
|
3473
3557
|
contentPaddingHorizontal:MAX(0, newViewProps.nativeTabBarContentPaddingHorizontal)
|
|
3558
|
+
contentPaddingVertical:MAX(0, newViewProps.nativeTabBarContentPaddingVertical)
|
|
3474
3559
|
itemSpacing:MAX(0, newViewProps.nativeTabBarItemSpacing)
|
|
3475
3560
|
fontSize:newViewProps.nativeTabBarFontSize > 0 ? newViewProps.nativeTabBarFontSize : 16
|
|
3476
3561
|
fontFamily:fontFamily
|
|
@@ -3504,6 +3589,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
3504
3589
|
if (!_nativeSubHeaderView.hidden) {
|
|
3505
3590
|
[_nativeSubHeaderView.superview bringSubviewToFront:_nativeSubHeaderView];
|
|
3506
3591
|
}
|
|
3592
|
+
[self bringReservedStickyControlToFront];
|
|
3507
3593
|
[_sharedHeaderHostView.superview bringSubviewToFront:_sharedHeaderHostView];
|
|
3508
3594
|
if (_stickyHeaderIsPinned) {
|
|
3509
3595
|
[_pinnedStickyHostView.superview bringSubviewToFront:_pinnedStickyHostView];
|
|
@@ -4581,8 +4667,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
4581
4667
|
|
|
4582
4668
|
- (void)updateNativeScrollerViewport:(UIScrollView *)scrollView stickyHeight:(CGFloat)height
|
|
4583
4669
|
{
|
|
4584
|
-
|
|
4585
|
-
|
|
4670
|
+
id host = scrollView.superview;
|
|
4671
|
+
if ([host respondsToSelector:@selector(setPagerStickyHeight:forOwner:)]) {
|
|
4672
|
+
[host setPagerStickyHeight:height forOwner:self];
|
|
4586
4673
|
}
|
|
4587
4674
|
}
|
|
4588
4675
|
|
|
@@ -5436,6 +5523,13 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
5436
5523
|
{
|
|
5437
5524
|
UIScrollView *scrollView = _pagerTopScrollView;
|
|
5438
5525
|
if (_dispatchingPagerTop || ![self matchesPagerTop:scrollView]) return;
|
|
5526
|
+
CGFloat clampedTarget = MIN(MAX(_pagerTopLogicalTarget, 0), _headerHeight);
|
|
5527
|
+
if (clampedTarget != _pagerTopLogicalTarget) {
|
|
5528
|
+
_pagerTopLogicalTarget = clampedTarget;
|
|
5529
|
+
CGFloat physicalTarget = RNCLimitRestoredScrollOffset(clampedTarget - scrollView.contentInset.top,
|
|
5530
|
+
scrollView.contentSize.height, scrollView.bounds.size.height, scrollView.contentInset.top, scrollView.contentInset.bottom);
|
|
5531
|
+
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, physicalTarget) animated:NO];
|
|
5532
|
+
}
|
|
5439
5533
|
CGFloat logical = scrollView.contentOffset.y + scrollView.contentInset.top;
|
|
5440
5534
|
if (fabs(logical - _pagerTopLogicalTarget) > 0.5) return;
|
|
5441
5535
|
_pageOffsets[_pagerTopPageKey] = @(_nativeSmoothHeaderScrollEnabled ? 0 : _pagerTopLogicalTarget);
|
|
@@ -5447,11 +5541,29 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
5447
5541
|
}
|
|
5448
5542
|
|
|
5449
5543
|
- (void)scrollToTop:(NSInteger)target animated:(BOOL)animated
|
|
5544
|
+
{
|
|
5545
|
+
if (target != 0 && target != 1) return;
|
|
5546
|
+
[self scrollHeaderToOffset:(target == 1 ? _headerHeight : 0) animated:animated];
|
|
5547
|
+
}
|
|
5548
|
+
|
|
5549
|
+
- (void)scrollHeaderBy:(double)delta
|
|
5550
|
+
{
|
|
5551
|
+
if (!std::isfinite(delta)) return;
|
|
5552
|
+
[self scrollHeaderToOffset:MIN(MAX(_headerOffset + delta, 0), _headerHeight) animated:NO];
|
|
5553
|
+
}
|
|
5554
|
+
|
|
5555
|
+
- (void)scrollHeaderToOffset:(double)offset
|
|
5556
|
+
{
|
|
5557
|
+
if (!std::isfinite(offset)) return;
|
|
5558
|
+
[self scrollHeaderToOffset:MIN(MAX(offset, 0), _headerHeight) animated:NO];
|
|
5559
|
+
}
|
|
5560
|
+
|
|
5561
|
+
- (void)scrollHeaderToOffset:(CGFloat)logicalTarget animated:(BOOL)animated
|
|
5450
5562
|
{
|
|
5451
5563
|
[self cancelTopInsetReboundCheck];
|
|
5452
5564
|
UIScrollView *scrollView = _observedScrollView;
|
|
5453
5565
|
NSString *key = _observedPageKey;
|
|
5454
|
-
if (
|
|
5566
|
+
if (_isBeingRecycled || _transitioning || _isPagerDragging ||
|
|
5455
5567
|
_transitionPageOffsets != nil || _syntheticPageRestore || scrollView == nil || scrollView.window == nil ||
|
|
5456
5568
|
key == nil || ![key isEqualToString:self.currentPageKey] || _observedPageIndex != _currentIndex ||
|
|
5457
5569
|
_observedPageGeneration != _generation || self.window == nil) return;
|
|
@@ -5467,7 +5579,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
|
|
|
5467
5579
|
[_alignedBodyKeys removeObject:key];
|
|
5468
5580
|
[_suspendedBodyKeys removeObject:key];
|
|
5469
5581
|
[_displayBodyOverrides removeObjectForKey:key];
|
|
5470
|
-
|
|
5582
|
+
logicalTarget = MIN(MAX(logicalTarget, 0), _headerHeight);
|
|
5471
5583
|
_pageOffsets[key] = @(_nativeSmoothHeaderScrollEnabled ? 0 : logicalTarget);
|
|
5472
5584
|
_pagerTopScrollView = scrollView;
|
|
5473
5585
|
_pagerTopPageKey = [key copy];
|