From a53fb36c1e7a9031d5601db2f306b5d121cf0feb Mon Sep 17 00:00:00 2001 From: Dominic Go <18517029+dominicstop@users.noreply.github.com> Date: Thu, 30 Mar 2023 15:36:21 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A0=20Refactor:=20Use=20`RNIModalView`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related: * `TODO:2023-03-28-18-52-17` - Pre-migration to `react-native-ios-utilites`. * `2023-03-29-05-04-40` - Refactor: Update `RNIModalView` + `ModalView` to use `RNIWrapperView`. Summary: Refactor to use `RNIModalView`. --- .../RNIModalView/RNIModalView.swift | 148 ++++-------------- .../RNIModalView/RNIModalViewController.swift | 86 ++++------ src/components/ModalView/ModalView.tsx | 31 +++- .../ModalView/ModalViewConstants.ts | 4 + src/components/ModalView/ModalViewTypes.ts | 4 +- 5 files changed, 93 insertions(+), 180 deletions(-) diff --git a/ios/src_library/React Native/RNIModalView/RNIModalView.swift b/ios/src_library/React Native/RNIModalView/RNIModalView.swift index 7fa2db00..4c5f12b8 100644 --- a/ios/src_library/React Native/RNIModalView/RNIModalView.swift +++ b/ios/src_library/React Native/RNIModalView/RNIModalView.swift @@ -14,15 +14,18 @@ class RNIModalView: UIView, RNIModalFocusNotifying, RNIModalIdentity, typealias CompletionHandler = (_ isSuccess: Bool, _ error: RNIModalViewError?) -> Void; + enum NativeIDKey: String { + case modalViewContent; + }; + // MARK: - Properties // ------------------ weak var bridge: RCTBridge?; - private var modalVC: RNIModalViewController?; + var modalContentWrapper: RNIWrapperView?; + var modalVC: RNIModalViewController?; - private var touchHandler: RCTTouchHandler!; - private var reactSubview: UIView?; // MARK: - Properties - RNIModalFocusNotifying // ------------------------------------------- @@ -256,8 +259,6 @@ class RNIModalView: UIView, RNIModalFocusNotifying, RNIModalIdentity, super.init(frame: CGRect()); self.bridge = bridge; - self.touchHandler = RCTTouchHandler(bridge: self.bridge); - RNIModalManagerShared.register(modal: self); }; @@ -269,26 +270,6 @@ class RNIModalView: UIView, RNIModalFocusNotifying, RNIModalIdentity, // MARK: - UIKit Lifecycle // ----------------------- - override func layoutSubviews() { - super.layoutSubviews(); - - guard let modalVC = self.modalVC, - let reactSubview = self.reactSubview else { return }; - - #if DEBUG - print( - "Log - RNIModalView.layoutSubviews" - + " - self.modalNativeID: '\(self.modalNativeID!)'" - + " - Unable to parse string value" - + " - instance reactTag: '\(self.reactTag ?? -1)'" - ); - #endif - - if !reactSubview.isDescendant(of: modalVC.view) { - modalVC.reactView = reactSubview; - }; - }; - override func didMoveToWindow() { super.didMoveToWindow(); if self.presentViaMount { @@ -309,79 +290,35 @@ class RNIModalView: UIView, RNIModalFocusNotifying, RNIModalIdentity, override func insertReactSubview(_ subview: UIView!, at atIndex: Int) { super.insertReactSubview(subview, at: atIndex); - guard (self.reactSubview == nil) else { - #if DEBUG - print( - "Error - RNIModalView.insertReactSubview" - + " - self.modalNativeID: '\(self.modalNativeID!)'" - + " - arg subview.reactTag: \(subview.reactTag ?? -1)" - + " - arg atIndex: \(atIndex)" - + " - Guard check failed: Modal view can only have one subview " - ); - #endif - return; - }; - - #if DEBUG - print( - "Log - RNIModalView.insertReactSubview" - + " - self.modalNativeID: '\(self.modalNativeID!)'" - + " - arg subview.reactTag: \(subview.reactTag ?? -1)" - + " - arg atIndex: \(atIndex)" - ); - #endif - - subview.removeFromSuperview(); - subview.frame = CGRect( - origin: CGPoint(x: 0, y: 0), - size : CGSize(width: 0, height: 0) - ); - - if let newBounds = modalVC?.view.bounds { - self.bridge?.uiManager.setSize(newBounds.size, for: subview); + guard let wrapperView = subview as? RNIWrapperView, + let nativeID = subview.nativeID, + let nativeIDKey = NativeIDKey(rawValue: nativeID) + else { return }; + + self.initControllers(); + wrapperView.isMovingToParent = true; + + switch nativeIDKey { + case .modalViewContent: + if let oldModalContentWrapper = self.modalContentWrapper, + wrapperView !== oldModalContentWrapper { + + oldModalContentWrapper.cleanup(); + self.modalContentWrapper = nil; + + self.deinitControllers(); + }; + + self.modalContentWrapper = wrapperView; + self.initControllers(); }; - self.reactSubview = subview; - self.touchHandler.attach(to: subview); - - // modal contents has been mounted, and is about to be presented so - // prepare for modal presentation and init the vc's - self.initControllers(); + wrapperView.removeFromSuperview(); + wrapperView.isMovingToParent = false; }; override func removeReactSubview(_ subview: UIView!) { super.removeReactSubview(subview); - - guard self.reactSubview == subview else { - #if DEBUG - print( - "Error - RNIModalView.removeReactSubview" - + " - self.modalNativeID: '\(self.modalNativeID!)'" - + " - arg subview.reactTag: '\(subview.reactTag ?? -1)'" - + " - Cannot remove view other than modal view" - + " - reactSubview.reactTag: '\(reactSubview?.reactTag ?? -1)'" - ); - #endif - return; - }; - - #if DEBUG - print( - "Log - RNIModalView.removeReactSubview" - + " - self.modalNativeID: '\(self.modalNativeID!)'" - + " - arg subview.reactTag: '\(subview.reactTag ?? -1)'" - + " - Cannot remove view other than modal view" - + " - reactSubview.reactTag: '\(reactSubview?.reactTag ?? -1)'" - ); - #endif - - // modal contents has been unmounted so reset react subview - self.reactSubview = nil; - self.modalVC?.reactView = nil; - - // cleanup - self.touchHandler.detach(from: subview); - self.deinitControllers(); }; // MARK: - Functions - Private @@ -404,18 +341,10 @@ class RNIModalView: UIView, RNIModalFocusNotifying, RNIModalIdentity, vc.blurEffectStyle = self.synthesizedModalBGBlurEffectStyle; - vc.boundsDidChangeBlock = { [weak self] (newBounds: CGRect) in - self?.notifyForBoundsChange(newBounds); - }; - - vc.presentationController?.delegate = self; - return vc; }(); }; - /// TODO:2023-03-22-12-18-37 - Refactor: Remove `deinitControllers` - /// * Refactor so that we don't have to constantly cleanup... private func deinitControllers(){ #if DEBUG print( @@ -424,29 +353,10 @@ class RNIModalView: UIView, RNIModalFocusNotifying, RNIModalIdentity, ); #endif - self.modalVC?.reactView = nil; - self.modalVC = nil; self.presentingViewController = nil; }; - private func notifyForBoundsChange(_ newBounds: CGRect){ - guard let bridge = self.bridge, - let reactSubview = self.reactSubview - else { return }; - - #if DEBUG - print( - "LOG - RNIModalView.notifyForBoundsChange" - + " - self.modalNativeID: \(self.modalNativeID!)" - + " - args newBounds: \(newBounds)" - + " - reactSubview.bounds: \(reactSubview.bounds)" - ); - #endif - - bridge.uiManager.setSize(newBounds.size, for: reactSubview); - }; - private func enableSwipeGesture(_ flag: Bool? = nil){ self.modalVC? .presentationController? diff --git a/ios/src_library/React Native/RNIModalView/RNIModalViewController.swift b/ios/src_library/React Native/RNIModalView/RNIModalViewController.swift index 615c82d5..f8ccb771 100644 --- a/ios/src_library/React Native/RNIModalView/RNIModalViewController.swift +++ b/ios/src_library/React Native/RNIModalView/RNIModalViewController.swift @@ -13,7 +13,7 @@ class RNIModalViewController: UIViewController { // MARK: - Properties // ------------------ - var prevViewFrame: CGRect?; + var prevBounds: CGRect?; var boundsDidChangeBlock: ((CGRect) -> Void)?; weak var modalViewRef: RNIModalView?; @@ -40,8 +40,12 @@ class RNIModalViewController: UIViewController { self.modalViewRef?.modalID as? String }; - // MARK: - Properties - Views - // -------------------------- + var modalContentWrapper: RNIWrapperView? { + self.modalViewRef?.modalContentWrapper; + }; + + // MARK: - Properties + // ------------------ var blurEffectView: UIVisualEffectView? = nil; @@ -66,77 +70,53 @@ class RNIModalViewController: UIViewController { } }; - var reactView: UIView? { - didSet { - - let didChange = - oldValue?.reactTag != self.reactView?.reactTag; - - let shouldRemoveOldView = - oldValue != nil && didChange; - - if shouldRemoveOldView, - let oldView = oldValue { - - oldView.removeFromSuperview(); - }; - - if didChange, - let newView = self.reactView { - - self.view.addSubview(newView); - }; - } - }; - // MARK: - View Controller Lifecycle // --------------------------------- override func viewDidLoad() { super.viewDidLoad(); - #if DEBUG - print( - "Log - RNIModalViewController.viewDidLoad" - + " - modalNativeID: '\(self.modalViewRef?.modalNativeID ?? "N/A")'" - ); - #endif - - // setup vc's view self.view = { let view = UIView(); view.autoresizingMask = [.flexibleHeight, .flexibleWidth]; + return view; }(); + if let modalContentWrapper = self.modalContentWrapper { + self.view.addSubview(modalContentWrapper); + modalContentWrapper.notifyForBoundsChange(size: self.view.bounds.size); + }; + self.updateBackgroundTransparency(); self.updateBackgroundBlur(); - - self.boundsDidChangeBlock?(self.view.bounds) }; override func viewDidLayoutSubviews(){ super.viewDidLayoutSubviews(); - let didChangeViewFrame: Bool = { - guard let lastViewFrame = self.prevViewFrame else { return true }; - return !lastViewFrame.equalTo(self.view.frame); + let didChangeBounds: Bool = { + guard let prevBounds = self.prevBounds else { return true }; + return !prevBounds.equalTo(self.view.bounds); }(); - if didChangeViewFrame, - let boundsDidChangeBlock = self.boundsDidChangeBlock { - - boundsDidChangeBlock(self.view.bounds); - self.prevViewFrame = self.view.frame; - - #if DEBUG - print( - "Log - RNIModalViewController.viewDidLayoutSubviews" - + " - modalNativeID: '\(self.modalViewRef?.modalNativeID ?? "N/A")'" - + " - invoke boundsDidChangeBlock" - ); - #endif - }; + guard didChangeBounds, + let modalContentWrapper = self.modalContentWrapper + else { return }; + + let nextBounds = self.view.bounds; + + #if DEBUG + print( + "Log - RNIModalViewController.viewDidLayoutSubviews" + + " - modalNativeID: '\(self.modalViewRef?.modalNativeID ?? "N/A")'" + + " - self.prevBounds: \(String(describing: self.prevBounds))" + + " - nextBounds: \(nextBounds)" + ); + #endif + + modalContentWrapper.notifyForBoundsChange(size: nextBounds.size); + self.prevBounds = nextBounds; }; // MARK: - Private Functions diff --git a/src/components/ModalView/ModalView.tsx b/src/components/ModalView/ModalView.tsx index c54d39dd..b97f4109 100644 --- a/src/components/ModalView/ModalView.tsx +++ b/src/components/ModalView/ModalView.tsx @@ -3,12 +3,13 @@ import React from 'react'; import { findNodeHandle, StyleSheet, - View, ScrollView, Platform, ViewProps, } from 'react-native'; +import { RNIWrapperView } from '../../temp'; + import { TSEventEmitter } from '@dominicstop/ts-event-emitter'; import * as Helpers from '../../functions/helpers'; @@ -36,6 +37,7 @@ import { import { hasScrollViewContext, + NATIVE_ID_KEYS, VirtualizedListContext, } from './ModalViewConstants'; @@ -81,6 +83,7 @@ export class ModalView extends isModalBGTransparent, isModalInPresentation, allowModalForceDismiss, + shouldEnableAggressiveCleanup, // native props - string modalID, @@ -146,6 +149,9 @@ export class ModalView extends isModalContentLazy: ( isModalContentLazy ?? true ), + shouldEnableAggressiveCleanup: ( + shouldEnableAggressiveCleanup ?? false + ), // B - Pass down... modalID, @@ -416,7 +422,7 @@ export class ModalView extends ref={(r) => { this.nativeRefModalView = r!; }} - style={styles.rootContainer} + style={styles.nativeModalView} onStartShouldSetResponder={this._shouldSetResponder} onModalBlur={this._handleOnModalBlur} onModalFocus={this._handleOnModalFocus} @@ -429,9 +435,15 @@ export class ModalView extends {...viewProps} > {shouldMountModalContent && ( - {React.cloneElement(props.children as any, { @@ -441,7 +453,7 @@ export class ModalView extends // pass down modalID modalID: props.modalID, })} - + )} ); @@ -488,13 +500,18 @@ export class ModalView extends } const styles = StyleSheet.create({ - rootContainer: { + nativeModalView: { position: 'absolute', width: 0, height: 0, overflow: 'hidden', }, - modalContentContainer: { + modalContentWrapper: { position: 'absolute', + overflow: 'visible', + top: 0, + bottom: 0, + left: 0, + right: 0, }, }); diff --git a/src/components/ModalView/ModalViewConstants.ts b/src/components/ModalView/ModalViewConstants.ts index 55980642..453ba715 100644 --- a/src/components/ModalView/ModalViewConstants.ts +++ b/src/components/ModalView/ModalViewConstants.ts @@ -1,6 +1,10 @@ import React from 'react'; import { ScrollView } from 'react-native'; +export const NATIVE_ID_KEYS = { + modalViewContent: 'modalViewContent', +}; + export const VirtualizedListContext = React.createContext(null); // fix for react-native 0.60 diff --git a/src/components/ModalView/ModalViewTypes.ts b/src/components/ModalView/ModalViewTypes.ts index 7c3d2c9c..8133e18f 100644 --- a/src/components/ModalView/ModalViewTypes.ts +++ b/src/components/ModalView/ModalViewTypes.ts @@ -36,7 +36,9 @@ export type ModalViewBaseProps = Partial< setModalInPresentationFromProps?: boolean; isModalContentLazy?: boolean; - containerStyle?: ViewStyle; + shouldEnableAggressiveCleanup?: boolean; + + containerStyle: ViewStyle; children?: React.ReactNode; };