Skip to content

Commit e8b2145

Browse files
sherginfacebook-github-bot
authored andcommitted
Fabric: Standard PullToRefresh component
Summary: This is implementation of standard PullToRefresh component that uses standard iOS component and modern integration approach. Reviewed By: mdvacca Differential Revision: D15403308 fbshipit-source-id: 5c877f7c18af9f5ac40e15a4ba44118614ba80bc
1 parent de47b47 commit e8b2145

11 files changed

+337
-2
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <UIKit/UIKit.h>
9+
10+
#import <React/RCTViewComponentView.h>
11+
12+
NS_ASSUME_NONNULL_BEGIN
13+
14+
/*
15+
* UIView class for root <PullToRefreshView> component.
16+
* This view is designed to only serve ViewController-like purpose for the actual `UIRefreshControl` view which is being
17+
* attached to some `UIScrollView` (not to this view).
18+
*/
19+
@interface RNPullToRefreshViewComponentView : RCTViewComponentView
20+
21+
@end
22+
23+
NS_ASSUME_NONNULL_END
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import "RNPullToRefreshViewComponentView.h"
9+
10+
#import <react/components/scrollview/PullToRefreshViewComponentDescriptor.h>
11+
#import <react/components/scrollview/PullToRefreshViewEventEmitter.h>
12+
#import <react/components/scrollview/PullToRefreshViewProps.h>
13+
14+
#import <React/RCTScrollViewComponentView.h>
15+
#import <React/RCTConversions.h>
16+
17+
18+
using namespace facebook::react;
19+
20+
@implementation RNPullToRefreshViewComponentView {
21+
UIRefreshControl *_refreshControl;
22+
RCTScrollViewComponentView *_scrollViewComponentView;
23+
}
24+
25+
- (instancetype)initWithFrame:(CGRect)frame
26+
{
27+
if (self = [super initWithFrame:frame]) {
28+
// This view is not designed to be visible, it only serves UIViewController-like purpose managing
29+
// attaching and detaching of a pull-to-refresh view to a scroll view.
30+
// The pull-to-refresh view is not a subview of this view.
31+
self.hidden = YES;
32+
33+
static auto const defaultProps = std::make_shared<PullToRefreshViewProps const>();
34+
_props = defaultProps;
35+
36+
_refreshControl = [[UIRefreshControl alloc] init];
37+
[_refreshControl addTarget:self action:@selector(handleUIControlEventValueChanged) forControlEvents:UIControlEventValueChanged];
38+
}
39+
40+
return self;
41+
}
42+
43+
#pragma mark - RCTComponentViewProtocol
44+
45+
+ (ComponentDescriptorProvider)componentDescriptorProvider
46+
{
47+
return concreteComponentDescriptorProvider<PullToRefreshViewComponentDescriptor>();
48+
}
49+
50+
- (void)updateProps:(SharedProps)props oldProps:(SharedProps)oldProps
51+
{
52+
auto const &oldConcreteProps = *std::static_pointer_cast<PullToRefreshViewProps const>(oldProps ?: _props);
53+
auto const &newConcreteProps = *std::static_pointer_cast<PullToRefreshViewProps const>(props);
54+
55+
[super updateProps:props oldProps:oldProps];
56+
57+
if (newConcreteProps.refreshing != oldConcreteProps.refreshing) {
58+
if (newConcreteProps.refreshing) {
59+
[_refreshControl beginRefreshing];
60+
} else {
61+
[_refreshControl endRefreshing];
62+
}
63+
}
64+
65+
BOOL needsUpdateTitle = NO;
66+
67+
if (newConcreteProps.title != oldConcreteProps.title) {
68+
needsUpdateTitle = YES;
69+
}
70+
71+
if (newConcreteProps.titleColor != oldConcreteProps.titleColor) {
72+
needsUpdateTitle = YES;
73+
}
74+
75+
if (needsUpdateTitle) {
76+
[self _updateTitle];
77+
}
78+
}
79+
80+
#pragma mark -
81+
82+
- (void)handleUIControlEventValueChanged
83+
{
84+
std::static_pointer_cast<PullToRefreshViewEventEmitter const>(_eventEmitter)->onRefresh();
85+
}
86+
87+
- (void)_updateTitle
88+
{
89+
auto const &concreteProps = *std::static_pointer_cast<PullToRefreshViewProps const>(_props);
90+
91+
if (concreteProps.title.empty()) {
92+
_refreshControl.attributedTitle = nil;
93+
return;
94+
}
95+
96+
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
97+
if (concreteProps.titleColor) {
98+
attributes[NSForegroundColorAttributeName] = RCTUIColorFromSharedColor(concreteProps.titleColor);
99+
}
100+
101+
_refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:RCTNSStringFromString(concreteProps.title) attributes:attributes];
102+
}
103+
104+
#pragma mark - Attaching & Detaching
105+
106+
- (void)didMoveToWindow
107+
{
108+
if (self.window) {
109+
[self _attach];
110+
} else {
111+
[self _detach];
112+
}
113+
}
114+
115+
- (void)_attach
116+
{
117+
if (_scrollViewComponentView) {
118+
[self _detach];
119+
}
120+
121+
_scrollViewComponentView = [RCTScrollViewComponentView findScrollViewComponentViewForView:self];
122+
if (!_scrollViewComponentView) {
123+
return;
124+
}
125+
126+
_scrollViewComponentView.scrollView.refreshControl = _refreshControl;
127+
}
128+
129+
- (void)_detach
130+
{
131+
if (!_scrollViewComponentView) {
132+
return;
133+
}
134+
135+
// iOS requires to end refreshing before unmounting.
136+
[_refreshControl endRefreshing];
137+
138+
_scrollViewComponentView.scrollView.refreshControl = nil;
139+
_scrollViewComponentView = nil;
140+
}
141+
142+
@end

