Compare commits

...

3 Commits

Author SHA1 Message Date
Marcos Rodriguez Velez
58c0ff31e6
make anchorViewTag optional 2024-07-18 13:47:13 -04:00
Marcos Rodriguez Vélez
7642126df6
Merge branch 'main' into popover 2024-07-16 23:56:54 -04:00
Marcos Rodriguez Velez
924f6b22b8
add popover support for iOS 2024-07-16 20:55:40 -04:00
4 changed files with 127 additions and 9 deletions

View File

@ -1,7 +1,7 @@
//
// TrueSheetView.swift
// Created by Jovanni Lo (@lodev09)
// Copyright (c) 2024-present. All rights reserved.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
//
@ -22,6 +22,10 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
@objc var initialIndex: NSNumber = -1
@objc var initialIndexAnimated = true
// MARK: - New Popover Properties
@objc var anchorViewTag: NSNumber?
// MARK: - Private properties
private var isPresented = false
@ -29,6 +33,7 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
private var bridge: RCTBridge?
private var touchHandler: RCTTouchHandler
private var viewController: TrueSheetViewController
private var currentPresentationStyle: UIModalPresentationStyle?
// MARK: - Content properties
@ -45,11 +50,6 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
private var rctScrollView: RCTScrollView?
private var uiManager: RCTUIManager? {
guard let uiManager = bridge?.uiManager else { return nil }
return uiManager
}
// MARK: - Setup
init(with bridge: RCTBridge) {
@ -122,6 +122,36 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
}
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updatePresentationStyle()
}
@objc
private func updatePresentationStyle() {
let isRegularSizeClass = traitCollection.horizontalSizeClass == .regular && traitCollection.verticalSizeClass == .regular
let newPresentationStyle: UIModalPresentationStyle = isRegularSizeClass && anchorViewTag != nil ? .popover : .pageSheet
if currentPresentationStyle == newPresentationStyle {
return
}
currentPresentationStyle = newPresentationStyle
if isPresented {
if let anchorTag = anchorViewTag, let anchorView = bridge?.uiManager.view(forReactTag: anchorTag), isRegularSizeClass {
viewController.modalPresentationStyle = .popover
if let popoverController = viewController.popoverPresentationController {
popoverController.sourceView = anchorView
popoverController.sourceRect = anchorView.bounds
popoverController.delegate = self
}
} else {
viewController.modalPresentationStyle = .pageSheet
}
}
}
// MARK: - ViewController delegate
func viewControllerKeyboardWillHide() {
@ -148,7 +178,7 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
guard let containerView else { return }
let size = CGSize(width: width, height: containerView.bounds.height)
uiManager?.setSize(size, for: containerView)
bridge?.uiManager.setSize(size, for: containerView)
}
func viewControllerWillAppear() {
@ -302,7 +332,7 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
@objc
func setScrollableHandle(_ tag: NSNumber?) {
let view = uiManager?.view(forReactTag: tag) as? RCTScrollView
let view = bridge?.uiManager.view(forReactTag: tag) as? RCTScrollView
rctScrollView = view
}
@ -382,6 +412,14 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
}
func present(at index: Int, promise: Promise?, animated: Bool = true) {
if traitCollection.horizontalSizeClass == .regular && traitCollection.verticalSizeClass == .regular && anchorViewTag != nil {
presentAsPopover(at: index, promise: promise, animated: animated)
} else {
presentAsSheet(at: index, promise: promise, animated: animated)
}
}
private func presentAsSheet(at index: Int = 0, promise: Promise?, animated: Bool = true) {
let rvc = reactViewController()
guard let rvc else {
@ -394,6 +432,55 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
return
}
viewController.modalPresentationStyle = .pageSheet
currentPresentationStyle = .pageSheet
viewController.configureSheet(at: index) { sizeInfo in
// Trigger onSizeChange event when size is changed while presenting
if self.isPresented {
self.viewControllerSheetDidChangeSize(sizeInfo)
promise?.resolve(nil)
} else {
// Keep track of the active index
self.activeIndex = index
self.isPresented = true
rvc.present(self.viewController, animated: animated) {
let data = self.sizeInfoData(from: sizeInfo)
self.onPresent?(data)
promise?.resolve(nil)
}
}
}
}
private func presentAsPopover(at index: Int = 0, promise: Promise?, animated: Bool = true) {
let rvc = reactViewController()
guard let rvc else {
promise?.reject(message: "No react view controller present.")
return
}
guard viewController.sizes.indices.contains(index) else {
promise?.reject(message: "Size at \(index) is not configured.")
return
}
if let anchorTag = anchorViewTag, let anchorView = bridge?.uiManager.view(forReactTag: anchorTag) {
viewController.modalPresentationStyle = .popover
currentPresentationStyle = .popover
if let popoverController = viewController.popoverPresentationController {
popoverController.sourceView = anchorView
popoverController.sourceRect = anchorView.bounds
popoverController.delegate = self // Set delegate to handle size adjustments
}
} else {
presentAsSheet(at: index, promise: promise, animated: animated)
return
}
viewController.configureSheet(at: index) { sizeInfo in
// Trigger onSizeChange event when size is changed while presenting
if self.isPresented {
@ -413,3 +500,24 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
}
}
}
// MARK: - UIPopoverPresentationControllerDelegate
extension TrueSheetView: UIPopoverPresentationControllerDelegate {
func prepareForPopoverPresentation(_ popoverPresentationController: UIPopoverPresentationController) {
// Wrap content in UIScrollView if needed
if let contentView = contentView, contentView.bounds.height > self.bounds.height {
let scrollView = UIScrollView(frame: self.bounds)
scrollView.contentSize = contentView.bounds.size
scrollView.addSubview(contentView)
self.addSubview(scrollView)
}
}
func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) {
// Cleanup UIScrollView if used
if let scrollView = self.subviews.first(where: { $0 is UIScrollView }) {
scrollView.removeFromSuperview()
}
}
}

View File

@ -41,6 +41,8 @@ RCT_EXPORT_VIEW_PROPERTY(dimmedIndex, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(initialIndex, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(initialIndexAnimated, BOOL)
RCT_EXPORT_VIEW_PROPERTY(anchorViewTag, NSNumber)
// Internal properties
RCT_EXPORT_VIEW_PROPERTY(contentHeight, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(footerHeight, NSNumber)

View File

@ -220,6 +220,7 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
style,
contentContainerStyle,
children,
anchorViewTag, // Add the anchorViewTag prop here
...rest
} = this.props
@ -245,6 +246,7 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
onPresent={this.onPresent}
onDismiss={this.onDismiss}
onSizeChange={this.onSizeChange}
anchorViewTag={anchorViewTag} // Pass the anchorViewTag prop to the native view
>
<View
collapsable={false}
@ -282,4 +284,4 @@ const $nativeSheet: ViewStyle = {
width: '100%',
left: -9999,
zIndex: -9999,
}
}

View File

@ -251,4 +251,10 @@ export interface TrueSheetProps extends ViewProps {
* Either by dragging or programatically.
*/
onSizeChange?: (info: SizeInfo) => void
/**
* React tag of the view to anchor the popover.
* @platform ios
*/
anchorViewTag?: number
}