Skip to content
This repository was archived by the owner on Jan 30, 2025. It is now read-only.

Commit 0bf7290

Browse files
authored
Implement iOS modal with different over-rides (#19)
* update readme * partial implementation of #31498 * complete implementation of #31498 * adding dismissModalHostView * minor changes * fixing issue with modal not closing * minor changes * remove PR diff - modal opens and closes * dummy impl ensurePresentedOnlyIfNeeded and dismissModalViewController * working touch handler and dismiss * working touch handler and dismiss * remove not required logic * re-implement basic modal example
1 parent 48dd46e commit 0bf7290

8 files changed

+262
-89
lines changed

example/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ PODS:
944944
- React-Mapbuffer (0.73.0):
945945
- glog
946946
- React-debug
947-
- react-native-improved (0.2.7):
947+
- react-native-improved (0.2.8):
948948
- glog
949949
- RCT-Folly (= 2022.05.16.00)
950950
- React-Core
@@ -1351,7 +1351,7 @@ SPEC CHECKSUMS:
13511351
React-jsinspector: 9f6fb9ed9f03a0fb961ab8dc2e0e0ee0dc729e77
13521352
React-logger: 008caec0d6a587abc1e71be21bfac5ba1662fe6a
13531353
React-Mapbuffer: 58fe558faf52ecde6705376700f848d0293d1cef
1354-
react-native-improved: 82e8e1912f5858a2ef60e56bb79ecba011fff0b2
1354+
react-native-improved: a79262806efea7f6fe5d51f1879f7faa730edaca
13551355
React-nativeconfig: a063483672b8add47a4875b0281e202908ff6747
13561356
React-NativeModulesApple: 169506a5fd708ab22811f76ee06a976595c367a1
13571357
React-perflogger: b61e5db8e5167f5e70366e820766c492847c082e
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <UIKit/UIKit.h>
9+
#import "RCTModalHostViewController.h"
10+
#import "RCTModalHostViewImproved.h"
11+
12+
@interface RCTModalHostViewControllerImproved : RCTModalHostViewController
13+
14+
@end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import "RCTModalHostViewControllerImproved.h"
9+
10+
#import "RCTLog.h"
11+
#import "RCTModalHostView.h"
12+
13+
@implementation RCTModalHostViewControllerImproved {
14+
CGRect _lastViewFrame;
15+
UIStatusBarStyle _preferredStatusBarStyle;
16+
BOOL _preferredStatusBarHidden;
17+
}
18+
19+
- (instancetype)init
20+
{
21+
if (!(self = [super init])) {
22+
return nil;
23+
}
24+
25+
self.modalInPresentation = YES;
26+
27+
// _preferredStatusBarStyle = [RCTSharedApplication() statusBarStyle];
28+
// _preferredStatusBarHidden = [RCTSharedApplication() isStatusBarHidden];
29+
30+
return self;
31+
}
32+
33+
34+
@end
35+

ios/Views/RCTModalHostViewImproved.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#import "RCTModalHostView.h"
99
#import "RCTModalHostViewImproved.h"
10+
#import "RCTModalHostViewControllerImproved.h"
11+
#import "RCTModalHostViewController.h"
1012

1113
NS_ASSUME_NONNULL_BEGIN
1214

@@ -15,17 +17,23 @@ NS_ASSUME_NONNULL_BEGIN
1517
@interface RCTModalHostViewImproved : RCTModalHostView
1618

1719
@property (nonatomic, weak) id<RCTModalHostViewInteractorImproved> delegate;
20+
@property (nonatomic, strong) UIWindow *modalWindow;
21+
22+
- (void)dismissModalViewControllerWithCompletion:(void (^)(void))completion;
1823

1924
@end
2025

2126
@protocol RCTModalHostViewInteractorImproved <NSObject>
2227

23-
- (void)presentModalHostView:(RCTModalHostViewImproved *)modalHostView
28+
- (void)presentModalHostView:(RCTModalHostView *)modalHostView
2429
withViewController:(RCTModalHostViewController *)viewController
2530
animated:(BOOL)animated;
2631
- (void)dismissModalHostView:(RCTModalHostViewImproved *)modalHostView
2732
withViewController:(RCTModalHostViewController *)viewController
2833
animated:(BOOL)animated;
34+
- (void)dismissModalHostViewWithCompletion:(RCTModalHostViewImproved *)modalHostView
35+
withViewController:(RCTModalHostViewController *)viewController
36+
animated:(BOOL)animated completion: (void (^)(void))completion;
2937

3038
@end
3139

ios/Views/RCTModalHostViewImproved.m

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <UIKit/UIKit.h>
9+
10+
#import "RCTAssert.h"
11+
#import "RCTBridge.h"
12+
#import "RCTModalHostViewControllerImproved.h"
13+
#import "RCTModalHostView.h"
14+
#import "RCTTouchHandler.h"
15+
#import "RCTUIManager.h"
16+
#import "RCTUtils.h"
17+
#import "UIView+React.h"
18+
19+
@implementation RCTModalHostViewImproved {
20+
__weak RCTBridge *_bridge;
21+
BOOL _isPresented;
22+
RCTModalHostViewController *_modalViewController;
23+
RCTTouchHandler *_touchHandler;
24+
UIView *_reactSubview;
25+
UIInterfaceOrientation _lastKnownOrientation;
26+
RCTDirectEventBlock _onRequestClose;
27+
}
28+
29+
RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
30+
RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : coder)
31+
32+
- (instancetype)initWithBridge:(RCTBridge *)bridge
33+
{
34+
self = [super initWithBridge:bridge];
35+
_modalViewController = [RCTModalHostViewController new];
36+
UIView *containerView = [UIView new];
37+
containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
38+
_modalViewController.view = containerView;
39+
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge];
40+
_isPresented = NO;
41+
42+
return self;
43+
}
44+
45+
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
46+
{
47+
[super insertReactSubview:subview atIndex:atIndex];
48+
[_modalViewController.view insertSubview:subview atIndex:0];
49+
}
50+
51+
- (void)ensurePresentedOnlyIfNeeded
52+
{
53+
BOOL shouldBePresented = !_isPresented && super.visible && self.window;
54+
if (shouldBePresented) {
55+
RCTAssert(self.reactViewController, @"Can't present modal view controller without a presenting view controller");
56+
_modalViewController.supportedInterfaceOrientations = [self supportedOrientationsMask];
57+
58+
if ([self.animationType isEqualToString:@"fade"]) {
59+
_modalViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
60+
} else if ([self.animationType isEqualToString:@"slide"]) {
61+
_modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
62+
}
63+
if (self.presentationStyle != UIModalPresentationNone) {
64+
_modalViewController.modalPresentationStyle = self.presentationStyle;
65+
}
66+
_modalViewController.presentationController.delegate = self;
67+
[self.delegate presentModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]];
68+
_isPresented = YES;
69+
}
70+
71+
BOOL shouldBeHidden = _isPresented && (!super.visible || !self.superview);
72+
if (shouldBeHidden) {
73+
[self dismissModalViewController];
74+
}
75+
}
76+
77+
- (UIInterfaceOrientationMask)supportedOrientationsMask
78+
{
79+
if (self.supportedOrientations.count == 0) {
80+
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
81+
return UIInterfaceOrientationMaskAll;
82+
} else {
83+
return UIInterfaceOrientationMaskPortrait;
84+
}
85+
}
86+
87+
UIInterfaceOrientationMask supportedOrientations = 0;
88+
for (NSString *orientation in self.supportedOrientations) {
89+
if ([orientation isEqualToString:@"portrait"]) {
90+
supportedOrientations |= UIInterfaceOrientationMaskPortrait;
91+
} else if ([orientation isEqualToString:@"portrait-upside-down"]) {
92+
supportedOrientations |= UIInterfaceOrientationMaskPortraitUpsideDown;
93+
} else if ([orientation isEqualToString:@"landscape"]) {
94+
supportedOrientations |= UIInterfaceOrientationMaskLandscape;
95+
} else if ([orientation isEqualToString:@"landscape-left"]) {
96+
supportedOrientations |= UIInterfaceOrientationMaskLandscapeLeft;
97+
} else if ([orientation isEqualToString:@"landscape-right"]) {
98+
supportedOrientations |= UIInterfaceOrientationMaskLandscapeRight;
99+
}
100+
}
101+
return supportedOrientations;
102+
}
103+
104+
- (void)dismissModalViewController
105+
{
106+
if (_isPresented) {
107+
[self.delegate dismissModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]];
108+
_isPresented = NO;
109+
}
110+
}
111+
112+
- (BOOL)hasAnimationType
113+
{
114+
return ![self.animationType isEqualToString:@"none"];
115+
}
116+
117+
@end

