diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 93ee34af86bbcf..e3b157a57dc84e 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -11,6 +11,7 @@ #import #import #import +#import #import "RCTConversions.h" #import "RCTEnhancedScrollView.h" @@ -44,6 +45,8 @@ - (instancetype)initWithFrame:(CGRect)frame return self; } +#pragma mark - ComponentViewProtocol + - (void)updateProps:(SharedProps)props oldProps:(SharedProps)oldProps { [super updateProps:props oldProps:oldProps]; @@ -104,8 +107,6 @@ - (void)updateLocalData:(SharedLocalData)localData _scrollView.contentSize = contentSize; } -#pragma mark - ComponentViewProtocol - - (void)mountChildComponentView:(UIView *)childComponentView index:(NSInteger)index { @@ -119,4 +120,62 @@ - (void)unmountChildComponentView:(UIView *)childCompo [childComponentView removeFromSuperview]; } +- (ScrollViewMetrics)_scrollViewMetrics +{ + ScrollViewMetrics metrics; + metrics.contentSize = RCTSizeFromCGSize(_scrollView.contentSize); + metrics.contentOffset = RCTPointFromCGPoint(_scrollView.contentOffset); + metrics.contentInset = RCTEdgeInsetsFromUIEdgeInsets(_scrollView.contentInset); + metrics.containerSize = RCTSizeFromCGSize(_scrollView.bounds.size); + metrics.zoomScale = _scrollView.zoomScale; + return metrics; +} + +#pragma mark - UIScrollViewDelegate + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + std::static_pointer_cast(_eventHandlers)->onScroll([self _scrollViewMetrics]); +} + +- (void)scrollViewDidZoom:(UIScrollView *)scrollView +{ + std::static_pointer_cast(_eventHandlers)->onScroll([self _scrollViewMetrics]); +} + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + std::static_pointer_cast(_eventHandlers)->onScrollBeginDrag([self _scrollViewMetrics]); +} + +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset +{ + std::static_pointer_cast(_eventHandlers)->onScrollEndDrag([self _scrollViewMetrics]); +} + +- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView +{ + std::static_pointer_cast(_eventHandlers)->onMomentumScrollBegin([self _scrollViewMetrics]); +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView +{ + std::static_pointer_cast(_eventHandlers)->onMomentumScrollEnd([self _scrollViewMetrics]); +} + +- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView +{ + std::static_pointer_cast(_eventHandlers)->onMomentumScrollEnd([self _scrollViewMetrics]); +} + +- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view +{ + std::static_pointer_cast(_eventHandlers)->onScrollBeginDrag([self _scrollViewMetrics]); +} + +- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale +{ + std::static_pointer_cast(_eventHandlers)->onScrollEndDrag([self _scrollViewMetrics]); +} + @end diff --git a/React/Fabric/RCTConversions.h b/React/Fabric/RCTConversions.h index 9e4184651da421..ba2b9cb1ffabb5 100644 --- a/React/Fabric/RCTConversions.h +++ b/React/Fabric/RCTConversions.h @@ -21,6 +21,10 @@ inline CGRect RCTCGRectFromRect(facebook::react::Rect rect) { return {RCTCGPointFromPoint(rect.origin), RCTCGSizeFromSize(rect.size)}; } +inline UIEdgeInsets RCTUIEdgeInsetsFromEdgeInsets(facebook::react::EdgeInsets edgeInsets) { + return {edgeInsets.top, edgeInsets.left, edgeInsets.bottom, edgeInsets.right}; +} + inline facebook::react::Point RCTPointFromCGPoint(CGPoint point) { return {point.x, point.y}; } @@ -32,3 +36,7 @@ inline facebook::react::Size RCTSizeFromCGSize(CGSize size) { inline facebook::react::Rect RCTRectFromCGRect(CGRect rect) { return {RCTPointFromCGPoint(rect.origin), RCTSizeFromCGSize(rect.size)}; } + +inline facebook::react::EdgeInsets RCTEdgeInsetsFromUIEdgeInsets(UIEdgeInsets edgeInsets) { + return {edgeInsets.top, edgeInsets.left, edgeInsets.bottom, edgeInsets.right}; +} diff --git a/ReactCommon/fabric/scrollview/ScrollViewEventHandlers.cpp b/ReactCommon/fabric/scrollview/ScrollViewEventHandlers.cpp new file mode 100644 index 00000000000000..c331f6b03c87b2 --- /dev/null +++ b/ReactCommon/fabric/scrollview/ScrollViewEventHandlers.cpp @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "ScrollViewEventHandlers.h" + +namespace facebook { +namespace react { + +void ScrollViewEventHandlers::onScroll(const ScrollViewMetrics &scrollViewMetrics) const { + dispatchScrollViewEvent("scroll", scrollViewMetrics); +} + +void ScrollViewEventHandlers::onScrollBeginDrag(const ScrollViewMetrics &scrollViewMetrics) const { + dispatchScrollViewEvent("scrollBeginDrag", scrollViewMetrics); +} + +void ScrollViewEventHandlers::onScrollEndDrag(const ScrollViewMetrics &scrollViewMetrics) const { + dispatchScrollViewEvent("scrollEndDrag", scrollViewMetrics); +} + +void ScrollViewEventHandlers::onMomentumScrollBegin(const ScrollViewMetrics &scrollViewMetrics) const { + dispatchScrollViewEvent("momentumScrollBegin", scrollViewMetrics); +} + +void ScrollViewEventHandlers::onMomentumScrollEnd(const ScrollViewMetrics &scrollViewMetrics) const { + dispatchScrollViewEvent("momentumScrollEnd", scrollViewMetrics); +} + +void ScrollViewEventHandlers::dispatchScrollViewEvent(const std::string &name, const ScrollViewMetrics &scrollViewMetrics, const folly::dynamic &payload) const { + folly::dynamic compoundPayload = folly::dynamic::object(); + + compoundPayload["contentOffset"] = folly::dynamic::object + ("x", scrollViewMetrics.contentOffset.x) + ("y", scrollViewMetrics.contentOffset.y); + + compoundPayload["contentInset"] = folly::dynamic::object + ("top", scrollViewMetrics.contentInset.top) + ("left", scrollViewMetrics.contentInset.left) + ("bottom", scrollViewMetrics.contentInset.bottom) + ("right", scrollViewMetrics.contentInset.right); + + compoundPayload["contentSize"] = folly::dynamic::object + ("width", scrollViewMetrics.contentSize.width) + ("height", scrollViewMetrics.contentSize.height); + + compoundPayload["layoutMeasurement"] = folly::dynamic::object + ("width", scrollViewMetrics.containerSize.width) + ("height", scrollViewMetrics.containerSize.height); + + compoundPayload["zoomScale"] = scrollViewMetrics.zoomScale; + + if (!payload.isNull()) { + compoundPayload.merge_patch(payload); + } + + dispatchEvent(name, compoundPayload); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/scrollview/ScrollViewEventHandlers.h b/ReactCommon/fabric/scrollview/ScrollViewEventHandlers.h new file mode 100644 index 00000000000000..8f6a93e7221a64 --- /dev/null +++ b/ReactCommon/fabric/scrollview/ScrollViewEventHandlers.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +#pragma once + +#include + +#include +#include +#include + +#include + +namespace facebook { +namespace react { + +class ScrollViewMetrics { +public: + Size contentSize; + Point contentOffset; + EdgeInsets contentInset; + Size containerSize; + Float zoomScale; +}; + +class ScrollViewEventHandlers; + +using SharedScrollViewEventHandlers = std::shared_ptr; + +class ScrollViewEventHandlers: + public ViewEventHandlers { + +public: + + using ViewEventHandlers::ViewEventHandlers; + + void onScroll(const ScrollViewMetrics &scrollViewMetrics) const; + void onScrollBeginDrag(const ScrollViewMetrics &scrollViewMetrics) const; + void onScrollEndDrag(const ScrollViewMetrics &scrollViewMetrics) const; + void onMomentumScrollBegin(const ScrollViewMetrics &scrollViewMetrics) const; + void onMomentumScrollEnd(const ScrollViewMetrics &scrollViewMetrics) const; + +private: + + void dispatchScrollViewEvent(const std::string &name, const ScrollViewMetrics &scrollViewMetrics, const folly::dynamic &payload = {}) const; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/scrollview/ScrollViewShadowNode.cpp b/ReactCommon/fabric/scrollview/ScrollViewShadowNode.cpp index 96c60b2e3f7469..2d3efaa8945bd5 100644 --- a/ReactCommon/fabric/scrollview/ScrollViewShadowNode.cpp +++ b/ReactCommon/fabric/scrollview/ScrollViewShadowNode.cpp @@ -33,7 +33,7 @@ void ScrollViewShadowNode::updateLocalData() { #pragma mark - LayoutableShadowNode void ScrollViewShadowNode::layout(LayoutContext layoutContext) { - ConcreteViewShadowNode::layout(layoutContext); + ConcreteViewShadowNode::layout(layoutContext); updateLocalData(); } diff --git a/ReactCommon/fabric/scrollview/ScrollViewShadowNode.h b/ReactCommon/fabric/scrollview/ScrollViewShadowNode.h index e53e93e29ea9ec..5a74f0c320df22 100644 --- a/ReactCommon/fabric/scrollview/ScrollViewShadowNode.h +++ b/ReactCommon/fabric/scrollview/ScrollViewShadowNode.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -24,7 +25,7 @@ using SharedScrollViewShadowNode = std::shared_ptr; * `ShadowNode` for component. */ class ScrollViewShadowNode final: - public ConcreteViewShadowNode { + public ConcreteViewShadowNode { public: