mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Fabric: Moving ScrollView's delegate splitter to RCTEnhancedScrollView
Summary: This diff moves the delegate splitter from RCTScrollViewComponentView class to RCTEnhancedScrollView. Now, it's a key feature of RCTEnhancedScrollView. We need this to make `delegate` property of our UIScrollView subclass as resilient to any abuse as possible. E.g., if some other part of the app, unrelated to RN (e.g. BottomSheet component), nils the property, all dependent RN specific infra should continue to work. To make it possible, we make an exposed property to use the internal delegate splitter instead of providing direct access to the property of a superclass. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D18752886 fbshipit-source-id: 04ec4758afefb8e17481d69471d53c61ab396698
This commit is contained in:
committed by
Facebook Github Bot
parent
4350132932
commit
a69c18cfbc
@@ -7,16 +7,30 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <React/RCTGenericDelegateSplitter.h>
|
||||
#import <React/RCTViewComponentView.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
/*
|
||||
* `UIScrollView` subclass which has some improvements and tweaks
|
||||
* which are not directly related to React.
|
||||
* which are not directly related to React Native.
|
||||
*/
|
||||
@interface RCTEnhancedScrollView : UIScrollView
|
||||
|
||||
/*
|
||||
* Returns a delegate splitter that can be used to create as many `UIScrollView` delegates as needed.
|
||||
* Use that instead of accessing `delegate` property directly.
|
||||
*
|
||||
* This class overrides the `delegate` property and wires that to the delegate splitter.
|
||||
*
|
||||
* We never know which another part of the app might introspect the view hierarchy and mess with `UIScrollView`'s
|
||||
* delegate, so we expose a fake delegate connected to the original one via the splitter to make the component as
|
||||
* resilient to other code as possible: even if something else nil the delegate, other delegates that were subscribed
|
||||
* via the splitter will continue working.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) RCTGenericDelegateSplitter<id<UIScrollViewDelegate>> *delegateSplitter;
|
||||
|
||||
@property (nonatomic, assign) BOOL pinchGestureEnabled;
|
||||
@property (nonatomic, assign) BOOL centerContent;
|
||||
|
||||
|
||||
@@ -7,7 +7,20 @@
|
||||
|
||||
#import "RCTEnhancedScrollView.h"
|
||||
|
||||
@implementation RCTEnhancedScrollView
|
||||
@implementation RCTEnhancedScrollView {
|
||||
__weak id<UIScrollViewDelegate> _publicDelegate;
|
||||
}
|
||||
|
||||
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
|
||||
{
|
||||
if ([key isEqualToString:@"delegate"]) {
|
||||
// For `delegate` property, we issue KVO notifications manually.
|
||||
// We need that to block notifications caused by setting the original `UIScrollView`s property.
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [super automaticallyNotifiesObserversForKey:key];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
@@ -18,9 +31,49 @@
|
||||
// and keeps it as an opt-in behavior.
|
||||
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
_delegateSplitter = [[RCTGenericDelegateSplitter alloc] initWithDelegateUpdateBlock:^(id delegate) {
|
||||
[weakSelf setPrivateDelegate:delegate];
|
||||
}];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// This is not strictly necessary but that prevents a crash caused by a bug in UIKit.
|
||||
[self setPrivateDelegate:nil];
|
||||
}
|
||||
|
||||
- (void)setPrivateDelegate:(id<UIScrollViewDelegate>)delegate
|
||||
{
|
||||
[super setDelegate:delegate];
|
||||
}
|
||||
|
||||
- (id<UIScrollViewDelegate>)delegate
|
||||
{
|
||||
return _publicDelegate;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<UIScrollViewDelegate>)delegate
|
||||
{
|
||||
if (_publicDelegate == delegate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_publicDelegate) {
|
||||
[_delegateSplitter removeDelegate:_publicDelegate];
|
||||
}
|
||||
|
||||
[self willChangeValueForKey:@"delegate"];
|
||||
_publicDelegate = delegate;
|
||||
[self didChangeValueForKey:@"delegate"];
|
||||
|
||||
if (_publicDelegate) {
|
||||
[_delegateSplitter addDelegate:_publicDelegate];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -71,12 +71,7 @@ static void RCTSendPaperScrollEvent_DEPRECATED(UIScrollView *scrollView, NSInteg
|
||||
_containerView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
[_scrollView addSubview:_containerView];
|
||||
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
_scrollViewDelegateSplitter = [[RCTGenericDelegateSplitter alloc] initWithDelegateUpdateBlock:^(id delegate) {
|
||||
weakSelf.scrollView.delegate = delegate;
|
||||
}];
|
||||
|
||||
[_scrollViewDelegateSplitter addDelegate:self];
|
||||
[self.scrollViewDelegateSplitter addDelegate:self];
|
||||
|
||||
_scrollEventThrottle = INFINITY;
|
||||
}
|
||||
@@ -84,10 +79,9 @@ static void RCTSendPaperScrollEvent_DEPRECATED(UIScrollView *scrollView, NSInteg
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
- (RCTGenericDelegateSplitter<id<UIScrollViewDelegate>> *)scrollViewDelegateSplitter
|
||||
{
|
||||
// This is not strictly necessary but that prevents a crash caused by a bug in UIKit.
|
||||
_scrollView.delegate = nil;
|
||||
return ((RCTEnhancedScrollView *)_scrollView).delegateSplitter;
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewProtocol
|
||||
|
||||
Reference in New Issue
Block a user