Fix RCTScrollContentView layout with fixed size (#453)

This commit is contained in:
Tommy Nguyen 2020-06-20 02:58:47 +02:00 коммит произвёл GitHub
Родитель c5458a7575
Коммит 374cef84e2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 118 добавлений и 20 удалений

Просмотреть файл

@ -0,0 +1,22 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTScrollContentLocalData : NSObject
@property (nonatomic, assign) CGFloat horizontalScrollerHeight;
@property (nonatomic, assign) CGFloat verticalScrollerWidth;
- (instancetype)initWithVerticalScroller:(nullable NSScroller *)verticalScroller
horizontalScroller:(nullable NSScroller *)horizontalScroller;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -0,0 +1,22 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTScrollContentLocalData.h"
@implementation RCTScrollContentLocalData
- (instancetype)initWithVerticalScroller:(NSScroller *)verticalScroller
horizontalScroller:(NSScroller *)horizontalScroller
{
if (self = [super init]) {
_verticalScrollerWidth = NSWidth([verticalScroller frame]);
_horizontalScrollerHeight = NSHeight([horizontalScroller frame]);
}
return self;
}
@end

Просмотреть файл

@ -9,10 +9,28 @@
#import <yoga/Yoga.h>
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
#import "RCTScrollContentLocalData.h"
#endif // ]TODO(macOS ISS#2323203)
#import "RCTUtils.h"
@implementation RCTScrollContentShadowView
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
- (void)setLocalData:(RCTScrollContentLocalData *)localData
{
RCTAssert(
[localData isKindOfClass:[RCTScrollContentLocalData class]],
@"Local data object for `RCTScrollContentView` must be `RCTScrollContentLocalData` instance.");
super.marginEnd = (YGValue){localData.verticalScrollerWidth, YGUnitPoint};
super.marginBottom = (YGValue){localData.horizontalScrollerHeight, YGUnitPoint};
[self didSetProps:@[@"marginEnd", @"marginBottom"]];
}
#endif // ]TODO(macOS ISS#2323203)
- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics
layoutContext:(RCTLayoutContext)layoutContext
{

Просмотреть файл

@ -10,9 +10,20 @@
#import <React/RCTAssert.h>
#import <React/UIView+React.h>
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
#import <React/RCTUIManager.h>
#import "RCTScrollContentLocalData.h"
#endif // ]TODO(macOS ISS#2323203)
#import "RCTScrollView.h"
@implementation RCTScrollContentView
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
{
BOOL _hasHorizontalScroller;
BOOL _hasVerticalScroller;
}
#endif // ]TODO(macOS ISS#2323203)
- (void)reactSetFrame:(CGRect)frame
{
@ -21,25 +32,6 @@
#else // [TODO(macOS ISS#2323203)
// macOS also has a NSClipView in its hierarchy
RCTScrollView *scrollView = (RCTScrollView *)self.superview.superview.superview;
if (scrollView != nil) {
// On macOS scroll indicators may float over the content view like they do in iOS
// or depending on system preferences they may be outside of the content view
// which means the clip view will be smaller than the scroll view itself.
// In such cases the content view layout must shrink accordingly otherwise
// the contents will overflow causing the scroll indicators to appear unnecessarily.
NSScrollView *platformScrollView = scrollView.scrollView;
if (platformScrollView.scrollerStyle == NSScrollerStyleLegacy) {
NSScroller *verticalScroller = platformScrollView.verticalScroller;
if (!verticalScroller.isHidden) {
frame.size.width -= verticalScroller.frame.size.width;
}
NSScroller *horizontalScroller = platformScrollView.horizontalScroller;
if (!horizontalScroller.isHidden) {
frame.size.height -= horizontalScroller.frame.size.height;
}
}
}
#endif // ]TODO(macOS ISS#2323203)
[super reactSetFrame:frame];
@ -52,6 +44,32 @@
@"Unexpected view hierarchy of RCTScrollView component.");
[scrollView updateContentOffsetIfNeeded];
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
// On macOS scroll indicators may float over the content view like they do in iOS
// or depending on system preferences they may be outside of the content view
// which means the clip view will be smaller than the scroll view itself.
// In such cases the content view layout must shrink accordingly otherwise
// the contents will overflow causing the scroll indicators to appear unnecessarily.
NSScrollView *platformScrollView = [scrollView scrollView];
if ([platformScrollView scrollerStyle] == NSScrollerStyleLegacy) {
const BOOL nextHasHorizontalScroller = [platformScrollView hasHorizontalScroller];
const BOOL nextHasVerticalScroller = [platformScrollView hasVerticalScroller];
if (_hasHorizontalScroller != nextHasHorizontalScroller ||
_hasVerticalScroller != nextHasVerticalScroller) {
_hasHorizontalScroller = nextHasHorizontalScroller;
_hasVerticalScroller = nextHasVerticalScroller;
RCTScrollContentLocalData *localData =
[[RCTScrollContentLocalData alloc]
initWithVerticalScroller:[platformScrollView verticalScroller]
horizontalScroller:[platformScrollView horizontalScroller]];
[[[scrollView bridge] uiManager] setLocalData:localData forView:self];
}
}
#endif // ]TODO(macOS ISS#2323203)
}
@end

Просмотреть файл

@ -23,6 +23,8 @@
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
@property (nonatomic, readonly) RCTBridge *bridge;
/**
* The `RCTScrollView` may have at most one single subview. This will ensure
* that the scroll view's `contentSize` will be efficiently set to the size of

Просмотреть файл

@ -187,7 +187,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
self.scrollEnabled = YES;
self.hasHorizontalScroller = YES;
self.hasVerticalScroller = YES;
self.autohidesScrollers = YES;
self.panGestureRecognizer = [[NSPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleCustomPan:)];
#else // ]TODO(macOS ISS#2323203)
[self.panGestureRecognizer addTarget:self action:@selector(handleCustomPan:)];
@ -566,6 +565,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
}
}
- (RCTBridge *)bridge
{
return [_eventDispatcher bridge];
}
- (RCTUIView *)contentView // TODO(macOS ISS#3536887)
{
return _scrollView.documentView;
@ -751,6 +755,11 @@ static inline void RCTApplyTransformationAccordingLayoutDirection(RCTPlatformVie
[self react_updateClippedSubviewsWithClipRect:clipRect relativeToView:clipView];
_lastClippedToRect = bounds;
}
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
[[self scrollView] setHasHorizontalScroller:[self isHorizontal:_scrollView]];
[[self scrollView] setHasVerticalScroller:[self isVertical:_scrollView]];
#endif // ]TODO(macOS ISS#2323203)
}
#if TARGET_OS_OSX // [TODO(macOS ISS#2323203)
@ -808,6 +817,13 @@ static inline void RCTApplyTransformationAccordingLayoutDirection(RCTPlatformVie
return scrollView.contentSize.width > self.frame.size.width;
}
#if TARGET_OS_OSX // [TODO(macOS Candidate ISS#2710739)
- (BOOL)isVertical:(RCTCustomScrollView *)scrollView
{
return scrollView.contentSize.height > self.frame.size.height;
}
#endif // ]TODO(macOS Candidate ISS#2710739)
- (void)scrollToOffset:(CGPoint)offset
{
[self scrollToOffset:offset animated:YES];