I have buttons displayed on the top of the screen (used react-native-scrollable-tab-view). Underneath the buttons, I have ListView
with section header on it.
Is there a way to get the header to stick to the bottom edge of the tab-view when I am scrolling?
I have had a hard time trying to get ListView
's section header to stick to the bottom of Facebook TabBar, and its default is to stick to the top of the screen.
When I scroll, the section header slides under the tab-bar.
Any thoughts on this? Is there anything I should change in FacebookTabBar.js to make this work?
Without tab bar at the top
With tab bar at the top
Note: It is strange how this GIF does not show the full animation correctly; you can imagine that the list is scrolled a lot and the section header slides under the tab bar.
Section header styles
catListHeaderContainer: {
padding: 12,
backgroundColor: '#1F2036',
}
FacebookTabBar styles
var styles = StyleSheet.create({
tab: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingBottom: 10,
},
tabs: {
height: 60,
flexDirection: 'row',
paddingTop: 5,
borderWidth: 0,
borderTopWidth: 0,
borderLeftWidth: 0,
borderRightWidth: 0,
borderBottomColor: 'rgba(0,0,0,0)',
},
activeTabTitle: {
marginTop: 40,
color: '#3B5998',
},
nonActiveTabTitle: {
marginTop: 40,
color: '#BDBDBD',
},
});
ListView Headers don't stick, you'll need to use renderSectionHeader
and cloneWithRowsAndSections
instead of cloneWithRows
to do this.
From the React Native documentation on ListView
renderSectionHeader function
(sectionData, sectionID) => renderable
If provided, a sticky header is rendered for this section. The sticky behavior means that it will scroll with the content at the top of the section until it reaches the top of the screen, at which point it will stick to the top until it is pushed off the screen by the next section header.
I tackled this same issue today. Here's how I handled it. First, in getInitialState
:
getInitialState: function() {
return {
dataBlob: {},
dataSource: new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2
}),
}
},
Then, during my API call that gets data back, I add that response data to my dataBlob
object. The key that stores it is considered the sectionId
for ListView.DataSource
. In this case, that sectionId
is going to be the date of the posts I retrieve:
getAllPosts: function() {
api.getAllPosts()
.then((responseData) => {
var tempDataBlob = this.state.dataBlob;
var date = new Date(responseData.posts[0].day).toDateString();
tempDataBlob[date] = responseData.posts;
this.setState({
dataBlob: tempDataBlob
});
;
}).then(() => {
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(this.state.dataBlob),
loaded: true
})
})
.done();
},
cloneWithRowsAndSections
accepts a dataBlob
(in my case, an object) as its first argument, and optional arguments of sectionIDs
and rowIDs
.
Here's how renderListView
looks:
renderListView: function() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderPostCell}
renderSectionHeader={this.renderSectionHeader}
renderFooter={this.renderFooter}
onEndReached={() => {this.getAllPosts(this.state.currentDay)}}
onEndReachedThreshold={40}
style={styles.postsListView} />
)
},
And here's how renderSectionHeader
looks:
renderSectionHeader: function(sectionData, sectionID) {
return (
<View style={styles.section}>
<Text style={styles.sectionText}>{sectionID}</Text>
</View>
)
},
Here's how it looks in the end: