Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

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

- (UIViewController *)createRootViewController
{
return [UIViewController new];
return [RCTViewController new];
}

- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions
Expand Down
17 changes: 17 additions & 0 deletions packages/react-native/React/Views/RCTViewController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/


#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface RCTViewController : UIViewController

@end

NS_ASSUME_NONNULL_END
29 changes: 29 additions & 0 deletions packages/react-native/React/Views/RCTViewController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// RCTViewController.m
// React-Core
//
// Created by Hanno Goedecke on 27.04.26.
//

#import "RCTViewController.h"
#import <React/UIViewController+React.h>

@interface RCTViewController ()

@end

@implementation RCTViewController

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self reactNotifyViewControllerDidAppear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self reactNotifyViewControllerDidDisappear:animated];
}

@end
4 changes: 2 additions & 2 deletions packages/react-native/React/Views/RCTWrapperViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
* LICENSE file in the root directory of this source tree.
*/

#import <UIKit/UIKit.h>
#import <React/RCTViewController.h>

@class RCTWrapperViewController;

@interface RCTWrapperViewController : UIViewController
@interface RCTWrapperViewController : RCTViewController

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

Expand Down
36 changes: 36 additions & 0 deletions packages/react-native/React/Views/UIViewController+React.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@protocol RCTViewControllerAppearanceListener <NSObject>

@optional
- (void)reactViewControllerDidAppear:(UIViewController *)viewController animated:(BOOL)animated;
- (void)reactViewControllerDidDisappear:(UIViewController *)viewController animated:(BOOL)animated;

@end

@interface UIViewController (React)

@property (nonatomic, assign, readonly) BOOL reactViewControllerIsVisible;

- (void)reactAddViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener>)listener;
- (void)reactRemoveViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener>)listener;

/**
* Call from `viewDidAppear:` / `viewDidDisappear:` in UIViewController subclasses
* that want to notify registered React Native appearance listeners.
*/
- (void)reactNotifyViewControllerDidAppear:(BOOL)animated;
- (void)reactNotifyViewControllerDidDisappear:(BOOL)animated;

@end

NS_ASSUME_NONNULL_END
89 changes: 89 additions & 0 deletions packages/react-native/React/Views/UIViewController+React.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import "UIViewController+React.h"

#import <objc/runtime.h>

@interface RCTViewControllerAppearanceState : NSObject

@property (nonatomic, strong, readonly) NSHashTable<id<RCTViewControllerAppearanceListener>> *listeners;
@property (nonatomic, assign) BOOL visible;

@end

@implementation RCTViewControllerAppearanceState

- (instancetype)init
{
if (self = [super init]) {
_listeners = [NSHashTable weakObjectsHashTable];
}
return self;
}

@end

@implementation UIViewController (React)

- (RCTViewControllerAppearanceState *)reactViewControllerAppearanceState
{
RCTViewControllerAppearanceState *state =
objc_getAssociatedObject(self, @selector(reactViewControllerAppearanceState));
if (!state) {
state = [RCTViewControllerAppearanceState new];
objc_setAssociatedObject(
self, @selector(reactViewControllerAppearanceState), state, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return state;
}

- (BOOL)reactViewControllerIsVisible
{
return [self reactViewControllerAppearanceState].visible;
}

- (void)reactAddViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener>)listener
{
RCTViewControllerAppearanceState *state = [self reactViewControllerAppearanceState];
[state.listeners addObject:listener];

if (state.visible && [listener respondsToSelector:@selector(reactViewControllerDidAppear:animated:)]) {
[listener reactViewControllerDidAppear:self animated:NO];
}
}

- (void)reactRemoveViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener>)listener
{
[[self reactViewControllerAppearanceState].listeners removeObject:listener];
}

- (void)reactNotifyViewControllerDidAppear:(BOOL)animated
{
RCTViewControllerAppearanceState *state = [self reactViewControllerAppearanceState];
state.visible = YES;

for (id<RCTViewControllerAppearanceListener> listener in state.listeners.allObjects) {
if ([listener respondsToSelector:@selector(reactViewControllerDidAppear:animated:)]) {
[listener reactViewControllerDidAppear:self animated:animated];
}
}
}