ios/Views/RCTModalHostViewImproved.mm

Lines changed: 0 additions & 41 deletions
This file was deleted.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import "RCTModalHostViewImprovedManager.h"
9+
#import "RCTModalHostViewControllerImproved.h"
10+
#import "RCTModalHostViewImproved.h"
11+
#import "RCTShadowView.h"
12+
#import "RCTModalManager.h"
13+
#import "RCTModalHostViewManager.h"
14+
15+
@interface RCTModalHostViewImprovedManager () <RCTModalHostViewInteractorImproved>
16+
17+
@end
18+
19+
@implementation RCTModalHostViewImprovedManager {
20+
NSPointerArray *_hostViews;
21+
}
22+
23+
RCT_EXPORT_MODULE()
24+
25+
- (UIView *)view
26+
{
27+
[super view];
28+
RCTModalHostViewImproved *view = [[RCTModalHostViewImproved alloc] initWithBridge:self.bridge];
29+
view.delegate = self;
30+
if (!_hostViews) {
31+
_hostViews = [NSPointerArray weakObjectsPointerArray];
32+
}
33+
[_hostViews addPointer:(__bridge void *)view];
34+
return view;
35+
}
36+
37+
- (void)presentModalHostView:(RCTModalHostViewImproved *)modalHostView
38+
withViewController:(RCTModalHostViewControllerImproved *)viewController
39+
animated:(BOOL)animated
40+
{
41+
dispatch_block_t completionBlock = ^{
42+
if (modalHostView.onShow) {
43+
modalHostView.onShow(nil);
44+
}
45+
};
46+
47+
dispatch_async(dispatch_get_main_queue(), ^{
48+
if (self.presentationBlock) {
49+
self.presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock);
50+
} else {
51+
[[modalHostView reactViewController] presentViewController:viewController
52+
animated:animated
53+
completion:completionBlock];
54+
}
55+
});
56+
}
57+
58+
- (void)dismissModalHostView:(RCTModalHostViewImproved *)modalHostView
59+
withViewController:(RCTModalHostViewControllerImproved *)viewController
60+
animated:(BOOL)animated
61+
{
62+
dispatch_block_t completionBlock = ^{
63+
if (modalHostView.identifier) {
64+
[[self.bridge moduleForClass:[RCTModalManager class]] modalDismissed:modalHostView.identifier];
65+
}
66+
};
67+
68+
dispatch_async(dispatch_get_main_queue(), ^{
69+
if (self.dismissalBlock) {
70+
self.dismissalBlock([modalHostView reactViewController], viewController, animated, completionBlock);
71+
} else {
72+
[viewController.presentingViewController dismissViewControllerAnimated:animated completion:completionBlock];
73+
}
74+
});
75+
}
76+
77+
- (void)invalidate
78+
{
79+
for (RCTModalHostView *hostView in _hostViews) {
80+
[hostView invalidate];
81+
}
82+
_hostViews = nil;
83+
}
84+
85+
@end

ios/Views/RCTModalHostViewImprovedManager.mm

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)