Created
September 27, 2016 16:33
-
-
Save majak/b0ee1fb6ca725d2cf810d2f0ab394f2e to your computer and use it in GitHub Desktop.
ios view clipping issue #8607
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/node_modules/react-native/React/Views/RCTScrollView.m b/node_modules/react-native/React/Views/RCTScrollView.m | |
| --- a/node_modules/react-native/React/Views/RCTScrollView.m | |
| +++ b/node_modules/react-native/React/Views/RCTScrollView.m | |
| @@ -136,7 +136,7 @@ | |
| * default UIKit behaviors such as textFields automatically scrolling | |
| * scroll views that contain them and support sticky headers. | |
| */ | |
| -@interface RCTCustomScrollView : UIScrollView<UIGestureRecognizerDelegate> | |
| +@interface RCTCustomScrollView : UIScrollView<UIGestureRecognizerDelegate, RCTClippingView> | |
| @property (nonatomic, copy) NSIndexSet *stickyHeaderIndices; | |
| @property (nonatomic, assign) BOOL centerContent; | |
| @@ -147,6 +147,7 @@ | |
| @implementation RCTCustomScrollView | |
| { | |
| + BOOL _removeClippedSubviews; | |
| __weak UIView *_dockedHeaderView; | |
| } | |
| @@ -367,6 +368,49 @@ | |
| [self addSubview:_rctRefreshControl]; | |
| } | |
| +#pragma mark - RCTClippingView | |
| + | |
| +- (void)reclipView:(UIView<RCTClippableView> *)clippableView | |
| +{ | |
| + // noop, our child is the content view, which is never clipped | |
| +} | |
| + | |
| +- (CGRect)clippingRectForClippingView:(UIView<RCTClippingView> *)clippingView | |
| +{ | |
| + RCTAssert(clippingView == [self contentView], @"Unexpected clipping view. Expected %@ got %@.", [self contentView], clippingView); | |
| + // Scrollview's content view is as big as all rows together. If we used its bound for clipping it wouldn't achieve anything. | |
| + // So we use our (scrollview's) bounds. | |
| + return [self convertRect:self.bounds toView:clippingView]; | |
| +} | |
| + | |
| +- (void)setRemoveClippedSubviews:(BOOL)removeClippedSubviews | |
| +{ | |
| + if (removeClippedSubviews != _removeClippedSubviews) { | |
| + _removeClippedSubviews = removeClippedSubviews; | |
| + // No matter if clipping has been turned on or off the child views will do the right thing. | |
| + if ([self contentView]) { | |
| + [(UIView<RCTClippableView> *)[self contentView] setReactClippingSuperview:self]; | |
| + } | |
| + } | |
| +} | |
| + | |
| +- (BOOL)removeClippedSubviews | |
| +{ | |
| + return _removeClippedSubviews; | |
| +} | |
| + | |
| +- (void)reactSetFrame:(CGRect)frame | |
| +{ | |
| + if (!CGRectEqualToRect(frame, self.frame)) { | |
| + [super setFrame:frame]; | |
| + for (UIView *view in [[self contentView] reactSubviews]) { | |
| + if ([view conformsToProtocol:@protocol(RCTClippableView)]) { | |
| + [((UIView<RCTClippingView> *)[self contentView]) reclipView:(UIView<RCTClippableView> *)view]; | |
| + } | |
| + } | |
| + } | |
| +} | |
| + | |
| @end | |
| @implementation RCTScrollView | |
| @@ -411,11 +455,6 @@ | |
| RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) | |
| RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) | |
| -- (void)setRemoveClippedSubviews:(__unused BOOL)removeClippedSubviews | |
| -{ | |
| - // Does nothing | |
| -} | |
| - | |
| - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex | |
| { | |
| [super insertReactSubview:view atIndex:atIndex]; | |
| @@ -425,6 +464,9 @@ | |
| RCTAssert(_contentView == nil, @"RCTScrollView may only contain a single subview"); | |
| _contentView = view; | |
| [_scrollView addSubview:view]; | |
| + if ([_contentView conformsToProtocol:@protocol(RCTClippableView)]) { | |
| + [(UIView<RCTClippableView> *)_contentView setReactClippingSuperview:_scrollView]; | |
| + } | |
| } | |
| } | |
| @@ -472,6 +514,14 @@ | |
| _scrollView.clipsToBounds = clipsToBounds; | |
| } | |
| +- (void)setRemoveClippedSubviews:(BOOL)removeClippedSubviews | |
| +{ | |
| + if (removeClippedSubviews != [super removeClippedSubviews]) { | |
| + [_scrollView setRemoveClippedSubviews:removeClippedSubviews]; | |
| + } | |
| + [super setRemoveClippedSubviews:removeClippedSubviews]; | |
| +} | |
| + | |
| - (void)dealloc | |
| { | |
| _scrollView.delegate = nil; | |
| @@ -484,26 +534,28 @@ | |
| RCTAssert([self.subviews lastObject] == _scrollView, @"our only subview should be a scrollview"); | |
| CGPoint originalOffset = _scrollView.contentOffset; | |
| - _scrollView.frame = self.bounds; | |
| + if (!CGRectEqualToRect(_scrollView.frame, self.bounds)) { | |
| + _scrollView.frame = self.bounds; | |
| + | |
| + for (UIView *view in [_contentView reactSubviews]) { | |
| + if ([view conformsToProtocol:@protocol(RCTClippableView)]) { | |
| + UIView<RCTClippableView> *clippableView = (UIView<RCTClippableView> *)view; | |
| + [clippableView.reactClippingSuperview reclipView:clippableView]; | |
| + } | |
| + } | |
| + | |
| + } | |
| _scrollView.contentOffset = originalOffset; | |
| // Adjust the refresh control frame if the scrollview layout changes. | |
| RCTRefreshControl *refreshControl = _scrollView.rctRefreshControl; | |
| if (refreshControl && refreshControl.refreshing) { | |
| refreshControl.frame = (CGRect){_scrollView.contentOffset, {_scrollView.frame.size.width, refreshControl.frame.size.height}}; | |
| } | |
| - | |
| - [self updateClippedSubviews]; | |
| } | |
| - (void)updateClippedSubviews | |
| { | |
| - // Find a suitable view to use for clipping | |
| - UIView *clipView = [self react_findClipView]; | |
| - if (!clipView) { | |
| - return; | |
| - } | |
| - | |
| static const CGFloat leeway = 1.0; | |
| const CGSize contentSize = _scrollView.contentSize; | |
| @@ -518,8 +570,12 @@ | |
| (scrollsVertically && (bounds.size.height < leeway || fabs(_lastClippedToRect.origin.y - bounds.origin.y) >= leeway)); | |
| if (shouldClipAgain) { | |
| - const CGRect clipRect = CGRectInset(clipView.bounds, -leeway, -leeway); | |
| - [self react_updateClippedSubviewsWithClipRect:clipRect relativeToView:clipView]; | |
| + for (UIView *view in [_contentView reactSubviews]) { | |
| + if ([view conformsToProtocol:@protocol(RCTClippableView)]) { | |
| + UIView<RCTClippableView> *clippableView = (UIView<RCTClippableView> *)view; | |
| + [clippableView.reactClippingSuperview reclipView:clippableView]; | |
| + } | |
| + } | |
| _lastClippedToRect = bounds; | |
| } | |
| } | |
| diff --git a/node_modules/react-native/React/Views/RCTView.h b/node_modules/react-native/React/Views/RCTView.h | |
| --- a/node_modules/react-native/React/Views/RCTView.h | |
| +++ b/node_modules/react-native/React/Views/RCTView.h | |
| @@ -19,7 +19,46 @@ | |
| @class RCTView; | |
| -@interface RCTView : UIView | |
| + | |
| +@protocol RCTClippableView; | |
| + | |
| + | |
| +/** | |
| + * Having views in view hierarchy that are not visible wastes resources. | |
| + * That's why we have implemented view clipping. The key idea is simple: | |
| + * When a view has clipping turned on, its subview is removed as long as it is outside of the view's bounds. | |
| + * These following two protocols help achieving this behavior. | |
| + * View that clips has to implement `RCTClippingView` protocol and a view that can be clipped `RCTClippableView` protocol. | |
| + * | |
| + * The parent (clipping) view is always responsible for doing the view manipulation. | |
| + * If a clippable view changes its position it should notify it's clipping superview to redo the clipping using the `reclipView:` call. | |
| + * | |
| + * In some special cases the direct superview doesn't have the right size for clipping. | |
| + * A scrollview is a nice example: Scrollview doesn't have rows as its direct subviews. | |
| + * There is a content view between them whose bounds contain all rows. Therefore in this case clipping have to be done based not on a direct superview, but one above. | |
| + * | |
| + * This is enabled by calling `clippingRectForClippingView:`. | |
| + * All clipping views should call this method during `reclipView:` on their parent to enable clipping rect override by parent. See implementation in `RCTView`. | |
| + */ | |
| +@protocol RCTClippingView <NSObject> | |
| +@property (nonatomic, assign) BOOL removeClippedSubviews; | |
| +- (void)reclipView:(UIView<RCTClippableView> *)clippableView; | |
| +- (CGRect)clippingRectForClippingView:(UIView<RCTClippingView> *)clippingView; | |
| +@end | |
| + | |
| +/** | |
| + * A view implementing this protocol can be clipped. | |
| + */ | |
| +@protocol RCTClippableView <NSObject> | |
| +/** | |
| + * This property has to be set by a clipping parent when clipping is active. | |
| + * Even if this view was clipped (it has no superview), this points to its parent view in the react hierarchy. | |
| + * (That doesn't have to be true for `reactSuperview) | |
| + */ | |
| +@property (weak, nonatomic) UIView<RCTClippingView> *reactClippingSuperview; | |
| +@end | |
| + | |
| +@interface RCTView : UIView <RCTClippingView, RCTClippableView> | |
| /** | |
| * Accessibility event handlers | |
| @@ -48,22 +87,6 @@ | |
| */ | |
| @property (nonatomic, assign) NSInteger reactZIndex; | |
| -/** | |
| - * This is an optimization used to improve performance | |
| - * for large scrolling views with many subviews, such as a | |
| - * list or table. If set to YES, any clipped subviews will | |
| - * be removed from the view hierarchy whenever -updateClippedSubviews | |
| - * is called. This would typically be triggered by a scroll event | |
| - */ | |
| -@property (nonatomic, assign) BOOL removeClippedSubviews; | |
| - | |
| -/** | |
| - * Hide subviews if they are outside the view bounds. | |
| - * This is an optimisation used predominantly with RKScrollViews | |
| - * but it is applied recursively to all subviews that have | |
| - * removeClippedSubviews set to YES | |
| - */ | |
| -- (void)updateClippedSubviews; | |
| /** | |
| * Border radii. | |
| diff --git a/node_modules/react-native/React/Views/RCTView.m b/node_modules/react-native/React/Views/RCTView.m | |
| --- a/node_modules/react-native/React/Views/RCTView.m | |
| +++ b/node_modules/react-native/React/Views/RCTView.m | |
| @@ -16,69 +16,6 @@ | |
| #import "RCTUtils.h" | |
| #import "UIView+React.h" | |
| -@implementation UIView (RCTViewUnmounting) | |
| - | |
| -- (void)react_remountAllSubviews | |
| -{ | |
| - // Normal views don't support unmounting, so all | |
| - // this does is forward message to our subviews, | |
| - // in case any of those do support it | |
| - | |
| - for (UIView *subview in self.subviews) { | |
| - [subview react_remountAllSubviews]; | |
| - } | |
| -} | |
| - | |
| -- (void)react_updateClippedSubviewsWithClipRect:(CGRect)clipRect relativeToView:(UIView *)clipView | |
| -{ | |
| - // Even though we don't support subview unmounting | |
| - // we do support clipsToBounds, so if that's enabled | |
| - // we'll update the clipping | |
| - | |
| - if (self.clipsToBounds && self.subviews.count > 0) { | |
| - clipRect = [clipView convertRect:clipRect toView:self]; | |
| - clipRect = CGRectIntersection(clipRect, self.bounds); | |
| - clipView = self; | |
| - } | |
| - | |
| - // Normal views don't support unmounting, so all | |
| - // this does is forward message to our subviews, | |
| - // in case any of those do support it | |
| - | |
| - for (UIView *subview in self.subviews) { | |
| - [subview react_updateClippedSubviewsWithClipRect:clipRect relativeToView:clipView]; | |
| - } | |
| -} | |
| - | |
| -- (UIView *)react_findClipView | |
| -{ | |
| - UIView *testView = self; | |
| - UIView *clipView = nil; | |
| - CGRect clipRect = self.bounds; | |
| - // We will only look for a clipping view up the view hierarchy until we hit the root view. | |
| - while (testView) { | |
| - if (testView.clipsToBounds) { | |
| - if (clipView) { | |
| - CGRect testRect = [clipView convertRect:clipRect toView:testView]; | |
| - if (!CGRectContainsRect(testView.bounds, testRect)) { | |
| - clipView = testView; | |
| - clipRect = CGRectIntersection(testView.bounds, testRect); | |
| - } | |
| - } else { | |
| - clipView = testView; | |
| - clipRect = [self convertRect:self.bounds toView:clipView]; | |
| - } | |
| - } | |
| - if ([testView isReactRootView]) { | |
| - break; | |
| - } | |
| - testView = testView.superview; | |
| - } | |
| - return clipView ?: self.window; | |
| -} | |
| - | |
| -@end | |
| - | |
| static NSString *RCTRecursiveAccessibilityLabel(UIView *view) | |
| { | |
| NSMutableString *str = [NSMutableString stringWithString:@""]; | |
| @@ -215,6 +152,30 @@ | |
| } | |
| } | |
| +- (void)didUpdateReactSubviews | |
| +{ | |
| + if (!_removeClippedSubviews) { | |
| + [super didUpdateReactSubviews]; | |
| + return; | |
| + } | |
| + | |
| + // We have to set clipping view to all new children. New children doesn't have reactClippingSuperview set yet. | |
| + for (UIView *view in self.sortedReactSubviews) { | |
| + if ([view conformsToProtocol:@protocol(RCTClippableView)]) { | |
| + UIView<RCTClippableView> *clippableView = (UIView<RCTClippableView> *)view; | |
| + if (!clippableView.reactClippingSuperview) { | |
| + [clippableView setReactClippingSuperview:self]; | |
| + [self reclipView:clippableView]; | |
| + } | |
| + } else { | |
| + if (!view.superview) { | |
| + [self addSubview:view]; | |
| + } | |
| + } | |
| + } | |
| + | |
| +} | |
| + | |
| - (NSString *)description | |
| { | |
| NSString *superDescription = super.description; | |
| @@ -271,112 +232,70 @@ | |
| return UIEdgeInsetsZero; | |
| } | |
| -#pragma mark - View unmounting | |
| - | |
| -- (void)react_remountAllSubviews | |
| -{ | |
| - if (_removeClippedSubviews) { | |
| - for (UIView *view in self.sortedReactSubviews) { | |
| - if (view.superview != self) { | |
| - [self addSubview:view]; | |
| - [view react_remountAllSubviews]; | |
| - } | |
| - } | |
| - } else { | |
| - // If _removeClippedSubviews is false, we must already be showing all subviews | |
| - [super react_remountAllSubviews]; | |
| - } | |
| -} | |
| - | |
| -- (void)react_updateClippedSubviewsWithClipRect:(CGRect)clipRect relativeToView:(UIView *)clipView | |
| -{ | |
| - // TODO (#5906496): for scrollviews (the primary use-case) we could | |
| - // optimize this by only doing a range check along the scroll axis, | |
| - // instead of comparing the whole frame | |
| - | |
| - if (!_removeClippedSubviews) { | |
| - // Use default behavior if unmounting is disabled | |
| - return [super react_updateClippedSubviewsWithClipRect:clipRect relativeToView:clipView]; | |
| - } | |
| +#pragma mark - RCTClippingView | |
| - if (self.reactSubviews.count == 0) { | |
| - // Do nothing if we have no subviews | |
| - return; | |
| - } | |
| - | |
| - if (CGSizeEqualToSize(self.bounds.size, CGSizeZero)) { | |
| - // Do nothing if layout hasn't happened yet | |
| - return; | |
| - } | |
| - | |
| - // Convert clipping rect to local coordinates | |
| - clipRect = [clipView convertRect:clipRect toView:self]; | |
| - clipRect = CGRectIntersection(clipRect, self.bounds); | |
| - clipView = self; | |
| +@synthesize removeClippedSubviews = _removeClippedSubviews; | |
| - // Mount / unmount views | |
| - for (UIView *view in self.sortedReactSubviews) { | |
| - if (!CGRectIsEmpty(CGRectIntersection(clipRect, view.frame))) { | |
| - | |
| - // View is at least partially visible, so remount it if unmounted | |
| - [self addSubview:view]; | |
| - | |
| - // Then test its subviews | |
| - if (CGRectContainsRect(clipRect, view.frame)) { | |
| - // View is fully visible, so remount all subviews | |
| - [view react_remountAllSubviews]; | |
| - } else { | |
| - // View is partially visible, so update clipped subviews | |
| - [view react_updateClippedSubviewsWithClipRect:clipRect relativeToView:clipView]; | |
| +- (void)setRemoveClippedSubviews:(BOOL)removeClippedSubviews | |
| +{ | |
| + if (removeClippedSubviews != _removeClippedSubviews) { | |
| + _removeClippedSubviews = removeClippedSubviews; | |
| + for (UIView *view in self.sortedReactSubviews) { | |
| + if ([view conformsToProtocol:@protocol(RCTClippableView)]) { | |
| + UIView<RCTClippableView> *clippableView = (UIView<RCTClippableView> *)view; | |
| + if (removeClippedSubviews) { | |
| + [clippableView setReactClippingSuperview:self]; | |
| + [self reclipView:clippableView]; | |
| + } else { | |
| + [clippableView setReactClippingSuperview:nil]; | |
| + [self addSubview:clippableView]; | |
| + } | |
| } | |
| - | |
| - } else if (view.superview) { | |
| - | |
| - // View is completely outside the clipRect, so unmount it | |
| - [view removeFromSuperview]; | |
| } | |
| } | |
| } | |
| -- (void)setRemoveClippedSubviews:(BOOL)removeClippedSubviews | |
| +- (CGRect)clippingRectForClippingView:(UIView<RCTClippingView> *)clippingView | |
| { | |
| - if (!removeClippedSubviews && _removeClippedSubviews) { | |
| - [self react_remountAllSubviews]; | |
| - } | |
| - _removeClippedSubviews = removeClippedSubviews; | |
| + return clippingView.bounds; | |
| } | |
| -- (void)didUpdateReactSubviews | |
| +- (void)reclipView:(UIView<RCTClippableView> *)clippableView | |
| { | |
| - if (_removeClippedSubviews) { | |
| - [self updateClippedSubviews]; | |
| - } else { | |
| - [super didUpdateReactSubviews]; | |
| + RCTAssert(self.removeClippedSubviews, @"We are trying to evalute clipping while it's turned off. (%@ clips %@?)", self, clippableView); | |
| + | |
| + CGRect clippingRect = self.bounds; | |
| + // If the superview is clipping as well it may want to override our clipping rect. | |
| + if (_reactClippingSuperview) { | |
| + clippingRect = [_reactClippingSuperview clippingRectForClippingView:self]; | |
| + } | |
| + | |
| + if (!clippableView.superview && CGRectIntersectsRect(clippableView.frame, clippingRect)) { | |
| + | |
| + // When adding a clipped view back, we have to make sure zIndex oredering is preserved. | |
| + // That means we have to find the first subview below the subview being added back. | |
| + // This is not the most effiecient way of doing it when all subviews are being reclipped one by one. | |
| + UIView *lastSubview = nil; | |
| + for (UIView *view in self.sortedReactSubviews) { | |
| + if (view.superview) { | |
| + lastSubview = view; | |
| + } else if (view == clippableView) { | |
| + break; | |
| + } | |
| + } | |
| + if (lastSubview) { | |
| + [self insertSubview:clippableView aboveSubview:lastSubview]; | |
| + } else { | |
| + [self insertSubview:clippableView atIndex:0]; | |
| + } | |
| + } else if (clippableView.superview && !CGRectIntersectsRect(clippableView.frame, clippingRect)) { | |
| + [clippableView removeFromSuperview]; | |
| } | |
| } | |
| -- (void)updateClippedSubviews | |
| -{ | |
| - // Find a suitable view to use for clipping | |
| - UIView *clipView = [self react_findClipView]; | |
| - if (clipView) { | |
| - [self react_updateClippedSubviewsWithClipRect:clipView.bounds relativeToView:clipView]; | |
| - } | |
| -} | |
| +#pragma mark - RCTClippableView | |
| -- (void)layoutSubviews | |
| -{ | |
| - // TODO (#5906496): this a nasty performance drain, but necessary | |
| - // to prevent gaps appearing when the loading spinner disappears. | |
| - // We might be able to fix this another way by triggering a call | |
| - // to updateClippedSubviews manually after loading | |
| - | |
| - [super layoutSubviews]; | |
| - | |
| - if (_removeClippedSubviews) { | |
| - [self updateClippedSubviews]; | |
| - } | |
| -} | |
| +@synthesize reactClippingSuperview = _reactClippingSuperview; | |
| #pragma mark - Borders | |
| @@ -449,7 +368,16 @@ | |
| // TODO: detect up-front if re-rendering is necessary | |
| CGSize oldSize = self.bounds.size; | |
| [super reactSetFrame:frame]; | |
| + [_reactClippingSuperview reclipView:self]; | |
| if (!CGSizeEqualToSize(self.bounds.size, oldSize)) { | |
| + if (_removeClippedSubviews) { | |
| + for (UIView *view in self.sortedReactSubviews) { | |
| + if ([view conformsToProtocol:@protocol(RCTClippableView)]) { | |
| + UIView<RCTClippableView> *clippableView = (UIView<RCTClippableView> *)view; | |
| + [self reclipView:clippableView]; | |
| + } | |
| + } | |
| + } | |
| [self.layer setNeedsDisplay]; | |
| } | |
| } | |
| diff --git a/node_modules/react-native/React/Views/RCTViewManager.m b/node_modules/react-native/React/Views/RCTViewManager.m | |
| --- a/node_modules/react-native/React/Views/RCTViewManager.m | |
| +++ b/node_modules/react-native/React/Views/RCTViewManager.m | |
| @@ -111,40 +111,48 @@ | |
| RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel, NSString) | |
| RCT_EXPORT_VIEW_PROPERTY(accessibilityTraits, UIAccessibilityTraits) | |
| RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor) | |
| +RCT_EXPORT_VIEW_PROPERTY(removeClippedSubviews, BOOL) | |
| RCT_REMAP_VIEW_PROPERTY(accessible, isAccessibilityElement, BOOL) | |
| RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString) | |
| RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t) | |
| RCT_REMAP_VIEW_PROPERTY(opacity, alpha, CGFloat) | |
| RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor) | |
| RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize) | |
| RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity, float) | |
| RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius, CGFloat) | |
| -RCT_CUSTOM_VIEW_PROPERTY(overflow, CSSOverflow, RCTView) | |
| +RCT_CUSTOM_VIEW_PROPERTY(overflow, CSSOverflow, UIView) | |
| { | |
| if (json) { | |
| view.clipsToBounds = [RCTConvert CSSOverflow:json] == CSSOverflowHidden; | |
| } else { | |
| view.clipsToBounds = defaultView.clipsToBounds; | |
| } | |
| } | |
| -RCT_CUSTOM_VIEW_PROPERTY(shouldRasterizeIOS, BOOL, RCTView) | |
| +RCT_CUSTOM_VIEW_PROPERTY(shouldRasterizeIOS, BOOL, UIView) | |
| { | |
| view.layer.shouldRasterize = json ? [RCTConvert BOOL:json] : defaultView.layer.shouldRasterize; | |
| view.layer.rasterizationScale = view.layer.shouldRasterize ? [UIScreen mainScreen].scale : defaultView.layer.rasterizationScale; | |
| } | |
| // TODO: t11041683 Remove this duplicate property name. | |
| -RCT_CUSTOM_VIEW_PROPERTY(transformMatrix, CATransform3D, RCTView) | |
| +RCT_CUSTOM_VIEW_PROPERTY(transformMatrix, CATransform3D, UIView) | |
| { | |
| view.layer.transform = json ? [RCTConvert CATransform3D:json] : defaultView.layer.transform; | |
| // TODO: Improve this by enabling edge antialiasing only for transforms with rotation or skewing | |
| view.layer.allowsEdgeAntialiasing = !CATransform3DIsIdentity(view.layer.transform); | |
| + if ([view conformsToProtocol:@protocol(RCTClippableView)]) { | |
| + UIView<RCTClippableView> *clippableView = (UIView<RCTClippableView> *)view; | |
| + [clippableView.reactClippingSuperview reclipView:clippableView]; | |
| + } | |
| } | |
| -RCT_CUSTOM_VIEW_PROPERTY(transform, CATransform3D, RCTView) | |
| +RCT_CUSTOM_VIEW_PROPERTY(transform, CATransform3D, UIView) | |
| { | |
| view.layer.transform = json ? [RCTConvert CATransform3D:json] : defaultView.layer.transform; | |
| // TODO: Improve this by enabling edge antialiasing only for transforms with rotation or skewing | |
| view.layer.allowsEdgeAntialiasing = !CATransform3DIsIdentity(view.layer.transform); | |
| -} | |
| + if ([view conformsToProtocol:@protocol(RCTClippableView)]) { | |
| + UIView<RCTClippableView> *clippableView = (UIView<RCTClippableView> *)view; | |
| + [clippableView.reactClippingSuperview reclipView:clippableView]; | |
| + }} | |
| RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTPointerEvents, RCTView) | |
| { | |
| if ([view respondsToSelector:@selector(setPointerEvents:)]) { | |
| @@ -172,12 +180,6 @@ | |
| RCTLogError(@"UIView base class does not support pointerEvent value: %@", json); | |
| } | |
| } | |
| -RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, BOOL, RCTView) | |
| -{ | |
| - if ([view respondsToSelector:@selector(setRemoveClippedSubviews:)]) { | |
| - view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews; | |
| - } | |
| -} | |
| RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView) { | |
| if ([view respondsToSelector:@selector(setBorderRadius:)]) { | |
| view.borderRadius = json ? [RCTConvert CGFloat:json] : defaultView.borderRadius; | |
| diff --git a/node_modules/react-native/React/Views/UIView+Private.h b/node_modules/react-native/React/Views/UIView+Private.h | |
| --- a/node_modules/react-native/React/Views/UIView+Private.h | |
| +++ b/node_modules/react-native/React/Views/UIView+Private.h | |
| @@ -11,11 +11,6 @@ | |
| @interface UIView (Private) | |
| -// remove clipped subviews implementation | |
| -- (void)react_remountAllSubviews; | |
| -- (void)react_updateClippedSubviewsWithClipRect:(CGRect)clipRect relativeToView:(UIView *)clipView; | |
| -- (UIView *)react_findClipView; | |
| - | |
| // zIndex sorting | |
| - (void)clearSortedSubviews; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment