Descargo de responsabilidad: lo que sigue es principalmente el resultado de mi propia experimentación en React Native 0.50. Actualmente, a la ScrollView
documentación le falta mucha de la información que se describe a continuación; por ejemplo, onScrollEndDrag
está completamente indocumentado. Dado que todo aquí se basa en un comportamiento indocumentado, desafortunadamente no puedo prometer que esta información seguirá siendo correcta dentro de un año o incluso un mes.
Además, todo lo que se muestra a continuación asume una vista de desplazamiento puramente vertical cuyo desplazamiento y nos interesa; traducir ax compensaciones, si es necesario, es con suerte un ejercicio fácil para el lector.
Varios controladores de eventos en una ScrollView
toma event
y le permiten obtener la posición de desplazamiento actual a través de event.nativeEvent.contentOffset.y
. Algunos de estos controladores tienen un comportamiento ligeramente diferente entre Android e iOS, como se detalla a continuación.
En Android
Dispara cada fotograma mientras el usuario se desplaza, en cada fotograma mientras la vista de desplazamiento se desliza después de que el usuario la suelta, en el fotograma final cuando la vista de desplazamiento se detiene, y también siempre que el desplazamiento de la vista de desplazamiento cambia como resultado de su fotograma cambiando (por ejemplo, debido a la rotación de paisaje a retrato).
En iOS
Se dispara mientras el usuario arrastra o mientras la vista de desplazamiento se desliza, a una frecuencia determinada por scrollEventThrottle
y como máximo una vez por cuadro cuando scrollEventThrottle={16}
. Si el usuario suelta la vista de desplazamiento mientras tiene suficiente impulso para planear, el onScroll
controlador también disparará cuando se detenga después de planear. Sin embargo, si el usuario arrastra y suelta el punto de vista de desplazamiento mientras está parado, onScroll
está no garantizada al fuego para la posición final a menos que scrollEventThrottle
se ha establecido de tal manera que onScroll
los incendios cada fotograma de desplazamiento.
Hay un costo de rendimiento en la configuración scrollEventThrottle={16}
que se puede reducir configurándolo en un número mayor. Sin embargo, esto significa que onScroll
no disparará todos los fotogramas.
Se dispara cuando la vista de desplazamiento se detiene después de planear. No dispara en absoluto si el usuario suelta la vista de desplazamiento mientras está estacionaria para que no se deslice.
onScrollEndDrag
Se dispara cuando el usuario deja de arrastrar la vista de desplazamiento, independientemente de si la vista de desplazamiento se deja estacionaria o comienza a deslizarse.
Dadas estas diferencias de comportamiento, la mejor manera de realizar un seguimiento de la compensación depende de sus circunstancias precisas. En el caso más complicado (necesita ser compatible con Android e iOS, incluido el manejo de cambios en el ScrollView
marco debido a la rotación, y no desea aceptar la penalización de rendimiento en Android de la configuración scrollEventThrottle
a 16), y debe manejar También cambia el contenido en la vista de desplazamiento, entonces es un maldito desastre.
El caso más simple es si solo necesita manejar Android; solo usa onScroll
:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
>
Para admitir adicionalmente iOS, si está contento de despedir al onScroll
controlador en cada cuadro y acepta las implicaciones de rendimiento de eso, y si no necesita manejar los cambios de cuadro, entonces es solo un poco más complicado:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={16}
>
Para reducir la sobrecarga de rendimiento en iOS y, al mismo tiempo, garantizar que registremos cualquier posición en la que se asiente la vista de desplazamiento, podemos aumentar scrollEventThrottle
y, además, proporcionar un onScrollEndDrag
controlador:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
onScrollEndDrag={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={160}
>
Pero si queremos manejar cambios de marco (por ejemplo, porque permitimos que se gire el dispositivo, cambiando la altura disponible para el marco de la vista de desplazamiento) y / o cambios de contenido, entonces debemos implementar adicionalmente tanto onContentSizeChange
yonLayout
realizar un seguimiento de la altura de ambos el marco de la vista de desplazamiento y su contenido y, por lo tanto, calcular continuamente el desplazamiento máximo posible e inferir cuándo el desplazamiento se ha reducido automáticamente debido a un cambio de tamaño del marco o del contenido:
<ScrollView
onLayout={event => {
this.frameHeight = event.nativeEvent.layout.height;
const maxOffset = this.contentHeight - this.frameHeight;
if (maxOffset < this.yOffset) {
this.yOffset = maxOffset;
}
}}
onContentSizeChange={(contentWidth, contentHeight) => {
this.contentHeight = contentHeight;
const maxOffset = this.contentHeight - this.frameHeight;
if (maxOffset < this.yOffset) {
this.yOffset = maxOffset;
}
}}
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y;
}}
onScrollEndDrag={event => {
this.yOffset = event.nativeEvent.contentOffset.y;
}}
scrollEventThrottle={160}
>
Sí, es bastante horrible. Tampoco estoy 100% seguro de que siempre funcione bien en los casos en los que cambie simultáneamente el tamaño del marco y el contenido de la vista de desplazamiento. Pero es lo mejor que se me ocurre, y hasta que esta característica se agregue dentro del marco en sí , creo que es lo mejor que cualquiera puede hacer.