Skip to content

Commit b6318ac

Browse files
JoshuaGrossfacebook-github-bot
authored andcommitted
Support image props for Slider component, feature parity with pre-Fabric Slider
Summary: The biggest change is that (1) the image proxy/observer code from the Image component has been generalized, (2) the four image props for the Slider component are fully supported, (3) a handful of props that were ignored or buggy on iOS now perform as expected. Reviewed By: shergin Differential Revision: D13954892 fbshipit-source-id: bec8ad3407c39a1cb186d9541a73b509dccc92ce
1 parent 7f646a5 commit b6318ac

File tree

14 files changed

+684
-71
lines changed

14 files changed

+684
-71
lines changed

React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.h

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,11 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
#import <UIKit/UIKit.h>
9-
10-
#import <React/RCTViewComponentView.h>
8+
#import "RCTViewComponentView.h"
9+
#import <React/RCTImageResponseDelegate.h>
1110

1211
NS_ASSUME_NONNULL_BEGIN
1312

14-
@protocol RCTImageResponseDelegate <NSObject>
15-
16-
- (void)didReceiveImage:(UIImage *)image fromObserver:(void*)observer;
17-
- (void)didReceiveProgress:(float)progress fromObserver:(void*)observer;
18-
- (void)didReceiveFailureFromObserver:(void*)observer;
19-
20-
@end
21-
2213
/**
2314
* UIView class for root <Image> component.
2415
*/

React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,49 +12,17 @@
1212
#import <react/components/image/ImageProps.h>
1313
#import <react/components/image/ImageShadowNode.h>
1414
#import <react/imagemanager/ImageRequest.h>
15-
#import <react/imagemanager/ImageResponse.h>
16-
#import <react/imagemanager/ImageResponseObserver.h>
1715
#import <react/imagemanager/RCTImagePrimitivesConversions.h>
16+
#import <React/RCTImageResponseObserverProxy.h>
1817

1918
#import "RCTConversions.h"
2019
#import "MainQueueExecutor.h"
2120

22-
using namespace facebook::react;
23-
24-
class ImageResponseObserverProxy: public ImageResponseObserver {
25-
public:
26-
ImageResponseObserverProxy(void* delegate): delegate_((__bridge id<RCTImageResponseDelegate>)delegate) {}
27-
28-
void didReceiveImage(const ImageResponse &imageResponse) override {
29-
UIImage *image = (__bridge UIImage *)imageResponse.getImage().get();
30-
void *this_ = this;
31-
dispatch_async(dispatch_get_main_queue(), ^{
32-
[delegate_ didReceiveImage:image fromObserver:this_];
33-
});
34-
}
35-
36-
void didReceiveProgress (float p) override {
37-
void *this_ = this;
38-
dispatch_async(dispatch_get_main_queue(), ^{
39-
[delegate_ didReceiveProgress:p fromObserver:this_];
40-
});
41-
}
42-
void didReceiveFailure() override {
43-
void *this_ = this;
44-
dispatch_async(dispatch_get_main_queue(), ^{
45-
[delegate_ didReceiveFailureFromObserver:this_];
46-
});
47-
}
48-
49-
private:
50-
id<RCTImageResponseDelegate> delegate_;
51-
};
52-
5321
@implementation RCTImageComponentView {
5422
UIImageView *_imageView;
5523
SharedImageLocalData _imageLocalData;
5624
std::shared_ptr<const ImageResponseObserverCoordinator> _coordinator;
57-
std::unique_ptr<ImageResponseObserverProxy> _imageResponseObserverProxy;
25+
std::unique_ptr<RCTImageResponseObserverProxy> _imageResponseObserverProxy;
5826
}
5927

6028
- (instancetype)initWithFrame:(CGRect)frame
@@ -68,7 +36,7 @@ - (instancetype)initWithFrame:(CGRect)frame
6836

6937
_imageView.contentMode = (UIViewContentMode)RCTResizeModeFromImageResizeMode(defaultProps->resizeMode);
7038

71-
_imageResponseObserverProxy = std::make_unique<ImageResponseObserverProxy>((__bridge void *)self);
39+
_imageResponseObserverProxy = std::make_unique<RCTImageResponseObserverProxy>((__bridge void *)self);
7240

