Closed joncursi closed 7 years ago
Thanks for the feedback, I wasn't aware of the new bottom sheet maps on iOS 10, and indeed it's very similar, i had no plans to support iOS when i was designing it, i, but now looking on the new iOS 10 maps it might make sense, but on android is just a wrapper on the android BottomSheetBehavior, and it's easier because i don't have to deal with animations at all, it's already on the component, it's reliable and well tested, i just need to expose it to react-native, but iOS maps is a closed source and there's no such thing (as far i know) on iOS standard library, so i need to reimplement and "fake" it to looks like iOS maps, which depending the implementation can disappoint some users, because the implementation will never be the same.
Searching a little while, i found this stackoverflow issue and a project answer https://github.com/AhmedElassuty/IOS-BottomSheet, and the implementation looks similar, but after playing along side with iOS maps the experience isn't the same thing.
There's also STPopup that provides a bottom sheet approach which could be more interesting.
If you know any approach to achieve that or external library that we can rely on it, would be great, but i've no plans for now.
STPopup is amazing anyone there (someone pro) for implement for react native ?
Hey guys, it's been a while, but i just want to share a very cool google maps implementation that works well on ios, it's actually physics based library by wix, check it out the react-native-interactable
Yes, i've tested myself, and it feels native and uses a declarative API relied on UIKit Dynamics, so it's very reliable and easy to implement, i will also use on my ios version, and remain this library on android.
I am going to close this, once i'm taking this library to a next step, and adding any other API will be highly incompatible and confused.
@cesardeazevedo The react-native-interactable can't handle a scrollview inside the panel. https://github.com/wix/react-native-interactable/issues/62 https://github.com/wix/react-native-interactable/issues/35
@RichardLindhout nested scrolls is actually a very difficult problem, the way that bottom-sheet-behavior works with nested-scroll-view is because it's backed by a coordinatorlayout on the native side, so didn't had to do much thing in order to make it work, but the react-native-interactable is a completely different propose, they will have to implement a different approach, so there's not much thing that i can do about it, sorry.
I'll maybe try to port https://github.com/AhmedElassuty/BottomSheetController/tree/develop to react-native
I ended up with just a ScrollView above all the content, I wanted to make the top of the scrollview pass trough touch events, I could not get there with just the normal react-native features. I looked and saw a nice library which does just that. https://github.com/rome2rio/react-native-touch-through-view.
Works like a charm, it should also work on Android!
import React, { PureComponent } from 'react'
import PT from 'prop-types'
import {
TouchThroughView,
TouchThroughWrapper,
} from 'react-native-touch-through-view'
import {
View,
Dimensions,
ScrollView,
TouchableWithoutFeedback,
} from 'react-native'
import keyboardHOC from '../logic/hocs/keyboardHOC'
import styles from './styles/BottomSheet.style'
import binder from '../logic/shared/helpers/binder'
import c from '../logic/shared/constants'
const getSize = () => ({
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
})
class BottomSheet extends PureComponent {
constructor(props) {
super(props)
binder(this, ['_handleScroll', '_close'])
this.state = {
opened: false,
}
this.y = 0
}
_close() {
this.setState(
{
opened: false,
},
() => this._scrollView.scrollTo({ y: 0, animated: true })
)
}
_handleScroll(event) {
if (event.nativeEvent.contentOffset.y === 0 && this.y !== 0) {
if (this.state.opened) {
this.setState({
opened: false,
})
}
this.props.collapsed()
}
if (this.y > 0) {
if (!this.state.opened) {
this.setState({
opened: true,
})
}
this.props.opened()
}
}
render() {
const { height } = getSize()
const offsetY =
this.y > this.props.keyboardHeight ? this.y : this.props.keyboardHeight
const BackDrop = this.state.opened ? View : TouchThroughView
return (
<TouchThroughWrapper style={styles.wrapper}>
<ScrollView
onScroll={this._handleScroll}
ref={component => {
this._scrollView = component
this.props.scrollViewRef(component)
}}
scrollEventThrottle={16}
alwaysBounceVertical={false}
bounces={false}
keyboardDismissMode={'on-drag'}
contentOffset={{
y: offsetY,
}}
>
<TouchableWithoutFeedback onPress={this._close}>
<BackDrop
style={{
height: height - c.panelHeight,
}}
/>
</TouchableWithoutFeedback>
<View
style={{
minHeight: height,
backgroundColor: '#fff',
}}
>
{this.props.children}
</View>
</ScrollView>
</TouchThroughWrapper>
)
}
}
BottomSheet.propTypes = {
children: PT.oneOfType([PT.arrayOf(PT.node), PT.node]).isRequired,
keyboardHeight: PT.number,
collapsed: PT.func,
opened: PT.func,
scrollViewRef: PT.func,
}
BottomSheet.defaultProps = {
keyboardHeight: 0,
count: 0,
collapsed: () => {},
opened: () => {},
scrollViewRef: () => {},
}
export default keyboardHOC(BottomSheet)
BottomSheet.style.js
import { StyleSheet } from 'react-native'
export default StyleSheet.create({
wrapper: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
})
keyboardHOC
import React, { Component } from 'react'
import { Keyboard, Platform, LayoutAnimation } from 'react-native'
import binder from '../shared/helpers/binder'
export default WrappedComponent => {
class HOC extends Component {
constructor(props) {
super(props)
this.state = {
keyboardHeight: 0,
}
binder(this, [
'_keyboardDidShow',
'_keyboardDidHide',
'_onKeyboardChange',
])
}
componentWillMount() {
if (WrappedComponent.componentDidMount)
WrappedComponent.componentDidMount()
if (Platform.OS === 'ios') {
this.keyboardDidChangeListener = Keyboard.addListener(
'keyboardWillChangeFrame',
this._onKeyboardChange
)
} else {
this.keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
this._keyboardDidShow
)
}
this.keyboardDidHideListener = Keyboard.addListener(
'keyboardWillHide',
this._keyboardDidHide
)
}
componentWillUnmount() {
if (WrappedComponent.componentWillUnmount)
WrappedComponent.componentWillUnmount()
if (Platform.OS === 'ios') {
this.keyboardDidChangeListener.remove()
} else {
this.keyboardDidShowListener.remove()
}
this.keyboardDidHideListener.remove()
}
_onKeyboardChange(event) {
if (!event) {
this.setState({ keyboardHeight: 0 })
return
}
const { duration, easing, endCoordinates } = event
const keyboardHeight = endCoordinates.height
if (duration && easing) {
LayoutAnimation.configureNext({
duration,
update: {
duration,
type: LayoutAnimation.Types[easing] || 'keyboard',
},
})
}
this.setState({ keyboardHeight })
}
_keyboardDidShow(e) {
const keyboardHeight = e.endCoordinates.height
if (e && keyboardHeight) {
this.setState({
keyboardHeight,
})
}
}
_keyboardDidHide() {
this.setState({
keyboardHeight: 0,
})
}
render() {
return <WrappedComponent {...this.state} {...this.props} />
}
}
return HOC
}
Use it like this.
<View>
//
// Put here any content e.g. Google Maps
//
<GoogleMaps />
<BottomSheet
scrollViewRef={component => {
this._bottomSheet = component
}}
collapsed={() => {}}
opened={()=>{}}
>
//Bottom sheet children could be anything
</BottomSheet>
</View>
The Google Maps iOS app, and many other material design apps, use this same bottom sheet behavior on both iOS and Android. Are there any plans to make this library cross-compatible with iOS as well?