- (void)reactNotifyViewControllerDidDisappear:(BOOL)animated
{
RCTViewControllerAppearanceState *state = [self reactViewControllerAppearanceState];
state.visible = NO;

for (id<RCTViewControllerAppearanceListener> listener in state.listeners.allObjects) {
if ([listener respondsToSelector:@selector(reactViewControllerDidDisappear:animated:)]) {
[listener reactViewControllerDidDisappear:self animated:animated];
}
}
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@
#import <React/RCTWrapperViewController.h>
#import <React/UIView+Private.h>
#import <React/UIView+React.h>
#import <React/UIViewController+React.h>

FOUNDATION_EXPORT double ReactVersionNumber;
FOUNDATION_EXPORT const unsigned char ReactVersionString[];
18 changes: 17 additions & 1 deletion scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,14 @@ category UIView(React) {
public virtual void removeReactSubview:(UIView* subview);
}

category UIViewController(React) {
public @property (assign, readonly) BOOL reactViewControllerIsVisible;
public virtual void reactAddViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener> listener);
public virtual void reactNotifyViewControllerDidAppear:(BOOL animated);
public virtual void reactNotifyViewControllerDidDisappear:(BOOL animated);
public virtual void reactRemoveViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener> listener);
}

class ObjCTimerRegistry : public facebook::react::PlatformTimerRegistry {
public ObjCTimerRegistry();
public RCTTiming* _Null_unspecified timing;
Expand Down Expand Up @@ -2467,6 +2475,9 @@ interface RCTViewComponentView : public UIView <RCTComponentViewProtocol, RCTTou
public virtual void updateProps:oldProps:(const facebook::react::Props::Shared& props, const facebook::react::Props::Shared& oldProps);
}

interface RCTViewController : public UIViewController {
}

interface RCTViewManager : public NSObject <RCTBridgeModule> {
public @property (weak) RCTBridge* bridge;
public CGFloat RCTJSONParseOnlyNumber(id json);
Expand Down Expand Up @@ -2530,7 +2541,7 @@ interface RCTWrapperView : public UIView {
public virtual instancetype initWithBridge:(RCTBridge* bridge);
}

interface RCTWrapperViewController : public UIViewController {
interface RCTWrapperViewController : public RCTViewController {
public virtual instancetype initWithContentView:(UIView* contentView);
}

Expand Down Expand Up @@ -3493,6 +3504,11 @@ protocol RCTValueAnimatedNodeObserver : public NSObject {
public virtual void animatedNode:didUpdateValue:(RCTValueAnimatedNode* node, CGFloat value);
}

protocol RCTViewControllerAppearanceListener : public NSObject {
public virtual void reactViewControllerDidAppear:animated:(UIViewController* viewController, BOOL animated);
public virtual void reactViewControllerDidDisappear:animated:(UIViewController* viewController, BOOL animated);
}

protocol RCTVirtualViewContainerProtocol {
public virtual RCTVirtualViewContainerState* virtualViewContainerState();
}
Expand Down
18 changes: 17 additions & 1 deletion scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,14 @@ category UIView(React) {
public virtual void removeReactSubview:(UIView* subview);
}

category UIViewController(React) {
public @property (assign, readonly) BOOL reactViewControllerIsVisible;
public virtual void reactAddViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener> listener);
public virtual void reactNotifyViewControllerDidAppear:(BOOL animated);
public virtual void reactNotifyViewControllerDidDisappear:(BOOL animated);
public virtual void reactRemoveViewControllerAppearanceListener:(id<RCTViewControllerAppearanceListener> listener);
}

class ObjCTimerRegistry : public facebook::react::PlatformTimerRegistry {
public ObjCTimerRegistry();
public RCTTiming* _Null_unspecified timing;
Expand Down Expand Up @@ -2467,6 +2475,9 @@ interface RCTViewComponentView : public UIView <RCTComponentViewProtocol, RCTTou
public virtual void updateProps:oldProps:(const facebook::react::Props::Shared& props, const facebook::react::Props::Shared& oldProps);
}

interface RCTViewController : public UIViewController {
}

interface RCTViewManager : public NSObject <RCTBridgeModule> {
public @property (weak) RCTBridge* bridge;
public CGFloat RCTJSONParseOnlyNumber(id json);
Expand Down Expand Up @@ -2530,7 +2541,7 @@ interface RCTWrapperView : public UIView {
public virtual instancetype initWithBridge:(RCTBridge* bridge);
}

interface RCTWrapperViewController : public UIViewController {
interface RCTWrapperViewController : public RCTViewController {
public virtual instancetype initWithContentView:(UIView* contentView);
}

Expand Down Expand Up @@ -3493,6 +3504,11 @@ protocol RCTValueAnimatedNodeObserver : public NSObject {
public virtual void animatedNode:didUpdateValue:(RCTValueAnimatedNode* node, CGFloat value);
}

protocol RCTViewControllerAppearanceListener : public NSObject {
public virtual void reactViewControllerDidAppear:animated:(UIViewController* viewController, BOOL animated);
public virtual void reactViewControllerDidDisappear:animated:(UIViewController* viewController, BOOL animated);
}

protocol RCTVirtualViewContainerProtocol {
public virtual RCTVirtualViewContainerState* virtualViewContainerState();
}
Expand Down
Loading