Skip to content

Commit

Permalink
⭐️ Impl: Sheet-Related Events
Browse files Browse the repository at this point in the history
Related:
* `TODO:2023-04-20-23-58-24` - Impl. sheets + detents.
* `TODO:2023-04-21-23-26-13` - Impl. modal event `onModalDetentDid`.
* `TODO:2023-04-21-23-26-13` - Impl. modal event `onModalDetentDidCompute`.

Summary: Impl. detent-related events.
  • Loading branch information
dominicstop committed Apr 22, 2023
1 parent 5df359d commit ab5b00b
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ import Foundation
@available(iOS 15.0, *)
extension UISheetPresentationController.Detent {

// <UISheetPresentationControllerDetent: 0x600000e8f510:
// _type=medium -> 2,
// _identifier=com.apple.UIKit.medium
// >
//
// <UISheetPresentationControllerDetent: 0x600000e8ffc0:
// _type=large -> 1,
// _identifier=com.apple.UIKit.large
// >

static func fromString(
_ string: String
) -> UISheetPresentationController.Detent? {
Expand All @@ -25,7 +35,18 @@ extension UISheetPresentationController.Detent {
};

@available(iOS 15.0, *)
extension UISheetPresentationController.Detent.Identifier {
extension UISheetPresentationController.Detent.Identifier:
CustomStringConvertible {

public var description: String {
switch self {
case .medium: return "medium";
case .large : return "large";

default: return self.rawValue;
};
};

init?(fromSystemIdentifierString string: String) {
switch string {
case "medium": self = .medium;
Expand Down
9 changes: 9 additions & 0 deletions ios/src_library/React Native/RNIModal/RNIModalEventData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,12 @@ public struct RNIOnModalFocusEventData: RNIDictionarySynthesizable {

public let isInitial: Bool;
};

public struct RNIModalDidChangeSelectedDetentIdentifierEventData: RNIDictionarySynthesizable {
public let sheetDetentStringPrevious: String?;
public let sheetDetentStringCurrent: String?;
};

public struct RNIModalDetentDidComputeEventData: RNIDictionarySynthesizable {
public let maximumDetentValue: CGFloat;
};
124 changes: 118 additions & 6 deletions ios/src_library/React Native/RNIModalView/RNIModalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public class RNIModalView:
var modalContentWrapper: RNIWrapperView?;
public var modalVC: RNIModalViewController?;


public var sheetDetentStringCurrent: String?;
public var sheetDetentStringPrevious: String?;

// MARK: - Properties - RNIModalPresentationNotifying
// --------------------------------------------------

Expand Down Expand Up @@ -88,6 +92,9 @@ public class RNIModalView:
@objc var onPresentationControllerDidDismiss: RCTBubblingEventBlock?;
@objc var onPresentationControllerDidAttemptToDismiss: RCTBubblingEventBlock?;

@objc var onModalDetentDidCompute: RCTBubblingEventBlock?;
@objc var onModalDidChangeSelectedDetentIdentifier: RCTBubblingEventBlock?;

// MARK: - Properties: React Props - General
// -----------------------------------------

Expand Down Expand Up @@ -247,14 +254,25 @@ public class RNIModalView:

@objc var sheetSelectedDetentIdentifier: String? {
willSet {
guard #available(iOS 15.0, *),
let oldValue = self.sheetSelectedDetentIdentifier;

guard oldValue != newValue,
#available(iOS 15.0, *),
let sheetController = self.sheetPresentationController
else { return };

let nextDetentID = self.synthesizedSheetSelectedDetentIdentifier;

self.sheetAnimateChangesIfNeeded {
sheetController.selectedDetentIdentifier =
self.synthesizedSheetSelectedDetentIdentifier;
sheetController.selectedDetentIdentifier = nextDetentID;
};

/// Delegate function does not get called when detent is changed via
/// setting `selectedDetentIdentifier`, so invoke manually...
///
self.sheetPresentationControllerDidChangeSelectedDetentIdentifier(
sheetController
);
}
};

Expand Down Expand Up @@ -355,13 +373,26 @@ public class RNIModalView:
@available(iOS 15.0, *)
public var synthesizedModalSheetDetents: [UISheetPresentationController.Detent]? {
self.modalSheetDetents?.compactMap {
if let string = $0 as? String {
return UISheetPresentationController.Detent.fromString(string);
if let string = $0 as? String,
let detent = UISheetPresentationController.Detent.fromString(string) {

return detent;

} else if #available(iOS 16.0, *),
let dict = $0 as? Dictionary<String, Any> {

let customDetent = RNIModalCustomSheetDetent(forDict: dict);
let customDetent = RNIModalCustomSheetDetent(forDict: dict) {
_, maximumDetentValue in

let eventData = RNIModalDetentDidComputeEventData(
maximumDetentValue: maximumDetentValue
);

self.onModalDetentDidCompute?(
eventData.synthesizedJSDictionary
);
};

return customDetent?.synthesizedDetent;
};

Expand Down Expand Up @@ -407,6 +438,7 @@ public class RNIModalView:
// MARK: - Properties: Computed
// ----------------------------

// TODO: Move to `RNIModal+Helpers`
@available(iOS 15.0, *)
var sheetPresentationController: UISheetPresentationController? {
guard let presentedVC = self.modalViewController else { return nil };
Expand All @@ -426,6 +458,27 @@ public class RNIModalView:
};
};

@available(iOS 15.0, *)
var currentSheetDetentID: UISheetPresentationController.Detent.Identifier? {
guard let sheetController = self.sheetPresentationController
else { return nil };

let detents = sheetController.detents;

if let selectedDetent = sheetController.selectedDetentIdentifier {
return selectedDetent;

} else if #available(iOS 16.0, *),
let firstDetent = detents.first {

/// The default value of `selectedDetentIdentifier` is nil, which means
/// the sheet displays at the smallest detent you specify in detents.
return firstDetent.identifier;
};

return nil;
};

// MARK: - Init
// ------------

Expand Down Expand Up @@ -687,6 +740,7 @@ public class RNIModalView:
if #available(iOS 15.0, *),
let sheetController = self.sheetPresentationController {

sheetController.delegate = self;
self.applyModalSheetProps(to: sheetController);
};

Expand Down Expand Up @@ -726,6 +780,16 @@ public class RNIModalView:

completion?(true, nil);

// let panGesture = self.modalVC?
// .presentationController?
// .presentedView?
// .gestureRecognizers?
// .first {
// $0 is UIPanGestureRecognizer
// };
//
// panGesture?.addTarget(self, action: #selector(Self.handleGestureRecognizer(_:)))

#if DEBUG
print(
"Log - RNIModalView.presentModal - Present modal finished"
Expand All @@ -737,6 +801,12 @@ public class RNIModalView:
};
};

// @objc func handleGestureRecognizer(_ sender: UIPanGestureRecognizer) {
// print(
// "Test - handleGestureRecognizer - \(sender.state.description)"
// );
// };

public func dismissModal(completion: CompletionHandler? = nil) {
guard self.computedIsModalPresented else {
#if DEBUG
Expand Down Expand Up @@ -945,6 +1015,48 @@ extension RNIModalView: UIAdaptivePresentationControllerDelegate {
};
};

@available(iOS 15.0, *)
extension RNIModalView: UISheetPresentationControllerDelegate {

/// `Note:2023-04-22-03-50-59`
///
/// * This function gets invoked when the sheet has snapped into a detent
///
/// * However, we don't get notified whenever the user is currently dragging
/// the sheet.
///
/// * The `presentedViewController.transitionCoordinator` is only available
/// during modal presentation and dismissal.
///
///
public func sheetPresentationControllerDidChangeSelectedDetentIdentifier(
_ sheetPresentationController: UISheetPresentationController
) {
let currentDetentID = self.currentSheetDetentID;

self.sheetDetentStringPrevious = self.sheetDetentStringCurrent;
self.sheetDetentStringCurrent = currentDetentID?.description;

#if DEBUG
print(
"Log - RNIModalView+UISheetPresentationControllerDelegate"
+ " - sheetPresentationControllerDidChangeSelectedDetentIdentifier"
+ " - sheetDetentStringPrevious: \(self.sheetDetentStringPrevious ?? "N/A")"
+ " - sheetDetentStringCurrent: \(self.sheetDetentStringCurrent ?? "N/A")"
);
#endif

let eventData = RNIModalDidChangeSelectedDetentIdentifierEventData(
sheetDetentStringPrevious: self.sheetDetentStringPrevious,
sheetDetentStringCurrent: self.sheetDetentStringCurrent
);

self.onModalDidChangeSelectedDetentIdentifier?(
eventData.synthesizedJSDictionary
);
};
};

// MARK: Extension: RNIModalRequestable
// ------------------------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ @interface RCT_EXTERN_MODULE(RNIModalViewManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(onPresentationControllerDidDismiss, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPresentationControllerDidAttemptToDismiss, RCTBubblingEventBlock);

RCT_EXPORT_VIEW_PROPERTY(onModalDetentDidCompute, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onModalDidChangeSelectedDetentIdentifier, RCTBubblingEventBlock);

// MARK: - Value Props - General
// -----------------------------

Expand Down
36 changes: 36 additions & 0 deletions src/components/ModalView/ModalView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import {
RNIModalView,
RNIModalBaseEvent,
RNIModalDeprecatedBaseEvent,
OnModalDetentDidComputeEvent,
OnModalDidChangeSelectedDetentIdentifierEvent,
} from '../../native_components/RNIModalView';

import { RNIModalViewModule } from '../../native_modules/RNIModalViewModule';
Expand Down Expand Up @@ -134,6 +136,8 @@ export class ModalView extends
onPresentationControllerWillDismiss,
onPresentationControllerDidDismiss,
onPresentationControllerDidAttemptToDismiss,
onModalDetentDidCompute,
onModalDidChangeSelectedDetentIdentifier,

// Component Props
autoCloseOnUnmount,
Expand Down Expand Up @@ -217,6 +221,8 @@ export class ModalView extends
onPresentationControllerWillDismiss,
onPresentationControllerDidDismiss,
onPresentationControllerDidAttemptToDismiss,
onModalDetentDidCompute,
onModalDidChangeSelectedDetentIdentifier,

// C - View-Related Props
children,
Expand Down Expand Up @@ -613,6 +619,34 @@ export class ModalView extends
);
};

private _handleOnModalDetentDidCompute:
OnModalDetentDidComputeEvent = (event) => {

const props = this.props;

props.onModalDetentDidCompute?.(event);
event.stopPropagation();

this.emitter.emit(
ModalViewEmitterEvents.onModalDetentDidCompute,
event.nativeEvent
);
};

private _handleOnModalDidChangeSelectedDetentIdentifier:
OnModalDidChangeSelectedDetentIdentifierEvent = (event) => {

const props = this.props;

props.onModalDidChangeSelectedDetentIdentifier?.(event);
event.stopPropagation();

this.emitter.emit(
ModalViewEmitterEvents.onModalDidChangeSelectedDetentIdentifier,
event.nativeEvent
);
};

private _renderModal() {
const { viewProps, ...props } = this.getProps();
const state = this.state;
Expand Down Expand Up @@ -657,6 +691,8 @@ export class ModalView extends
onPresentationControllerWillDismiss={this._handleOnPresentationControllerWillDismiss}
onPresentationControllerDidDismiss={this._handleOnPresentationControllerDidDismiss}
onPresentationControllerDidAttemptToDismiss={this._handleOnPresentationControllerDidAttemptToDismiss}
onModalDetentDidCompute={this._handleOnModalDetentDidCompute}
onModalDidChangeSelectedDetentIdentifier={this._handleOnModalDidChangeSelectedDetentIdentifier}
{...overrideProps}
{...viewProps}
>
Expand Down
6 changes: 6 additions & 0 deletions src/components/ModalView/ModalViewEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import type {
OnPresentationControllerWillDismissEventObject,
OnPresentationControllerDidDismissEventObject,
OnPresentationControllerDidAttemptToDismissEventObject,
OnModalDetentDidComputeEventObject,
OnModalDidChangeSelectedDetentIdentifierEventObject,
} from 'src/native_components/RNIModalView';

import type { KeyMapType } from '../../types/UtilityTypes';
Expand All @@ -43,6 +45,8 @@ export enum ModalViewEmitterEvents {
onPresentationControllerWillDismiss = 'onPresentationControllerWillDismiss',
onPresentationControllerDidDismiss = 'onPresentationControllerDidDismiss',
onPresentationControllerDidAttemptToDismiss = 'onPresentationControllerDidAttemptToDismiss',
onModalDetentDidCompute = 'onModalDetentDidCompute',
onModalDidChangeSelectedDetentIdentifier = 'onModalDidChangeSelectedDetentIdentifier',

onLayoutModalContentContainer = 'onLayoutModalContentContainer',
}
Expand All @@ -69,6 +73,8 @@ export type ModalViewEmitterEventMap =
onPresentationControllerWillDismiss: OnPresentationControllerWillDismissEventObject['nativeEvent'];
onPresentationControllerDidDismiss: OnPresentationControllerDidDismissEventObject['nativeEvent'];
onPresentationControllerDidAttemptToDismiss: OnPresentationControllerDidAttemptToDismissEventObject['nativeEvent'];
onModalDetentDidCompute: OnModalDetentDidComputeEventObject['nativeEvent'];
onModalDidChangeSelectedDetentIdentifier: OnModalDidChangeSelectedDetentIdentifierEventObject['nativeEvent'];

onLayoutModalContentContainer: LayoutChangeEvent['nativeEvent'];
}
Expand Down
2 changes: 2 additions & 0 deletions src/components/ModalView/ModalViewTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export type ModalViewBaseProps = Partial<
| 'onPresentationControllerWillDismiss'
| 'onPresentationControllerDidDismiss'
| 'onPresentationControllerDidAttemptToDismiss'
| 'onModalDetentDidCompute'
| 'onModalDidChangeSelectedDetentIdentifier'
>
> & {
// TODO: See TODO:2023-03-04-13-02-45 - Refactor: Rename to
Expand Down
Loading

0 comments on commit ab5b00b

Please sign in to comment.