diff options
author | Hailey <me@haileyok.com> | 2024-10-04 13:24:12 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-04 13:24:12 -0700 |
commit | 00486e94991f344353ffb083dd631283a84c3ad3 (patch) | |
tree | a5dc4da5e5e71912d73a099e84761517fa8c62a9 /modules/bottom-sheet/ios/SheetView.swift | |
parent | 9802ebe20d32dc1867a069dc377b3d4c43ce45f0 (diff) | |
download | voidsky-00486e94991f344353ffb083dd631283a84c3ad3.tar.zst |
[Sheets] [Pt. 1] Root PR (#5557)
Co-authored-by: Samuel Newman <mozzius@protonmail.com> Co-authored-by: Eric Bailey <git@esb.lol> Co-authored-by: dan <dan.abramov@gmail.com> Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'modules/bottom-sheet/ios/SheetView.swift')
-rw-r--r-- | modules/bottom-sheet/ios/SheetView.swift | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/modules/bottom-sheet/ios/SheetView.swift b/modules/bottom-sheet/ios/SheetView.swift new file mode 100644 index 000000000..cf2019c6a --- /dev/null +++ b/modules/bottom-sheet/ios/SheetView.swift @@ -0,0 +1,189 @@ +import ExpoModulesCore +import UIKit + +class SheetView: ExpoView, UISheetPresentationControllerDelegate { + // Views + private var sheetVc: SheetViewController? + private var innerView: UIView? + private var touchHandler: RCTTouchHandler? + + // Events + private let onAttemptDismiss = EventDispatcher() + private let onSnapPointChange = EventDispatcher() + private let onStateChange = EventDispatcher() + + // Open event firing + private var isOpen: Bool = false { + didSet { + onStateChange([ + "state": isOpen ? "open" : "closed" + ]) + } + } + + // React view props + var preventDismiss = false + var preventExpansion = false + var cornerRadius: CGFloat? + var minHeight = 0.0 + var maxHeight: CGFloat! { + didSet { + let screenHeight = Util.getScreenHeight() ?? 0 + if maxHeight > screenHeight { + maxHeight = screenHeight + } + } + } + + private var isOpening = false { + didSet { + if isOpening { + onStateChange([ + "state": "opening" + ]) + } + } + } + private var isClosing = false { + didSet { + if isClosing { + onStateChange([ + "state": "closing" + ]) + } + } + } + private var selectedDetentIdentifier: UISheetPresentationController.Detent.Identifier? { + didSet { + if selectedDetentIdentifier == .large { + onSnapPointChange([ + "snapPoint": 2 + ]) + } else { + onSnapPointChange([ + "snapPoint": 1 + ]) + } + } + } + + // MARK: - Lifecycle + + required init (appContext: AppContext? = nil) { + super.init(appContext: appContext) + self.maxHeight = Util.getScreenHeight() + self.touchHandler = RCTTouchHandler(bridge: appContext?.reactBridge) + SheetManager.shared.add(self) + } + + deinit { + self.destroy() + } + + // We don't want this view to actually get added to the tree, so we'll simply store it for adding + // to the SheetViewController + override func insertReactSubview(_ subview: UIView!, at atIndex: Int) { + self.touchHandler?.attach(to: subview) + self.innerView = subview + } + + // We'll grab the content height from here so we know the initial detent to set + override func layoutSubviews() { + super.layoutSubviews() + + guard let innerView = self.innerView else { + return + } + + if innerView.subviews.count != 1 { + return + } + + self.present() + } + + private func destroy() { + self.isClosing = false + self.isOpen = false + self.sheetVc = nil + self.touchHandler?.detach(from: self.innerView) + self.touchHandler = nil + self.innerView = nil + SheetManager.shared.remove(self) + } + + // MARK: - Presentation + + func present() { + guard !self.isOpen, + !self.isOpening, + !self.isClosing, + let innerView = self.innerView, + let contentHeight = innerView.subviews.first?.frame.height, + let rvc = self.reactViewController() else { + return + } + + let sheetVc = SheetViewController() + sheetVc.setDetents(contentHeight: self.clampHeight(contentHeight), preventExpansion: self.preventExpansion) + if let sheet = sheetVc.sheetPresentationController { + sheet.delegate = self + sheet.preferredCornerRadius = self.cornerRadius + self.selectedDetentIdentifier = sheet.selectedDetentIdentifier + } + sheetVc.view.addSubview(innerView) + + self.sheetVc = sheetVc + self.isOpening = true + + rvc.present(sheetVc, animated: true) { [weak self] in + self?.isOpening = false + self?.isOpen = true + } + } + + func updateLayout() { + if let contentHeight = self.innerView?.subviews.first?.frame.size.height { + self.sheetVc?.updateDetents(contentHeight: self.clampHeight(contentHeight), + preventExpansion: self.preventExpansion) + self.selectedDetentIdentifier = self.sheetVc?.getCurrentDetentIdentifier() + } + } + + func dismiss() { + self.isClosing = true + self.sheetVc?.dismiss(animated: true) { [weak self] in + self?.destroy() + } + } + + // MARK: - Utils + + private func clampHeight(_ height: CGFloat) -> CGFloat { + if height < self.minHeight { + return self.minHeight + } else if height > self.maxHeight { + return self.maxHeight + } + return height + } + + // MARK: - UISheetPresentationControllerDelegate + + func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool { + self.onAttemptDismiss() + return !self.preventDismiss + } + + func presentationControllerWillDismiss(_ presentationController: UIPresentationController) { + self.isClosing = true + } + + func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { + self.destroy() + } + + func sheetPresentationControllerDidChangeSelectedDetentIdentifier(_ sheetPresentationController: UISheetPresentationController) { + self.selectedDetentIdentifier = sheetPresentationController.selectedDetentIdentifier + } +} |