Skip to content

Commit e5a9227

Browse files
authored
[macOS] Fix build on fabric (#3154)
## Description Right now Gesture Handler fails to build on macOS on new architecture. This PR brings changes necessary to fix this problem. Fixes #3144. ## Test plan Build macOS example app on new architecture.
1 parent 8351e4b commit e5a9227

5 files changed

+89
-4
lines changed

apple/RNGestureHandlerButton.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
#import "RNGestureHandler.h"
1010

1111
#if TARGET_OS_OSX
12+
13+
#include <react/renderer/core/LayoutMetrics.h>
14+
15+
@protocol RCTComponentViewProtocol;
16+
1217
@interface RNGestureHandlerButton : NSControl
1318
#else
1419
@interface RNGestureHandlerButton : UIControl
@@ -19,4 +24,11 @@
1924
@property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
2025
@property (nonatomic) BOOL userEnabled;
2126

27+
#if TARGET_OS_OSX
28+
- (void)mountChildComponentView:(RNGHUIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index;
29+
- (void)unmountChildComponentView:(RNGHUIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index;
30+
- (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics
31+
oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics;
32+
#endif
33+
2234
@end

apple/RNGestureHandlerButton.m renamed to apple/RNGestureHandlerButton.mm

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@
1010

1111
#if !TARGET_OS_OSX
1212
#import <UIKit/UIKit.h>
13+
#else
14+
#import <React/RCTUIKit.h>
1315
#endif
1416

17+
#import <React/RCTConversions.h>
18+
#import <React/RCTFabricComponentsPlugins.h>
19+
1520
/**
1621
* Gesture Handler Button components overrides standard mechanism used by RN
1722
* to determine touch target, which normally would reurn the UIView that is placed
@@ -82,4 +87,59 @@ - (RNGHUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
8287
}
8388
#endif
8489

90+
#if TARGET_OS_OSX
91+
- (void)mountChildComponentView:(RNGHUIView *)childComponentView index:(NSInteger)index
92+
{
93+
if (childComponentView.superview != nil) {
94+
return;
95+
}
96+
97+
if (index < [[self subviews] count]) {
98+
// Get the view currently at your desired index
99+
NSView *existingView = [[self subviews] objectAtIndex:index];
100+
101+
// Now use this to insert your new view above the existing one
102+
[self addSubview:childComponentView positioned:NSWindowAbove relativeTo:existingView];
103+
} else {
104+
// if the index is out of bounds, add the new subview at the end
105+
[self addSubview:childComponentView];
106+
}
107+
}
108+
109+
- (void)unmountChildComponentView:(RNGHUIView *)childComponentView index:(NSInteger)index
110+
{
111+
[childComponentView removeFromSuperview];
112+
}
113+
114+
- (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics
115+
oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics
116+
{
117+
bool forceUpdate = oldLayoutMetrics == facebook::react::EmptyLayoutMetrics;
118+
119+
if (forceUpdate || (layoutMetrics.frame != oldLayoutMetrics.frame)) {
120+
CGRect frame = RCTCGRectFromRect(layoutMetrics.frame);
121+
122+
if (!std::isfinite(frame.origin.x) || !std::isfinite(frame.origin.y) || !std::isfinite(frame.size.width) ||
123+
!std::isfinite(frame.size.height)) {
124+
// CALayer will crash if we pass NaN or Inf values.
125+
// It's unclear how to detect this case on cross-platform manner holistically, so we have to do it on the mounting
126+
// layer as well. NaN/Inf is a kinda valid result of some math operations. Even if we can (and should) detect (and
127+
// report early) incorrect (NaN and Inf) values which come from JavaScript side, we sometimes cannot backtrace the
128+
// sources of a calculation that produced an incorrect/useless result.
129+
RCTLogWarn(
130+
@"-[UIView(ComponentViewProtocol) updateLayoutMetrics:oldLayoutMetrics:]: Received invalid layout metrics (%@) for a view (%@).",
131+
NSStringFromCGRect(frame),
132+
self);
133+
} else {
134+
self.frame = frame;
135+
self.bounds = CGRect{CGPointZero, frame.size};
136+
}
137+
}
138+
139+
if (forceUpdate || (layoutMetrics.displayType != oldLayoutMetrics.displayType)) {
140+
self.hidden = layoutMetrics.displayType == facebook::react::DisplayType::None;
141+
}
142+
}
143+
#endif
144+
85145
@end

apple/RNGestureHandlerButtonComponentView.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#ifdef RCT_NEW_ARCH_ENABLED
22

3+
#if !TARGET_OS_OSX
34
#import <UIKit/UIKit.h>
5+
#else
6+
#import <React/RCTUIKit.h>
7+
#endif
48

59
#import <React/RCTViewComponentView.h>
610

apple/RNGestureHandlerButtonComponentView.mm

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ @implementation RNGestureHandlerButtonComponentView {
2121
RNGestureHandlerButton *_buttonView;
2222
}
2323

24+
#if TARGET_OS_OSX
25+
// Here we want to disable view recycling on buttons. Listeners are not removed from views when they're being unmounted,
26+
// therefore after navigating through other screens buttons may have different actions then they are supposed to have.
27+
+ (BOOL)shouldBeRecycled
28+
{
29+
return NO;
30+
}
31+
#endif
32+
2433
// Needed because of this: https://github.com/facebook/react-native/pull/37274
2534
+ (void)load
2635
{
@@ -40,12 +49,12 @@ - (instancetype)initWithFrame:(CGRect)frame
4049
return self;
4150
}
4251

43-
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
52+
- (void)mountChildComponentView:(RNGHUIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
4453
{
4554
[_buttonView mountChildComponentView:childComponentView index:index];
4655
}
4756

48-
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
57+
- (void)unmountChildComponentView:(RNGHUIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
4958
{
5059
[_buttonView unmountChildComponentView:childComponentView index:index];
5160
}
@@ -97,7 +106,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
97106
const auto &newProps = *std::static_pointer_cast<const RNGestureHandlerButtonProps>(props);
98107

99108
_buttonView.userEnabled = newProps.enabled;
100-
#if !TARGET_OS_TV
109+
#if !TARGET_OS_TV && !TARGET_OS_OSX
101110
_buttonView.exclusiveTouch = newProps.exclusive;
102111
#endif
103112
_buttonView.hitTestEdgeInsets = UIEdgeInsetsMake(

apple/RNGestureHandlerButtonManager.m renamed to apple/RNGestureHandlerButtonManager.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ @implementation RNGestureHandlerButtonManager
3030

3131
- (RNGHUIView *)view
3232
{
33-
return [RNGestureHandlerButton new];
33+
return (RNGHUIView *)[RNGestureHandlerButton new];
3434
}
3535

3636
@end

0 commit comments

Comments
 (0)