@swmansion/react-native-bottom-sheet 0.10.0-next.1 → 0.10.0-next.3
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.
|
@@ -62,6 +62,7 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
62
62
|
private var pendingIndex: Int? = null
|
|
63
63
|
private var hasLaidOut = false
|
|
64
64
|
private var isPanning = false
|
|
65
|
+
private var panStartingIndex: Int? = null
|
|
65
66
|
|
|
66
67
|
// MARK: - Internal
|
|
67
68
|
|
|
@@ -292,12 +293,15 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
292
293
|
return
|
|
293
294
|
}
|
|
294
295
|
|
|
296
|
+
val previousMaxHeight = detentSpecs.maxOfOrNull { it.height } ?: resolvedMaxDetentHeight()
|
|
295
297
|
detentSpecs = resolvedDetents
|
|
296
298
|
if (width > 0 && height > 0 && detentSpecs.isNotEmpty()) {
|
|
297
299
|
layoutSheetContainer(width, height)
|
|
298
300
|
|
|
299
301
|
if (hasLaidOut && !isPanning) {
|
|
300
302
|
targetIndex = targetIndex.coerceIn(0, detentSpecs.size - 1)
|
|
303
|
+
val newMaxHeight = detentSpecs.maxOfOrNull { it.height } ?: resolvedMaxDetentHeight()
|
|
304
|
+
val targetTy = translationY(targetIndex)
|
|
301
305
|
if (activeAnimation != null && isTargetingClosedDetent) {
|
|
302
306
|
suppressScrimForClosingTarget = true
|
|
303
307
|
hideScrim()
|
|
@@ -309,17 +313,26 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
309
313
|
activeAnimation = null
|
|
310
314
|
activeAnimationEmitsSettle = false
|
|
311
315
|
stopChoreographer()
|
|
312
|
-
|
|
313
|
-
|
|
316
|
+
// Re-anchor the in-flight position to the new container height so the
|
|
317
|
+
// sheet surface keeps the same on-screen height across the resize.
|
|
318
|
+
val visibleHeight = previousMaxHeight - currentTy
|
|
319
|
+
sheetContainer.translationY = (newMaxHeight - visibleHeight).coerceIn(0f, newMaxHeight)
|
|
314
320
|
emitPosition()
|
|
315
321
|
snapToIndex(targetIndex, 0f, emitIndexChange = false, emitSettle = shouldEmitSettle)
|
|
316
322
|
} else {
|
|
317
|
-
val
|
|
318
|
-
val
|
|
319
|
-
if (
|
|
323
|
+
val currentVisibleHeight = previousMaxHeight - sheetContainer.translationY
|
|
324
|
+
val targetHeight = detentSpecs.getOrNull(targetIndex)?.height ?: 0f
|
|
325
|
+
if (targetHeight <= currentVisibleHeight + 0.5f) {
|
|
326
|
+
// Content shrank (or is unchanged): snap immediately. Animating here
|
|
327
|
+
// would expose blank space below the shrunken content.
|
|
320
328
|
sheetContainer.translationY = targetTy
|
|
321
329
|
emitPosition()
|
|
322
330
|
} else {
|
|
331
|
+
// Content grew: re-anchor at the current visible height, then animate
|
|
332
|
+
// up to the taller detent.
|
|
333
|
+
sheetContainer.translationY =
|
|
334
|
+
(newMaxHeight - currentVisibleHeight).coerceIn(0f, newMaxHeight)
|
|
335
|
+
emitPosition()
|
|
323
336
|
snapToIndex(targetIndex, 0f, emitIndexChange = false, emitSettle = false)
|
|
324
337
|
}
|
|
325
338
|
}
|
|
@@ -381,20 +394,29 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
381
394
|
private val isTargetingClosedDetent: Boolean
|
|
382
395
|
get() = closedIndex?.let { targetIndex == it } == true
|
|
383
396
|
|
|
384
|
-
private
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
397
|
+
private fun snapCandidateIndices(includeIndex: Int? = null): List<Int> {
|
|
398
|
+
val indices = detentSpecs.indices.filter { !detentSpecs[it].programmatic }.toMutableList()
|
|
399
|
+
if (
|
|
400
|
+
includeIndex != null &&
|
|
401
|
+
includeIndex in detentSpecs.indices &&
|
|
402
|
+
detentSpecs[includeIndex].programmatic
|
|
403
|
+
) {
|
|
404
|
+
indices.add(includeIndex)
|
|
388
405
|
}
|
|
406
|
+
return indices.distinct().sortedBy { detentSpecs[it].height }
|
|
407
|
+
}
|
|
389
408
|
|
|
390
|
-
private
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
409
|
+
private fun draggableRange(includeIndex: Int? = null): ClosedFloatingPointRange<Float> {
|
|
410
|
+
val candidates = snapCandidateIndices(includeIndex)
|
|
411
|
+
if (candidates.isEmpty()) return 0f..0f
|
|
412
|
+
val translations = candidates.map(::translationY)
|
|
413
|
+
return (translations.minOrNull() ?: 0f)..(translations.maxOrNull() ?: 0f)
|
|
414
|
+
}
|
|
395
415
|
|
|
396
|
-
private
|
|
397
|
-
|
|
416
|
+
private fun isAtMaxDragCandidate(includeIndex: Int? = null): Boolean {
|
|
417
|
+
val range = draggableRange(includeIndex)
|
|
418
|
+
return sheetContainer.translationY <= range.start + 1f
|
|
419
|
+
}
|
|
398
420
|
|
|
399
421
|
private fun emitPosition() {
|
|
400
422
|
val maxHeight = detentSpecs.maxOfOrNull { it.height } ?: resolvedMaxDetentHeight()
|
|
@@ -498,24 +520,24 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
498
520
|
spring.start()
|
|
499
521
|
}
|
|
500
522
|
|
|
501
|
-
private fun bestSnapIndex(currentHeight: Float, velocity: Float): Int {
|
|
502
|
-
val
|
|
503
|
-
if (
|
|
523
|
+
private fun bestSnapIndex(currentHeight: Float, velocity: Float, includeIndex: Int? = null): Int {
|
|
524
|
+
val candidates = snapCandidateIndices(includeIndex)
|
|
525
|
+
if (candidates.isEmpty()) return targetIndex
|
|
504
526
|
|
|
505
527
|
val flickThreshold = 600f * density
|
|
506
528
|
|
|
507
529
|
if (velocity < -flickThreshold) {
|
|
508
|
-
return
|
|
509
|
-
?:
|
|
530
|
+
return candidates.firstOrNull { detentSpecs[it].height > currentHeight }
|
|
531
|
+
?: candidates.lastOrNull()
|
|
510
532
|
?: targetIndex
|
|
511
533
|
}
|
|
512
534
|
if (velocity > flickThreshold) {
|
|
513
|
-
return
|
|
514
|
-
?:
|
|
535
|
+
return candidates.lastOrNull { detentSpecs[it].height < currentHeight }
|
|
536
|
+
?: candidates.firstOrNull()
|
|
515
537
|
?: targetIndex
|
|
516
538
|
}
|
|
517
539
|
|
|
518
|
-
return
|
|
540
|
+
return candidates.minByOrNull { abs(detentSpecs[it].height - currentHeight) } ?: targetIndex
|
|
519
541
|
}
|
|
520
542
|
|
|
521
543
|
// MARK: - Touch handling
|
|
@@ -551,11 +573,12 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
551
573
|
val dx = x - initialTouchX
|
|
552
574
|
val dy = y - initialTouchY
|
|
553
575
|
|
|
554
|
-
|
|
576
|
+
val dragRange = draggableRange(targetIndex)
|
|
577
|
+
if (abs(dy) > touchSlop && abs(dy) > abs(dx) && dragRange.start < dragRange.endInclusive) {
|
|
555
578
|
if (disableScrollableNegotiation && findScrollableAtTouch() != null) {
|
|
556
579
|
return false
|
|
557
580
|
}
|
|
558
|
-
if (!
|
|
581
|
+
if (!isAtMaxDragCandidate(targetIndex)) {
|
|
559
582
|
lastTouchY = y
|
|
560
583
|
requestDisallowInterceptTouchEvent(false)
|
|
561
584
|
// Cancel in-flight JS touches. React Native's JSTouchDispatcher
|
|
@@ -628,7 +651,9 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
628
651
|
val dy = y - lastTouchY
|
|
629
652
|
lastTouchY = y
|
|
630
653
|
|
|
631
|
-
val
|
|
654
|
+
val dragRange = draggableRange(panStartingIndex)
|
|
655
|
+
val newTy =
|
|
656
|
+
(sheetContainer.translationY + dy).coerceIn(dragRange.start, dragRange.endInclusive)
|
|
632
657
|
sheetContainer.translationY = newTy
|
|
633
658
|
emitPosition()
|
|
634
659
|
return true
|
|
@@ -647,7 +672,8 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
647
672
|
velocityTracker = null
|
|
648
673
|
val maxHeight = detentSpecs.maxOfOrNull { it.height } ?: resolvedMaxDetentHeight()
|
|
649
674
|
val currentHeight = maxHeight - sheetContainer.translationY
|
|
650
|
-
val index = bestSnapIndex(currentHeight, velocity)
|
|
675
|
+
val index = bestSnapIndex(currentHeight, velocity, panStartingIndex)
|
|
676
|
+
panStartingIndex = null
|
|
651
677
|
snapToIndex(index, velocity)
|
|
652
678
|
return true
|
|
653
679
|
}
|
|
@@ -667,6 +693,7 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
667
693
|
|
|
668
694
|
private fun beginPan(event: MotionEvent) {
|
|
669
695
|
isPanning = true
|
|
696
|
+
panStartingIndex = targetIndex
|
|
670
697
|
activePointerId = event.getPointerId(0)
|
|
671
698
|
lastTouchY = event.y
|
|
672
699
|
velocityTracker?.recycle()
|
|
@@ -759,6 +786,7 @@ class BottomSheetView(context: Context) : ReactViewGroup(context) {
|
|
|
759
786
|
pendingIndex = null
|
|
760
787
|
hasLaidOut = false
|
|
761
788
|
isPanning = false
|
|
789
|
+
panStartingIndex = null
|
|
762
790
|
initialTouchY = 0f
|
|
763
791
|
initialTouchX = 0f
|
|
764
792
|
lastTouchY = 0f
|
|
@@ -59,8 +59,10 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
59
59
|
private var pendingIndex: Int?
|
|
60
60
|
private var hasLaidOut = false
|
|
61
61
|
private var isPanning = false
|
|
62
|
+
private var panStartingIndex: Int?
|
|
62
63
|
private var isContentInteractionDisabled = false
|
|
63
|
-
private
|
|
64
|
+
private var contentHeightMarker: UIView?
|
|
65
|
+
private static var markerObservationContext = 0
|
|
64
66
|
|
|
65
67
|
override public init(frame: CGRect) {
|
|
66
68
|
super.init(frame: frame)
|
|
@@ -236,8 +238,9 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
236
238
|
pendingIndex = nil
|
|
237
239
|
hasLaidOut = false
|
|
238
240
|
isPanning = false
|
|
241
|
+
panStartingIndex = nil
|
|
239
242
|
setContentInteractionEnabled(true)
|
|
240
|
-
|
|
243
|
+
stopObservingContentHeightMarker()
|
|
241
244
|
sheetContainer.transform = .identity
|
|
242
245
|
scrimView.alpha = 0
|
|
243
246
|
scrimView.isHidden = true
|
|
@@ -259,11 +262,27 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
259
262
|
return maxHeight - snapHeight
|
|
260
263
|
}
|
|
261
264
|
|
|
262
|
-
private
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
private func snapCandidateIndices(including index: Int? = nil) -> [Int] {
|
|
266
|
+
var indices = detentSpecs.indices.filter { !detentSpecs[$0].programmatic }
|
|
267
|
+
if
|
|
268
|
+
let index,
|
|
269
|
+
detentSpecs.indices.contains(index),
|
|
270
|
+
detentSpecs[index].programmatic
|
|
271
|
+
{
|
|
272
|
+
indices.append(index)
|
|
273
|
+
}
|
|
274
|
+
return Array(Set(indices)).sorted {
|
|
275
|
+
detentSpecs[$0].height < detentSpecs[$1].height
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
private func draggableRange(including index: Int? = nil) -> (minTy: CGFloat, maxTy: CGFloat) {
|
|
280
|
+
let candidates = snapCandidateIndices(including: index)
|
|
281
|
+
guard !candidates.isEmpty else { return (minTy: 0, maxTy: 0) }
|
|
282
|
+
return (
|
|
283
|
+
minTy: candidates.map { translationY(for: $0) }.min() ?? 0,
|
|
284
|
+
maxTy: candidates.map { translationY(for: $0) }.max() ?? 0
|
|
285
|
+
)
|
|
267
286
|
}
|
|
268
287
|
|
|
269
288
|
private var closedIndex: Int? {
|
|
@@ -392,6 +411,7 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
392
411
|
switch gesture.state {
|
|
393
412
|
case .began:
|
|
394
413
|
isPanning = true
|
|
414
|
+
panStartingIndex = targetIndex
|
|
395
415
|
sheetContainer.endEditing(true)
|
|
396
416
|
setContentInteractionEnabled(false)
|
|
397
417
|
if let handler = surfaceTouchHandler {
|
|
@@ -410,8 +430,9 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
410
430
|
case .changed:
|
|
411
431
|
let delta = gesture.translation(in: self).y
|
|
412
432
|
gesture.setTranslation(.zero, in: self)
|
|
413
|
-
let
|
|
414
|
-
let
|
|
433
|
+
let range = draggableRange(including: panStartingIndex)
|
|
434
|
+
let minTy = range.minTy
|
|
435
|
+
let maxTy = range.maxTy
|
|
415
436
|
let newTy = max(minTy, min(maxTy, sheetContainer.transform.ty + delta))
|
|
416
437
|
sheetContainer.transform = CGAffineTransform(translationX: 0, y: newTy)
|
|
417
438
|
emitPosition()
|
|
@@ -420,7 +441,8 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
420
441
|
isPanning = false
|
|
421
442
|
let velocity = gesture.velocity(in: self).y
|
|
422
443
|
let currentHeight = maxHeight - sheetContainer.transform.ty
|
|
423
|
-
let index = bestSnapIndex(for: currentHeight, velocity: velocity)
|
|
444
|
+
let index = bestSnapIndex(for: currentHeight, velocity: velocity, including: panStartingIndex)
|
|
445
|
+
panStartingIndex = nil
|
|
424
446
|
snapToIndex(index, velocity: velocity)
|
|
425
447
|
|
|
426
448
|
case .cancelled:
|
|
@@ -428,11 +450,17 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
428
450
|
setContentInteractionEnabled(true)
|
|
429
451
|
let cancelVelocity = gesture.velocity(in: self).y
|
|
430
452
|
let cancelHeight = maxHeight - sheetContainer.transform.ty
|
|
431
|
-
let cancelIndex = bestSnapIndex(
|
|
453
|
+
let cancelIndex = bestSnapIndex(
|
|
454
|
+
for: cancelHeight,
|
|
455
|
+
velocity: cancelVelocity,
|
|
456
|
+
including: panStartingIndex
|
|
457
|
+
)
|
|
458
|
+
panStartingIndex = nil
|
|
432
459
|
snapToIndex(cancelIndex, velocity: cancelVelocity)
|
|
433
460
|
|
|
434
461
|
case .failed:
|
|
435
462
|
isPanning = false
|
|
463
|
+
panStartingIndex = nil
|
|
436
464
|
setContentInteractionEnabled(true)
|
|
437
465
|
|
|
438
466
|
default:
|
|
@@ -440,24 +468,28 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
440
468
|
}
|
|
441
469
|
}
|
|
442
470
|
|
|
443
|
-
private func bestSnapIndex(
|
|
444
|
-
|
|
445
|
-
|
|
471
|
+
private func bestSnapIndex(
|
|
472
|
+
for height: CGFloat,
|
|
473
|
+
velocity: CGFloat,
|
|
474
|
+
including index: Int? = nil
|
|
475
|
+
) -> Int {
|
|
476
|
+
let candidates = snapCandidateIndices(including: index)
|
|
477
|
+
guard !candidates.isEmpty else { return targetIndex }
|
|
446
478
|
|
|
447
479
|
let flickThreshold: CGFloat = 600
|
|
448
480
|
|
|
449
481
|
if velocity < -flickThreshold {
|
|
450
|
-
return
|
|
451
|
-
??
|
|
482
|
+
return candidates.first(where: { detentSpecs[$0].height > height })
|
|
483
|
+
?? candidates.last ?? targetIndex
|
|
452
484
|
}
|
|
453
485
|
if velocity > flickThreshold {
|
|
454
|
-
return
|
|
455
|
-
??
|
|
486
|
+
return candidates.last(where: { detentSpecs[$0].height < height })
|
|
487
|
+
?? candidates.first ?? targetIndex
|
|
456
488
|
}
|
|
457
489
|
|
|
458
|
-
return
|
|
459
|
-
abs($0.
|
|
460
|
-
})
|
|
490
|
+
return candidates.min(by: {
|
|
491
|
+
abs(detentSpecs[$0].height - height) < abs(detentSpecs[$1].height - height)
|
|
492
|
+
}) ?? targetIndex
|
|
461
493
|
}
|
|
462
494
|
|
|
463
495
|
private func isVerticallyScrollable(_ scrollView: UIScrollView) -> Bool {
|
|
@@ -497,8 +529,8 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
497
529
|
let velocity = panGesture.velocity(in: self)
|
|
498
530
|
guard abs(velocity.y) > abs(velocity.x) else { return false }
|
|
499
531
|
|
|
500
|
-
let
|
|
501
|
-
guard
|
|
532
|
+
let candidates = snapCandidateIndices(including: targetIndex)
|
|
533
|
+
guard candidates.count > 1 else { return false }
|
|
502
534
|
|
|
503
535
|
if disableScrollableNegotiation {
|
|
504
536
|
let locationInContainer = panGesture.location(in: sheetContainer)
|
|
@@ -507,9 +539,9 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
507
539
|
}
|
|
508
540
|
}
|
|
509
541
|
|
|
510
|
-
let
|
|
542
|
+
let maxCandidateHeight = candidates.map { detentSpecs[$0].height }.max() ?? 0
|
|
511
543
|
// Below max: allow drag in either direction to reach other detents.
|
|
512
|
-
guard
|
|
544
|
+
guard currentSheetHeight >= maxCandidateHeight - 0.5 else { return true }
|
|
513
545
|
// At max: only allow downward drag, and only when the scroll view (if any)
|
|
514
546
|
// is at its top edge — otherwise the scroll view should handle the gesture.
|
|
515
547
|
if velocity.y < 0 {
|
|
@@ -576,6 +608,7 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
576
608
|
return
|
|
577
609
|
}
|
|
578
610
|
|
|
611
|
+
let previousMaxHeight = maximumResolvedDetentHeight ?? resolvedMaxDetentHeight
|
|
579
612
|
detentSpecs = resolvedDetents
|
|
580
613
|
|
|
581
614
|
guard bounds.width > 0, bounds.height > 0, !detentSpecs.isEmpty else {
|
|
@@ -584,6 +617,8 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
584
617
|
|
|
585
618
|
if hasLaidOut, !isPanning {
|
|
586
619
|
targetIndex = max(0, min(detentSpecs.count - 1, targetIndex))
|
|
620
|
+
let newMaxHeight = maximumResolvedDetentHeight ?? resolvedMaxDetentHeight
|
|
621
|
+
let targetTy = translationY(for: targetIndex)
|
|
587
622
|
|
|
588
623
|
if let animator = activeAnimator {
|
|
589
624
|
stopDisplayLink()
|
|
@@ -592,19 +627,27 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
592
627
|
animator.stopAnimation(true)
|
|
593
628
|
activeAnimator = nil
|
|
594
629
|
activeAnimatorEmitsSettle = false
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
)
|
|
630
|
+
// Re-anchor the in-flight position to the new container height so the
|
|
631
|
+
// sheet surface keeps the same on-screen height across the resize.
|
|
632
|
+
let visibleHeight = previousMaxHeight - visualTy
|
|
633
|
+
let reanchoredTy = min(max(newMaxHeight - visibleHeight, 0), newMaxHeight)
|
|
634
|
+
sheetContainer.transform = CGAffineTransform(translationX: 0, y: reanchoredTy)
|
|
599
635
|
emitPosition()
|
|
600
636
|
snapToIndex(targetIndex, velocity: 0, emitIndexChange: false, emitSettle: shouldEmitSettle)
|
|
601
637
|
} else {
|
|
602
|
-
let
|
|
603
|
-
let
|
|
604
|
-
if
|
|
638
|
+
let currentVisibleHeight = previousMaxHeight - currentTranslationY
|
|
639
|
+
let targetHeight = detent(at: targetIndex).height
|
|
640
|
+
if targetHeight <= currentVisibleHeight + 0.5 {
|
|
641
|
+
// Content shrank (or is unchanged): snap immediately. Animating here
|
|
642
|
+
// would expose blank space below the shrunken content.
|
|
605
643
|
sheetContainer.transform = CGAffineTransform(translationX: 0, y: targetTy)
|
|
606
644
|
emitPosition()
|
|
607
645
|
} else {
|
|
646
|
+
// Content grew: re-anchor at the current visible height, then animate
|
|
647
|
+
// up to the taller detent.
|
|
648
|
+
let startTy = min(max(newMaxHeight - currentVisibleHeight, 0), newMaxHeight)
|
|
649
|
+
sheetContainer.transform = CGAffineTransform(translationX: 0, y: startTy)
|
|
650
|
+
emitPosition()
|
|
608
651
|
snapToIndex(targetIndex, velocity: 0, emitIndexChange: false, emitSettle: false)
|
|
609
652
|
}
|
|
610
653
|
}
|
|
@@ -612,7 +655,50 @@ public final class RNSBottomSheetHostingView: UIView {
|
|
|
612
655
|
}
|
|
613
656
|
|
|
614
657
|
private func refreshContentHeightMarker() {
|
|
615
|
-
|
|
658
|
+
let marker = findContentHeightMarker()
|
|
659
|
+
guard marker !== contentHeightMarker else { return }
|
|
660
|
+
stopObservingContentHeightMarker()
|
|
661
|
+
contentHeightMarker = marker
|
|
662
|
+
if let marker {
|
|
663
|
+
// The marker's frame is updated by React Native when content above it
|
|
664
|
+
// resizes; observe its layer so we can re-resolve detents immediately
|
|
665
|
+
// instead of waiting for an unrelated layout pass. This is the iOS
|
|
666
|
+
// counterpart to Android's OnLayoutChangeListener on the marker.
|
|
667
|
+
marker.layer.addObserver(
|
|
668
|
+
self, forKeyPath: "position", options: [], context: &Self.markerObservationContext
|
|
669
|
+
)
|
|
670
|
+
marker.layer.addObserver(
|
|
671
|
+
self, forKeyPath: "bounds", options: [], context: &Self.markerObservationContext
|
|
672
|
+
)
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
private func stopObservingContentHeightMarker() {
|
|
677
|
+
guard let marker = contentHeightMarker else { return }
|
|
678
|
+
marker.layer.removeObserver(
|
|
679
|
+
self, forKeyPath: "position", context: &Self.markerObservationContext
|
|
680
|
+
)
|
|
681
|
+
marker.layer.removeObserver(
|
|
682
|
+
self, forKeyPath: "bounds", context: &Self.markerObservationContext
|
|
683
|
+
)
|
|
684
|
+
contentHeightMarker = nil
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
override public func observeValue(
|
|
688
|
+
forKeyPath keyPath: String?,
|
|
689
|
+
of object: Any?,
|
|
690
|
+
change: [NSKeyValueChangeKey: Any]?,
|
|
691
|
+
context: UnsafeMutableRawPointer?
|
|
692
|
+
) {
|
|
693
|
+
if context == &Self.markerObservationContext {
|
|
694
|
+
refreshDetentsFromLayout()
|
|
695
|
+
} else {
|
|
696
|
+
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
deinit {
|
|
701
|
+
stopObservingContentHeightMarker()
|
|
616
702
|
}
|
|
617
703
|
|
|
618
704
|
private func findContentHeightMarker() -> UIView? {
|
package/package.json
CHANGED