Skip to content

Commit 3c97a7c

Browse files
committed
[ptmt#18] ScrollView: add inverted behavior, update examples
1 parent d7dadc8 commit 3c97a7c

File tree

9 files changed

+108
-30
lines changed

9 files changed

+108
-30
lines changed

Examples/UIExplorer/ScrollViewExample.js

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ var {
2020
ScrollView,
2121
StyleSheet,
2222
View,
23-
Image
23+
Image,
24+
Text
2425
} = React;
2526

2627
exports.displayName = (undefined: ?string);
@@ -54,6 +55,21 @@ exports.examples = [
5455
</ScrollView>
5556
);
5657
}
58+
},
59+
, {
60+
title: 'Inverted <ScrollView>',
61+
description: 'Auto scroll content to bottom',
62+
render: function() {
63+
const texts = TEXTS.map((t, i)=> <Text key={i}>{i} - {t}</Text>);
64+
return (
65+
<ScrollView
66+
automaticallyAdjustContentInsets={false}
67+
style={styles.scrollView}
68+
autoScrollToBottom={true}>
69+
{texts}
70+
</ScrollView>
71+
);
72+
}
5773
}];
5874

5975
var Thumb = React.createClass({
@@ -69,13 +85,21 @@ var Thumb = React.createClass({
6985
}
7086
});
7187

72-
var THUMBS = ['https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851549_767334479959628_274486868_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851561_767334496626293_1958532586_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851579_767334503292959_179092627_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851589_767334513292958_1747022277_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851563_767334559959620_1193692107_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851593_767334566626286_1953955109_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851591_767334523292957_797560749_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851567_767334529959623_843148472_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851548_767334489959627_794462220_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851575_767334539959622_441598241_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851573_767334549959621_534583464_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851583_767334573292952_1519550680_n.png'];
73-
THUMBS = THUMBS.concat(THUMBS); // double length of THUMBS
88+
var THUMBS = new Array(20)
89+
.join()
90+
.split(',')
91+
.map(x => 'http://facebook.github.io/react/img/logo_og.png');
92+
93+
var TEXTS = new Array(100)
94+
.join()
95+
.split(',')
96+
.map(x => Math.random().toString(36).replace(/[^a-z]+/g, ''));
97+
7498
var createThumbRow = (uri, i) => <Thumb key={i} uri={uri} />;
7599

76100
var styles = StyleSheet.create({
77101
scrollView: {
78-
backgroundColor: '#6A85B1',
102+
//backgroundColor: '#6A85B1',
79103
height: 300,
80104
},
81105
horizontalScrollView: {
@@ -87,6 +111,9 @@ var styles = StyleSheet.create({
87111
backgroundColor: '#527FE4',
88112
padding: 5,
89113
},
114+
inverted: {
115+
transform: [{scaleY: -1}]
116+
},
90117
text: {
91118
fontSize: 20,
92119
color: '#888888',

Examples/UIExplorer/TransformExample.js

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,7 @@ var Flip = React.createClass({
3030
},
3131

3232
componentDidMount() {
33-
// setTimeout(() => {
34-
// this.refs.view1.setNativeProps({style: {transform: [{scaleX: 0.5}]}})
35-
// }, 400)
36-
// setTimeout(() => {
37-
// this.refs.view1.setNativeProps({style: {transform: [{scaleX: 1.5}]}})
38-
// }, 600)
39-
//this._animate();
33+
this._animate();
4034
},
4135

4236
_animate() {
@@ -50,11 +44,10 @@ var Flip = React.createClass({
5044
render() {
5145
return (
5246
<View style={styles.flipCardContainer}>
53-
<Animated.View ref={'view1'} style={[
47+
<Animated.View style={[
5448
styles.flipCard,
5549
{transform: [
5650
{perspective: 850},
57-
{scaleX: this.state.scale},
5851
{rotateX: this.state.theta.interpolate({
5952
inputRange: [0, 180],
6053
outputRange: ['0deg', '180deg']
@@ -100,7 +93,7 @@ var styles = StyleSheet.create({
10093
{translateY: 50},
10194
{rotate: '30deg'},
10295
{scaleX: 2},
103-
{scaleY: 2},
96+
{scaleY: -2},
10497
],
10598
width: 50,
10699
},
@@ -219,7 +212,7 @@ exports.examples = [
219212
},
220213
{
221214
title: 'Translate, Rotate, Scale',
222-
description: "translateX: 100, translateY: 50, rotate: '30deg', scaleX: 2, scaleY: 2",
215+
description: "translateX: 100, translateY: 50, rotate: '30deg', scaleX: 2, scaleY: -2",
223216
render() {
224217
return (
225218
<View style={styles.container}>

Examples/UIExplorer/UIExplorerListBase.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class ListView extends React.Component {
3838
var componentRows = this.props.dataSource.components.map((c, i) => this.props.renderRow(c, i));
3939
var apiRows = this.props.dataSource.apis.map((c, i) => this.props.renderRow(c, i));
4040
return (
41-
<ScrollView>
41+
<ScrollView showsVerticalScrollIndicator={true}>
4242
{this.props.renderSectionHeader(null, 'Components:')}
4343
{componentRows}
4444
{this.props.renderSectionHeader(null, 'APIs:')}
@@ -119,7 +119,6 @@ class UIExplorerListBase extends React.Component {
119119
return (
120120
<TouchableHighlight onPress={() => this.onPressRow(example)}
121121
onMouseEnter={() => this.setState({hovered: example.title})}
122-
// onMouseLeave={() => this.setState({hovered: -1})}
123122
key={i} style={[styles.row, hovered, selected]}>
124123
<View>
125124
<Text style={styles.rowTitleText}>

Libraries/Components/ScrollView/ScrollView.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ var ScrollView = React.createClass({
223223
* When true, shows a vertical scroll indicator.
224224
*/
225225
showsVerticalScrollIndicator: PropTypes.bool,
226+
/**
227+
* When true, scrolls to bottom.
228+
*/
229+
autoScrollToBottom: PropTypes.bool,
226230
/**
227231
* An array of child indices determining which children get docked to the
228232
* top of the screen when scrolling. For example, passing

Libraries/Text/RCTSecureTextField.m

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,6 @@ - (void)setPlaceholder:(NSString *)placeholder
7070
}
7171
}
7272

73-
//- (void)drawRect:(NSRect)rect
74-
//{
75-
// [super drawRect:rect];
76-
// if ([[self stringValue] isEqualToString:@""] && self != [[self window] firstResponder] && _placeholderTextColor != nil) {
77-
// NSDictionary *txtDict = [NSDictionary dictionaryWithObjectsAndKeys:_placeholderTextColor, NSForegroundColorAttributeName, nil];
78-
// [[[NSAttributedString alloc] initWithString:_placeholderString attributes:txtDict] drawAtPoint:NSMakePoint(0,0)];
79-
// }
80-
//}
81-
8273
- (NSArray *)reactSubviews
8374
{
8475
// TODO: do we support subviews of textfield in React?

React/Base/RCTRootView.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,6 @@ - (void)bundleFinishedLoading:(RCTBridge *)bridge
211211

212212
- (void)layout
213213
{
214-
NSLog(@"RCTRootView: layout");
215214
[super layout];
216215
_contentView.frame = self.bounds;
217216
// TODO: set center coordinates

React/Views/RCTScrollView.m

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,6 @@ - (void)updateClippedSubviews
521521
(scrollsHorizontally && (bounds.size.width < leeway || fabs(_lastClippedToRect.origin.x - bounds.origin.x) >= leeway)) ||
522522
(scrollsVertically && (bounds.size.height < leeway || fabs(_lastClippedToRect.origin.y - bounds.origin.y) >= leeway));
523523

524-
NSLog(@"countentSize.height %hhd %hhd", shouldClipAgain, scrollsVertically);
525524
if (shouldClipAgain) {
526525
const CGRect clipRect = CGRectInset(clipView.bounds, -leeway, -leeway);
527526
[self react_updateClippedSubviewsWithClipRect:clipRect relativeToView:clipView];
@@ -913,7 +912,10 @@ - (void)sendScrollEventWithType:(RCTScrollEventType)type
913912
@implementation RCTNativeScrollView
914913
{
915914
NSColor * _backgroundColor;
915+
BOOL _autoScrollToBottom;
916+
BOOL _inAutoScrollToBottom;
916917
RCTEventDispatcher *_eventDispatcher;
918+
NSRect _oldDocumentFrame;
917919
}
918920

919921
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
@@ -933,6 +935,68 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
933935
- (void)insertReactSubview:(NSView *)view atIndex:(__unused NSInteger)atIndex
934936
{
935937
[self setDocumentView:view];
938+
if (_autoScrollToBottom) {
939+
[[NSNotificationCenter defaultCenter] addObserver:self
940+
selector:@selector(documentFrameDidChange:)
941+
name:NSViewFrameDidChangeNotification
942+
object:view];
943+
944+
[self scrollToBottom];
945+
}
946+
}
947+
948+
- (void)setAutoScrollToBottom:(BOOL)autoScrollToBottom
949+
{
950+
_autoScrollToBottom = autoScrollToBottom;
951+
[self setDocumentView:[self documentView]];
952+
[self setFrame:[self frame]];
953+
}
954+
955+
956+
- (void)setFrame:(NSRect)frameRect
957+
{
958+
BOOL autoScroll = NO;
959+
960+
if (_autoScrollToBottom) {
961+
NSRect documentVisibleRect = [self documentVisibleRect];
962+
NSRect documentFrame = [[self documentView] frame];
963+
964+
//Autoscroll if we're scrolled close to the bottom
965+
autoScroll = ((documentVisibleRect.origin.y + documentVisibleRect.size.height) > (documentFrame.size.height - 20));
966+
}
967+
968+
[super setFrame:frameRect];
969+
970+
if (autoScroll) {
971+
[self scrollToBottom];
972+
}
973+
}
974+
975+
//When our document resizes
976+
- (void)documentFrameDidChange:(__unused NSNotification *)notification
977+
{
978+
//We guard against a recursive call to this method, which may occur if the user is resizing the view at the same time
979+
//content is being modified
980+
if (_autoScrollToBottom && !_inAutoScrollToBottom) {
981+
NSRect documentVisibleRect = [self documentVisibleRect];
982+
NSRect newDocumentFrame = [[self documentView] frame];
983+
984+
//We autoscroll if the height of the document frame changed AND (Using the old frame to calculate) we're scrolled close to the bottom.
985+
if ((newDocumentFrame.size.height != _oldDocumentFrame.size.height) &&
986+
((documentVisibleRect.origin.y + documentVisibleRect.size.height) > (_oldDocumentFrame.size.height - 20))) {
987+
_inAutoScrollToBottom = YES;
988+
[self scrollToBottom];
989+
_inAutoScrollToBottom = NO;
990+
}
991+
992+
//Remember the new frame
993+
_oldDocumentFrame = newDocumentFrame;
994+
}
995+
}
996+
997+
- (void)scrollToBottom
998+
{
999+
[[self documentView] scrollPoint:NSMakePoint(0, 100000)]; // TODO: avoid this hack
9361000
}
9371001

9381002
- (BOOL)opaque
@@ -963,7 +1027,7 @@ - (BOOL)isFlipped
9631027

9641028
- (void)setBackgroundColor:(NSColor *)backgroundColor
9651029
{
966-
if ([_backgroundColor isEqual:backgroundColor]) {
1030+
if ([_backgroundColor isEqual:backgroundColor] || backgroundColor == NULL) {
9671031
return;
9681032
}
9691033
_backgroundColor = backgroundColor;

React/Views/RCTScrollViewManager.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,6 @@ - (NSView *)view
129129

130130
RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL)
131131
RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL)
132+
RCT_EXPORT_VIEW_PROPERTY(autoScrollToBottom, BOOL)
132133

133134
@end

React/Views/RCTViewManager.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused RCTSpars
144144
RCT_CUSTOM_VIEW_PROPERTY(transformMatrix, CATransform3D, RCTView)
145145
{
146146
CATransform3D transform = json ? [RCTConvert CATransform3D:json] : defaultView.layer.transform;
147-
if (!view.superview) {
147+
if ([view respondsToSelector:@selector(shouldBeTransformed)] && !view.superview) {
148148
view.shouldBeTransformed = YES;
149149
view.transform = transform;
150150
} else {

0 commit comments

Comments
 (0)