Skip to content

Commit d0b645e

Browse files
t0maboroCopilot
andauthored
refactor(iOS, FormSheet v5): Move AppearanceCoordinator/Applicator to ContentController (#4074)
## Description Previously, there was a tight coupling where `RNSFormSheetContentController` and `RNSFormSheetHostComponentView` had a direct dependency. By introducing dedicated provider protocols, the UIKit is now completely agnostic of React. Closes: software-mansion/react-native-screens-labs#1454 ## Changes - defining contracts that Host now implements to supply data. - removed all `RNSFormSheetHostComponentView` dependencies from `RNSFormSheetContentController` and `RNSFormSheetAppearanceApplicator` - moved invalidation flags logic from the Host to the Controller ## Before & after - visual documentation N/A - refactor ## Test plan No regression in existing examples ## Checklist - [ ] Included code example that can be used to test this change. - [ ] For visual changes, included screenshots / GIFs / recordings documenting the change. - [ ] For API changes, updated relevant public types. - [ ] Ensured that CI passes --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 84cd0d1 commit d0b645e

7 files changed

Lines changed: 216 additions & 68 deletions

ios/gamma/modals/form-sheet/RNSFormSheetAppearanceApplicator.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
#pragma once
22

33
#import <Foundation/Foundation.h>
4+
#import "RNSFormSheetProviders.h"
45

56
@class RNSFormSheetAppearanceCoordinator;
67
@class RNSFormSheetContentController;
7-
@class RNSFormSheetHostComponentView;
88

99
NS_ASSUME_NONNULL_BEGIN
1010

1111
@interface RNSFormSheetAppearanceApplicator : NSObject
1212

13-
- (void)updateAppearanceIfNeededForHost:(RNSFormSheetHostComponentView *)host
14-
controller:(RNSFormSheetContentController *)controller
15-
coordinator:(RNSFormSheetAppearanceCoordinator *)coordinator;
13+
- (void)updateAppearanceIfNeededWithAppearanceProvider:(id<RNSFormSheetAppearanceProvider>)appearanceProvider
14+
behaviorProvider:(id<RNSFormSheetBehaviorProvider>)behaviorProvider
15+
controller:(RNSFormSheetContentController *)controller
16+
coordinator:(RNSFormSheetAppearanceCoordinator *)coordinator;
1617

1718
- (void)resetInitialDetent;
1819

ios/gamma/modals/form-sheet/RNSFormSheetAppearanceApplicator.mm

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#import "RNSFormSheetAppearanceUpdateFlags.h"
77
#import "RNSFormSheetContentController.h"
88
#import "RNSFormSheetDetentResolver.h"
9-
#import "RNSFormSheetHostComponentView.h"
109

1110
@implementation RNSFormSheetAppearanceApplicator {
1211
BOOL _initialDetentApplied;
@@ -25,20 +24,24 @@ - (void)resetInitialDetent
2524
_initialDetentApplied = NO;
2625
}
2726

28-
- (void)updateAppearanceIfNeededForHost:(RNSFormSheetHostComponentView *)host
29-
controller:(RNSFormSheetContentController *)controller
30-
coordinator:(RNSFormSheetAppearanceCoordinator *)coordinator
27+
- (void)updateAppearanceIfNeededWithAppearanceProvider:(id<RNSFormSheetAppearanceProvider>)appearanceProvider
28+
behaviorProvider:(id<RNSFormSheetBehaviorProvider>)behaviorProvider
29+
controller:(RNSFormSheetContentController *)controller
30+
coordinator:(RNSFormSheetAppearanceCoordinator *)coordinator
3131
{
3232
[coordinator updateIfNeeds:RNSFormSheetAppearanceUpdateFlagsConfiguration
3333
performOperations:^{
34-
[self updateSheetConfigurationForHost:host controller:controller];
34+
[self updateSheetConfigurationForAppearanceProvider:appearanceProvider
35+
behaviorProvider:behaviorProvider
36+
controller:controller];
3537
}];
3638
}
3739

3840
#pragma mark - Updaters
3941

40-
- (void)updateSheetConfigurationForHost:(RNSFormSheetHostComponentView *)host
41-
controller:(RNSFormSheetContentController *)controller
42+
- (void)updateSheetConfigurationForAppearanceProvider:(id<RNSFormSheetAppearanceProvider>)appearanceProvider
43+
behaviorProvider:(id<RNSFormSheetBehaviorProvider>)behaviorProvider
44+
controller:(RNSFormSheetContentController *)controller
4245
{
4346
#if !TARGET_OS_TV
4447
UISheetPresentationController *sheet = controller.sheetPresentationController;
@@ -47,22 +50,23 @@ - (void)updateSheetConfigurationForHost:(RNSFormSheetHostComponentView *)host
4750
@"[RNScreens] sheetPresentationController is nil. Ensure modalPresentationStyle is set to UIModalPresentationFormSheet.");
4851

4952
NSArray<UISheetPresentationControllerDetent *> *nativeDetents =
50-
[RNSFormSheetDetentResolver buildSheetDetentsForFractions:host.detents];
53+
[RNSFormSheetDetentResolver buildSheetDetentsForFractions:behaviorProvider.detents];
5154

5255
UISheetPresentationControllerDetentIdentifier initialDetentIdentifier = nil;
5356
if (!_initialDetentApplied) {
54-
initialDetentIdentifier = [RNSFormSheetDetentResolver initialDetentIdentifierForDetents:nativeDetents
55-
atRequestedIndex:host.initialDetentIndex];
57+
initialDetentIdentifier =
58+
[RNSFormSheetDetentResolver initialDetentIdentifierForDetents:nativeDetents
59+
atRequestedIndex:behaviorProvider.initialDetentIndex];
5660
_initialDetentApplied = YES;
5761
}
5862

59-
UISheetPresentationControllerDetentIdentifier largestUndimmedDetentIdentifier =
60-
[RNSFormSheetDetentResolver largestUndimmedDetentIdentifierForDetents:nativeDetents
61-
atRequestedIndex:host.largestUndimmedDetentIndex];
63+
UISheetPresentationControllerDetentIdentifier largestUndimmedDetentIdentifier = [RNSFormSheetDetentResolver
64+
largestUndimmedDetentIdentifierForDetents:nativeDetents
65+
atRequestedIndex:appearanceProvider.largestUndimmedDetentIndex];
6266

63-
BOOL prefersGrabberVisible = host.prefersGrabberVisible;
64-
CGFloat preferredCornerRadius = host.preferredCornerRadius;
65-
BOOL prefersScrollingExpandsWhenScrolledToEdge = host.prefersScrollingExpandsWhenScrolledToEdge;
67+
BOOL prefersGrabberVisible = appearanceProvider.prefersGrabberVisible;
68+
CGFloat preferredCornerRadius = appearanceProvider.preferredCornerRadius;
69+
BOOL prefersScrollingExpandsWhenScrolledToEdge = behaviorProvider.prefersScrollingExpandsWhenScrolledToEdge;
6670

6771
[sheet animateChanges:^{
6872
sheet.detents = nativeDetents;

ios/gamma/modals/form-sheet/RNSFormSheetContentController.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#import <UIKit/UIKit.h>
4+
#import "RNSFormSheetProviders.h"
45

56
NS_ASSUME_NONNULL_BEGIN
67

@@ -20,10 +21,21 @@ NS_ASSUME_NONNULL_BEGIN
2021

2122
@property (nonatomic, weak, nullable) id<RNSFormSheetContentControllerDelegate> delegate;
2223

24+
@property (nonatomic, weak, nullable) id<RNSFormSheetPresentationProvider> presentationProvider;
25+
@property (nonatomic, weak, nullable) id<RNSFormSheetAppearanceProvider> appearanceProvider;
26+
@property (nonatomic, weak, nullable) id<RNSFormSheetBehaviorProvider> behaviorProvider;
27+
2328
@property (nonatomic, readonly, nonnull) RNSFormSheetContentView *contentView;
2429

25-
- (void)presentFromWindowIfNeeded:(nonnull UIWindow *)window;
26-
- (void)dismissIfNeeded;
30+
#pragma mark - Signals
31+
32+
- (void)setNeedsPresentationUpdate;
33+
- (void)setNeedsAppearanceUpdate;
34+
- (void)setNeedsInitialDetentReset;
35+
36+
#pragma mark - Updates
37+
38+
- (void)flushPendingUpdates;
2739

2840
@end
2941

ios/gamma/modals/form-sheet/RNSFormSheetContentController.mm

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#import "RNSFormSheetContentController.h"
2+
#import "RNSFormSheetAppearanceApplicator.h"
3+
#import "RNSFormSheetAppearanceCoordinator.h"
4+
#import "RNSFormSheetAppearanceUpdateFlags.h"
25
#import "RNSFormSheetContentView.h"
36
#import "RNSPresentationSourceProvider.h"
47

@@ -13,12 +16,22 @@ @interface RNSFormSheetContentController () <UIAdaptivePresentationControllerDel
1316
>
1417
@end
1518

16-
@implementation RNSFormSheetContentController
19+
@implementation RNSFormSheetContentController {
20+
RNSFormSheetAppearanceCoordinator *_Nonnull _appearanceCoordinator;
21+
RNSFormSheetAppearanceApplicator *_Nonnull _appearanceApplicator;
22+
23+
BOOL _needsInitialDetentReset;
24+
}
1725

1826
- (instancetype)init
1927
{
2028
if (self = [super init]) {
2129
self.modalPresentationStyle = UIModalPresentationFormSheet;
30+
31+
_appearanceCoordinator = [RNSFormSheetAppearanceCoordinator new];
32+
_appearanceApplicator = [RNSFormSheetAppearanceApplicator new];
33+
34+
_needsInitialDetentReset = NO;
2235
}
2336
return self;
2437
}
@@ -46,6 +59,27 @@ - (void)viewDidLayoutSubviews
4659

4760
#pragma mark - Presentation
4861

62+
- (void)updatePresentationState
63+
{
64+
id<RNSFormSheetPresentationProvider> presentationProvider = self.presentationProvider;
65+
66+
RCTAssert(presentationProvider != nil,
67+
@"[RNScreens] Presentation provider must be set before updating presentation state.");
68+
69+
if (presentationProvider == nil) {
70+
return;
71+
}
72+
73+
if (presentationProvider.isOpen) {
74+
UIWindow *window = presentationProvider.hostWindow;
75+
if (window != nil) {
76+
[self presentFromWindowIfNeeded:window];
77+
}
78+
} else {
79+
[self dismissIfNeeded];
80+
}
81+
}
82+
4983
- (void)prepareForPresentation
5084
{
5185
// The presentation controller is recreated by UIKit on every present/dismiss cycle.
@@ -88,6 +122,62 @@ - (void)dismissIfNeeded
88122
[self dismissViewControllerAnimated:YES completion:nil];
89123
}
90124

125+
#pragma mark - Appearance
126+
127+
- (void)updateAppearanceIfNeeded
128+
{
129+
id<RNSFormSheetAppearanceProvider> appearanceProvider = self.appearanceProvider;
130+
id<RNSFormSheetBehaviorProvider> behaviorProvider = self.behaviorProvider;
131+
132+
RCTAssert(appearanceProvider != nil, @"[RNScreens] Appearance provider must be set before updating appearance.");
133+
134+
RCTAssert(behaviorProvider != nil, @"[RNScreens] Behavior provider must be set before updating appearance.");
135+
136+
if (appearanceProvider == nil || behaviorProvider == nil) {
137+
return;
138+
}
139+
140+
if (_needsInitialDetentReset) {
141+
_needsInitialDetentReset = NO;
142+
[_appearanceApplicator resetInitialDetent];
143+
}
144+
145+
[_appearanceApplicator updateAppearanceIfNeededWithAppearanceProvider:appearanceProvider
146+
behaviorProvider:behaviorProvider
147+
controller:self
148+
coordinator:_appearanceCoordinator];
149+
150+
// TODO: @t0maboro - decouple presentation logic from AppearanceCoordinator
151+
[_appearanceCoordinator updateIfNeeds:RNSFormSheetAppearanceUpdateFlagsPresentation
152+
performOperations:^{
153+
[self updatePresentationState];
154+
}];
155+
}
156+
157+
#pragma mark - Signals
158+
159+
- (void)setNeedsPresentationUpdate
160+
{
161+
[_appearanceCoordinator setNeeds:RNSFormSheetAppearanceUpdateFlagsPresentation];
162+
}
163+
164+
- (void)setNeedsAppearanceUpdate
165+
{
166+
[_appearanceCoordinator setNeeds:RNSFormSheetAppearanceUpdateFlagsConfiguration];
167+
}
168+
169+
- (void)setNeedsInitialDetentReset
170+
{
171+
_needsInitialDetentReset = YES;
172+
}
173+
174+
#pragma mark - Updates
175+
176+
- (void)flushPendingUpdates
177+
{
178+
[self updateAppearanceIfNeeded];
179+
}
180+
91181
#pragma mark - UIAdaptivePresentationControllerDelegate
92182

93183
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController

ios/gamma/modals/form-sheet/RNSFormSheetHostComponentView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
2020
- (const std::vector<double> &)detents;
2121
#endif
2222

23+
@property (nonatomic, readonly) BOOL isOpen;
2324
@property (nonatomic, readonly) BOOL prefersGrabberVisible;
2425
@property (nonatomic, readonly) CGFloat preferredCornerRadius;
2526
@property (nonatomic, readonly) NSInteger largestUndimmedDetentIndex;

0 commit comments

Comments
 (0)