7341
self.contentView = _imageView;
7442
}
@@ -110,12 +78,18 @@ - (void)updateProps:(SharedProps)props oldProps:(SharedProps)oldProps
11078
- (void)updateLocalData:(SharedLocalData)localData
11179
oldLocalData:(SharedLocalData)oldLocalData
11280
{
81+
SharedImageLocalData previousData = _imageLocalData;
11382
_imageLocalData = std::static_pointer_cast<const ImageLocalData>(localData);
11483
assert(_imageLocalData);
115-
self.coordinator = _imageLocalData->getImageRequest().getObserverCoordinator();
84+
bool havePreviousData = previousData != nullptr;
11685

117-
// Loading actually starts a little before this
118-
std::static_pointer_cast<const ImageEventEmitter>(_eventEmitter)->onLoadStart();
86+
if (!havePreviousData || _imageLocalData->getImageSource() != previousData->getImageSource()) {
87+
self.coordinator = _imageLocalData->getImageRequest().getObserverCoordinator();
88+
89+
// Loading actually starts a little before this, but this is the first time we know
90+
// the image is loading and can fire an event from this component
91+
std::static_pointer_cast<const ImageEventEmitter>(_eventEmitter)->onLoadStart();
92+
}
11993
}
12094

12195
- (void)setCoordinator:(std::shared_ptr<const ImageResponseObserverCoordinator>)coordinator {

React/Fabric/Mounting/ComponentViews/Slider/RCTSliderComponentView.mm

Lines changed: 209 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,31 @@
1010
#import <react/components/slider/SliderEventEmitter.h>
1111
#import <react/components/slider/SliderProps.h>
1212
#import <react/components/slider/SliderShadowNode.h>
13+
#import <react/components/slider/SliderLocalData.h>
14+
#import <React/RCTImageResponseObserverProxy.h>
15+
16+
#import "MainQueueExecutor.h"
1317

1418
using namespace facebook::react;
1519

1620
@implementation RCTSliderComponentView {
1721
UISlider *_sliderView;
18-
float _prevValue;
22+
float _previousValue;
23+
SharedSliderLocalData _sliderLocalData;
24+
25+
UIImage *_trackImage;
26+
UIImage *_minimumTrackImage;
27+
UIImage *_maximumTrackImage;
28+
UIImage *_thumbImage;
29+
30+
std::shared_ptr<const ImageResponseObserverCoordinator> _trackImageCoordinator;
31+
std::unique_ptr<RCTImageResponseObserverProxy> _trackImageResponseObserverProxy;
32+
std::shared_ptr<const ImageResponseObserverCoordinator> _minimumTrackImageCoordinator;
33+
std::unique_ptr<RCTImageResponseObserverProxy> _minimumTrackImageResponseObserverProxy;
34+
std::shared_ptr<const ImageResponseObserverCoordinator> _maximumTrackImageCoordinator;
35+
std::unique_ptr<RCTImageResponseObserverProxy> _maximumTrackImageResponseObserverProxy;
36+
std::shared_ptr<const ImageResponseObserverCoordinator> _thumbImageCoordinator;
37+
std::unique_ptr<RCTImageResponseObserverProxy> _thumbImageResponseObserverProxy;
1938
}
2039

2140
- (instancetype)initWithFrame:(CGRect)frame
@@ -29,9 +48,19 @@ - (instancetype)initWithFrame:(CGRect)frame
2948
[_sliderView addTarget:self
3049
action:@selector(onChange:)
3150
forControlEvents:UIControlEventValueChanged];
51+
[_sliderView addTarget:self
52+
action:@selector(sliderTouchEnd:)
53+
forControlEvents:(UIControlEventTouchUpInside |
54+
UIControlEventTouchUpOutside |
55+
UIControlEventTouchCancel)];
3256

3357
_sliderView.value = defaultProps->value;
3458

59+
_trackImageResponseObserverProxy = std::make_unique<RCTImageResponseObserverProxy>((__bridge void *)self);
60+
_minimumTrackImageResponseObserverProxy = std::make_unique<RCTImageResponseObserverProxy>((__bridge void *)self);
61+
_maximumTrackImageResponseObserverProxy = std::make_unique<RCTImageResponseObserverProxy>((__bridge void *)self);
62+
_thumbImageResponseObserverProxy = std::make_unique<RCTImageResponseObserverProxy>((__bridge void *)self);
63+
3564
self.contentView = _sliderView;
3665
}
3766

@@ -55,7 +84,17 @@ - (void)updateProps:(SharedProps)props oldProps:(SharedProps)oldProps
5584
// `value`
5685
if (oldSliderProps.value != newSliderProps.value) {
5786
_sliderView.value = newSliderProps.value;
58-
_prevValue = newSliderProps.value;
87+
_previousValue = newSliderProps.value;
88+
}
89+
90+
// `minimumValue`
91+
if (oldSliderProps.minimumValue != newSliderProps.minimumValue) {
92+
_sliderView.minimumValue = newSliderProps.minimumValue;
93+
}
94+
95+
// `maximumValue`
96+
if (oldSliderProps.maximumValue != newSliderProps.maximumValue) {
97+
_sliderView.maximumValue = newSliderProps.maximumValue;
5998
}
6099

