Skip to content

Commit

Permalink
[macOS] Fix build on fabric (#3154)
Browse files Browse the repository at this point in the history
## 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.
  • Loading branch information
m-bert authored Oct 17, 2024
1 parent 8351e4b commit e5a9227
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 4 deletions.
12 changes: 12 additions & 0 deletions apple/RNGestureHandlerButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
#import "RNGestureHandler.h"

#if TARGET_OS_OSX

#include <react/renderer/core/LayoutMetrics.h>

@protocol RCTComponentViewProtocol;

@interface RNGestureHandlerButton : NSControl
#else
@interface RNGestureHandlerButton : UIControl
Expand All @@ -19,4 +24,11 @@
@property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
@property (nonatomic) BOOL userEnabled;

#if TARGET_OS_OSX
- (void)mountChildComponentView:(RNGHUIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index;
- (void)unmountChildComponentView:(RNGHUIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index;
- (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics
oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics;
#endif

@end
60 changes: 60 additions & 0 deletions apple/RNGestureHandlerButton.m → apple/RNGestureHandlerButton.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@

#if !TARGET_OS_OSX
#import <UIKit/UIKit.h>
#else
#import <React/RCTUIKit.h>
#endif

#import <React/RCTConversions.h>
#import <React/RCTFabricComponentsPlugins.h>

/**
* Gesture Handler Button components overrides standard mechanism used by RN
* to determine touch target, which normally would reurn the UIView that is placed
Expand Down Expand Up @@ -82,4 +87,59 @@ - (RNGHUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
}
#endif

#if TARGET_OS_OSX
- (void)mountChildComponentView:(RNGHUIView *)childComponentView index:(NSInteger)index
{
if (childComponentView.superview != nil) {
return;
}

if (index < [[self subviews] count]) {
// Get the view currently at your desired index
NSView *existingView = [[self subviews] objectAtIndex:index];

// Now use this to insert your new view above the existing one
[self addSubview:childComponentView positioned:NSWindowAbove relativeTo:existingView];
} else {
// if the index is out of bounds, add the new subview at the end
[self addSubview:childComponentView];
}
}

- (void)unmountChildComponentView:(RNGHUIView *)childComponentView index:(NSInteger)index
{
[childComponentView removeFromSuperview];
}

- (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics
oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics
{
bool forceUpdate = oldLayoutMetrics == facebook::react::EmptyLayoutMetrics;

if (forceUpdate || (layoutMetrics.frame != oldLayoutMetrics.frame)) {
CGRect frame = RCTCGRectFromRect(layoutMetrics.frame);

if (!std::isfinite(frame.origin.x) || !std::isfinite(frame.origin.y) || !std::isfinite(frame.size.width) ||
!std::isfinite(frame.size.height)) {
// CALayer will crash if we pass NaN or Inf values.
// It's unclear how to detect this case on cross-platform manner holistically, so we have to do it on the mounting
// layer as well. NaN/Inf is a kinda valid result of some math operations. Even if we can (and should) detect (and
// report early) incorrect (NaN and Inf) values which come from JavaScript side, we sometimes cannot backtrace the
// sources of a calculation that produced an incorrect/useless result.
RCTLogWarn(
@"-[UIView(ComponentViewProtocol) updateLayoutMetrics:oldLayoutMetrics:]: Received invalid layout metrics (%@) for a view (%@).",
NSStringFromCGRect(frame),
self);
} else {
self.frame = frame;
self.bounds = CGRect{CGPointZero, frame.size};
}
}

if (forceUpdate || (layoutMetrics.displayType != oldLayoutMetrics.displayType)) {
self.hidden = layoutMetrics.displayType == facebook::react::DisplayType::None;
}
}
#endif

@end
4 changes: 4 additions & 0 deletions apple/RNGestureHandlerButtonComponentView.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#ifdef RCT_NEW_ARCH_ENABLED

#if !TARGET_OS_OSX
#import <UIKit/UIKit.h>
#else
#import <React/RCTUIKit.h>
#endif

#import <React/RCTViewComponentView.h>

Expand Down
15 changes: 12 additions & 3 deletions apple/RNGestureHandlerButtonComponentView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ @implementation RNGestureHandlerButtonComponentView {
RNGestureHandlerButton *_buttonView;
}

#if TARGET_OS_OSX
// Here we want to disable view recycling on buttons. Listeners are not removed from views when they're being unmounted,
// therefore after navigating through other screens buttons may have different actions then they are supposed to have.
+ (BOOL)shouldBeRecycled
{
return NO;
}
#endif

// Needed because of this: https://github.com/facebook/react-native/pull/37274
+ (void)load
{
Expand All @@ -40,12 +49,12 @@ - (instancetype)initWithFrame:(CGRect)frame
return self;
}

- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
- (void)mountChildComponentView:(RNGHUIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
{
[_buttonView mountChildComponentView:childComponentView index:index];
}

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

_buttonView.userEnabled = newProps.enabled;
#if !TARGET_OS_TV
#if !TARGET_OS_TV && !TARGET_OS_OSX
_buttonView.exclusiveTouch = newProps.exclusive;
#endif
_buttonView.hitTestEdgeInsets = UIEdgeInsetsMake(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ @implementation RNGestureHandlerButtonManager

- (RNGHUIView *)view
{
return [RNGestureHandlerButton new];
return (RNGHUIView *)[RNGestureHandlerButton new];
}

@end

0 comments on commit e5a9227

Please sign in to comment.