React/Fabric/Mounting/RCTComponentViewFactory.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#import "RCTSwitchComponentView.h"
2424
#import "RCTUnimplementedNativeComponentView.h"
2525
#import "RCTViewComponentView.h"
26+
#import "RNPullToRefreshViewComponentView.h"
2627

2728
using namespace facebook::react;
2829

@@ -39,6 +40,7 @@ + (RCTComponentViewFactory *)standardComponentViewFactory
3940
[componentViewFactory registerComponentViewClass:[RCTViewComponentView class]];
4041
[componentViewFactory registerComponentViewClass:[RCTRootComponentView class]];
4142
[componentViewFactory registerComponentViewClass:[RCTScrollViewComponentView class]];
43+
[componentViewFactory registerComponentViewClass:[RNPullToRefreshViewComponentView class]];
4244
[componentViewFactory registerComponentViewClass:[RCTImageComponentView class]];
4345
[componentViewFactory registerComponentViewClass:[RCTParagraphComponentView class]];
4446
[componentViewFactory registerComponentViewClass:[RCTActivityIndicatorViewComponentView class]];
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <react/components/scrollview/PullToRefreshViewShadowNode.h>
11+
#include <react/core/ConcreteComponentDescriptor.h>
12+
13+
namespace facebook {
14+
namespace react {
15+
16+
using PullToRefreshViewComponentDescriptor =
17+
ConcreteComponentDescriptor<PullToRefreshViewShadowNode>;
18+
19+
} // namespace react
20+
} // namespace facebook
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "PullToRefreshViewEventEmitter.h"
9+
10+
namespace facebook {
11+
namespace react {
12+
13+
void PullToRefreshViewEventEmitter::onRefresh() const {
14+
dispatchEvent("refresh");
15+
}
16+
17+
} // namespace react
18+
} // namespace facebook
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
#pragma once
8+
9+
#include <memory>
10+
11+
#include <react/components/view/ViewEventEmitter.h>
12+
#include <react/core/EventEmitter.h>
13+
14+
namespace facebook {
15+
namespace react {
16+
17+
class PullToRefreshViewEventEmitter : public ViewEventEmitter {
18+
public:
19+
using ViewEventEmitter::ViewEventEmitter;
20+
21+
void onRefresh() const;
22+
};
23+
24+
} // namespace react
25+
} // namespace facebook
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "PullToRefreshViewProps.h"
9+
10+
#include <react/core/propsConversions.h>
11+
#include <react/graphics/conversions.h>
12+
13+
namespace facebook {
14+
namespace react {
15+
16+
PullToRefreshViewProps::PullToRefreshViewProps(
17+
PullToRefreshViewProps const &sourceProps,
18+
RawProps const &rawProps)
19+
: ViewProps(sourceProps, rawProps),
20+
refreshing(
21+
convertRawProp(rawProps, "refreshing", sourceProps.refreshing)),
22+
tintColor(convertRawProp(rawProps, "tintColor", sourceProps.tintColor)),
23+
title(convertRawProp(rawProps, "title", sourceProps.title)),
24+
titleColor(
25+
convertRawProp(rawProps, "titleColor", sourceProps.titleColor)) {}
26+
27+
} // namespace react
28+
} // namespace facebook
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <react/components/view/ViewProps.h>
11+
12+
namespace facebook {
13+
namespace react {
14+
15+
class PullToRefreshViewProps final : public ViewProps {
16+
public:
17+
PullToRefreshViewProps() = default;
18+
PullToRefreshViewProps(
19+
PullToRefreshViewProps const &sourceProps,
20+
RawProps const &rawProps);
21+
22+
#pragma mark - Props
23+
24+
bool const refreshing{};
25+
SharedColor const tintColor{};
26+
std::string const title{};
27+
SharedColor const titleColor{};
28+
};
29+
30+
} // namespace react
31+
} // namespace facebook
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "PullToRefreshViewShadowNode.h"
9+
10+
namespace facebook {
11+
namespace react {
12+
13+
const char PullToRefreshViewComponentName[] = "RefreshControl";
14+
15+
} // namespace react
16+
} // namespace facebook
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <react/components/scrollview/PullToRefreshViewEventEmitter.h>
11+
#include <react/components/scrollview/PullToRefreshViewProps.h>
12+
#include <react/components/view/ConcreteViewShadowNode.h>
13+
14+
namespace facebook {
15+
namespace react {
16+
17+
extern const char PullToRefreshViewComponentName[];
18+
19+
/*
20+
* `ShadowNode` for <PullToRefreshView> component.
21+
*/
22+
class PullToRefreshViewShadowNode final : public ConcreteViewShadowNode<
23+
PullToRefreshViewComponentName,
24+
PullToRefreshViewProps,
25+
PullToRefreshViewEventEmitter> {
26+
public:
27+
using ConcreteViewShadowNode::ConcreteViewShadowNode;
28+
};
29+
30+
} // namespace react
31+
} // namespace facebook

0 commit comments

Comments
 (0)