Skip to content

Commit 3b90fd6

Browse files
committed
feat(ios): RCTViewControllerAppearanceListener
1 parent c0adcea commit 3b90fd6

7 files changed

Lines changed: 156 additions & 7 deletions

File tree

packages/react-native/Libraries/AppDelegate/RCTDefaultReactNativeFactoryDelegate.mm

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

88
#import "RCTDefaultReactNativeFactoryDelegate.h"
99
#import <ReactCommon/RCTHost.h>
10+
#import <React/RCTViewController.h>
1011
#import "RCTAppSetupUtils.h"
1112
#import "RCTDependencyProvider.h"
1213
#if USE_THIRD_PARTY_JSC != 1
@@ -28,7 +29,7 @@ - (NSURL *_Nullable)sourceURLForBridge:(nonnull RCTBridge *)bridge
2829

2930
- (UIViewController *)createRootViewController
3031
{
31-
return [UIViewController new];
32+
return [RCTViewController new];
3233
}
3334

3435
- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions

packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
#import <UIKit/UIKit.h>
8+
#import <React/RCTViewController.h>
99

1010
@protocol RCTFabricModalHostViewControllerDelegate <NSObject>
1111
- (void)boundsDidChange:(CGRect)newBounds;
1212
@end
1313

14-
@interface RCTFabricModalHostViewController : UIViewController
14+
@interface RCTFabricModalHostViewController : RCTViewController
1515

1616
@property (nonatomic, weak) id<RCTFabricModalHostViewControllerDelegate> delegate;
1717

packages/react-native/React/Views/RCTModalHostViewController.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
#import <UIKit/UIKit.h>
8+
#import <React/RCTViewController.h>
99

1010
#ifndef RCT_REMOVE_LEGACY_ARCH
1111

1212
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
13-
@interface RCTModalHostViewController : UIViewController
13+
@interface RCTModalHostViewController : RCTViewController
1414

1515
@property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds);
1616

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
NS_ASSUME_NONNULL_BEGIN
11+
12+
@protocol RCTViewControllerAppearanceListener <NSObject>
13+
14+
@optional
15+
- (void)reactViewControllerDidAppear:(UIViewController *)viewController animated:(BOOL)animated;
16+
- (void)reactViewControllerDidDisappear:(UIViewController *)viewController animated:(BOOL)animated;
17+
18+
@end
19+
20+
@interface UIViewController (RCTViewControllerAppearance)
21+
22+
@property (nonatomic, assign, readonly) BOOL reactViewControllerIsVisible;
23+
24+
- (void)reactAddViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener>)listener;
25+
- (void)reactRemoveViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener>)listener;
26+
27+
/**
28+
* Call from `viewDidAppear:` / `viewDidDisappear:` in UIViewController subclasses
29+
* that cannot inherit from RCTViewController.
30+
*/
31+
- (void)reactNotifyViewControllerDidAppear:(BOOL)animated;
32+
- (void)reactNotifyViewControllerDidDisappear:(BOOL)animated;
33+
34+
@end
35+
36+
/**
37+
* UIViewController subclass that forwards appearance events to registered listeners.
38+
*/
39+
@interface RCTViewController : UIViewController
40+
@end
41+
42+
NS_ASSUME_NONNULL_END
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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 "RCTViewController.h"
9+
10+
#import <objc/runtime.h>
11+
12+
@interface RCTViewControllerAppearanceState : NSObject
13+
14+
@property (nonatomic, strong, readonly) NSHashTable<id<RCTViewControllerAppearanceListener>> *listeners;
15+
@property (nonatomic, assign) BOOL visible;
16+
17+
@end
18+
19+
@implementation RCTViewControllerAppearanceState
20+
21+
- (instancetype)init
22+
{
23+
if (self = [super init]) {
24+
_listeners = [NSHashTable weakObjectsHashTable];
25+
}
26+
return self;
27+
}
28+
29+
@end
30+
31+
@implementation UIViewController (RCTViewControllerAppearance)
32+
33+
- (RCTViewControllerAppearanceState *)reactViewControllerAppearanceState
34+
{
35+
RCTViewControllerAppearanceState *state =
36+
objc_getAssociatedObject(self, @selector(reactViewControllerAppearanceState));
37+
if (!state) {
38+
state = [RCTViewControllerAppearanceState new];
39+
objc_setAssociatedObject(
40+
self, @selector(reactViewControllerAppearanceState), state, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
41+
}
42+
return state;
43+
}
44+
45+
- (BOOL)reactViewControllerIsVisible
46+
{
47+
return [self reactViewControllerAppearanceState].visible;
48+
}
49+
50+
- (void)reactAddViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener>)listener
51+
{
52+
RCTViewControllerAppearanceState *state = [self reactViewControllerAppearanceState];
53+
[state.listeners addObject:listener];
54+
55+
if (state.visible && [listener respondsToSelector:@selector(reactViewControllerDidAppear:animated:)]) {
56+
[listener reactViewControllerDidAppear:self animated:NO];
57+
}
58+
}
59+
60+
- (void)reactRemoveViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener>)listener
61+
{
62+
[[self reactViewControllerAppearanceState].listeners removeObject:listener];
63+
}
64+
65+
- (void)reactNotifyViewControllerDidAppear:(BOOL)animated
66+
{
67+
RCTViewControllerAppearanceState *state = [self reactViewControllerAppearanceState];
68+
state.visible = YES;
69+
70+
for (id<RCTViewControllerAppearanceListener> listener in state.listeners.allObjects) {
71+
if ([listener respondsToSelector:@selector(reactViewControllerDidAppear:animated:)]) {
72+
[listener reactViewControllerDidAppear:self animated:animated];
73+
}
74+
}
75+
}
76+
77+
- (void)reactNotifyViewControllerDidDisappear:(BOOL)animated
78+
{
79+
RCTViewControllerAppearanceState *state = [self reactViewControllerAppearanceState];
80+
state.visible = NO;
81+
82+
for (id<RCTViewControllerAppearanceListener> listener in state.listeners.allObjects) {
83+
if ([listener respondsToSelector:@selector(reactViewControllerDidDisappear:animated:)]) {
84+
[listener reactViewControllerDidDisappear:self animated:animated];
85+
}
86+
}
87+
}
88+
89+
@end
90+
91+
@implementation RCTViewController
92+
93+
- (void)viewDidAppear:(BOOL)animated
94+
{
95+
[super viewDidAppear:animated];
96+
[self reactNotifyViewControllerDidAppear:animated];
97+
}
98+
99+
- (void)viewDidDisappear:(BOOL)animated
100+
{
101+
[super viewDidDisappear:animated];
102+
[self reactNotifyViewControllerDidDisappear:animated];
103+
}
104+
105+
@end

packages/react-native/React/Views/RCTWrapperViewController.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
#import <UIKit/UIKit.h>
8+
#import <React/RCTViewController.h>
99

1010
@class RCTWrapperViewController;
1111

12-
@interface RCTWrapperViewController : UIViewController
12+
@interface RCTWrapperViewController : RCTViewController
1313

1414
- (instancetype)initWithContentView:(UIView *)contentView NS_DESIGNATED_INITIALIZER;
1515

packages/react-native/scripts/ios-prebuild/templates/React-umbrella.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@
274274
#import <React/RCTVibration.h>
275275
#import <React/RCTVibrationPlugins.h>
276276
#import <React/RCTView.h>
277+
#import <React/RCTViewController.h>
277278
#import <React/RCTViewManager.h>
278279
#import <React/RCTViewUtils.h>
279280
#import <React/RCTVirtualTextShadowView.h>

0 commit comments

Comments
 (0)