From e72bccde5cdeb0a1cf640b9e2c35663eb7bb120b Mon Sep 17 00:00:00 2001 From: Cameron Chisholm Date: Wed, 26 Feb 2025 18:43:01 +0000 Subject: [PATCH 1/5] feat: add dimmedAlpha prop on iOS --- ios/TrueSheetView.swift | 13 +++++++++++++ ios/TrueSheetViewController.swift | 6 ++++++ ios/TrueSheetViewManager.m | 1 + src/TrueSheet.tsx | 2 ++ src/TrueSheet.types.ts | 7 +++++++ 5 files changed, 29 insertions(+) diff --git a/ios/TrueSheetView.swift b/ios/TrueSheetView.swift index 45a39eb..7cfae90 100644 --- a/ios/TrueSheetView.swift +++ b/ios/TrueSheetView.swift @@ -25,6 +25,7 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { @objc var initialIndex: NSNumber = -1 @objc var initialIndexAnimated = true + @objc var dimmedAlpha: CGFloat = 0.75 // MARK: - Private properties @@ -202,6 +203,18 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { // Add constraints to fix weirdness and support ScrollView contentView.pinTo(view: containerView, constraints: nil) scrollView.pinTo(view: contentView, constraints: nil) + + if viewController.dimmed { + UIView.animate(withDuration: 0.3) { + self.viewController.presentingViewController?.view.alpha = self.dimmedAlpha + } + } + } + + func viewControllerWillDisappear() { + UIView.animate(withDuration: 0.3) { + self.viewController.presentingViewController?.view.alpha = 1 + } } func viewControllerDidDismiss() { diff --git a/ios/TrueSheetViewController.swift b/ios/TrueSheetViewController.swift index a116617..aea33e6 100644 --- a/ios/TrueSheetViewController.swift +++ b/ios/TrueSheetViewController.swift @@ -20,6 +20,7 @@ protocol TrueSheetViewControllerDelegate: AnyObject { func viewControllerDidDismiss() func viewControllerDidChangeSize(_ sizeInfo: SizeInfo?) func viewControllerWillAppear() + func viewControllerWillDisappear() func viewControllerKeyboardWillShow(_ keyboardHeight: CGFloat) func viewControllerKeyboardWillHide() func viewControllerDidDrag(_ state: UIPanGestureRecognizer.State, _ height: CGFloat) @@ -144,6 +145,11 @@ class TrueSheetViewController: UIViewController, UISheetPresentationControllerDe delegate?.viewControllerWillAppear() } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + delegate?.viewControllerWillDisappear() + } + override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) delegate?.viewControllerDidDismiss() diff --git a/ios/TrueSheetViewManager.m b/ios/TrueSheetViewManager.m index d849bb5..086573e 100644 --- a/ios/TrueSheetViewManager.m +++ b/ios/TrueSheetViewManager.m @@ -45,6 +45,7 @@ @interface RCT_EXTERN_REMAP_MODULE (TrueSheetView, TrueSheetViewManager, RCTView RCT_EXPORT_VIEW_PROPERTY(dimmedIndex, NSNumber) RCT_EXPORT_VIEW_PROPERTY(initialIndex, NSNumber) RCT_EXPORT_VIEW_PROPERTY(initialIndexAnimated, BOOL) +RCT_EXPORT_VIEW_PROPERTY(dimmedAlpha, CGFloat) // Internal properties RCT_EXPORT_VIEW_PROPERTY(contentHeight, NSNumber) diff --git a/src/TrueSheet.tsx b/src/TrueSheet.tsx index a9c0eea..bde6c36 100644 --- a/src/TrueSheet.tsx +++ b/src/TrueSheet.tsx @@ -251,6 +251,7 @@ export class TrueSheet extends PureComponent { keyboardMode = 'resize', initialIndex, dimmedIndex, + dimmedAlpha = 0.75, grabberProps, blurTint, cornerRadius, @@ -276,6 +277,7 @@ export class TrueSheet extends PureComponent { grabber={grabber} dimmed={dimmed} dimmedIndex={dimmedIndex} + dimmedAlpha={dimmedAlpha} edgeToEdge={edgeToEdge} initialIndex={initialIndex} initialIndexAnimated={initialIndexAnimated} diff --git a/src/TrueSheet.types.ts b/src/TrueSheet.types.ts index bef5541..9083ea0 100644 --- a/src/TrueSheet.types.ts +++ b/src/TrueSheet.types.ts @@ -166,6 +166,13 @@ export interface TrueSheetProps extends ViewProps { */ dimmedIndex?: number + /** + * The alpha value of the dimmed background. + * + * @default 0.75 + */ + dimmedAlpha?: number + /** * Prevents interactive dismissal of the Sheet. * From 9dcc5aee8251c0868f33291171de9b3627224221 Mon Sep 17 00:00:00 2001 From: Cameron Chisholm Date: Wed, 26 Feb 2025 20:20:35 +0000 Subject: [PATCH 2/5] feat: support stacked sheets --- ios/TrueSheetView.swift | 36 ++++++++++++++++++++++++++----- ios/TrueSheetViewController.swift | 6 ++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/ios/TrueSheetView.swift b/ios/TrueSheetView.swift index 7cfae90..e81c9f0 100644 --- a/ios/TrueSheetView.swift +++ b/ios/TrueSheetView.swift @@ -8,6 +8,11 @@ @objc(TrueSheetView) class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { + // MARK: - Static properties + + // Keep track of active sheet opacity values (stack) + private static var sheetOpacityStack: [CGFloat] = [] + // MARK: - React properties // MARK: - Events @@ -203,17 +208,38 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { // Add constraints to fix weirdness and support ScrollView contentView.pinTo(view: containerView, constraints: nil) scrollView.pinTo(view: contentView, constraints: nil) + } - if viewController.dimmed { - UIView.animate(withDuration: 0.3) { - self.viewController.presentingViewController?.view.alpha = self.dimmedAlpha + func viewControllerDidAppear() { + // Add this sheet's opacity to the stack + TrueSheetView.sheetOpacityStack.append(dimmedAlpha) + + // Dim root view controller + UIView.animate(withDuration: 0.3) { + if let rootViewController = UIApplication.shared.windows.first?.rootViewController { + rootViewController.view.alpha = self.dimmedAlpha } } } func viewControllerWillDisappear() { - UIView.animate(withDuration: 0.3) { - self.viewController.presentingViewController?.view.alpha = 1 + if viewController.isBeingDismissed { + // Remove the topmost opacity value (which should be this sheet's) + if !TrueSheetView.sheetOpacityStack.isEmpty { + TrueSheetView.sheetOpacityStack.removeLast() + } + + UIView.animate(withDuration: 0.3) { + if let rootViewController = UIApplication.shared.windows.first?.rootViewController { + // If there are still active sheets, apply the alpha of the topmost one + if let topmostOpacity = TrueSheetView.sheetOpacityStack.last { + rootViewController.view.alpha = topmostOpacity + } else { + // If no sheets are left, restore alpha to 1 + rootViewController.view.alpha = 1 + } + } + } } } diff --git a/ios/TrueSheetViewController.swift b/ios/TrueSheetViewController.swift index aea33e6..fbcda48 100644 --- a/ios/TrueSheetViewController.swift +++ b/ios/TrueSheetViewController.swift @@ -20,6 +20,7 @@ protocol TrueSheetViewControllerDelegate: AnyObject { func viewControllerDidDismiss() func viewControllerDidChangeSize(_ sizeInfo: SizeInfo?) func viewControllerWillAppear() + func viewControllerDidAppear() func viewControllerWillDisappear() func viewControllerKeyboardWillShow(_ keyboardHeight: CGFloat) func viewControllerKeyboardWillHide() @@ -145,6 +146,11 @@ class TrueSheetViewController: UIViewController, UISheetPresentationControllerDe delegate?.viewControllerWillAppear() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + delegate?.viewControllerDidAppear() + } + override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) delegate?.viewControllerWillDisappear() From b97570b072177fcb9be9f3c4b70629bd29f7a216 Mon Sep 17 00:00:00 2001 From: Cameron Chisholm Date: Wed, 26 Feb 2025 21:29:48 +0000 Subject: [PATCH 3/5] chore: respect dimmed prop --- ios/TrueSheetView.swift | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/ios/TrueSheetView.swift b/ios/TrueSheetView.swift index e81c9f0..a99cd92 100644 --- a/ios/TrueSheetView.swift +++ b/ios/TrueSheetView.swift @@ -211,19 +211,22 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { } func viewControllerDidAppear() { - // Add this sheet's opacity to the stack - TrueSheetView.sheetOpacityStack.append(dimmedAlpha) - - // Dim root view controller - UIView.animate(withDuration: 0.3) { - if let rootViewController = UIApplication.shared.windows.first?.rootViewController { - rootViewController.view.alpha = self.dimmedAlpha + // Only apply dimming if the sheet has dimmed property set to true + if viewController.dimmed { + // Add this sheet's opacity to the stack + TrueSheetView.sheetOpacityStack.append(dimmedAlpha) + + // Dim root view controller + UIView.animate(withDuration: 0.3) { + if let rootViewController = UIApplication.shared.windows.first?.rootViewController { + rootViewController.view.alpha = self.dimmedAlpha + } } } } func viewControllerWillDisappear() { - if viewController.isBeingDismissed { + if viewController.isBeingDismissed && viewController.dimmed { // Remove the topmost opacity value (which should be this sheet's) if !TrueSheetView.sheetOpacityStack.isEmpty { TrueSheetView.sheetOpacityStack.removeLast() From 7e36dfecf9126e7830b0bc4da61c2a9952b262e1 Mon Sep 17 00:00:00 2001 From: Cameron Chisholm Date: Wed, 5 Mar 2025 17:37:20 +0000 Subject: [PATCH 4/5] feat: add android support --- .../com/lodev09/truesheet/TrueSheetDialog.kt | 92 +++++++++++++++++-- .../com/lodev09/truesheet/TrueSheetView.kt | 10 +- .../lodev09/truesheet/TrueSheetViewManager.kt | 5 + ios/TrueSheetView.swift | 5 +- 4 files changed, 96 insertions(+), 16 deletions(-) diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt index f4b38fc..d92c354 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt @@ -2,11 +2,13 @@ package com.lodev09.truesheet import android.annotation.SuppressLint import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.shapes.RoundRectShape import android.view.View import android.view.ViewGroup import android.view.WindowManager +import android.widget.FrameLayout import com.facebook.react.uimanager.ThemedReactContext import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog @@ -23,6 +25,9 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val private var keyboardManager = KeyboardManager(reactContext) private var windowAnimation: Int = 0 + // Custom dimming overlay view + private var dimmingView: View? = null + // First child of the rootSheetView private val containerView: ViewGroup? get() = if (rootSheetView.childCount > 0) { @@ -39,6 +44,10 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val * Set to `false` to allow interaction with the background components. */ var dimmed = true + set(value) { + field = value + updateDimmingView() + } /** * The size index that the sheet should start to dim the background. @@ -46,6 +55,17 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val */ var dimmedIndex = 0 + /** + * The alpha value of the dimmed background. + * + * @default 0.75f + */ + var dimmedAlpha = 0.75f + set(value) { + field = value + updateDimmingView() + } + /** * The maximum window height */ @@ -93,12 +113,58 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val window?.apply { // Store current windowAnimation value to toggle later windowAnimation = attributes.windowAnimations + + // Disable default dimming + clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) } + // Create and add our custom dimming view + createDimmingView() + // Update the usable sheet height maxScreenHeight = Utils.screenHeight(reactContext, edgeToEdge) } + /** + * Create and add a custom dimming view to the dialog + */ + private fun createDimmingView() { + // Get the content view of the dialog + val decorView = window?.decorView as? ViewGroup + val contentView = decorView?.findViewById(android.R.id.content) + + // Create a new view for dimming + dimmingView = View(context).apply { + layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT + ) + background = ColorDrawable(Color.BLACK) + alpha = if (dimmed) dimmedAlpha else 0f + visibility = if (dimmed) View.VISIBLE else View.GONE + elevation = -1f // Make sure it's below other content + + // Add the view at index 0 so it's behind everything else + contentView?.addView(this, 0) + + // Make sure it's behind the sheet + bringToFront() + } + + // Make sure the sheet is on top of the dimming view + rootSheetView.bringToFront() + } + + /** + * Update the dimming view based on current settings + */ + private fun updateDimmingView() { + dimmingView?.apply { + alpha = if (dimmed) dimmedAlpha else 0f + visibility = if (dimmed) View.VISIBLE else View.GONE + } + } + override fun getEdgeToEdgeEnabled(): Boolean = edgeToEdge || super.getEdgeToEdgeEnabled() override fun onStart() { @@ -114,6 +180,9 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION } } + + // Make sure dimming is applied + updateDimmingView() } /** @@ -150,11 +219,9 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val // Remove touch listener view.setOnTouchListener(null) - // Add the dimmed background - setFlags( - WindowManager.LayoutParams.FLAG_DIM_BEHIND, - WindowManager.LayoutParams.FLAG_DIM_BEHIND - ) + // Show dimming view with the specified alpha + dimmingView?.visibility = View.VISIBLE + dimmingView?.alpha = dimmedAlpha setCanceledOnTouchOutside(dismissible) } else { @@ -165,8 +232,8 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val false } - // Remove the dimmed background - clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) + // Hide our custom dimming view + dimmingView?.visibility = View.GONE setCanceledOnTouchOutside(false) } @@ -183,11 +250,12 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val * Present the sheet. */ fun present(sizeIndex: Int, animated: Boolean = true) { - setupDimmedBackground(sizeIndex) if (isShowing) { + setupDimmedBackground(sizeIndex) setStateForSizeIndex(sizeIndex) } else { configure() + setupDimmedBackground(sizeIndex) setStateForSizeIndex(sizeIndex) if (!animated) { @@ -196,6 +264,14 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val } show() + + // Ensure dimming is applied after showing + if (dimmed && sizeIndex >= dimmedIndex) { + dimmingView?.visibility = View.VISIBLE + dimmingView?.alpha = dimmedAlpha + } else { + dimmingView?.visibility = View.GONE + } } } diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt index 5627a42..9b1f240 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt @@ -297,7 +297,6 @@ class TrueSheetView(context: Context) : } currentSizeIndex = it.index - sheetDialog.setupDimmedBackground(it.index) // Dispatch onSizeChange event dispatchEvent(TrueSheetEvent.SIZE_CHANGE, sizeInfoData(it)) @@ -347,9 +346,12 @@ class TrueSheetView(context: Context) : if (sheetDialog.dimmed == dimmed) return sheetDialog.dimmed = dimmed - if (sheetDialog.isShowing) { - sheetDialog.setupDimmedBackground(currentSizeIndex) - } + } + + fun setDimmedAlpha(alpha: Float) { + if (sheetDialog.dimmedAlpha == alpha) return + + sheetDialog.dimmedAlpha = alpha } fun setDimmedIndex(index: Int) { diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt index 5ece7e4..bd2b97f 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt @@ -54,6 +54,11 @@ class TrueSheetViewManager : ViewGroupManager() { view.setDimmed(dimmed) } + @ReactProp(name = "dimmedAlpha") + fun setDimmedAlpha(view: TrueSheetView, alpha: Float) { + view.setDimmedAlpha(alpha) + } + @ReactProp(name = "initialIndex") fun setInitialIndex(view: TrueSheetView, index: Int) { view.initialIndex = index diff --git a/ios/TrueSheetView.swift b/ios/TrueSheetView.swift index a99cd92..75cf108 100644 --- a/ios/TrueSheetView.swift +++ b/ios/TrueSheetView.swift @@ -213,10 +213,8 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { func viewControllerDidAppear() { // Only apply dimming if the sheet has dimmed property set to true if viewController.dimmed { - // Add this sheet's opacity to the stack + // Add this sheet's opacity to the stack and dim root view controller TrueSheetView.sheetOpacityStack.append(dimmedAlpha) - - // Dim root view controller UIView.animate(withDuration: 0.3) { if let rootViewController = UIApplication.shared.windows.first?.rootViewController { rootViewController.view.alpha = self.dimmedAlpha @@ -227,7 +225,6 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { func viewControllerWillDisappear() { if viewController.isBeingDismissed && viewController.dimmed { - // Remove the topmost opacity value (which should be this sheet's) if !TrueSheetView.sheetOpacityStack.isEmpty { TrueSheetView.sheetOpacityStack.removeLast() } From 6f6ffd8618f3cc0936e3c69c250aa65d1c68828d Mon Sep 17 00:00:00 2001 From: Cameron Chisholm Date: Thu, 27 Mar 2025 14:14:13 +0000 Subject: [PATCH 5/5] chore: use viewControllerWillAppear --- ios/TrueSheetView.swift | 23 +++++++++++------------ ios/TrueSheetViewController.swift | 6 ------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/ios/TrueSheetView.swift b/ios/TrueSheetView.swift index 75cf108..38c42ae 100644 --- a/ios/TrueSheetView.swift +++ b/ios/TrueSheetView.swift @@ -201,26 +201,25 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { } func viewControllerWillAppear() { - guard let contentView, let scrollView, let containerView else { - return - } - - // Add constraints to fix weirdness and support ScrollView - contentView.pinTo(view: containerView, constraints: nil) - scrollView.pinTo(view: contentView, constraints: nil) - } - - func viewControllerDidAppear() { // Only apply dimming if the sheet has dimmed property set to true if viewController.dimmed { + let opacity = 1 - dimmedAlpha // Add this sheet's opacity to the stack and dim root view controller - TrueSheetView.sheetOpacityStack.append(dimmedAlpha) + TrueSheetView.sheetOpacityStack.append(opacity) UIView.animate(withDuration: 0.3) { if let rootViewController = UIApplication.shared.windows.first?.rootViewController { - rootViewController.view.alpha = self.dimmedAlpha + rootViewController.view.alpha = opacity } } } + + guard let contentView, let scrollView, let containerView else { + return + } + + // Add constraints to fix weirdness and support ScrollView + contentView.pinTo(view: containerView, constraints: nil) + scrollView.pinTo(view: contentView, constraints: nil) } func viewControllerWillDisappear() { diff --git a/ios/TrueSheetViewController.swift b/ios/TrueSheetViewController.swift index fbcda48..aea33e6 100644 --- a/ios/TrueSheetViewController.swift +++ b/ios/TrueSheetViewController.swift @@ -20,7 +20,6 @@ protocol TrueSheetViewControllerDelegate: AnyObject { func viewControllerDidDismiss() func viewControllerDidChangeSize(_ sizeInfo: SizeInfo?) func viewControllerWillAppear() - func viewControllerDidAppear() func viewControllerWillDisappear() func viewControllerKeyboardWillShow(_ keyboardHeight: CGFloat) func viewControllerKeyboardWillHide() @@ -146,11 +145,6 @@ class TrueSheetViewController: UIViewController, UISheetPresentationControllerDe delegate?.viewControllerWillAppear() } - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - delegate?.viewControllerDidAppear() - } - override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) delegate?.viewControllerWillDisappear()