diff --git a/ios/Library/ModalShadowVIew.swift b/ios/Library/ModalShadowVIew.swift deleted file mode 100644 index 3bc7af5..0000000 --- a/ios/Library/ModalShadowVIew.swift +++ /dev/null @@ -1,8 +0,0 @@ -import React - -class ModalShadowView: RCTShadowView { - override func insertReactSubview(_ subview: RCTShadowView!, at atIndex: Int) { - super.insertReactSubview(subview, at: atIndex) - subview.size = RCTScreenSize() - } -} diff --git a/ios/Library/ModalView.swift b/ios/Library/ModalView.swift deleted file mode 100644 index c0d55bb..0000000 --- a/ios/Library/ModalView.swift +++ /dev/null @@ -1,75 +0,0 @@ -import React -import UIKit - -final class ModalView: UIView { - let uiManager: RCTUIManager - let touchHandler: RCTTouchHandler - - var modalViewController: ModalViewController? - var isMounted = false - - init(bridge: RCTBridge) { - self.uiManager = bridge.uiManager - self.touchHandler = RCTTouchHandler(bridge: bridge) - self.modalViewController = ModalViewController() - super.init(frame: .zero) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func layoutSubviews() { - super.layoutSubviews() - self.setupIfNeeded() - } - - // React Native views - - override func insertReactSubview(_ subview: UIView!, at atIndex: Int) { - self.touchHandler.attach(to: subview) - - if (atIndex == 0) { - self.modalViewController?.addReactSubview(subview) - UIAccessibility.post(notification: .screenChanged, argument: subview) - } - } - - // React lifecycle - - func setupIfNeeded() { - if self.isMounted { - self.update() - } else { - self.mount() - } - } - - func mount() { - guard let rvc = self.reactViewController() else { - print("reactViewController not found") - return - } - - self.modalViewController?.present(on: rvc, onView: rvc.view) - - self.isMounted = true - } - - func unmount() { - self.modalViewController?.dismiss() - self.modalViewController = nil - self.isMounted = false - UIAccessibility.post(notification: .screenChanged, argument: nil) - } - - func update() { - // No actions required - } -} - -extension ModalView: RCTInvalidating { - func invalidate() { - self.unmount() - } -} diff --git a/ios/Library/ModalViewController.swift b/ios/Library/ModalViewController.swift deleted file mode 100644 index c03073d..0000000 --- a/ios/Library/ModalViewController.swift +++ /dev/null @@ -1,55 +0,0 @@ -import UIKit - -protocol ModalViewControllerProtocol { - func present(on parentVC: UIViewController, onView parentView: UIView) - func dismiss() - func addReactSubview(_ view: UIView) -} - -final class ModalViewController: UIViewController { - private var reactSubviewContainer = UIView() - private var contentView = UIView() - - init() { - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - self.view.addSubview(reactSubviewContainer) - - self.reactSubviewContainer.translatesAutoresizingMaskIntoConstraints = false - - NSLayoutConstraint.activate([ - self.reactSubviewContainer.topAnchor.constraint(equalTo: self.view.topAnchor), - self.reactSubviewContainer.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), - self.reactSubviewContainer.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), - self.reactSubviewContainer.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), - ]) - } -} - -extension ModalViewController: ModalViewControllerProtocol { - func present(on parentVC: UIViewController, onView parentView: UIView) { - self.modalPresentationStyle = .custom - self.view.accessibilityViewIsModal = true - - self.willMove(toParent: parentVC) - parentVC.addChild(self) - parentView.addSubview(self.view) - self.didMove(toParent: parentVC) - } - - func dismiss() { - self.willMove(toParent: nil) - self.view.removeFromSuperview() - self.removeFromParent() - } - - func addReactSubview(_ view: UIView) { - self.reactSubviewContainer.addSubview(view) - } -} diff --git a/ios/Library/RNTModalShadowView/RNTModalShadowView.h b/ios/Library/RNTModalShadowView/RNTModalShadowView.h new file mode 100644 index 0000000..864439d --- /dev/null +++ b/ios/Library/RNTModalShadowView/RNTModalShadowView.h @@ -0,0 +1,9 @@ +#ifndef RNTModalShadowView_h +#define RNTModalShadowView_h + +#import + +@interface RNTModalShadowView : RCTShadowView +@end + +#endif /* RNTModalShadowView_h */ diff --git a/ios/Library/RNTModalShadowView/RNTModalShadowView.mm b/ios/Library/RNTModalShadowView/RNTModalShadowView.mm new file mode 100644 index 0000000..5730986 --- /dev/null +++ b/ios/Library/RNTModalShadowView/RNTModalShadowView.mm @@ -0,0 +1,13 @@ +#import "RNTModalShadowView.h" +#import + +@implementation RNTModalShadowView + +- (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex { + [super insertReactSubview:subview atIndex:atIndex]; + if ([subview isKindOfClass:[RCTShadowView class]]) { + ((RCTShadowView *)subview).size = RCTScreenSize(); + } +} + +@end diff --git a/ios/Library/RNTModalViewController/RNTModalViewController.h b/ios/Library/RNTModalViewController/RNTModalViewController.h new file mode 100644 index 0000000..7889aa2 --- /dev/null +++ b/ios/Library/RNTModalViewController/RNTModalViewController.h @@ -0,0 +1,16 @@ +#ifndef RNTModalViewController_h +#define RNTModalViewController_h + +#import + +@protocol RNTModalViewControllerProtocol +- (void)presentOn:(UIViewController *)parentVC onView:(UIView *)parentView; +- (void)dismiss; +- (void)addReactSubview:(UIView *)view; +@end + +@interface RNTModalViewController : UIViewController +@property (nonatomic, strong) UIView *reactSubviewContainer; +@end + +#endif /* RNTModalViewController_h */ diff --git a/ios/Library/RNTModalViewController/RNTModalViewController.mm b/ios/Library/RNTModalViewController/RNTModalViewController.mm new file mode 100644 index 0000000..0631a11 --- /dev/null +++ b/ios/Library/RNTModalViewController/RNTModalViewController.mm @@ -0,0 +1,55 @@ +#import +#import "RNTModalViewController.h" + +@implementation RNTModalViewController + +- (instancetype)init { + self = [super initWithNibName:nil bundle:nil]; + if (self) { + _reactSubviewContainer = [[UIView alloc] init]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + [NSException raise:@"initWithCoder" format:@"init(coder:) has not been implemented"]; + return nil; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self.view addSubview:self.reactSubviewContainer]; + self.reactSubviewContainer.translatesAutoresizingMaskIntoConstraints = NO; + + [NSLayoutConstraint activateConstraints:@[ + [self.reactSubviewContainer.topAnchor constraintEqualToAnchor:self.view.topAnchor], + [self.reactSubviewContainer.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], + [self.reactSubviewContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], + [self.reactSubviewContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor] + ]]; +} + +#pragma mark - ModalViewControllerProtocol + +- (void)presentOn:(UIViewController *)parentVC onView:(UIView *)parentView { + self.modalPresentationStyle = UIModalPresentationCustom; + self.view.accessibilityViewIsModal = YES; + + [self willMoveToParentViewController:parentVC]; + [parentVC addChildViewController:self]; + [parentView addSubview:self.view]; + [self didMoveToParentViewController:parentVC]; +} + +- (void)dismiss { + [self willMoveToParentViewController:nil]; + [self.view removeFromSuperview]; + [self removeFromParentViewController]; +} + +- (void)addReactSubview:(UIView *)view { + [self.reactSubviewContainer addSubview:view]; +} + +@end diff --git a/ios/RNTModal.xcodeproj/project.pbxproj b/ios/RNTModal.xcodeproj/project.pbxproj index b098dac..79259be 100644 --- a/ios/RNTModal.xcodeproj/project.pbxproj +++ b/ios/RNTModal.xcodeproj/project.pbxproj @@ -7,11 +7,10 @@ objects = { /* Begin PBXBuildFile section */ - AF43775E2CB5A09C00334A86 /* RNTModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF43775C2CB5A09C00334A86 /* RNTModalView.swift */; }; - AF43775F2CB5A09C00334A86 /* RNTModalViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43775D2CB5A09C00334A86 /* RNTModalViewManager.m */; }; - AFEFC13A2CC7F0210008863D /* ModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFEFC1372CC7F0210008863D /* ModalView.swift */; }; - AFEFC13B2CC7F0210008863D /* ModalShadowVIew.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFEFC1362CC7F0210008863D /* ModalShadowVIew.swift */; }; - AFEFC13C2CC7F0210008863D /* ModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFEFC1382CC7F0210008863D /* ModalViewController.swift */; }; + AFEFC15E2CC8339B0008863D /* RNTModalViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = AFEFC1552CC8339B0008863D /* RNTModalViewController.mm */; }; + AFEFC15F2CC8339B0008863D /* RNTModalView.mm in Sources */ = {isa = PBXBuildFile; fileRef = AFEFC1592CC8339B0008863D /* RNTModalView.mm */; }; + AFEFC1602CC8339B0008863D /* RNTModalShadowView.mm in Sources */ = {isa = PBXBuildFile; fileRef = AFEFC1522CC8339B0008863D /* RNTModalShadowView.mm */; }; + AFEFC1612CC8339B0008863D /* RNTModalViewManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = AFEFC15B2CC8339B0008863D /* RNTModalViewManager.mm */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -27,13 +26,15 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - AF43775C2CB5A09C00334A86 /* RNTModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNTModalView.swift; sourceTree = ""; }; - AF43775D2CB5A09C00334A86 /* RNTModalViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNTModalViewManager.m; sourceTree = ""; }; AF4377602CB5A09D00334A86 /* RNTModal-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNTModal-Bridging-Header.h"; sourceTree = ""; }; AFEFC1352CC7F0140008863D /* libRNTModal.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNTModal.a; sourceTree = BUILT_PRODUCTS_DIR; }; - AFEFC1362CC7F0210008863D /* ModalShadowVIew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalShadowVIew.swift; sourceTree = ""; }; - AFEFC1372CC7F0210008863D /* ModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalView.swift; sourceTree = ""; }; - AFEFC1382CC7F0210008863D /* ModalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalViewController.swift; sourceTree = ""; }; + AFEFC1512CC8339B0008863D /* RNTModalShadowView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNTModalShadowView.h; sourceTree = ""; }; + AFEFC1522CC8339B0008863D /* RNTModalShadowView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RNTModalShadowView.mm; sourceTree = ""; }; + AFEFC1542CC8339B0008863D /* RNTModalViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNTModalViewController.h; sourceTree = ""; }; + AFEFC1552CC8339B0008863D /* RNTModalViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RNTModalViewController.mm; sourceTree = ""; }; + AFEFC1592CC8339B0008863D /* RNTModalView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RNTModalView.mm; sourceTree = ""; }; + AFEFC15A2CC8339B0008863D /* RNTModalVIew.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNTModalVIew.h; sourceTree = ""; }; + AFEFC15B2CC8339B0008863D /* RNTModalViewManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RNTModalViewManager.mm; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -50,20 +51,38 @@ AF4377422CB59EF500334A86 = { isa = PBXGroup; children = ( - AFEFC1392CC7F0210008863D /* Library */, - AF43775C2CB5A09C00334A86 /* RNTModalView.swift */, - AF43775D2CB5A09C00334A86 /* RNTModalViewManager.m */, + AFEFC1572CC8339B0008863D /* Library */, + AFEFC1592CC8339B0008863D /* RNTModalView.mm */, + AFEFC15A2CC8339B0008863D /* RNTModalVIew.h */, + AFEFC15B2CC8339B0008863D /* RNTModalViewManager.mm */, AF4377602CB5A09D00334A86 /* RNTModal-Bridging-Header.h */, AFEFC1352CC7F0140008863D /* libRNTModal.a */, ); sourceTree = ""; }; - AFEFC1392CC7F0210008863D /* Library */ = { + AFEFC1532CC8339B0008863D /* RNTModalShadowView */ = { isa = PBXGroup; children = ( - AFEFC1362CC7F0210008863D /* ModalShadowVIew.swift */, - AFEFC1372CC7F0210008863D /* ModalView.swift */, - AFEFC1382CC7F0210008863D /* ModalViewController.swift */, + AFEFC1512CC8339B0008863D /* RNTModalShadowView.h */, + AFEFC1522CC8339B0008863D /* RNTModalShadowView.mm */, + ); + path = RNTModalShadowView; + sourceTree = ""; + }; + AFEFC1562CC8339B0008863D /* RNTModalViewController */ = { + isa = PBXGroup; + children = ( + AFEFC1542CC8339B0008863D /* RNTModalViewController.h */, + AFEFC1552CC8339B0008863D /* RNTModalViewController.mm */, + ); + path = RNTModalViewController; + sourceTree = ""; + }; + AFEFC1572CC8339B0008863D /* Library */ = { + isa = PBXGroup; + children = ( + AFEFC1532CC8339B0008863D /* RNTModalShadowView */, + AFEFC1562CC8339B0008863D /* RNTModalViewController */, ); path = Library; sourceTree = ""; @@ -130,11 +149,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - AF43775E2CB5A09C00334A86 /* RNTModalView.swift in Sources */, - AF43775F2CB5A09C00334A86 /* RNTModalViewManager.m in Sources */, - AFEFC13A2CC7F0210008863D /* ModalView.swift in Sources */, - AFEFC13B2CC7F0210008863D /* ModalShadowVIew.swift in Sources */, - AFEFC13C2CC7F0210008863D /* ModalViewController.swift in Sources */, + AFEFC15E2CC8339B0008863D /* RNTModalViewController.mm in Sources */, + AFEFC15F2CC8339B0008863D /* RNTModalView.mm in Sources */, + AFEFC1602CC8339B0008863D /* RNTModalShadowView.mm in Sources */, + AFEFC1612CC8339B0008863D /* RNTModalViewManager.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/RNTModalVIew.h b/ios/RNTModalVIew.h new file mode 100644 index 0000000..4325370 --- /dev/null +++ b/ios/RNTModalVIew.h @@ -0,0 +1,25 @@ +#ifndef RNTModalVIew_h +#define RNTModalVIew_h + +#import +#import +#import +#import +#import "RNTModalViewController.h" + +@interface RNTModalView : UIView + +@property (nonatomic, strong) RCTUIManager *uiManager; +@property (nonatomic, strong) RCTTouchHandler *touchHandler; +@property (nonatomic, strong) RNTModalViewController *modalViewController; +@property (nonatomic, assign) BOOL isMounted; + +- (instancetype)initWithBridge:(RCTBridge *)bridge; +- (void)setupIfNeeded; +- (void)mount; +- (void)unmount; +- (void)update; + +@end + +#endif /* RNTModalVIew_h */ diff --git a/ios/RNTModalView.mm b/ios/RNTModalView.mm new file mode 100644 index 0000000..cbb2d0c --- /dev/null +++ b/ios/RNTModalView.mm @@ -0,0 +1,74 @@ +#import +#import +#import +#import +#import "RNTModalViewController.h" +#import "RNTModalVIew.h" + +@implementation RNTModalView + +- (instancetype)initWithBridge:(RCTBridge *)bridge { + self = [super initWithFrame:CGRectZero]; + if (self) { + _uiManager = bridge.uiManager; + _touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge]; + _modalViewController = [[RNTModalViewController alloc] init]; + _isMounted = NO; + } + return self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + [self setupIfNeeded]; +} + +- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex { + [super insertReactSubview:subview atIndex:atIndex]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self.touchHandler attachToView:subview]; + [self.modalViewController addReactSubview:subview]; + }); +} + +- (void)removeReactSubview:(UIView *)subview +{ + [super removeReactSubview:subview]; + [_touchHandler detachFromView:subview]; + [subview removeFromSuperview]; +} + +- (void)setupIfNeeded { + if (self.isMounted) { + [self update]; + } else { + [self mount]; + } +} + +- (void)mount { + UIViewController *rvc = [self reactViewController]; + if (!rvc) { + NSLog(@"reactViewController not found"); + return; + } + + [self.modalViewController presentOn:rvc onView:rvc.view]; + self.isMounted = YES; +} + +- (void)unmount { + [self.modalViewController dismiss]; + self.modalViewController = nil; + self.isMounted = NO; +} + +- (void)update { + // No actions required +} + +- (void)invalidate { + [self unmount]; +} + +@end diff --git a/ios/RNTModalView.swift b/ios/RNTModalView.swift deleted file mode 100644 index e683e40..0000000 --- a/ios/RNTModalView.swift +++ /dev/null @@ -1,17 +0,0 @@ -import React - -@objc(RNTModalView) -class RNTModalView: RCTViewManager { - override func view() -> ModalView { - return ModalView(bridge: self.bridge) - } - - override func shadowView() -> RCTShadowView! { - return ModalShadowView() - } - - @objc - override static func requiresMainQueueSetup() -> Bool { - return true - } -} diff --git a/ios/RNTModalViewManager.m b/ios/RNTModalViewManager.m deleted file mode 100644 index b6d1d51..0000000 --- a/ios/RNTModalViewManager.m +++ /dev/null @@ -1,7 +0,0 @@ -#import - -@interface RCT_EXTERN_MODULE(RNTModalView, RCTViewManager) - -RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock) - -@end diff --git a/ios/RNTModalViewManager.mm b/ios/RNTModalViewManager.mm new file mode 100644 index 0000000..22cc833 --- /dev/null +++ b/ios/RNTModalViewManager.mm @@ -0,0 +1,30 @@ +#import +#import +#import "RCTBridge.h" +#import "Utils.h" +#import "RNTModalShadowView.h" +#import "RNTModalView.h" + +@interface RNTModalViewManager : RCTViewManager +@end + +@implementation RNTModalViewManager + +RCT_EXPORT_MODULE(RNTModalViewManager) + +- (RNTModalView *)view +{ + return [[RNTModalView alloc] initWithBridge:self.bridge]; +} + +- (RNTModalShadowView *)shadowView +{ + return [[RNTModalShadowView alloc] init]; +} + ++ (BOOL)requiresMainQueueSetup +{ + return true; +} + +@end