61100
// `disabled`
@@ -79,14 +118,178 @@ - (void)updateProps:(SharedProps)props oldProps:(SharedProps)oldProps
79118
}
80119
}
81120

82-
- (void)onChange:(UISlider *)sender
121+
- (void)updateLocalData:(SharedLocalData)localData
122+
oldLocalData:(SharedLocalData)oldLocalData
83123
{
84-
if (_prevValue == sender.value) {
124+
SharedSliderLocalData previousData = _sliderLocalData;
125+
_sliderLocalData = std::static_pointer_cast<const SliderLocalData>(localData);
126+
assert(_sliderLocalData);
127+
bool havePreviousData = previousData != nullptr;
128+
129+
if (!havePreviousData || _sliderLocalData->getTrackImageSource() != previousData->getTrackImageSource()) {
130+
self.trackImageCoordinator = _sliderLocalData->getTrackImageRequest().getObserverCoordinator();
131+
}
132+
if (!havePreviousData || _sliderLocalData->getMinimumTrackImageSource() != previousData->getMinimumTrackImageSource()) {
133+
self.minimumTrackImageCoordinator = _sliderLocalData->getMinimumTrackImageRequest().getObserverCoordinator();
134+
}
135+
if (!havePreviousData || _sliderLocalData->getMaximumTrackImageSource() != previousData->getMaximumTrackImageSource()) {
136+
self.maximumTrackImageCoordinator = _sliderLocalData->getMaximumTrackImageRequest().getObserverCoordinator();
137+
}
138+
if (!havePreviousData || _sliderLocalData->getThumbImageSource() != previousData->getThumbImageSource()) {
139+
self.thumbImageCoordinator = _sliderLocalData->getThumbImageRequest().getObserverCoordinator();
140+
}
141+
}
142+
143+
- (void)setTrackImageCoordinator:(std::shared_ptr<const ImageResponseObserverCoordinator>)coordinator {
144+
if (_trackImageCoordinator) {
145+
_trackImageCoordinator->removeObserver(_trackImageResponseObserverProxy.get());
146+
}
147+
_trackImageCoordinator = coordinator;
148+
if (_trackImageCoordinator != nullptr) {
149+
_trackImageCoordinator->addObserver(_trackImageResponseObserverProxy.get());
150+
}
151+
}
152+
153+
- (void)setMinimumTrackImageCoordinator:(std::shared_ptr<const ImageResponseObserverCoordinator>)coordinator {
154+
if (_minimumTrackImageCoordinator) {
155+
_minimumTrackImageCoordinator->removeObserver(_minimumTrackImageResponseObserverProxy.get());
156+
}
157+
_minimumTrackImageCoordinator = coordinator;
158+
if (_minimumTrackImageCoordinator != nullptr) {
159+
_minimumTrackImageCoordinator->addObserver(_minimumTrackImageResponseObserverProxy.get());
160+
}
161+
}
162+
163+
- (void)setMaximumTrackImageCoordinator:(std::shared_ptr<const ImageResponseObserverCoordinator>)coordinator {
164+
if (_maximumTrackImageCoordinator) {
165+
_maximumTrackImageCoordinator->removeObserver(_maximumTrackImageResponseObserverProxy.get());
166+
}
167+
_maximumTrackImageCoordinator = coordinator;
168+
if (_maximumTrackImageCoordinator != nullptr) {
169+
_maximumTrackImageCoordinator->addObserver(_maximumTrackImageResponseObserverProxy.get());
170+
}
171+
}
172+
173+
- (void)setThumbImageCoordinator:(std::shared_ptr<const ImageResponseObserverCoordinator>)coordinator {
174+
if (_thumbImageCoordinator) {
175+
_thumbImageCoordinator->removeObserver(_thumbImageResponseObserverProxy.get());
176+
}
177+
_thumbImageCoordinator = coordinator;
178+
if (_thumbImageCoordinator != nullptr) {
179+
_thumbImageCoordinator->addObserver(_thumbImageResponseObserverProxy.get());
180+
}
181+
}
182+
183+
- (void)setTrackImage:(UIImage *)trackImage {
184+
if ([trackImage isEqual:_trackImage]) {
185+
return;
186+
}
187+
188+
_trackImage = trackImage;
189+
_minimumTrackImage = nil;
190+
_maximumTrackImage = nil;
191+
CGFloat width = trackImage.size.width / 2;
192+
UIImage *minimumTrackImage = [trackImage resizableImageWithCapInsets:(UIEdgeInsets){
193+
0, width, 0, width
194+
} resizingMode:UIImageResizingModeStretch];
195+
UIImage *maximumTrackImage = [trackImage resizableImageWithCapInsets:(UIEdgeInsets){
196+
0, width, 0, width
197+
} resizingMode:UIImageResizingModeStretch];
198+
[_sliderView setMinimumTrackImage:minimumTrackImage forState:UIControlStateNormal];
199+
[_sliderView setMaximumTrackImage:maximumTrackImage forState:UIControlStateNormal];
200+
}
201+
202+
-(void)setMinimumTrackImage:(UIImage *)minimumTrackImage {
203+
if ([minimumTrackImage isEqual:_minimumTrackImage] && _trackImage == nil) {
85204
return;
86205
}
87-
_prevValue = sender.value;
206+
207+
_trackImage = nil;
208+
_minimumTrackImage = minimumTrackImage;
209+
_minimumTrackImage = [_minimumTrackImage resizableImageWithCapInsets:(UIEdgeInsets) {
210+
0, _minimumTrackImage.size.width, 0, 0
211+
} resizingMode:UIImageResizingModeStretch];
212+
[_sliderView setMinimumTrackImage:_minimumTrackImage forState:UIControlStateNormal];
213+
}
88214

89-
std::dynamic_pointer_cast<const SliderEventEmitter>(_eventEmitter)->onValueChange(sender.value);
215+
-(void)setMaximumTrackImage:(UIImage *)maximumTrackImage {
216+
if ([maximumTrackImage isEqual:_maximumTrackImage] && _trackImage == nil) {
217+
return;
218+
}
219+
220+
_trackImage = nil;
221+
_maximumTrackImage = maximumTrackImage;
222+
_maximumTrackImage = [_maximumTrackImage resizableImageWithCapInsets:(UIEdgeInsets) {
223+
0, 0, 0, _maximumTrackImage.size.width
224+
} resizingMode:UIImageResizingModeStretch];
225+
[_sliderView setMaximumTrackImage:_maximumTrackImage forState:UIControlStateNormal];
90226
}
91227

228+
-(void)setThumbImage:(UIImage *)thumbImage {
229+
if ([thumbImage isEqual:_thumbImage]) {
230+
return;
231+
}
232+
233+
_thumbImage = thumbImage;
234+
[_sliderView setThumbImage:thumbImage forState:UIControlStateNormal];
235+
}
236+
237+
- (void)onChange:(UISlider *)sender
238+
{
239+
[self onChange:sender withContinuous:YES];
240+
}
241+
242+
- (void)sliderTouchEnd:(UISlider *)sender
243+
{
244+
[self onChange:sender withContinuous:NO];
245+
}
246+
247+
- (void)onChange:(UISlider *)sender withContinuous:(BOOL)continuous
248+
{
249+
float value = sender.value;
250+
251+
const auto &props = *std::static_pointer_cast<const SliderProps>(_props);
252+
253+
if (props.step > 0 && value <= (props.maximumValue - props.minimumValue)) {
254+
value = MAX(props.minimumValue,
255+
MIN(props.maximumValue,
256+
props.minimumValue + round((value - props.minimumValue) / props.step) * props.step
257+
)
258+
);
259+
260+
[_sliderView setValue:value animated:YES];
261+
}
262+
263+
if (continuous && _previousValue != value) {
264+
std::dynamic_pointer_cast<const SliderEventEmitter>(_eventEmitter)->onValueChange(value);
265+
}
266+
if (!continuous) {
267+
std::dynamic_pointer_cast<const SliderEventEmitter>(_eventEmitter)->onSlidingComplete(value);
268+
}
269+
270+
_previousValue = value;
271+
}
272+
273+
#pragma mark - RCTImageResponseDelegate
274+
275+
- (void)didReceiveImage:(UIImage *)image fromObserver:(void *)observer
276+
{
277+
if (observer == _trackImageResponseObserverProxy.get()) {
278+
self.trackImage = image;
279+
} else if (observer == _minimumTrackImageResponseObserverProxy.get()) {
280+
self.minimumTrackImage = image;
281+
} else if (observer == _maximumTrackImageResponseObserverProxy.get()) {
282+
self.maximumTrackImage = image;
283+
} else if (observer == _thumbImageResponseObserverProxy.get()) {
284+
self.thumbImage = image;
285+
}
286+
}
287+
288+
- (void)didReceiveProgress:(float)progress fromObserver:(void *)observer {
289+
}
290+
291+
- (void)didReceiveFailureFromObserver:(void *)observer {
292+
}
293+
294+
92295
@end
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+
#import <UIKit/UIKit.h>
9+
10+
NS_ASSUME_NONNULL_BEGIN
11+
12+
@protocol RCTImageResponseDelegate <NSObject>
13+
14+
- (void)didReceiveImage:(UIImage *)image fromObserver:(void*)observer;
15+
- (void)didReceiveProgress:(float)progress fromObserver:(void*)observer;
16+
- (void)didReceiveFailureFromObserver:(void*)observer;
17+
18+
@end
19+
20+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)