Updates from Fri Feb 13
- [ReactNative] Fix throttle warning and warn in callback instead of render | Christopher Chedeau - [react-packager][streamline oss] Remove react-page-middleware | Amjad Masad - [ReactNative] Turn on perf measurement around a group feed load | Jing Chen - Implemented Layout animations | Nick Lockwood - [ReactNative] Revert D1815137 - avoid dropping touch start on missing target | Eric Vicenti - Moved RKPOPAnimationManager into FBReactKitComponents | Nick Lockwood - Extracted RKAnimationManager | Nick Lockwood
This commit is contained in:
parent
472c287cd3
commit
ef842c285b
|
@ -0,0 +1,250 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* @providesModule ListViewPagingExample
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react-native');
|
||||||
|
var {
|
||||||
|
Image,
|
||||||
|
LayoutAnimation,
|
||||||
|
ListView,
|
||||||
|
ListViewDataSource,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} = React;
|
||||||
|
|
||||||
|
var PAGE_SIZE = 4;
|
||||||
|
var THUMB_URLS = ['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'];
|
||||||
|
var NUM_SECTIONS = 100;
|
||||||
|
var NUM_ROWS_PER_SECTION = 10;
|
||||||
|
|
||||||
|
var Thumb = React.createClass({
|
||||||
|
getInitialState: function() {
|
||||||
|
return {thumbIndex: this._getThumbIdx(), dir: 'row'};
|
||||||
|
},
|
||||||
|
_getThumbIdx: function() {
|
||||||
|
return Math.floor(Math.random() * THUMB_URLS.length);
|
||||||
|
},
|
||||||
|
_onPressThumb: function() {
|
||||||
|
var config = layoutAnimationConfigs[this.state.thumbIndex % layoutAnimationConfigs.length];
|
||||||
|
LayoutAnimation.configureNext(config);
|
||||||
|
this.setState({
|
||||||
|
thumbIndex: this._getThumbIdx(),
|
||||||
|
dir: this.state.dir === 'row' ? 'column' : 'row',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<TouchableOpacity onPress={this._onPressThumb}>
|
||||||
|
<View style={[styles.buttonContents, {flexDirection: this.state.dir}]}>
|
||||||
|
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
|
||||||
|
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
|
||||||
|
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
|
||||||
|
{this.state.dir === 'column' ?
|
||||||
|
<Text>
|
||||||
|
Oooo, look at this new text! So awesome it may just be crazy.
|
||||||
|
Let me keep typing here so it wraps at least one line.
|
||||||
|
</Text> :
|
||||||
|
<Text />
|
||||||
|
}
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var ListViewPagingExample = React.createClass({
|
||||||
|
statics: {
|
||||||
|
title: '<ListView> - Paging',
|
||||||
|
description: 'Floating headers & layout animations.'
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
var getSectionData = (dataBlob, sectionID) => {
|
||||||
|
return dataBlob[sectionID];
|
||||||
|
};
|
||||||
|
var getRowData = (dataBlob, sectionID, rowID) => {
|
||||||
|
return dataBlob[rowID];
|
||||||
|
};
|
||||||
|
|
||||||
|
var dataSource = new ListViewDataSource({
|
||||||
|
getRowData: getRowData,
|
||||||
|
getSectionHeaderData: getSectionData,
|
||||||
|
rowHasChanged: (row1, row2) => row1 !== row2,
|
||||||
|
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
|
||||||
|
});
|
||||||
|
|
||||||
|
var dataBlob = {};
|
||||||
|
var sectionIDs = [];
|
||||||
|
var rowIDs = [];
|
||||||
|
for (var ii = 0; ii < NUM_SECTIONS; ii++) {
|
||||||
|
var sectionName = 'Section ' + ii;
|
||||||
|
sectionIDs.push(sectionName);
|
||||||
|
dataBlob[sectionName] = sectionName;
|
||||||
|
rowIDs[ii] = [];
|
||||||
|
|
||||||
|
for (var jj = 0; jj < NUM_ROWS_PER_SECTION; jj++) {
|
||||||
|
var rowName = 'S' + ii + ', R' + jj;
|
||||||
|
rowIDs[ii].push(rowName);
|
||||||
|
dataBlob[rowName] = rowName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
dataSource: dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs),
|
||||||
|
headerPressCount: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
renderRow: function(rowData, sectionID, rowID) {
|
||||||
|
return (<Thumb text={rowData}/>);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderSectionHeader: function(sectionData, sectionID) {
|
||||||
|
return (
|
||||||
|
<View style={styles.section}>
|
||||||
|
<Text style={styles.text}>
|
||||||
|
{sectionData}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderHeader: function() {
|
||||||
|
var headerLikeText = this.state.headerPressCount % 2 ?
|
||||||
|
<View><Text style={styles.text}>1 Like</Text></View> :
|
||||||
|
null;
|
||||||
|
return (
|
||||||
|
<TouchableOpacity onPress={this._onPressHeader}>
|
||||||
|
<View style={styles.header}>
|
||||||
|
{headerLikeText}
|
||||||
|
<View>
|
||||||
|
<Text style={styles.text}>
|
||||||
|
Table Header (click me)
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderFooter: function() {
|
||||||
|
return (
|
||||||
|
<View style={styles.header}>
|
||||||
|
<Text onPress={() => console.log('Footer!')} style={styles.text}>
|
||||||
|
Table Footer
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<ListView
|
||||||
|
style={styles.listview}
|
||||||
|
dataSource={this.state.dataSource}
|
||||||
|
onChangeVisibleRows={(visibleRows, changedRows) => console.log({visibleRows, changedRows})}
|
||||||
|
renderHeader={this.renderHeader}
|
||||||
|
renderFooter={this.renderFooter}
|
||||||
|
renderSectionHeader={this.renderSectionHeader}
|
||||||
|
renderRow={this.renderRow}
|
||||||
|
initialListSize={10}
|
||||||
|
pageSize={PAGE_SIZE}
|
||||||
|
scrollRenderAheadDistance={2000}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onPressHeader: function() {
|
||||||
|
var config = layoutAnimationConfigs[Math.floor(this.state.headerPressCount / 2) % layoutAnimationConfigs.length];
|
||||||
|
LayoutAnimation.configureNext(config);
|
||||||
|
this.setState({headerPressCount: this.state.headerPressCount + 1});
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var styles = StyleSheet.create({
|
||||||
|
listview: {
|
||||||
|
backgroundColor: '#B0C4DE',
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
height: 40,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: '#3B5998',
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
color: 'white',
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
},
|
||||||
|
rowText: {
|
||||||
|
color: '#888888',
|
||||||
|
},
|
||||||
|
thumbText: {
|
||||||
|
fontSize: 20,
|
||||||
|
color: '#888888',
|
||||||
|
},
|
||||||
|
buttonContents: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginHorizontal: 5,
|
||||||
|
marginVertical: 3,
|
||||||
|
padding: 5,
|
||||||
|
backgroundColor: '#EAEAEA',
|
||||||
|
borderRadius: 3,
|
||||||
|
paddingVertical: 10,
|
||||||
|
},
|
||||||
|
img: {
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
marginHorizontal: 10,
|
||||||
|
},
|
||||||
|
section: {
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
padding: 6,
|
||||||
|
backgroundColor: '#5890ff',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var animations = {
|
||||||
|
layout: {
|
||||||
|
spring: {
|
||||||
|
duration: 0.75,
|
||||||
|
create: {
|
||||||
|
duration: 0.3,
|
||||||
|
type: LayoutAnimation.Types.easeInEaseOut,
|
||||||
|
property: LayoutAnimation.Properties.opacity,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
type: LayoutAnimation.Types.spring,
|
||||||
|
springDamping: 0.4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
easeInEaseOut: {
|
||||||
|
duration: 0.3,
|
||||||
|
create: {
|
||||||
|
type: LayoutAnimation.Types.easeInEaseOut,
|
||||||
|
property: LayoutAnimation.Properties.scaleXY,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
delay: 0.1,
|
||||||
|
type: LayoutAnimation.Types.easeInEaseOut,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var layoutAnimationConfigs = [
|
||||||
|
animations.layout.spring,
|
||||||
|
animations.layout.easeInEaseOut,
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = ListViewPagingExample;
|
|
@ -24,6 +24,7 @@ var EXAMPLES = [
|
||||||
require('./ExpandingTextExample'),
|
require('./ExpandingTextExample'),
|
||||||
require('./ImageExample'),
|
require('./ImageExample'),
|
||||||
require('./ListViewSimpleExample'),
|
require('./ListViewSimpleExample'),
|
||||||
|
require('./ListViewPagingExample'),
|
||||||
require('./NavigatorIOSExample'),
|
require('./NavigatorIOSExample'),
|
||||||
require('./StatusBarIOSExample'),
|
require('./StatusBarIOSExample'),
|
||||||
require('./PointerEventsExample'),
|
require('./PointerEventsExample'),
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* @providesModule LayoutAnimation
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var PropTypes = require('ReactPropTypes');
|
||||||
|
var RKUIManager = require('NativeModules').RKUIManager;
|
||||||
|
|
||||||
|
var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker');
|
||||||
|
var keyMirror = require('keyMirror');
|
||||||
|
|
||||||
|
var Types = keyMirror({
|
||||||
|
spring: true,
|
||||||
|
linear: true,
|
||||||
|
easeInEaseOut: true,
|
||||||
|
easeIn: true,
|
||||||
|
easeOut: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var Properties = keyMirror({
|
||||||
|
opacity: true,
|
||||||
|
scaleXY: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var animChecker = createStrictShapeTypeChecker({
|
||||||
|
duration: PropTypes.number,
|
||||||
|
delay: PropTypes.number,
|
||||||
|
springDamping: PropTypes.number,
|
||||||
|
initialVelocity: PropTypes.number,
|
||||||
|
type: PropTypes.oneOf(
|
||||||
|
Object.keys(Types)
|
||||||
|
),
|
||||||
|
property: PropTypes.oneOf( // Only applies to create/delete
|
||||||
|
Object.keys(Properties)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
var configChecker = createStrictShapeTypeChecker({
|
||||||
|
duration: PropTypes.number.isRequired,
|
||||||
|
create: animChecker,
|
||||||
|
update: animChecker,
|
||||||
|
delete: animChecker,
|
||||||
|
});
|
||||||
|
|
||||||
|
var LayoutAnimation = {
|
||||||
|
configureNext(config, onAnimationDidEnd, onError) {
|
||||||
|
configChecker({config}, 'config', 'LayoutAnimation.configureNext');
|
||||||
|
RKUIManager.configureNextLayoutAnimation(config, onAnimationDidEnd, onError);
|
||||||
|
},
|
||||||
|
create(duration, type, creationProp) {
|
||||||
|
return {
|
||||||
|
duration,
|
||||||
|
create: {
|
||||||
|
type,
|
||||||
|
property: creationProp,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
type,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Types: Types,
|
||||||
|
Properties: Properties,
|
||||||
|
configChecker: configChecker,
|
||||||
|
};
|
||||||
|
|
||||||
|
LayoutAnimation.Presets = {
|
||||||
|
easeInEaseOut: LayoutAnimation.create(
|
||||||
|
0.3, Types.easeInEaseOut, Properties.opacity
|
||||||
|
),
|
||||||
|
linear: LayoutAnimation.create(
|
||||||
|
0.5, Types.linear, Properties.opacity
|
||||||
|
),
|
||||||
|
spring: {
|
||||||
|
duration: 0.7,
|
||||||
|
create: {
|
||||||
|
type: Types.linear,
|
||||||
|
property: Properties.opacity,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
type: Types.spring,
|
||||||
|
springDamping: 0.4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = LayoutAnimation;
|
|
@ -0,0 +1,250 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* @providesModule POPAnimationMixin
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var POPAnimation = require('POPAnimation');
|
||||||
|
if (!POPAnimation) {
|
||||||
|
// POP animation isn't available in the OSS fork - this is a temporary
|
||||||
|
// workaround to enable its availability to be determined at runtime.
|
||||||
|
module.exports = null;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var invariant = require('invariant');
|
||||||
|
var warning = require('warning');
|
||||||
|
|
||||||
|
var POPAnimationMixin = {
|
||||||
|
/**
|
||||||
|
* Different ways to interpolate between beginning and end states
|
||||||
|
* of properties during animation, such as spring, linear, and decay.
|
||||||
|
*/
|
||||||
|
AnimationTypes: POPAnimation.Types,
|
||||||
|
AnimationProperties: POPAnimation.Properties,
|
||||||
|
|
||||||
|
getInitialState: function(): Object {
|
||||||
|
return {
|
||||||
|
_currentAnimationsByNodeHandle: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureBookkeepingSetup: function(nodeHandle: any) {
|
||||||
|
if (!this.state._currentAnimationsByNodeHandle[nodeHandle]) {
|
||||||
|
this.state._currentAnimationsByNodeHandle[nodeHandle] = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start animating the View with ref `refKey`.
|
||||||
|
*
|
||||||
|
* @param {key} refKey The key to reference the View to be animated.
|
||||||
|
*
|
||||||
|
* @param {number|Object} anim Either the identifier returned by
|
||||||
|
* POPAnimation.create* or an object defining all the necessary
|
||||||
|
* properties of the animation you wish to start (including type, matching
|
||||||
|
* an entry in AnimationTypes).
|
||||||
|
*
|
||||||
|
* @param {func} doneCallback A callback fired when the animation is done, and
|
||||||
|
* is passed a `finished` param that indicates whether the animation
|
||||||
|
* completely finished, or was interrupted.
|
||||||
|
*/
|
||||||
|
startAnimation: function(
|
||||||
|
refKey: string,
|
||||||
|
anim: number | {type: number; property: number;},
|
||||||
|
doneCallback: (finished: bool) => void
|
||||||
|
) {
|
||||||
|
var animID: number = 0;
|
||||||
|
if (typeof anim === 'number') {
|
||||||
|
animID = anim;
|
||||||
|
} else {
|
||||||
|
invariant(
|
||||||
|
anim instanceof Object &&
|
||||||
|
anim.type !== undefined &&
|
||||||
|
anim.property !== undefined,
|
||||||
|
'Animation definitions must specify a type of animation and a ' +
|
||||||
|
'property to animate.'
|
||||||
|
);
|
||||||
|
animID = POPAnimation.createAnimation(anim.type, anim);
|
||||||
|
}
|
||||||
|
invariant(
|
||||||
|
this.refs[refKey],
|
||||||
|
'Invalid refKey ' + refKey + ' for anim:\n' + JSON.stringify(anim) +
|
||||||
|
'\nvalid refs: ' + JSON.stringify(Object.keys(this.refs))
|
||||||
|
);
|
||||||
|
var refNodeHandle = this.refs[refKey].getNodeHandle();
|
||||||
|
this.startAnimationWithNodeHandle(refNodeHandle, animID, doneCallback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an animation on a native node.
|
||||||
|
*
|
||||||
|
* @param {NodeHandle} nodeHandle Handle to underlying native node.
|
||||||
|
* @see `startAnimation`.
|
||||||
|
*/
|
||||||
|
startAnimationWithNodeHandle: function(
|
||||||
|
nodeHandle: any,
|
||||||
|
animID: number,
|
||||||
|
doneCallback: (finished: bool) => void
|
||||||
|
) {
|
||||||
|
this._ensureBookkeepingSetup(nodeHandle);
|
||||||
|
var animations = this.state._currentAnimationsByNodeHandle[nodeHandle];
|
||||||
|
var animIndex = animations.length;
|
||||||
|
animations.push(animID);
|
||||||
|
var cleanupWrapper = (finished) => {
|
||||||
|
if (!this.isMounted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
animations[animIndex] = 0; // zero it out so we don't try to stop it
|
||||||
|
var allDone = true;
|
||||||
|
for (var ii = 0; ii < animations.length; ii++) {
|
||||||
|
if (animations[ii]) {
|
||||||
|
allDone = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allDone) {
|
||||||
|
this.state._currentAnimationsByNodeHandle[nodeHandle] = undefined;
|
||||||
|
}
|
||||||
|
doneCallback && doneCallback(finished);
|
||||||
|
};
|
||||||
|
POPAnimation.addAnimation(nodeHandle, animID, cleanupWrapper);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts multiple animations with one shared callback that is called when all
|
||||||
|
* animations complete.
|
||||||
|
*
|
||||||
|
* @param {Array(Object} animations Array of objects defining all the
|
||||||
|
* animations to start, each with shape `{ref|nodeHandle, anim}`.
|
||||||
|
* @param {func} onSuccess A callback fired when all animations have returned,
|
||||||
|
* and is passed a finished arg that is true if all animations finished
|
||||||
|
* completely.
|
||||||
|
* @param {func} onFailure Not supported yet.
|
||||||
|
*/
|
||||||
|
startAnimations: function(
|
||||||
|
animations: Array<Object>,
|
||||||
|
onSuccess: (finished: boolean) => void,
|
||||||
|
onFailure: () => void
|
||||||
|
) {
|
||||||
|
var numReturned = 0;
|
||||||
|
var numFinished = 0;
|
||||||
|
var numAnimations = animations.length;
|
||||||
|
var metaCallback = (finished) => {
|
||||||
|
if (finished) {
|
||||||
|
++numFinished;
|
||||||
|
}
|
||||||
|
if (++numReturned === numAnimations) {
|
||||||
|
onSuccess && onSuccess(numFinished === numAnimations);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
animations.forEach((anim) => {
|
||||||
|
warning(
|
||||||
|
anim.ref != null || anim.nodeHandle != null &&
|
||||||
|
!anim.ref !== !anim.nodeHandle,
|
||||||
|
'Animations must be specified with either ref xor nodeHandle'
|
||||||
|
);
|
||||||
|
if (anim.ref) {
|
||||||
|
this.startAnimation(anim.ref, anim.anim, metaCallback);
|
||||||
|
} else if (anim.nodeHandle) {
|
||||||
|
this.startAnimationWithNodeHandle(anim.nodeHandle, anim.anim, metaCallback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop any and all animations operating on the View with native node handle
|
||||||
|
* `nodeHandle`.
|
||||||
|
*
|
||||||
|
* @param {NodeHandle} component The instance to stop animations
|
||||||
|
* on. Do not pass a composite component.
|
||||||
|
*/
|
||||||
|
stopNodeHandleAnimations: function(nodeHandle: any) {
|
||||||
|
if (!this.state._currentAnimationsByNodeHandle[nodeHandle]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var anims = this.state._currentAnimationsByNodeHandle[nodeHandle];
|
||||||
|
for (var i = 0; i < anims.length; i++) {
|
||||||
|
var anim = anims[i];
|
||||||
|
if (anim) {
|
||||||
|
// Note: Converting the string key to a number `nodeHandle`.
|
||||||
|
POPAnimation.removeAnimation(+nodeHandle, anim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.state._currentAnimationsByNodeHandle[nodeHandle] = undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop any and all animations operating on the View with ref `refKey`.
|
||||||
|
*
|
||||||
|
* @param {key} refKey The key to reference the View to be animated.
|
||||||
|
*/
|
||||||
|
stopAnimations: function(refKey: string) {
|
||||||
|
invariant(this.refs[refKey], 'invalid ref');
|
||||||
|
this.stopNodeHandleAnimations(this.refs[refKey].getNodeHandle());
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop any and all animations created by this component on itself and
|
||||||
|
* subviews.
|
||||||
|
*/
|
||||||
|
stopAllAnimations: function() {
|
||||||
|
for (var nodeHandle in this.state._currentAnimationsByNodeHandle) {
|
||||||
|
this.stopNodeHandleAnimations(nodeHandle);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates size and position of a view referenced by `refKey` to a specific
|
||||||
|
* frame.
|
||||||
|
*
|
||||||
|
* @param {key} refKey ref key for view to animate.
|
||||||
|
* @param {Object} frame The frame to animate the view to, specified as {left,
|
||||||
|
* top, width, height}.
|
||||||
|
* @param {const} type What type of interpolation to use, selected from
|
||||||
|
* `inperpolationTypes`.
|
||||||
|
* @param {Object} event Event encapsulating synthetic and native data that
|
||||||
|
* may have triggered this animation. Velocity is extracted from it if
|
||||||
|
* possible and applied to the animation.
|
||||||
|
* @param {func} doneCallback A callback fired when the animation is done, and
|
||||||
|
* is passed a `finished` param that indicates whether the animation
|
||||||
|
* completely finished, or was interrupted.
|
||||||
|
*/
|
||||||
|
animateToFrame: function(
|
||||||
|
refKey: string,
|
||||||
|
frame: {left: number; top: number; width: number; height: number;},
|
||||||
|
type: number,
|
||||||
|
velocity: number,
|
||||||
|
doneCallback: (finished: boolean) => void
|
||||||
|
) {
|
||||||
|
var animFrame = { // Animations use a centered coordinate system.
|
||||||
|
x: frame.left + frame.width / 2,
|
||||||
|
y: frame.top + frame.height / 2,
|
||||||
|
w: frame.width,
|
||||||
|
h: frame.height
|
||||||
|
};
|
||||||
|
frame = undefined;
|
||||||
|
var velocity = velocity || [0, 0];
|
||||||
|
var posAnim = POPAnimation.createAnimation(type, {
|
||||||
|
property: POPAnimation.Properties.position,
|
||||||
|
toValue: [animFrame.x, animFrame.y],
|
||||||
|
velocity: velocity,
|
||||||
|
});
|
||||||
|
var sizeAnim = POPAnimation.createAnimation(type, {
|
||||||
|
property: POPAnimation.Properties.size,
|
||||||
|
toValue: [animFrame.w, animFrame.h]
|
||||||
|
});
|
||||||
|
this.startAnimation(refKey, posAnim, doneCallback);
|
||||||
|
this.startAnimation(refKey, sizeAnim);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Cleanup any potentially leaked animations.
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
this.stopAllAnimations();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = POPAnimationMixin;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* @providesModule POPAnimation
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var RKPOPAnimationManager = require('NativeModulesDeprecated').RKPOPAnimationManager;
|
||||||
|
if (!RKPOPAnimationManager) {
|
||||||
|
// POP animation isn't available in the OSS fork - this is a temporary
|
||||||
|
// workaround to enable its availability to be determined at runtime.
|
||||||
|
module.exports = null;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var ReactPropTypes = require('ReactPropTypes');
|
||||||
|
var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker');
|
||||||
|
var getObjectValues = require('getObjectValues');
|
||||||
|
var invariant = require('invariant');
|
||||||
|
var merge = require('merge');
|
||||||
|
|
||||||
|
var RKTypes = RKPOPAnimationManager.Types;
|
||||||
|
var RKProperties = RKPOPAnimationManager.Properties;
|
||||||
|
|
||||||
|
var Properties = {
|
||||||
|
bounds: RKProperties.bounds,
|
||||||
|
opacity: RKProperties.opacity,
|
||||||
|
position: RKProperties.position,
|
||||||
|
positionX: RKProperties.positionX,
|
||||||
|
positionY: RKProperties.positionY,
|
||||||
|
zPosition: RKProperties.zPosition,
|
||||||
|
rotation: RKProperties.rotation,
|
||||||
|
rotationX: RKProperties.rotationX,
|
||||||
|
rotationY: RKProperties.rotationY,
|
||||||
|
scaleX: RKProperties.scaleX,
|
||||||
|
scaleXY: RKProperties.scaleXY,
|
||||||
|
scaleY: RKProperties.scaleY,
|
||||||
|
shadowColor: RKProperties.shadowColor,
|
||||||
|
shadowOffset: RKProperties.shadowOffset,
|
||||||
|
shadowOpacity: RKProperties.shadowOpacity,
|
||||||
|
shadowRadius: RKProperties.shadowRadius,
|
||||||
|
size: RKProperties.size,
|
||||||
|
subscaleXY: RKProperties.subscaleXY,
|
||||||
|
subtranslationX: RKProperties.subtranslationX,
|
||||||
|
subtranslationXY: RKProperties.subtranslationXY,
|
||||||
|
subtranslationY: RKProperties.subtranslationY,
|
||||||
|
subtranslationZ: RKProperties.subtranslationZ,
|
||||||
|
translationX: RKProperties.translationX,
|
||||||
|
translationXY: RKProperties.translationXY,
|
||||||
|
translationY: RKProperties.translationY,
|
||||||
|
translationZ: RKProperties.translationZ,
|
||||||
|
};
|
||||||
|
|
||||||
|
var Types = {
|
||||||
|
decay: RKTypes.decay,
|
||||||
|
easeIn: RKTypes.easeIn,
|
||||||
|
easeInEaseOut: RKTypes.easeInEaseOut,
|
||||||
|
easeOut: RKTypes.easeOut,
|
||||||
|
linear: RKTypes.linear,
|
||||||
|
spring: RKTypes.spring,
|
||||||
|
};
|
||||||
|
|
||||||
|
var POPAnimation = {
|
||||||
|
Types: Types,
|
||||||
|
Properties: Properties,
|
||||||
|
|
||||||
|
attributeChecker: createStrictShapeTypeChecker({
|
||||||
|
type: ReactPropTypes.oneOf(getObjectValues(Types)),
|
||||||
|
property: ReactPropTypes.oneOf(getObjectValues(Properties)),
|
||||||
|
fromValue: ReactPropTypes.any,
|
||||||
|
toValue: ReactPropTypes.any,
|
||||||
|
duration: ReactPropTypes.any,
|
||||||
|
velocity: ReactPropTypes.any,
|
||||||
|
deceleration: ReactPropTypes.any,
|
||||||
|
springBounciness: ReactPropTypes.any,
|
||||||
|
dynamicsFriction: ReactPropTypes.any,
|
||||||
|
dynamicsMass: ReactPropTypes.any,
|
||||||
|
dynamicsTension: ReactPropTypes.any,
|
||||||
|
}),
|
||||||
|
|
||||||
|
lastUsedTag: 0,
|
||||||
|
allocateTagForAnimation: function() {
|
||||||
|
return ++this.lastUsedTag;
|
||||||
|
},
|
||||||
|
|
||||||
|
createAnimation: function(typeName, attrs) {
|
||||||
|
var tag = this.allocateTagForAnimation();
|
||||||
|
|
||||||
|
if (__DEV__) {
|
||||||
|
POPAnimation.attributeChecker(
|
||||||
|
{attrs},
|
||||||
|
'attrs',
|
||||||
|
'POPAnimation.createAnimation'
|
||||||
|
);
|
||||||
|
POPAnimation.attributeChecker(
|
||||||
|
{attrs: {type: typeName}},
|
||||||
|
'attrs',
|
||||||
|
'POPAnimation.createAnimation'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
RKPOPAnimationManager.createAnimationInternal(tag, typeName, attrs);
|
||||||
|
return tag;
|
||||||
|
},
|
||||||
|
|
||||||
|
createSpringAnimation: function(attrs) {
|
||||||
|
return this.createAnimation(this.Types.spring, attrs);
|
||||||
|
},
|
||||||
|
|
||||||
|
createDecayAnimation: function(attrs) {
|
||||||
|
return this.createAnimation(this.Types.decay, attrs);
|
||||||
|
},
|
||||||
|
|
||||||
|
createLinearAnimation: function(attrs) {
|
||||||
|
return this.createAnimation(this.Types.linear, attrs);
|
||||||
|
},
|
||||||
|
|
||||||
|
createEaseInAnimation: function(attrs) {
|
||||||
|
return this.createAnimation(this.Types.easeIn, attrs);
|
||||||
|
},
|
||||||
|
|
||||||
|
createEaseOutAnimation: function(attrs) {
|
||||||
|
return this.createAnimation(this.Types.easeOut, attrs);
|
||||||
|
},
|
||||||
|
|
||||||
|
createEaseInEaseOutAnimation: function(attrs) {
|
||||||
|
return this.createAnimation(this.Types.easeInEaseOut, attrs);
|
||||||
|
},
|
||||||
|
|
||||||
|
addAnimation: function(nodeHandle, anim, callback) {
|
||||||
|
RKPOPAnimationManager.addAnimation(nodeHandle, anim, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAnimation: function(nodeHandle, anim) {
|
||||||
|
RKPOPAnimationManager.removeAnimation(nodeHandle, anim);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure that we correctly propagate RKPOPAnimationManager constants
|
||||||
|
// to POPAnimation
|
||||||
|
if (__DEV__) {
|
||||||
|
var allProperties = merge(
|
||||||
|
RKPOPAnimationManager.Properties,
|
||||||
|
RKPOPAnimationManager.Properties
|
||||||
|
);
|
||||||
|
for (var key in allProperties) {
|
||||||
|
invariant(
|
||||||
|
POPAnimation.Properties[key] === RKPOPAnimationManager.Properties[key],
|
||||||
|
'POPAnimation doesn\'t copy property ' + key + ' correctly'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var allTypes = merge(
|
||||||
|
RKPOPAnimationManager.Types,
|
||||||
|
RKPOPAnimationManager.Types
|
||||||
|
);
|
||||||
|
for (var key in allTypes) {
|
||||||
|
invariant(
|
||||||
|
POPAnimation.Types[key] === RKPOPAnimationManager.Types[key],
|
||||||
|
'POPAnimation doesn\'t copy type ' + key + ' correctly'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = POPAnimation;
|
||||||
|
|
||||||
|
}
|
|
@ -25,7 +25,6 @@ var invariant = require('invariant');
|
||||||
var merge = require('merge');
|
var merge = require('merge');
|
||||||
var nativePropType = require('nativePropType');
|
var nativePropType = require('nativePropType');
|
||||||
var validAttributesFromPropTypes = require('validAttributesFromPropTypes');
|
var validAttributesFromPropTypes = require('validAttributesFromPropTypes');
|
||||||
var warning = require('warning');
|
|
||||||
|
|
||||||
var PropTypes = React.PropTypes;
|
var PropTypes = React.PropTypes;
|
||||||
|
|
||||||
|
@ -194,14 +193,19 @@ var ScrollView = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
warning(
|
if (this.props.onScroll && !this.props.throttleScrollCallbackMS) {
|
||||||
this.props.onScroll && !this.props.throttleScrollCallbackMS,
|
var onScroll = this.props.onScroll;
|
||||||
|
this.props.onScroll = function() {
|
||||||
|
console.log(
|
||||||
'You specified `onScroll` on a <ScrollView> but not ' +
|
'You specified `onScroll` on a <ScrollView> but not ' +
|
||||||
'`throttleScrollCallbackMS`. You will only receive one event. ' +
|
'`throttleScrollCallbackMS`. You will only receive one event. ' +
|
||||||
'Using `16` you get all the events but be aware that it may cause ' +
|
'Using `16` you get all the events but be aware that it may ' +
|
||||||
'frame drops, use a bigger number if you don\'t need as much ' +
|
'cause frame drops, use a bigger number if you don\'t need as ' +
|
||||||
'precision.'
|
'much precision.'
|
||||||
);
|
);
|
||||||
|
onScroll.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentContainer =
|
var contentContainer =
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* @providesModule TouchableOpacity
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||||
|
var POPAnimationMixin = require('POPAnimationMixin');
|
||||||
|
var React = require('React');
|
||||||
|
var Touchable = require('Touchable');
|
||||||
|
|
||||||
|
var cloneWithProps = require('cloneWithProps');
|
||||||
|
var ensureComponentIsNative = require('ensureComponentIsNative');
|
||||||
|
var keyOf = require('keyOf');
|
||||||
|
var onlyChild = require('onlyChild');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TouchableOpacity - A wrapper for making views respond properly to touches.
|
||||||
|
* On press down, the opacity of the wrapped view is decreased, dimming it.
|
||||||
|
* This is done without actually changing the view hierarchy, and in general is
|
||||||
|
* easy to add to an app without weird side-effects. Example:
|
||||||
|
*
|
||||||
|
* renderButton: function() {
|
||||||
|
* return (
|
||||||
|
* <TouchableOpacity onPress={this._onPressButton}>
|
||||||
|
* <Image
|
||||||
|
* style={styles.button}
|
||||||
|
* source={ix('myButton')}
|
||||||
|
* />
|
||||||
|
* </View>
|
||||||
|
* );
|
||||||
|
* },
|
||||||
|
*
|
||||||
|
* More example code in TouchableExample.js, and more in-depth discussion in
|
||||||
|
* Touchable.js. See also TouchableHighlight.js and
|
||||||
|
* TouchableWithoutFeedback.js.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var TouchableOpacity = React.createClass({
|
||||||
|
mixins: [Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
/**
|
||||||
|
* Called when the touch is released, but not if cancelled (e.g. by
|
||||||
|
* a scroll that steals the responder lock).
|
||||||
|
*/
|
||||||
|
onPress: React.PropTypes.func,
|
||||||
|
/**
|
||||||
|
* Determines what the opacity of the wrapped view should be when touch is
|
||||||
|
* active.
|
||||||
|
*/
|
||||||
|
activeOpacity: React.PropTypes.number,
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
activeOpacity: 0.5,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return this.touchableGetInitialState();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
ensureComponentIsNative(this.refs[CHILD_REF]);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidUpdate: function() {
|
||||||
|
ensureComponentIsNative(this.refs[CHILD_REF]);
|
||||||
|
},
|
||||||
|
|
||||||
|
setOpacityTo: function(value) {
|
||||||
|
if (POPAnimationMixin) {
|
||||||
|
// Reset with animation if POP is available
|
||||||
|
this.stopAllAnimations();
|
||||||
|
var anim = {
|
||||||
|
type: this.AnimationTypes.linear,
|
||||||
|
property: this.AnimationProperties.opacity,
|
||||||
|
toValue: value,
|
||||||
|
};
|
||||||
|
this.startAnimation(CHILD_REF, anim);
|
||||||
|
} else {
|
||||||
|
// Reset immediately if POP is unavailable
|
||||||
|
this.refs[CHILD_REF].setNativeProps({
|
||||||
|
opacity: value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
|
||||||
|
* defined on your component.
|
||||||
|
*/
|
||||||
|
touchableHandleActivePressIn: function() {
|
||||||
|
this.refs[CHILD_REF].setNativeProps({
|
||||||
|
opacity: this.props.activeOpacity
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
touchableHandleActivePressOut: function() {
|
||||||
|
this.setOpacityTo(1.0);
|
||||||
|
},
|
||||||
|
|
||||||
|
touchableHandlePress: function() {
|
||||||
|
this.setOpacityTo(1.0);
|
||||||
|
this.props.onPress && this.props.onPress();
|
||||||
|
},
|
||||||
|
|
||||||
|
touchableGetPressRectOffset: function() {
|
||||||
|
return PRESS_RECT_OFFSET; // Always make sure to predeclare a constant!
|
||||||
|
},
|
||||||
|
|
||||||
|
touchableGetHighlightDelayMS: function() {
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return cloneWithProps(onlyChild(this.props.children), {
|
||||||
|
ref: CHILD_REF,
|
||||||
|
accessible: true,
|
||||||
|
testID: this.props.testID,
|
||||||
|
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||||
|
onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest,
|
||||||
|
onResponderGrant: this.touchableHandleResponderGrant,
|
||||||
|
onResponderMove: this.touchableHandleResponderMove,
|
||||||
|
onResponderRelease: this.touchableHandleResponderRelease,
|
||||||
|
onResponderTerminate: this.touchableHandleResponderTerminate,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the scroll view is disabled, this defines how far your touch may move
|
||||||
|
* off of the button, before deactivating the button. Once deactivated, try
|
||||||
|
* moving it back and you'll see that the button is once again reactivated!
|
||||||
|
* Move it back and forth several times while the scroll view is disabled.
|
||||||
|
*/
|
||||||
|
var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
||||||
|
|
||||||
|
var CHILD_REF = keyOf({childRef: null});
|
||||||
|
|
||||||
|
module.exports = TouchableOpacity;
|
|
@ -9,6 +9,7 @@ var NativeModules = require('NativeModules');
|
||||||
var NativeModulesDeprecated = require('NativeModulesDeprecated');
|
var NativeModulesDeprecated = require('NativeModulesDeprecated');
|
||||||
var RKUIManager = NativeModules.RKUIManager;
|
var RKUIManager = NativeModules.RKUIManager;
|
||||||
var RKUIManagerDeprecated = NativeModulesDeprecated.RKUIManager;
|
var RKUIManagerDeprecated = NativeModulesDeprecated.RKUIManager;
|
||||||
|
var RKPOPAnimationManagerDeprecated = NativeModulesDeprecated.RKPOPAnimationManager;
|
||||||
var TextInputState = require('TextInputState');
|
var TextInputState = require('TextInputState');
|
||||||
|
|
||||||
var flattenStyle = require('flattenStyle');
|
var flattenStyle = require('flattenStyle');
|
||||||
|
@ -19,19 +20,19 @@ var animationIDInvariant = function(funcName, anim) {
|
||||||
invariant(
|
invariant(
|
||||||
anim,
|
anim,
|
||||||
funcName + ' must be called with a valid animation ID returned from' +
|
funcName + ' must be called with a valid animation ID returned from' +
|
||||||
' ReactIOSAnimation.createAnimation, received: "' + anim + '"'
|
' POPAnimation.createAnimation, received: "' + anim + '"'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
var NativeMethodsMixin = {
|
var NativeMethodsMixin = {
|
||||||
addAnimation: function(anim, callback) {
|
addAnimation: function(anim, callback) {
|
||||||
animationIDInvariant('addAnimation', anim);
|
animationIDInvariant('addAnimation', anim);
|
||||||
RKUIManagerDeprecated.addAnimation(this.getNodeHandle(), anim, callback);
|
RKPOPAnimationManagerDeprecated.addAnimation(this.getNodeHandle(), anim, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeAnimation: function(anim) {
|
removeAnimation: function(anim) {
|
||||||
animationIDInvariant('removeAnimation', anim);
|
animationIDInvariant('removeAnimation', anim);
|
||||||
RKUIManagerDeprecated.removeAnimation(this.getNodeHandle(), anim);
|
RKPOPAnimationManagerDeprecated.removeAnimation(this.getNodeHandle(), anim);
|
||||||
},
|
},
|
||||||
|
|
||||||
measure: function(callback) {
|
measure: function(callback) {
|
||||||
|
|
|
@ -169,20 +169,12 @@ var ReactIOSEventEmitter = merge(ReactEventEmitterMixin, {
|
||||||
var target = nativeEvent.target;
|
var target = nativeEvent.target;
|
||||||
if (target !== null && target !== undefined) {
|
if (target !== null && target !== undefined) {
|
||||||
if (target < ReactIOSTagHandles.tagsStartAt) {
|
if (target < ReactIOSTagHandles.tagsStartAt) {
|
||||||
// When we get multiple touches at the same time, only the first touch
|
|
||||||
// actually has a view attached to it. The rest of the touches do not.
|
|
||||||
// This is presumably because iOS doesn't want to send touch events to
|
|
||||||
// two views for a single multi touch. Therefore this warning is only
|
|
||||||
// appropriate when it happens to the first touch. (hence jj === 0)
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
if (jj === 0) {
|
|
||||||
warning(
|
warning(
|
||||||
false,
|
false,
|
||||||
'A view is reporting that a touch occured on tag zero.'
|
'A view is reporting that a touch occured on tag zero.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else {
|
} else {
|
||||||
rootNodeID = NodeHandle.getRootNodeID(target);
|
rootNodeID = NodeHandle.getRootNodeID(target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,20 +9,35 @@ var ReactDefaultPerf = require('ReactDefaultPerf');
|
||||||
var ReactPerf = require('ReactPerf');
|
var ReactPerf = require('ReactPerf');
|
||||||
|
|
||||||
var invariant = require('invariant');
|
var invariant = require('invariant');
|
||||||
|
|
||||||
var perfModules = [];
|
var perfModules = [];
|
||||||
|
var enabled = false;
|
||||||
|
|
||||||
var RCTRenderingPerf = {
|
var RCTRenderingPerf = {
|
||||||
|
// Once perf is enabled, it stays enabled
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
if (ReactPerf.enableMeasure) {
|
console.log('Render perfomance measurements enabled');
|
||||||
|
enabled = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
start: function() {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDefaultPerf.start();
|
||||||
|
perfModules.forEach((module) => module.start());
|
||||||
|
},
|
||||||
|
|
||||||
|
stop: function() {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ReactDefaultPerf.stop();
|
ReactDefaultPerf.stop();
|
||||||
ReactDefaultPerf.printInclusive();
|
ReactDefaultPerf.printInclusive();
|
||||||
ReactDefaultPerf.printWasted();
|
ReactDefaultPerf.printWasted();
|
||||||
perfModules.forEach((module) => module.stop());
|
perfModules.forEach((module) => module.stop());
|
||||||
} else {
|
|
||||||
ReactDefaultPerf.start();
|
|
||||||
console.log('Render perfomance measurements started');
|
|
||||||
perfModules.forEach((module) => module.start());
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
register: function(module) {
|
register: function(module) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ var ReactNative = {
|
||||||
Bundler: require('Bundler'),
|
Bundler: require('Bundler'),
|
||||||
ExpandingText: require('ExpandingText'),
|
ExpandingText: require('ExpandingText'),
|
||||||
Image: require('Image'),
|
Image: require('Image'),
|
||||||
|
LayoutAnimation: require('LayoutAnimation'),
|
||||||
ListView: require('ListView'),
|
ListView: require('ListView'),
|
||||||
ListViewDataSource: require('ListViewDataSource'),
|
ListViewDataSource: require('ListViewDataSource'),
|
||||||
NavigatorIOS: require('NavigatorIOS'),
|
NavigatorIOS: require('NavigatorIOS'),
|
||||||
|
@ -22,6 +23,7 @@ var ReactNative = {
|
||||||
TextInput: require('TextInput'),
|
TextInput: require('TextInput'),
|
||||||
TimerMixin: require('TimerMixin'),
|
TimerMixin: require('TimerMixin'),
|
||||||
TouchableHighlight: require('TouchableHighlight'),
|
TouchableHighlight: require('TouchableHighlight'),
|
||||||
|
TouchableOpacity: require('TouchableOpacity'),
|
||||||
TouchableWithoutFeedback: require('TouchableWithoutFeedback'),
|
TouchableWithoutFeedback: require('TouchableWithoutFeedback'),
|
||||||
View: require('View'),
|
View: require('View'),
|
||||||
invariant: require('invariant'),
|
invariant: require('invariant'),
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, RCTAnimationType) {
|
||||||
|
RCTAnimationTypeSpring = 0,
|
||||||
|
RCTAnimationTypeLinear,
|
||||||
|
RCTAnimationTypeEaseIn,
|
||||||
|
RCTAnimationTypeEaseOut,
|
||||||
|
RCTAnimationTypeEaseInEaseOut,
|
||||||
|
};
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#import "Layout.h"
|
#import "Layout.h"
|
||||||
#import "RCTPointerEvents.h"
|
#import "RCTPointerEvents.h"
|
||||||
|
#import "RCTAnimationType.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides a collection of conversion functions for mapping
|
* This class provides a collection of conversion functions for mapping
|
||||||
|
@ -64,6 +65,7 @@
|
||||||
+ (css_wrap_type_t)css_wrap_type_t:(id)json;
|
+ (css_wrap_type_t)css_wrap_type_t:(id)json;
|
||||||
|
|
||||||
+ (RCTPointerEvents)RCTPointerEvents:(id)json;
|
+ (RCTPointerEvents)RCTPointerEvents:(id)json;
|
||||||
|
+ (RCTAnimationType)RCTAnimationType:(id)json;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ NSString *const RCTBoldFontWeight = @"bold";
|
||||||
return code; \
|
return code; \
|
||||||
} \
|
} \
|
||||||
@catch (__unused NSException *e) { \
|
@catch (__unused NSException *e) { \
|
||||||
RCTLogMustFix(@"JSON value '%@' of type '%@' cannot be converted to '%s'", \
|
RCTLogError(@"JSON value '%@' of type '%@' cannot be converted to '%s'", \
|
||||||
json, [json class], #type); \
|
json, [json class], #type); \
|
||||||
json = nil; \
|
json = nil; \
|
||||||
return code; \
|
return code; \
|
||||||
|
@ -45,15 +45,15 @@ RCT_CONVERTER_CUSTOM(type, name, [json getter])
|
||||||
if ([[mapping allValues] containsObject:json] || [json getter] == default) { \
|
if ([[mapping allValues] containsObject:json] || [json getter] == default) { \
|
||||||
return [json getter]; \
|
return [json getter]; \
|
||||||
} \
|
} \
|
||||||
RCTLogMustFix(@"Invalid %s '%@'. should be one of: %@", #type, json, [mapping allValues]); \
|
RCTLogError(@"Invalid %s '%@'. should be one of: %@", #type, json, [mapping allValues]); \
|
||||||
return default; \
|
return default; \
|
||||||
} \
|
} \
|
||||||
if (![json isKindOfClass:[NSString class]]) { \
|
if (![json isKindOfClass:[NSString class]]) { \
|
||||||
RCTLogMustFix(@"Expected NSNumber or NSString for %s, received %@: %@", #type, [json class], json); \
|
RCTLogError(@"Expected NSNumber or NSString for %s, received %@: %@", #type, [json class], json); \
|
||||||
} \
|
} \
|
||||||
id value = mapping[json]; \
|
id value = mapping[json]; \
|
||||||
if(!value && [json description].length > 0) { \
|
if(!value && [json description].length > 0) { \
|
||||||
RCTLogMustFix(@"Invalid %s '%@'. should be one of: %@", #type, json, [mapping allKeys]); \
|
RCTLogError(@"Invalid %s '%@'. should be one of: %@", #type, json, [mapping allKeys]); \
|
||||||
} \
|
} \
|
||||||
return value ? [value getter] : default; \
|
return value ? [value getter] : default; \
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ RCT_CONVERTER_CUSTOM(type, name, [json getter])
|
||||||
type result; \
|
type result; \
|
||||||
if ([json isKindOfClass:[NSArray class]]) { \
|
if ([json isKindOfClass:[NSArray class]]) { \
|
||||||
if ([json count] != count) { \
|
if ([json count] != count) { \
|
||||||
RCTLogMustFix(@"Expected array with count %zd, but count is %zd: %@", count, [json count], json); \
|
RCTLogError(@"Expected array with count %zd, but count is %zd: %@", count, [json count], json); \
|
||||||
} else { \
|
} else { \
|
||||||
for (NSUInteger i = 0; i < count; i++) { \
|
for (NSUInteger i = 0; i < count; i++) { \
|
||||||
((CGFloat *)&result)[i] = [json[i] doubleValue]; \
|
((CGFloat *)&result)[i] = [json[i] doubleValue]; \
|
||||||
|
@ -80,7 +80,7 @@ RCT_CONVERTER_CUSTOM(type, name, [json getter])
|
||||||
} \
|
} \
|
||||||
} else { \
|
} else { \
|
||||||
if (![json isKindOfClass:[NSDictionary class]]) { \
|
if (![json isKindOfClass:[NSDictionary class]]) { \
|
||||||
RCTLogMustFix(@"Expected NSArray or NSDictionary for %s, received %@: %@", #type, [json class], json); \
|
RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", #type, [json class], json); \
|
||||||
} else { \
|
} else { \
|
||||||
for (NSUInteger i = 0; i < count; i++) { \
|
for (NSUInteger i = 0; i < count; i++) { \
|
||||||
((CGFloat *)&result)[i] = [json[fields[i]] doubleValue]; \
|
((CGFloat *)&result)[i] = [json[fields[i]] doubleValue]; \
|
||||||
|
@ -90,7 +90,7 @@ RCT_CONVERTER_CUSTOM(type, name, [json getter])
|
||||||
return result; \
|
return result; \
|
||||||
} \
|
} \
|
||||||
@catch (__unused NSException *e) { \
|
@catch (__unused NSException *e) { \
|
||||||
RCTLogMustFix(@"JSON value '%@' cannot be converted to '%s'", json, #type); \
|
RCTLogError(@"JSON value '%@' cannot be converted to '%s'", json, #type); \
|
||||||
type result; \
|
type result; \
|
||||||
return result; \
|
return result; \
|
||||||
} \
|
} \
|
||||||
|
@ -111,7 +111,7 @@ RCT_CONVERTER_CUSTOM(NSUInteger, NSUInteger, [json unsignedIntegerValue])
|
||||||
+ (NSURL *)NSURL:(id)json
|
+ (NSURL *)NSURL:(id)json
|
||||||
{
|
{
|
||||||
if (![json isKindOfClass:[NSString class]]) {
|
if (![json isKindOfClass:[NSString class]]) {
|
||||||
RCTLogMustFix(@"Expected NSString for NSURL, received %@: %@", [json class], json);
|
RCTLogError(@"Expected NSString for NSURL, received %@: %@", [json class], json);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,10 +376,10 @@ RCT_STRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"]
|
||||||
} else if ([colorString hasPrefix:@"rgb("]) {
|
} else if ([colorString hasPrefix:@"rgb("]) {
|
||||||
sscanf([colorString UTF8String], "rgb(%zd,%zd,%zd)", &red, &green, &blue);
|
sscanf([colorString UTF8String], "rgb(%zd,%zd,%zd)", &red, &green, &blue);
|
||||||
} else {
|
} else {
|
||||||
RCTLogMustFix(@"Unrecognized color format '%@', must be one of #hex|rgba|rgb", colorString);
|
RCTLogError(@"Unrecognized color format '%@', must be one of #hex|rgba|rgb", colorString);
|
||||||
}
|
}
|
||||||
if (red == -1 || green == -1 || blue == -1 || alpha > 1.0 || alpha < 0.0) {
|
if (red == -1 || green == -1 || blue == -1 || alpha > 1.0 || alpha < 0.0) {
|
||||||
RCTLogMustFix(@"Invalid color string '%@'", colorString);
|
RCTLogError(@"Invalid color string '%@'", colorString);
|
||||||
} else {
|
} else {
|
||||||
color = [UIColor colorWithRed:red / 255.0 green:green / 255.0 blue:blue / 255.0 alpha:alpha];
|
color = [UIColor colorWithRed:red / 255.0 green:green / 255.0 blue:blue / 255.0 alpha:alpha];
|
||||||
}
|
}
|
||||||
|
@ -388,7 +388,7 @@ RCT_STRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"]
|
||||||
|
|
||||||
if ([json count] < 3 || [json count] > 4) {
|
if ([json count] < 3 || [json count] > 4) {
|
||||||
|
|
||||||
RCTLogMustFix(@"Expected array with count 3 or 4, but count is %zd: %@", [json count], json);
|
RCTLogError(@"Expected array with count 3 or 4, but count is %zd: %@", [json count], json);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -409,7 +409,7 @@ RCT_STRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"]
|
||||||
|
|
||||||
} else if (json && ![json isKindOfClass:[NSNull class]]) {
|
} else if (json && ![json isKindOfClass:[NSNull class]]) {
|
||||||
|
|
||||||
RCTLogMustFix(@"Expected NSArray, NSDictionary or NSString for UIColor, received %@: %@", [json class], json);
|
RCTLogError(@"Expected NSArray, NSDictionary or NSString for UIColor, received %@: %@", [json class], json);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default color
|
// Default color
|
||||||
|
@ -509,7 +509,7 @@ RCT_STRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"]
|
||||||
+ (UIImage *)UIImage:(id)json
|
+ (UIImage *)UIImage:(id)json
|
||||||
{
|
{
|
||||||
if (![json isKindOfClass:[NSString class]]) {
|
if (![json isKindOfClass:[NSString class]]) {
|
||||||
RCTLogMustFix(@"Expected NSString for UIImage, received %@: %@", [json class], json);
|
RCTLogError(@"Expected NSString for UIImage, received %@: %@", [json class], json);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,6 +657,14 @@ RCT_ENUM_CONVERTER(RCTPointerEvents, (@{
|
||||||
@"boxnone": @(RCTPointerEventsBoxNone)
|
@"boxnone": @(RCTPointerEventsBoxNone)
|
||||||
}), RCTPointerEventsUnspecified, integerValue)
|
}), RCTPointerEventsUnspecified, integerValue)
|
||||||
|
|
||||||
|
RCT_ENUM_CONVERTER(RCTAnimationType, (@{
|
||||||
|
@"spring": @(RCTAnimationTypeSpring),
|
||||||
|
@"linear": @(RCTAnimationTypeLinear),
|
||||||
|
@"easeIn": @(RCTAnimationTypeEaseIn),
|
||||||
|
@"easeOut": @(RCTAnimationTypeEaseOut),
|
||||||
|
@"easeInEaseOut": @(RCTAnimationTypeEaseInEaseOut),
|
||||||
|
}), RCTAnimationTypeEaseInEaseOut, integerValue)
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static NSString *RCTGuessTypeEncoding(id target, NSString *key, id value, NSString *encoding)
|
static NSString *RCTGuessTypeEncoding(id target, NSString *key, id value, NSString *encoding)
|
||||||
|
@ -832,6 +840,9 @@ BOOL RCTSetProperty(id target, NSString *keypath, id value)
|
||||||
@"extAlignment": ^(id val) {
|
@"extAlignment": ^(id val) {
|
||||||
return [RCTConvert NSTextAlignment:val];
|
return [RCTConvert NSTextAlignment:val];
|
||||||
},
|
},
|
||||||
|
@"ointerEvents": ^(id val) {
|
||||||
|
return [RCTConvert RCTPointerEvents:val];
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
for (NSString *subkey in converters) {
|
for (NSString *subkey in converters) {
|
||||||
|
|
|
@ -6,15 +6,11 @@
|
||||||
#import "RCTInvalidating.h"
|
#import "RCTInvalidating.h"
|
||||||
|
|
||||||
@class RCTRootView;
|
@class RCTRootView;
|
||||||
@class RCTShadowView;
|
|
||||||
@class RCTSparseArray;
|
|
||||||
|
|
||||||
@protocol RCTScrollableProtocol;
|
@protocol RCTScrollableProtocol;
|
||||||
|
|
||||||
@interface RCTUIManager : NSObject <RCTBridgeModule, RCTInvalidating>
|
@interface RCTUIManager : NSObject <RCTBridgeModule, RCTInvalidating>
|
||||||
|
|
||||||
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry;
|
|
||||||
@property (nonatomic, strong) RCTSparseArray *viewRegistry;
|
|
||||||
@property (nonatomic, weak) id<RCTScrollableProtocol> mainScrollView;
|
@property (nonatomic, weak) id<RCTScrollableProtocol> mainScrollView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
#import <AVFoundation/AVFoundation.h>
|
#import <AVFoundation/AVFoundation.h>
|
||||||
#import <objc/message.h>
|
#import <objc/message.h>
|
||||||
#import <pthread.h>
|
|
||||||
|
|
||||||
#import "Layout.h"
|
#import "Layout.h"
|
||||||
|
#import "RCTAnimationType.h"
|
||||||
#import "RCTAssert.h"
|
#import "RCTAssert.h"
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTConvert.h"
|
#import "RCTConvert.h"
|
||||||
|
@ -22,8 +22,6 @@
|
||||||
#import "RCTViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
#import "UIView+ReactKit.h"
|
#import "UIView+ReactKit.h"
|
||||||
|
|
||||||
@class RCTAnimationConfig;
|
|
||||||
|
|
||||||
typedef void (^react_view_node_block_t)(id<RCTViewNodeProtocol>);
|
typedef void (^react_view_node_block_t)(id<RCTViewNodeProtocol>);
|
||||||
|
|
||||||
static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_block_t block)
|
static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_block_t block)
|
||||||
|
@ -75,40 +73,151 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
return modules;
|
return modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@interface RCTAnimation : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, readonly) NSTimeInterval duration;
|
||||||
|
@property (nonatomic, readonly) NSTimeInterval delay;
|
||||||
|
@property (nonatomic, readonly, copy) NSString *property;
|
||||||
|
@property (nonatomic, readonly) id fromValue;
|
||||||
|
@property (nonatomic, readonly) id toValue;
|
||||||
|
@property (nonatomic, readonly) CGFloat springDamping;
|
||||||
|
@property (nonatomic, readonly) CGFloat initialVelocity;
|
||||||
|
@property (nonatomic, readonly) RCTAnimationType animationType;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTAnimation
|
||||||
|
|
||||||
|
UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case RCTAnimationTypeLinear:
|
||||||
|
return UIViewAnimationCurveLinear;
|
||||||
|
case RCTAnimationTypeEaseIn:
|
||||||
|
return UIViewAnimationCurveEaseIn;
|
||||||
|
case RCTAnimationTypeEaseOut:
|
||||||
|
return UIViewAnimationCurveEaseOut;
|
||||||
|
case RCTAnimationTypeEaseInEaseOut:
|
||||||
|
return UIViewAnimationCurveEaseInOut;
|
||||||
|
default:
|
||||||
|
RCTCAssert(NO, @"Unsupported animation type %zd", type);
|
||||||
|
return UIViewAnimationCurveEaseInOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithDuration:(NSTimeInterval)duration dictionary:(NSDictionary *)config
|
||||||
|
{
|
||||||
|
if (!config) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_property = [RCTConvert NSString:config[@"property"]];
|
||||||
|
|
||||||
|
// TODO: this should be provided in ms, not seconds
|
||||||
|
_duration = [RCTConvert NSTimeInterval:config[@"duration"]] ?: duration;
|
||||||
|
_delay = [RCTConvert NSTimeInterval:config[@"delay"]];
|
||||||
|
_animationType = [RCTConvert RCTAnimationType:config[@"type"]];
|
||||||
|
if (_animationType == RCTAnimationTypeSpring) {
|
||||||
|
_springDamping = [RCTConvert CGFloat:config[@"springDamping"]];
|
||||||
|
_initialVelocity = [RCTConvert CGFloat:config[@"initialVelocity"]];
|
||||||
|
}
|
||||||
|
_fromValue = config[@"fromValue"];
|
||||||
|
_toValue = config[@"toValue"];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)performAnimations:(void (^)(void))animations
|
||||||
|
withCompletionBlock:(void (^)(BOOL completed))completionBlock
|
||||||
|
{
|
||||||
|
if (_animationType == RCTAnimationTypeSpring) {
|
||||||
|
|
||||||
|
[UIView animateWithDuration:_duration
|
||||||
|
delay:_delay
|
||||||
|
usingSpringWithDamping:_springDamping
|
||||||
|
initialSpringVelocity:_initialVelocity
|
||||||
|
options:UIViewAnimationOptionBeginFromCurrentState
|
||||||
|
animations:animations
|
||||||
|
completion:completionBlock];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState |
|
||||||
|
UIViewAnimationCurveFromRCTAnimationType(_animationType);
|
||||||
|
|
||||||
|
[UIView animateWithDuration:_duration
|
||||||
|
delay:_delay
|
||||||
|
options:options
|
||||||
|
animations:animations
|
||||||
|
completion:completionBlock];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface RCTLayoutAnimation : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, strong) RCTAnimation *createAnimation;
|
||||||
|
@property (nonatomic, strong) RCTAnimation *updateAnimation;
|
||||||
|
@property (nonatomic, strong) RCTAnimation *deleteAnimation;
|
||||||
|
@property (nonatomic, strong) RCTResponseSenderBlock callback;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTLayoutAnimation
|
||||||
|
|
||||||
|
- (instancetype)initWithDictionary:(NSDictionary *)config callback:(RCTResponseSenderBlock)callback
|
||||||
|
{
|
||||||
|
if (!config) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((self = [super init])) {
|
||||||
|
|
||||||
|
// TODO: this should be provided in ms, not seconds
|
||||||
|
NSTimeInterval duration = [RCTConvert NSTimeInterval:config[@"duration"]];
|
||||||
|
|
||||||
|
_createAnimation = [[RCTAnimation alloc] initWithDuration:duration dictionary:config[@"create"]];
|
||||||
|
_updateAnimation = [[RCTAnimation alloc] initWithDuration:duration dictionary:config[@"update"]];
|
||||||
|
_deleteAnimation = [[RCTAnimation alloc] initWithDuration:duration dictionary:config[@"delete"]];
|
||||||
|
_callback = callback;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation RCTUIManager
|
@implementation RCTUIManager
|
||||||
{
|
{
|
||||||
// Root views are only mutated on the shadow queue
|
// Root views are only mutated on the shadow queue
|
||||||
NSDictionary *_viewManagers;
|
|
||||||
NSMutableSet *_rootViewTags;
|
NSMutableSet *_rootViewTags;
|
||||||
NSMutableArray *_pendingUIBlocks;
|
NSMutableArray *_pendingUIBlocks;
|
||||||
|
NSLock *_pendingUIBlocksLock;
|
||||||
|
|
||||||
pthread_mutex_t _pendingUIBlocksMutex;
|
// Animation
|
||||||
NSDictionary *_nextLayoutAnimationConfig; // RCT thread only
|
RCTLayoutAnimation *_nextLayoutAnimation; // RCT thread only
|
||||||
RCTResponseSenderBlock _nextLayoutAnimationCallback; // RCT thread only
|
RCTLayoutAnimation *_layoutAnimation; // Main thread only
|
||||||
RCTResponseSenderBlock _layoutAnimationCallbackMT; // Main thread only
|
|
||||||
|
|
||||||
NSMutableDictionary *_defaultShadowViews;
|
// Keyed by moduleName
|
||||||
NSMutableDictionary *_defaultViews;
|
NSMutableDictionary *_defaultShadowViews; // RCT thread only
|
||||||
|
NSMutableDictionary *_defaultViews; // Main thread only
|
||||||
|
NSDictionary *_viewManagers;
|
||||||
|
|
||||||
|
// Keyed by React tag
|
||||||
|
RCTSparseArray *_viewManagerRegistry; // RCT thread only
|
||||||
|
RCTSparseArray *_shadowViewRegistry; // RCT thread only
|
||||||
|
RCTSparseArray *_viewRegistry; // Main thread only
|
||||||
|
|
||||||
__weak RCTBridge *_bridge;
|
__weak RCTBridge *_bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (RCTViewManager *)_managerInstanceForViewWithModuleName:(NSString *)moduleName
|
|
||||||
{
|
|
||||||
RCTViewManager *managerInstance = _viewManagers[moduleName];
|
|
||||||
if (managerInstance == nil) {
|
|
||||||
RCTLogWarn(@"No manager class found for view with module name \"%@\"", moduleName);
|
|
||||||
managerInstance = [[RCTViewManager alloc] init];
|
|
||||||
}
|
|
||||||
return managerInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
|
|
||||||
_bridge = bridge;
|
_bridge = bridge;
|
||||||
pthread_mutex_init(&_pendingUIBlocksMutex, NULL);
|
_pendingUIBlocksLock = [[NSLock alloc] init];
|
||||||
|
|
||||||
// Instantiate view managers
|
// Instantiate view managers
|
||||||
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
|
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
|
||||||
|
@ -116,16 +225,16 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
viewManagers[moduleName] = [[moduleClass alloc] initWithEventDispatcher:_bridge.eventDispatcher];
|
viewManagers[moduleName] = [[moduleClass alloc] initWithEventDispatcher:_bridge.eventDispatcher];
|
||||||
}];
|
}];
|
||||||
_viewManagers = viewManagers;
|
_viewManagers = viewManagers;
|
||||||
|
_defaultShadowViews = [[NSMutableDictionary alloc] init];
|
||||||
|
_defaultViews = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
_viewRegistry = [[RCTSparseArray alloc] init];
|
_viewManagerRegistry = [[RCTSparseArray alloc] init];
|
||||||
_shadowViewRegistry = [[RCTSparseArray alloc] init];
|
_shadowViewRegistry = [[RCTSparseArray alloc] init];
|
||||||
|
_viewRegistry = [[RCTSparseArray alloc] init];
|
||||||
|
|
||||||
// Internal resources
|
// Internal resources
|
||||||
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
||||||
_rootViewTags = [[NSMutableSet alloc] init];
|
_rootViewTags = [[NSMutableSet alloc] init];
|
||||||
|
|
||||||
_defaultShadowViews = [[NSMutableDictionary alloc] init];
|
|
||||||
_defaultViews = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -138,7 +247,6 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
RCTAssert(!self.valid, @"must call -invalidate before -dealloc");
|
RCTAssert(!self.valid, @"must call -invalidate before -dealloc");
|
||||||
pthread_mutex_destroy(&_pendingUIBlocksMutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isValid
|
- (BOOL)isValid
|
||||||
|
@ -153,9 +261,9 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
_viewRegistry = nil;
|
_viewRegistry = nil;
|
||||||
_shadowViewRegistry = nil;
|
_shadowViewRegistry = nil;
|
||||||
|
|
||||||
pthread_mutex_lock(&_pendingUIBlocksMutex);
|
[_pendingUIBlocksLock lock];
|
||||||
_pendingUIBlocks = nil;
|
_pendingUIBlocks = nil;
|
||||||
pthread_mutex_unlock(&_pendingUIBlocksMutex);
|
[_pendingUIBlocksLock unlock];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)registerRootView:(RCTRootView *)rootView;
|
- (void)registerRootView:(RCTRootView *)rootView;
|
||||||
|
@ -171,6 +279,9 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
_viewRegistry[reactTag] = rootView;
|
_viewRegistry[reactTag] = rootView;
|
||||||
CGRect frame = rootView.frame;
|
CGRect frame = rootView.frame;
|
||||||
|
|
||||||
|
// Register manager (TODO: should we do this, or leave it nil?)
|
||||||
|
_viewManagerRegistry[reactTag] = _viewManagers[[RCTViewManager moduleName]];
|
||||||
|
|
||||||
// Register shadow view
|
// Register shadow view
|
||||||
dispatch_async(_bridge.shadowQueue, ^{
|
dispatch_async(_bridge.shadowQueue, ^{
|
||||||
|
|
||||||
|
@ -192,7 +303,7 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
{
|
{
|
||||||
for (id<RCTViewNodeProtocol> child in children) {
|
for (id<RCTViewNodeProtocol> child in children) {
|
||||||
RCTTraverseViewNodes(registry[child.reactTag], ^(id<RCTViewNodeProtocol> subview) {
|
RCTTraverseViewNodes(registry[child.reactTag], ^(id<RCTViewNodeProtocol> subview) {
|
||||||
RCTAssert(![subview isReactRootView], @"Host views should not be unregistered");
|
RCTAssert(![subview isReactRootView], @"Root views should not be unregistered");
|
||||||
if ([subview conformsToProtocol:@protocol(RCTInvalidating)]) {
|
if ([subview conformsToProtocol:@protocol(RCTInvalidating)]) {
|
||||||
[(id<RCTInvalidating>)subview invalidate];
|
[(id<RCTInvalidating>)subview invalidate];
|
||||||
}
|
}
|
||||||
|
@ -203,8 +314,8 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
|
|
||||||
- (void)addUIBlock:(RCTViewManagerUIBlock)block
|
- (void)addUIBlock:(RCTViewManagerUIBlock)block
|
||||||
{
|
{
|
||||||
// This assert is fragile. This is temporary pending t4698600
|
|
||||||
RCTAssert(![NSThread isMainThread], @"This method should only be called on the shadow thread");
|
RCTAssert(![NSThread isMainThread], @"This method should only be called on the shadow thread");
|
||||||
|
|
||||||
__weak RCTUIManager *weakViewManager = self;
|
__weak RCTUIManager *weakViewManager = self;
|
||||||
__weak RCTSparseArray *weakViewRegistry = _viewRegistry;
|
__weak RCTSparseArray *weakViewRegistry = _viewRegistry;
|
||||||
dispatch_block_t outerBlock = ^{
|
dispatch_block_t outerBlock = ^{
|
||||||
|
@ -215,34 +326,12 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_mutex_lock(&_pendingUIBlocksMutex);
|
[_pendingUIBlocksLock lock];
|
||||||
[_pendingUIBlocks addObject:[outerBlock copy]];
|
[_pendingUIBlocks addObject:outerBlock];
|
||||||
pthread_mutex_unlock(&_pendingUIBlocksMutex);
|
[_pendingUIBlocksLock unlock];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setViewLayout:(UIView *)view withAnchorPoint:(CGPoint)anchorPoint position:(CGPoint)position bounds:(CGRect)bounds config:(RCTAnimationConfig *)config completion:(void (^)(BOOL finished))completion
|
- (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)rootShadowView
|
||||||
{
|
|
||||||
if (isnan(position.x) || isnan(position.y) ||
|
|
||||||
isnan(bounds.origin.x) || isnan(bounds.origin.y) ||
|
|
||||||
isnan(bounds.size.width) || isnan(bounds.size.height)) {
|
|
||||||
RCTLogError(@"Invalid layout for (%zd)%@. position: %@. bounds: %@", [view reactTag], self, NSStringFromCGPoint(position), NSStringFromCGRect(bounds));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
view.layer.anchorPoint = anchorPoint;
|
|
||||||
view.layer.position = position;
|
|
||||||
view.layer.bounds = bounds;
|
|
||||||
completion(YES);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: `RCTBridge` has first class knowledge of this method. We should either:
|
|
||||||
* 1. Require that the JS trigger this after a batch - almost like a flush.
|
|
||||||
* 2. Build in support to the `<BatchedExports>` protocol so that each module
|
|
||||||
* may return values to JS via a third callback function passed in, but can
|
|
||||||
* return a tuple that is `(UIThreadBlocks, JSThreadBlocks)`.
|
|
||||||
*/
|
|
||||||
- (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)hostShadowView
|
|
||||||
{
|
{
|
||||||
NSMutableSet *viewsWithNewFrames = [NSMutableSet setWithCapacity:1];
|
NSMutableSet *viewsWithNewFrames = [NSMutableSet setWithCapacity:1];
|
||||||
|
|
||||||
|
@ -250,12 +339,8 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
// `frameTags`/`frames` that is created/mutated in the JS thread. We access
|
// `frameTags`/`frames` that is created/mutated in the JS thread. We access
|
||||||
// these structures in the UI-thread block. `NSMutableArray` is not thread
|
// these structures in the UI-thread block. `NSMutableArray` is not thread
|
||||||
// safe so we rely on the fact that we never mutate it after it's passed to
|
// safe so we rely on the fact that we never mutate it after it's passed to
|
||||||
// the main thread. To help protect against mutation, we alias the variable to
|
// the main thread.
|
||||||
// a threadsafe `NSArray`, however the `NSArray` doesn't guarantee deep
|
[rootShadowView collectRootUpdatedFrames:viewsWithNewFrames parentConstraint:(CGSize){CSS_UNDEFINED, CSS_UNDEFINED}];
|
||||||
// immutability so we must be very careful.
|
|
||||||
// https://developer.apple.com/library/mac/documentation/Cocoa/
|
|
||||||
// Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html
|
|
||||||
[hostShadowView collectRootUpdatedFrames:viewsWithNewFrames parentConstraint:CGSizeMake(CSS_UNDEFINED, CSS_UNDEFINED)];
|
|
||||||
|
|
||||||
// Parallel arrays
|
// Parallel arrays
|
||||||
NSMutableArray *frameReactTags = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
NSMutableArray *frameReactTags = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||||
|
@ -269,37 +354,75 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
[areNew addObject:@(shadowView.isNewView)];
|
[areNew addObject:@(shadowView.isNewView)];
|
||||||
[parentsAreNew addObject:@(shadowView.superview.isNewView)];
|
[parentsAreNew addObject:@(shadowView.superview.isNewView)];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||||
// We have to do this after we build the parentsAreNew array.
|
// We have to do this after we build the parentsAreNew array.
|
||||||
shadowView.newView = NO;
|
shadowView.newView = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSArray *immutableFrameReactTags = frameReactTags;
|
// Perform layout (possibly animated)
|
||||||
NSArray *immutableFrames = frames;
|
NSNumber *rootViewTag = rootShadowView.reactTag;
|
||||||
|
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
NSNumber *rootViewTag = hostShadowView.reactTag;
|
for (NSUInteger ii = 0; ii < frames.count; ii++) {
|
||||||
return ^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
NSNumber *reactTag = frameReactTags[ii];
|
||||||
for (NSUInteger ii = 0; ii < immutableFrames.count; ii++) {
|
|
||||||
NSNumber *reactTag = immutableFrameReactTags[ii];
|
|
||||||
UIView *view = viewRegistry[reactTag];
|
UIView *view = viewRegistry[reactTag];
|
||||||
CGRect frame = [immutableFrames[ii] CGRectValue];
|
CGRect frame = [frames[ii] CGRectValue];
|
||||||
|
|
||||||
// These frames are in terms of anchorPoint = topLeft, but internally the
|
// These frames are in terms of anchorPoint = topLeft, but internally the
|
||||||
// views are anchorPoint = center for easier scale and rotation animations.
|
// views are anchorPoint = center for easier scale and rotation animations.
|
||||||
// Convert the frame so it works with anchorPoint = center.
|
// Convert the frame so it works with anchorPoint = center.
|
||||||
__weak RCTUIManager *weakSelf = self;
|
CGPoint position = {CGRectGetMidX(frame), CGRectGetMidY(frame)};
|
||||||
[self setViewLayout:view
|
CGRect bounds = {0, 0, frame.size};
|
||||||
withAnchorPoint:CGPointMake(0.5, 0.5)
|
|
||||||
position:CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame))
|
// Avoid crashes due to nan coords
|
||||||
bounds:CGRectMake(0, 0, frame.size.width, frame.size.height)
|
if (isnan(position.x) || isnan(position.y) ||
|
||||||
config:/*!isNew ? _layoutAnimationConfigMT.updateConfig : */nil // TODO: !!!
|
isnan(bounds.origin.x) || isnan(bounds.origin.y) ||
|
||||||
completion:^(BOOL finished) {
|
isnan(bounds.size.width) || isnan(bounds.size.height)) {
|
||||||
__strong RCTUIManager *strongSelf = weakSelf;
|
RCTLogError(@"Invalid layout for (%zd)%@. position: %@. bounds: %@", [view reactTag], self, NSStringFromCGPoint(position), NSStringFromCGRect(bounds));
|
||||||
if (strongSelf->_layoutAnimationCallbackMT) {
|
continue;
|
||||||
strongSelf->_layoutAnimationCallbackMT(@[@(finished)]);
|
|
||||||
}
|
}
|
||||||
}];
|
|
||||||
|
void (^completion)(BOOL finished) = ^(BOOL finished) {
|
||||||
|
if (self->_layoutAnimation.callback) {
|
||||||
|
self->_layoutAnimation.callback(@[@(finished)]);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Animate view update
|
||||||
|
BOOL isNew = [areNew[ii] boolValue];
|
||||||
|
RCTAnimation *updateAnimation = isNew ? nil: _layoutAnimation.updateAnimation;
|
||||||
|
if (updateAnimation) {
|
||||||
|
[updateAnimation performAnimations:^{
|
||||||
|
view.layer.position = position;
|
||||||
|
view.layer.bounds = bounds;
|
||||||
|
} withCompletionBlock:completion];
|
||||||
|
} else {
|
||||||
|
view.layer.position = position;
|
||||||
|
view.layer.bounds = bounds;
|
||||||
|
completion(YES);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animate view creations
|
||||||
|
BOOL shouldAnimateCreation = isNew && ![parentsAreNew[ii] boolValue];
|
||||||
|
RCTAnimation *createAnimation = _layoutAnimation.createAnimation;
|
||||||
|
if (shouldAnimateCreation && createAnimation) {
|
||||||
|
if ([createAnimation.property isEqualToString:@"scaleXY"]) {
|
||||||
|
view.layer.transform = CATransform3DMakeScale(0, 0, 0);
|
||||||
|
} else if ([createAnimation.property isEqualToString:@"opacity"]) {
|
||||||
|
view.layer.opacity = 0.0;
|
||||||
|
}
|
||||||
|
[createAnimation performAnimations:^{
|
||||||
|
if ([createAnimation.property isEqual:@"scaleXY"]) {
|
||||||
|
view.layer.transform = CATransform3DIdentity;
|
||||||
|
} else if ([createAnimation.property isEqual:@"opacity"]) {
|
||||||
|
view.layer.opacity = 1.0;
|
||||||
|
} else {
|
||||||
|
RCTLogError(@"Unsupported layout animation createConfig property %@", createAnimation.property);
|
||||||
|
}
|
||||||
|
} withCompletionBlock:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RCTRootView *rootView = _viewRegistry[rootViewTag];
|
RCTRootView *rootView = _viewRegistry[rootViewTag];
|
||||||
RCTTraverseViewNodes(rootView, ^(id<RCTViewNodeProtocol> view) {
|
RCTTraverseViewNodes(rootView, ^(id<RCTViewNodeProtocol> view) {
|
||||||
if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) {
|
if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) {
|
||||||
|
@ -314,7 +437,7 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
NSMutableSet *applierBlocks = [NSMutableSet setWithCapacity:1];
|
NSMutableSet *applierBlocks = [NSMutableSet setWithCapacity:1];
|
||||||
[topView collectUpdatedProperties:applierBlocks parentProperties:@{}];
|
[topView collectUpdatedProperties:applierBlocks parentProperties:@{}];
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
for (RCTApplierBlock block in applierBlocks) {
|
for (RCTApplierBlock block in applierBlocks) {
|
||||||
block(viewRegistry);
|
block(viewRegistry);
|
||||||
}
|
}
|
||||||
|
@ -391,10 +514,10 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
[self _purgeChildren:@[rootShadowView] fromRegistry:_shadowViewRegistry];
|
[self _purgeChildren:@[rootShadowView] fromRegistry:_shadowViewRegistry];
|
||||||
[_rootViewTags removeObject:rootReactTag];
|
[_rootViewTags removeObject:rootReactTag];
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry){
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||||
RCTCAssertMainThread();
|
RCTCAssertMainThread();
|
||||||
UIView *rootView = viewRegistry[rootReactTag];
|
UIView *rootView = viewRegistry[rootReactTag];
|
||||||
[viewManager _purgeChildren:@[rootView] fromRegistry:viewRegistry];
|
[uiManager _purgeChildren:@[rootView] fromRegistry:viewRegistry];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,9 +560,9 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
removeAtIndices:removeAtIndices
|
removeAtIndices:removeAtIndices
|
||||||
registry:_shadowViewRegistry];
|
registry:_shadowViewRegistry];
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry){
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||||
RCTCAssertMainThread();
|
|
||||||
[viewManager _manageChildren:containerReactTag
|
[uiManager _manageChildren:containerReactTag
|
||||||
moveFromIndices:moveFromIndices
|
moveFromIndices:moveFromIndices
|
||||||
moveToIndices:moveToIndices
|
moveToIndices:moveToIndices
|
||||||
addChildReactTags:addChildReactTags
|
addChildReactTags:addChildReactTags
|
||||||
|
@ -549,7 +672,11 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
{
|
{
|
||||||
RCT_EXPORT(createView);
|
RCT_EXPORT(createView);
|
||||||
|
|
||||||
RCTViewManager *manager = [self _managerInstanceForViewWithModuleName:moduleName];
|
RCTViewManager *manager = _viewManagers[moduleName];
|
||||||
|
if (manager == nil) {
|
||||||
|
RCTLogWarn(@"No manager class found for view with module name \"%@\"", moduleName);
|
||||||
|
manager = [[RCTViewManager alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
// Generate default view, used for resetting default props
|
// Generate default view, used for resetting default props
|
||||||
if (!_defaultShadowViews[moduleName]) {
|
if (!_defaultShadowViews[moduleName]) {
|
||||||
|
@ -565,6 +692,9 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||||
RCTCAssertMainThread();
|
RCTCAssertMainThread();
|
||||||
|
|
||||||
|
// Register manager (TODO: should we do this, or leave it nil?)
|
||||||
|
uiManager->_viewManagerRegistry[reactTag] = manager;
|
||||||
|
|
||||||
// Generate default view, used for resetting default props
|
// Generate default view, used for resetting default props
|
||||||
if (!uiManager->_defaultViews[moduleName]) {
|
if (!uiManager->_defaultViews[moduleName]) {
|
||||||
// Note the default is setup after the props are read for the first time ever
|
// Note the default is setup after the props are read for the first time ever
|
||||||
|
@ -575,6 +705,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
|
|
||||||
UIView *view = [manager view];
|
UIView *view = [manager view];
|
||||||
if (view) {
|
if (view) {
|
||||||
|
|
||||||
// Set required properties
|
// Set required properties
|
||||||
view.reactTag = reactTag;
|
view.reactTag = reactTag;
|
||||||
view.multipleTouchEnabled = YES;
|
view.multipleTouchEnabled = YES;
|
||||||
|
@ -588,18 +719,20 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateView:(NSNumber *)reactTag moduleName:(NSString *)moduleName props:(NSDictionary *)props
|
// TODO: remove moduleName param as it isn't needed
|
||||||
|
- (void)updateView:(NSNumber *)reactTag moduleName:(__unused NSString *)_ props:(NSDictionary *)props
|
||||||
{
|
{
|
||||||
RCT_EXPORT();
|
RCT_EXPORT();
|
||||||
|
|
||||||
|
RCTViewManager *viewManager = _viewManagerRegistry[reactTag];
|
||||||
|
NSString *moduleName = [[viewManager class] moduleName];
|
||||||
|
|
||||||
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
||||||
RCTViewManager *manager = [self _managerInstanceForViewWithModuleName:moduleName];
|
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[moduleName], viewManager);
|
||||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[moduleName], manager);
|
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
RCTCAssertMainThread();
|
UIView *view = uiManager->_viewRegistry[reactTag];
|
||||||
UIView *view = viewRegistry[reactTag];
|
RCTSetViewProps(props, view, uiManager->_defaultViews[moduleName], viewManager);
|
||||||
RCTSetViewProps(props, view, uiManager->_defaultViews[moduleName], manager);
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,7 +741,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
RCT_EXPORT(focus);
|
RCT_EXPORT(focus);
|
||||||
|
|
||||||
if (!reactTag) return;
|
if (!reactTag) return;
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
UIView *newResponder = viewRegistry[reactTag];
|
UIView *newResponder = viewRegistry[reactTag];
|
||||||
[newResponder becomeFirstResponder];
|
[newResponder becomeFirstResponder];
|
||||||
}];
|
}];
|
||||||
|
@ -619,7 +752,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
RCT_EXPORT(blur);
|
RCT_EXPORT(blur);
|
||||||
|
|
||||||
if (!reactTag) return;
|
if (!reactTag) return;
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry){
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||||
UIView *currentResponder = viewRegistry[reactTag];
|
UIView *currentResponder = viewRegistry[reactTag];
|
||||||
[currentResponder resignFirstResponder];
|
[currentResponder resignFirstResponder];
|
||||||
}];
|
}];
|
||||||
|
@ -630,24 +763,40 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
// First copy the previous blocks into a temporary variable, then reset the
|
// First copy the previous blocks into a temporary variable, then reset the
|
||||||
// pending blocks to a new array. This guards against mutation while
|
// pending blocks to a new array. This guards against mutation while
|
||||||
// processing the pending blocks in another thread.
|
// processing the pending blocks in another thread.
|
||||||
|
|
||||||
for (RCTViewManager *manager in _viewManagers.allValues) {
|
for (RCTViewManager *manager in _viewManagers.allValues) {
|
||||||
RCTViewManagerUIBlock uiBlock = [manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
|
RCTViewManagerUIBlock uiBlock = [manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
|
||||||
if (uiBlock != nil) {
|
if (uiBlock) {
|
||||||
[self addUIBlock:uiBlock];
|
[self addUIBlock:uiBlock];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up next layout animation
|
||||||
|
if (_nextLayoutAnimation) {
|
||||||
|
RCTLayoutAnimation *layoutAnimation = _nextLayoutAnimation;
|
||||||
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
|
uiManager->_layoutAnimation = layoutAnimation;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform layout
|
||||||
for (NSNumber *reactTag in _rootViewTags) {
|
for (NSNumber *reactTag in _rootViewTags) {
|
||||||
RCTShadowView *rootView = _shadowViewRegistry[reactTag];
|
RCTShadowView *rootView = _shadowViewRegistry[reactTag];
|
||||||
[self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
|
[self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
|
||||||
[self _amendPendingUIBlocksWithStylePropagationUpdateForRootView:rootView];
|
[self _amendPendingUIBlocksWithStylePropagationUpdateForRootView:rootView];
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&_pendingUIBlocksMutex);
|
// Clear layout animations
|
||||||
|
if (_nextLayoutAnimation) {
|
||||||
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
|
uiManager->_layoutAnimation = nil;
|
||||||
|
}];
|
||||||
|
_nextLayoutAnimation = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_pendingUIBlocksLock lock];
|
||||||
NSArray *previousPendingUIBlocks = _pendingUIBlocks;
|
NSArray *previousPendingUIBlocks = _pendingUIBlocks;
|
||||||
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
||||||
pthread_mutex_unlock(&_pendingUIBlocksMutex);
|
[_pendingUIBlocksLock unlock];
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
for (dispatch_block_t block in previousPendingUIBlocks) {
|
for (dispatch_block_t block in previousPendingUIBlocks) {
|
||||||
|
@ -656,16 +805,6 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layoutRootShadowView:(RCTShadowView *)rootShadowView
|
|
||||||
{
|
|
||||||
RCTViewManagerUIBlock uiBlock = [self uiBlockWithLayoutUpdateForRootView:rootShadowView];
|
|
||||||
__weak RCTUIManager *weakViewManager = self;
|
|
||||||
__weak RCTSparseArray *weakViewRegistry = _viewRegistry;
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
uiBlock(weakViewManager, weakViewRegistry);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)measure:(NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback
|
- (void)measure:(NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback
|
||||||
{
|
{
|
||||||
RCT_EXPORT();
|
RCT_EXPORT();
|
||||||
|
@ -675,7 +814,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
UIView *view = viewRegistry[reactTag];
|
UIView *view = viewRegistry[reactTag];
|
||||||
if (!view) {
|
if (!view) {
|
||||||
RCTLogError(@"measure cannot find view with tag %zd", reactTag);
|
RCTLogError(@"measure cannot find view with tag %zd", reactTag);
|
||||||
|
@ -705,7 +844,6 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)requestSchedulingJavaScriptNavigation:(NSNumber *)reactTag
|
- (void)requestSchedulingJavaScriptNavigation:(NSNumber *)reactTag
|
||||||
errorCallback:(RCTResponseSenderBlock)errorCallback
|
errorCallback:(RCTResponseSenderBlock)errorCallback
|
||||||
callback:(RCTResponseSenderBlock)callback
|
callback:(RCTResponseSenderBlock)callback
|
||||||
|
@ -716,7 +854,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
RCTLogError(@"Callback not provided for navigation scheduling.");
|
RCTLogError(@"Callback not provided for navigation scheduling.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry){
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||||
if (reactTag) {
|
if (reactTag) {
|
||||||
//TODO: This is nasty - why is RCTNavigator hard-coded?
|
//TODO: This is nasty - why is RCTNavigator hard-coded?
|
||||||
id rkObject = viewRegistry[reactTag];
|
id rkObject = viewRegistry[reactTag];
|
||||||
|
@ -870,24 +1008,24 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
{
|
{
|
||||||
RCT_EXPORT();
|
RCT_EXPORT();
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry){
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||||
// - There should be at most one designated "main scroll view"
|
// - There should be at most one designated "main scroll view"
|
||||||
// - There should be at most one designated "`nativeMainScrollDelegate`"
|
// - There should be at most one designated "`nativeMainScrollDelegate`"
|
||||||
// - The one designated main scroll view should have the one designated
|
// - The one designated main scroll view should have the one designated
|
||||||
// `nativeMainScrollDelegate` set as its `nativeMainScrollDelegate`.
|
// `nativeMainScrollDelegate` set as its `nativeMainScrollDelegate`.
|
||||||
if (viewManager.mainScrollView) {
|
if (uiManager.mainScrollView) {
|
||||||
viewManager.mainScrollView.nativeMainScrollDelegate = nil;
|
uiManager.mainScrollView.nativeMainScrollDelegate = nil;
|
||||||
}
|
}
|
||||||
if (reactTag) {
|
if (reactTag) {
|
||||||
id rkObject = viewRegistry[reactTag];
|
id rkObject = viewRegistry[reactTag];
|
||||||
if ([rkObject conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
if ([rkObject conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
||||||
viewManager.mainScrollView = (id<RCTScrollableProtocol>)rkObject;
|
uiManager.mainScrollView = (id<RCTScrollableProtocol>)rkObject;
|
||||||
((id<RCTScrollableProtocol>)rkObject).nativeMainScrollDelegate = viewManager.nativeMainScrollDelegate;
|
((id<RCTScrollableProtocol>)rkObject).nativeMainScrollDelegate = uiManager.nativeMainScrollDelegate;
|
||||||
} else {
|
} else {
|
||||||
RCTCAssert(NO, @"Tag %@ does not conform to RCTScrollableProtocol", reactTag);
|
RCTCAssert(NO, @"Tag %@ does not conform to RCTScrollableProtocol", reactTag);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
viewManager.mainScrollView = nil;
|
uiManager.mainScrollView = nil;
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
@ -896,7 +1034,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
{
|
{
|
||||||
RCT_EXPORT(scrollTo);
|
RCT_EXPORT(scrollTo);
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry){
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||||
UIView *view = viewRegistry[reactTag];
|
UIView *view = viewRegistry[reactTag];
|
||||||
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
||||||
[(id<RCTScrollableProtocol>)view scrollToOffset:CGPointMake([offsetX floatValue], [offsetY floatValue])];
|
[(id<RCTScrollableProtocol>)view scrollToOffset:CGPointMake([offsetX floatValue], [offsetY floatValue])];
|
||||||
|
@ -910,7 +1048,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
{
|
{
|
||||||
RCT_EXPORT(zoomToRect);
|
RCT_EXPORT(zoomToRect);
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry){
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||||
UIView *view = viewRegistry[reactTag];
|
UIView *view = viewRegistry[reactTag];
|
||||||
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
||||||
[(id<RCTScrollableProtocol>)view zoomToRect:[RCTConvert CGRect:rectDict] animated:YES];
|
[(id<RCTScrollableProtocol>)view zoomToRect:[RCTConvert CGRect:rectDict] animated:YES];
|
||||||
|
@ -924,7 +1062,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
{
|
{
|
||||||
RCT_EXPORT();
|
RCT_EXPORT();
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
UIView *view = viewRegistry[reactTag];
|
UIView *view = viewRegistry[reactTag];
|
||||||
if (!view) {
|
if (!view) {
|
||||||
NSString *error = [[NSString alloc] initWithFormat:@"cannot find view with tag %@", reactTag];
|
NSString *error = [[NSString alloc] initWithFormat:@"cannot find view with tag %@", reactTag];
|
||||||
|
@ -947,7 +1085,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
{
|
{
|
||||||
RCT_EXPORT();
|
RCT_EXPORT();
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
_jsResponder = viewRegistry[reactTag];
|
_jsResponder = viewRegistry[reactTag];
|
||||||
if (!_jsResponder) {
|
if (!_jsResponder) {
|
||||||
RCTLogMustFix(@"Invalid view set to be the JS responder - tag %zd", reactTag);
|
RCTLogMustFix(@"Invalid view set to be the JS responder - tag %zd", reactTag);
|
||||||
|
@ -959,7 +1097,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
{
|
{
|
||||||
RCT_EXPORT();
|
RCT_EXPORT();
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
_jsResponder = nil;
|
_jsResponder = nil;
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
@ -1189,18 +1327,19 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
return allJSConstants;
|
return allJSConstants;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)configureNextLayoutAnimation:(NSDictionary *)config withCallback:(RCTResponseSenderBlock)callback errorCallback:(RCTResponseSenderBlock)errorCallback
|
- (void)configureNextLayoutAnimation:(NSDictionary *)config
|
||||||
|
withCallback:(RCTResponseSenderBlock)callback
|
||||||
|
errorCallback:(RCTResponseSenderBlock)errorCallback
|
||||||
{
|
{
|
||||||
RCT_EXPORT();
|
RCT_EXPORT();
|
||||||
|
|
||||||
if (_nextLayoutAnimationCallback || _nextLayoutAnimationConfig) {
|
if (_nextLayoutAnimation) {
|
||||||
RCTLogWarn(@"Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@.", _nextLayoutAnimationConfig, config);
|
RCTLogWarn(@"Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@.", _nextLayoutAnimation, config);
|
||||||
}
|
}
|
||||||
if (config[@"delete"] != nil) {
|
if (config[@"delete"] != nil) {
|
||||||
RCTLogError(@"LayoutAnimation only supports create and update right now. Config: %@", config);
|
RCTLogError(@"LayoutAnimation only supports create and update right now. Config: %@", config);
|
||||||
}
|
}
|
||||||
_nextLayoutAnimationConfig = config;
|
_nextLayoutAnimation = [[RCTLayoutAnimation alloc] initWithDictionary:config callback:callback];
|
||||||
_nextLayoutAnimationCallback = callback;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static UIView *_jsResponder;
|
static UIView *_jsResponder;
|
||||||
|
|
|
@ -124,6 +124,7 @@
|
||||||
13B080241A694A8400A75B9A /* RCTWrapperViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWrapperViewController.m; sourceTree = "<group>"; };
|
13B080241A694A8400A75B9A /* RCTWrapperViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWrapperViewController.m; sourceTree = "<group>"; };
|
||||||
13B080271A694C4900A75B9A /* RCTDataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDataManager.h; sourceTree = "<group>"; };
|
13B080271A694C4900A75B9A /* RCTDataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDataManager.h; sourceTree = "<group>"; };
|
||||||
13B080281A694C4900A75B9A /* RCTDataManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDataManager.m; sourceTree = "<group>"; };
|
13B080281A694C4900A75B9A /* RCTDataManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDataManager.m; sourceTree = "<group>"; };
|
||||||
|
13DB9D681A8CC58200429C20 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = "<group>"; };
|
||||||
13E067481A70F434002CDEE1 /* RCTUIManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUIManager.h; sourceTree = "<group>"; };
|
13E067481A70F434002CDEE1 /* RCTUIManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUIManager.h; sourceTree = "<group>"; };
|
||||||
13E067491A70F434002CDEE1 /* RCTUIManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManager.m; sourceTree = "<group>"; };
|
13E067491A70F434002CDEE1 /* RCTUIManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManager.m; sourceTree = "<group>"; };
|
||||||
13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowView.h; sourceTree = "<group>"; };
|
13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowView.h; sourceTree = "<group>"; };
|
||||||
|
@ -330,6 +331,7 @@
|
||||||
137029521A69923600575408 /* RCTImageDownloader.m */,
|
137029521A69923600575408 /* RCTImageDownloader.m */,
|
||||||
13B07FCD1A683B5F00A75B9A /* RCTScrollableProtocol.h */,
|
13B07FCD1A683B5F00A75B9A /* RCTScrollableProtocol.h */,
|
||||||
13ED13891A80C9D40050A8F9 /* RCTPointerEvents.h */,
|
13ED13891A80C9D40050A8F9 /* RCTPointerEvents.h */,
|
||||||
|
13DB9D681A8CC58200429C20 /* RCTAnimationType.h */,
|
||||||
);
|
);
|
||||||
path = Base;
|
path = Base;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
|
@ -14,7 +14,7 @@ typedef NS_ENUM(NSUInteger, RCTLayoutLifecycle) {
|
||||||
RCTLayoutLifecycleDirtied,
|
RCTLayoutLifecycleDirtied,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: is this redundact now?
|
// TODO: is this still needed?
|
||||||
typedef NS_ENUM(NSUInteger, RCTPropagationLifecycle) {
|
typedef NS_ENUM(NSUInteger, RCTPropagationLifecycle) {
|
||||||
RCTPropagationLifecycleUninitialized = 0,
|
RCTPropagationLifecycleUninitialized = 0,
|
||||||
RCTPropagationLifecycleComputed,
|
RCTPropagationLifecycleComputed,
|
||||||
|
@ -72,7 +72,8 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
|
||||||
@property (nonatomic, assign) CGFloat height;
|
@property (nonatomic, assign) CGFloat height;
|
||||||
@property (nonatomic, assign) CGRect frame;
|
@property (nonatomic, assign) CGRect frame;
|
||||||
|
|
||||||
- (void)updateShadowViewLayout;
|
- (void)setTopLeft:(CGPoint)topLeft;
|
||||||
|
- (void)setSize:(CGSize)size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border. Defaults to 0.
|
* Border. Defaults to 0.
|
||||||
|
@ -110,12 +111,22 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
|
||||||
|
|
||||||
// The following are implementation details exposed to subclasses. Do not call them directly
|
// The following are implementation details exposed to subclasses. Do not call them directly
|
||||||
- (void)dirtyLayout;
|
- (void)dirtyLayout;
|
||||||
- (void)dirtyPropagation;
|
- (BOOL)isLayoutDirty;
|
||||||
|
|
||||||
|
// TODO: is this still needed?
|
||||||
|
- (void)dirtyPropagation;
|
||||||
|
- (BOOL)isPropagationDirty;
|
||||||
|
|
||||||
|
// TODO: move this to text node?
|
||||||
- (void)dirtyText;
|
- (void)dirtyText;
|
||||||
- (BOOL)isTextDirty;
|
- (BOOL)isTextDirty;
|
||||||
- (void)setTextComputed;
|
- (void)setTextComputed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers a recalculation of the shadow view's layout.
|
||||||
|
*/
|
||||||
|
- (void)updateShadowViewLayout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the recursive offset, meaning the sum of all descendant offsets -
|
* Computes the recursive offset, meaning the sum of all descendant offsets -
|
||||||
* this is the sum of all positions inset from parents. This is not merely the
|
* this is the sum of all positions inset from parents. This is not merely the
|
||||||
|
|
|
@ -103,7 +103,7 @@ static css_node_t *RCTGetChild(void *context, int i)
|
||||||
static bool RCTIsDirty(void *context)
|
static bool RCTIsDirty(void *context)
|
||||||
{
|
{
|
||||||
RCTShadowView *shadowView = (__bridge RCTShadowView *)context;
|
RCTShadowView *shadowView = (__bridge RCTShadowView *)context;
|
||||||
return shadowView.layoutLifecycle != RCTLayoutLifecycleComputed;
|
return [shadowView isLayoutDirty];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforces precedence rules, e.g. marginLeft > marginHorizontal > margin.
|
// Enforces precedence rules, e.g. marginLeft > marginHorizontal > margin.
|
||||||
|
@ -325,6 +325,11 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)isLayoutDirty
|
||||||
|
{
|
||||||
|
return _layoutLifecycle != RCTLayoutLifecycleComputed;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)dirtyPropagation
|
- (void)dirtyPropagation
|
||||||
{
|
{
|
||||||
if (_propagationLifecycle != RCTPropagationLifecycleDirtied) {
|
if (_propagationLifecycle != RCTPropagationLifecycleDirtied) {
|
||||||
|
@ -333,6 +338,11 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)isPropagationDirty
|
||||||
|
{
|
||||||
|
return _propagationLifecycle != RCTLayoutLifecycleComputed;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)dirtyText
|
- (void)dirtyText
|
||||||
{
|
{
|
||||||
if (_textLifecycle != RCTTextLifecycleDirtied) {
|
if (_textLifecycle != RCTTextLifecycleDirtied) {
|
||||||
|
@ -391,23 +401,6 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||||
return self.reactTag;
|
return self.reactTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateShadowViewLayout
|
|
||||||
{
|
|
||||||
if (_recomputePadding) {
|
|
||||||
RCTProcessMetaProps(_paddingMetaProps, _cssNode->style.padding);
|
|
||||||
}
|
|
||||||
if (_recomputeMargin) {
|
|
||||||
RCTProcessMetaProps(_marginMetaProps, _cssNode->style.margin);
|
|
||||||
}
|
|
||||||
if (_recomputePadding || _recomputeMargin) {
|
|
||||||
[self dirtyLayout];
|
|
||||||
}
|
|
||||||
[self fillCSSNode:_cssNode];
|
|
||||||
_recomputeMargin = NO;
|
|
||||||
_recomputePadding = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Margin
|
// Margin
|
||||||
|
|
||||||
#define RCT_MARGIN_PROPERTY(prop, metaProp) \
|
#define RCT_MARGIN_PROPERTY(prop, metaProp) \
|
||||||
|
@ -503,6 +496,20 @@ RCT_POSITION_PROPERTY(Left, left, LEFT)
|
||||||
[self dirtyLayout];
|
[self dirtyLayout];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setTopLeft:(CGPoint)topLeft
|
||||||
|
{
|
||||||
|
_cssNode->style.position[CSS_LEFT] = topLeft.x;
|
||||||
|
_cssNode->style.position[CSS_TOP] = topLeft.y;
|
||||||
|
[self dirtyLayout];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setSize:(CGSize)size
|
||||||
|
{
|
||||||
|
_cssNode->style.dimensions[CSS_WIDTH] = size.width;
|
||||||
|
_cssNode->style.dimensions[CSS_HEIGHT] = size.height;
|
||||||
|
[self dirtyLayout];
|
||||||
|
}
|
||||||
|
|
||||||
// Flex
|
// Flex
|
||||||
|
|
||||||
#define RCT_STYLE_PROPERTY(setProp, getProp, cssProp, type) \
|
#define RCT_STYLE_PROPERTY(setProp, getProp, cssProp, type) \
|
||||||
|
@ -530,4 +537,20 @@ RCT_STYLE_PROPERTY(FlexWrap, flexWrap, flex_wrap, css_wrap_type_t)
|
||||||
[self dirtyPropagation];
|
[self dirtyPropagation];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)updateShadowViewLayout
|
||||||
|
{
|
||||||
|
if (_recomputePadding) {
|
||||||
|
RCTProcessMetaProps(_paddingMetaProps, _cssNode->style.padding);
|
||||||
|
}
|
||||||
|
if (_recomputeMargin) {
|
||||||
|
RCTProcessMetaProps(_marginMetaProps, _cssNode->style.margin);
|
||||||
|
}
|
||||||
|
if (_recomputePadding || _recomputeMargin) {
|
||||||
|
[self dirtyLayout];
|
||||||
|
}
|
||||||
|
[self fillCSSNode:_cssNode];
|
||||||
|
_recomputeMargin = NO;
|
||||||
|
_recomputePadding = NO;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -80,12 +80,12 @@ RCT_REMAP_VIEW_PROPERTY(containerBackgroundColor, backgroundColor)
|
||||||
// layout to copy its properties across?
|
// layout to copy its properties across?
|
||||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry
|
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry
|
||||||
{
|
{
|
||||||
NSMutableArray *shadowBlocks = [NSMutableArray new];
|
NSMutableArray *uiBlocks = [NSMutableArray new];
|
||||||
|
|
||||||
// TODO: are modules global, or specific to a given rootView?
|
// TODO: are modules global, or specific to a given rootView?
|
||||||
for (RCTShadowView *rootView in shadowViewRegistry.allObjects) {
|
for (RCTShadowView *rootView in shadowViewRegistry.allObjects) {
|
||||||
if (![rootView isReactRootView]) {
|
if (![rootView isReactRootView]) {
|
||||||
// This isn't a host view
|
// This isn't a root view
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ RCT_REMAP_VIEW_PROPERTY(containerBackgroundColor, backgroundColor)
|
||||||
[shadowView setTextComputed];
|
[shadowView setTextComputed];
|
||||||
}
|
}
|
||||||
|
|
||||||
[shadowBlocks addObject:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
[uiBlocks addObject:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
[reactTaggedAttributedStrings enumerateObjectsUsingBlock:^(NSAttributedString *attributedString, NSNumber *reactTag, BOOL *stop) {
|
[reactTaggedAttributedStrings enumerateObjectsUsingBlock:^(NSAttributedString *attributedString, NSNumber *reactTag, BOOL *stop) {
|
||||||
RCTText *text = viewRegistry[reactTag];
|
RCTText *text = viewRegistry[reactTag];
|
||||||
text.attributedText = attributedString;
|
text.attributedText = attributedString;
|
||||||
|
@ -125,9 +125,9 @@ RCT_REMAP_VIEW_PROPERTY(containerBackgroundColor, backgroundColor)
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
for (RCTViewManagerUIBlock shadowBlock in shadowBlocks) {
|
for (RCTViewManagerUIBlock shadowBlock in uiBlocks) {
|
||||||
shadowBlock(viewManager, viewRegistry);
|
shadowBlock(uiManager, viewRegistry);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,6 @@ var FileWatcher = require('../../FileWatcher');
|
||||||
var DependencyGraph = require('./DependencyGraph');
|
var DependencyGraph = require('./DependencyGraph');
|
||||||
var ModuleDescriptor = require('../ModuleDescriptor');
|
var ModuleDescriptor = require('../ModuleDescriptor');
|
||||||
|
|
||||||
var DEFAULT_POLYFILLS = [
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
var DEFINE_MODULE_CODE =
|
var DEFINE_MODULE_CODE =
|
||||||
'__d(' +
|
'__d(' +
|
||||||
'\'_moduleName_\',' +
|
'\'_moduleName_\',' +
|
||||||
|
@ -39,6 +35,8 @@ function HasteDependencyResolver(config) {
|
||||||
: path.join(__dirname, 'polyfills/prelude.js'),
|
: path.join(__dirname, 'polyfills/prelude.js'),
|
||||||
path.join(__dirname, 'polyfills/require.js'),
|
path.join(__dirname, 'polyfills/require.js'),
|
||||||
path.join(__dirname, 'polyfills/polyfills.js'),
|
path.join(__dirname, 'polyfills/polyfills.js'),
|
||||||
|
path.join(__dirname, 'polyfills/console.js'),
|
||||||
|
path.join(__dirname, 'polyfills/error-guard.js'),
|
||||||
].concat(
|
].concat(
|
||||||
config.polyfillModuleNames || []
|
config.polyfillModuleNames || []
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue