Можно ли прокрутить ScrollView вниз?

87

Для приложения, похожего на чат, я хочу, чтобы ScrollViewкомпонент был прокручен вниз, потому что самые новые сообщения появляются под старыми. Можем ли мы отрегулировать положение прокрутки a ScrollView?

Штирман
источник
3
Я ничего не знаю о react-native, но имейте в виду, что иногда пользователи хотят прокрутить вверх, чтобы просмотреть историю чата. Убедитесь, что вы прокручиваете чат только в том случае, если чат уже был прокручен до конца, когда приходит новое сообщение.
Дэвид
Это хорошая идея. Мы отслеживаем эту проблему здесь: github.com/facebook/react-native/issues/107
Эрик Висенти,
Я только что ответил на аналогичный вопрос, проверьте его здесь - stackoverflow.com/a/32652967/1541508
Кохвер,
Отвечает ли это на ваш вопрос? Как прокрутить React Native ListView
вниз

Ответы:

165

Для React Native 0.41 и новее вы можете сделать это с помощью встроенного scrollToEndметода:

<ScrollView
    ref={ref => {this.scrollView = ref}}
    onContentSizeChange={() => this.scrollView.scrollToEnd({animated: true})}>
</ScrollView>
Анируддха Шевле
источник
3
Из response native 0.55.4 и выше мне пришлось использовать rootиначе scrollToEnd не определено. т.е.this.scrollView.root.scrollToEnd
Саймон
4
В настоящее время используется react native 0.58.6, а использование rootфактически вызывает неопределенную ошибку.
Хосе Бернхардт
1
Если это так: ref={ref => {this.scrollView = ref}}если вы не хотите возвращать задание
Hampycalc
25

Используйте onContentSizeChange, чтобы отслеживать нижнюю часть Y прокрутки (высота)

https://facebook.github.io/react-native/docs/scrollview.html#oncontentsizechange

var _scrollToBottomY

<ScrollView
    onContentSizeChange={(contentWidth, contentHeight)=>{
    _scrollToBottomY = contentHeight;
    }}>
</ScrollView>

затем используйте имя ссылки для просмотра прокрутки, например

this.refs.scrollView.scrollTo(_scrollToBottomY);
дрикода
источник
2
Обратите внимание, что при этом вид полностью прокручивается вниз, поэтому вид практически не отображается (если вы не добавили что-то к нему после измерения). Это имеет смысл, поскольку код прокручивается до высоты прокручиваемой области, а не до высоты детей, переполняющих прокручиваемую область (вероятно, НЕ то, что хотел OP). См. Вместо этого stackoverflow.com/a/39563193/1526986 .
xixixao
20

Вот простейшее решение, которое я смог найти:

 <ListView
ref={ref => (this.listView = ref)}
onLayout={event => {
    this.listViewHeight = event.nativeEvent.layout.height;
}}
onContentSizeChange={() => {
    this.listView.scrollTo({
        y: this.listView.getMetrics().contentLength - this.listViewHeight
    });
}}
/>;
сеньорнестор
источник
1
Когда я использую этот или другие похожие ответы, элементы в списке исчезают (похоже, он прокручивается слишком далеко, но я не могу быть уверен). Небольшая прокрутка заставляет все снова появляться и выглядит так, как будто оно возвращается в поле зрения (отсюда и теория слишком далекой прокрутки ). Любая очевидная идея, почему это может быть?
tscizzle
11

Для тех, кто пишет функциональный компонент, который может использовать ловушку,

import React, { useRef } from 'react';
import { ScrollView } from 'react-native';

const ScreenComponent = (props) => {
  const scrollViewRef = useRef();
  return (
    <ScrollView
      ref={scrollViewRef}
      onContentSizeChange={() => scrollViewRef.current.scrollToEnd({ animated: true })}
    />
  );
};
cltsang
источник
5

попробуйте это решение

xcode 10.0

РН: 56.0.0

<ScrollView ref="scrollView"
             onContentSizeChange={(width,height) => this.refs.scrollView.scrollTo({y:height})}>  </ScrollView> // OR height -  width
Вишал Вагасия
источник
почему ref=? похоже на реликвию веб-разработчиков
YungGun
5

Если кто-то в 2020 году или позже задается вопросом, как добиться этого с помощью функциональных компонентов. Вот как я это сделал:

ref={ref => scrollView = ref }
onContentSizeChange={() => scrollView.scrollToEnd({ animated: true })}>
Марлон Энглемам
источник
Примечание: вы можете упомянуть, что используете useref. большинство людей не знают, как использовать ref с хуками.
ʎzɐɹƆ,
да. Дело в том, что это получилось даже без использования хука useRef. Но правильный способ - это использовать! Вы правы
Марлон Энглемам
4

Вы можете инвертировать вид прокрутки / списка с помощью react-native-invertible-scroll-viewмодуля , а затем просто вызвать ref.scrollTo(0)прокрутку вниз.

Установите модуль:

npm install --save react-native-invertible-scroll-view

Затем импортируйте компонент:

import InvertibleScrollView from 'react-native-invertible-scroll-view';

Затем используйте следующий JSX вместо ScrollView:

<InvertibleScrollView inverted
    ref={ref => { this.scrollView = ref; }}
    onContentSizeChange={() => {
        this.scrollView.scrollTo({y: 0, animated: true});
    }}
>
    { /* content */ }
</InvertibleScrollView>

Это работает, потому что react-native-invertible-scroll-viewпереворачивает все содержимое представления. Обратите внимание, что дочерние элементы представления также будут отображаться в порядке, обратном нормальному виду - первый дочерний элемент будет отображаться внизу .

Ирфаан
источник
3

На самом деле ответ @Aniruddha не сработал для меня, потому что я использую, "react-native": "0.59.8"а this.scrollView.root.scrollToEndтакже не определен

но я понял это, и это работает this.scrollView.scrollResponderScrollToEnd({animated: true});

Если вы используете 0.59.8это, будет работать ИЛИ если вы используете более позднюю версию, и это работает для вас. пожалуйста, добавьте версии в этот ответ, чтобы у других не было проблем.

Полный код здесь

<ScrollView
    ref={ref => this.scrollView = ref}
    onContentSizeChange={(contentWidth, contentHeight)=>{        
        this.scrollView.scrollResponderScrollToEnd({animated: true});
    }}>
</ScrollView>
RajnishCoder
источник
1

Этот метод немного взломан, но я не мог найти лучшего способа

  1. Сначала добавьте ссылку в свой ScrollView.
  2. Во-вторых, добавьте фиктивный вид перед закрытием тега ScrollView
  3. Получите положение y этого фиктивного представления
  4. Всякий раз, когда вы хотите прокрутить вниз, прокрутите до положения фиктивного представления

var footerY; //Y position of the dummy view
render() {    
    return (
      <View>
          <ScrollView 
          ref='_scrollView'>
            // This is where all the things that you want to display in the scroll view goes
            
            // Below is the dummy View
            <View onLayout={(e)=> {
              footerY = e.nativeEvent.layout.y;
              }}/>
          </ScrollView>
              
          //Below is the button to scroll to the bottom    
          <TouchableHighlight
            onPress={() => { this.refs._scrollView.scrollTo(footerY); }}>
            <Text>Scroll to Bottom</Text>
          </TouchableHighlight>
        
      </View>
    );
  }

Шритам
источник
Это не совсем то, о чем просили; это прокручивает вниз при касании, вместо того, чтобы прокручивать прокрутку вниз по мере добавления содержимого. В любом случае, scrollToBottomделает такие подходы устаревшими в новых версиях React Native.
Марк Эмери
1

Это отвечает на ваш вопрос: https://stackoverflow.com/a/39563100/1917250

Вам нужно постоянно прокручивать страницу вниз, вычисляя высоту содержимого за вычетом высоты его scrollView.

Пол Рада
источник
Это правильный ответ на вопрос. Вы можете знать, когда выполнять прокрутку, ответ в ссылке показывает, как получить нужные числа. В частности, если вы добавляете компоненты в список, высота содержимого не будет включать их при вызове componentDidUpdate, поэтому вы хотите помнить, что вместо этого вы хотите прокручивать и прокручивать onContentSizeChange.
xixixao
1

Если вы используете TypeScript, вам нужно сделать это

interface Props extends TextInputProps { 
     scrollRef?: any;
}

export class Input extends React.Component<Props, any> {
     scrollRef;
constructor(props) {
    this.scrollRef = React.createRef();
}
<ScrollView
    ref={ref => (this.scrollRef = ref)}
    onContentSizeChange={() => {
    this.scrollRef.scrollToEnd();
    }}
>
</ScrollView>
Forhad
источник
0

Я боролся с этим некоторое время и, наконец, придумал что-то, что действительно хорошо и гладко для меня работало. Как и многие, я безуспешно пытался .scrollToEnd. Решение , которое работало для меня было сочетание onContentSizeChange, onLayoutреквизита и немного больше думать визуально «что прокрутка вниз» на самом деле означает. Последняя часть кажется мне сейчас тривиальной, но мне потребовалась целая вечность, чтобы понять.

Учитывая, что ScrollViewимеет фиксированную высоту, когда высота содержимого превышает высоту контейнера, я рассчитал смещение, вычитая prevHeight(фиксированную высоту ScrollView) для currHeight(высота содержимого). Если вы можете визуально представить это, прокрутка до этой разницы по оси Y перемещает высоту содержимого по своему контейнеру на это смещение. Я увеличил смещение и сделал мою текущую высоту содержимого моей предыдущей высотой и продолжал вычислять новое смещение.

Вот код. Надеюсь, это кому-то поможет.

class ChatRoomCorrespondence extends PureComponent {
  currHeight = 0;
  prevHeight = 0;
  scrollHeight = 0;

  scrollToBottom = () => {
    this.refs.scrollView.getScrollResponder().scrollResponderScrollTo({
      x: 0,
      y: this.scrollHeight,
      animated: true
    });
  };

  render() {
    return (
      <ScrollView
        style={Styles.container}
        ref="scrollView"
        onContentSizeChange={(w, h) => {
          this.currHeight = h;

          if (
            this.prevHeight > 0 &&
            this.currHeight - this.scrollHeight > this.prevHeight
          ) {
            this.scrollHeight += this.currHeight - this.prevHeight;
            console.log("--------------------------------------------");
            console.log("Curr: ", this.currHeight);
            console.log("Prev: ", this.prevHeight);
            console.log("Scroll: ", this.scrollHeight);
            this.prevHeight = this.currHeight;
            console.log("PREV: ", this.prevHeight);
            console.log("--------------------------------------------");

            this.scrollToBottom();
          }
        }}
        onLayout={ev => {
          // Fires once
          const fixedContentHeight = ev.nativeEvent.layout.height;
          this.prevHeight = fixedContentHeight;
        }}
      >
        <FlatList
          data={this.props.messages}
          renderItem={({ item }) => (
            <ChatMessage
              text={item.text}
              sender={item.sender}
              time={item.time}
              username={this.props.username}
            />
          )}
          keyExtractor={(item, index) => index.toString()}
        />
      </ScrollView>
    );
  }
}
Нелли Сугу
источник
0

Думаю, еще слишком поздно отвечать, но у меня есть одна хитрость! Если вы используете FlatListвы можете включить invertedсвойство , которое возвращается к установке transform scaleна -1(что является приемлемым для других компонентов , таких как список ScrollView...). При рендеринге FlatListс данными он всегда будет внизу!

Абдумутал Абдусаматов
источник
-1

Попробуйте установить для свойства contentOffset представления прокрутки текущую высоту содержимого.

Чтобы выполнить то, что вам нужно, вы можете сделать это как часть события onScroll. Однако это может ухудшить взаимодействие с пользователем, поэтому может оказаться более удобным делать это только при добавлении нового сообщения.

Джек
источник
-1; Неа. Как и многие из ответов здесь, это имеет эффект прокрутки ScrollViewвниз к ниже все его содержание, а не просто прокрутка вниз.
Марк Эмери
-1

У меня была аналогичная проблема, но после прочтения этой страницы (которая вызывает у меня головную боль!) Я нахожу этот очень хороший пакет npm, который просто инвертирует ListView и упрощает работу приложения чата !!

Мехди Сабури
источник
-1; это дублирует ранее опубликованный здесь ответ (и также немного сбивает с толку; этот вопрос касается a ScrollView, а не ListView).
Марк Эмери
-3

Немного поздно ... но посмотрите на этот вопрос. Прокрутите вверх ScrollView

ScrollView имеет метод scrollTo.

Джордж Б
источник
1
Да, в ScrollView есть метод scrollTo, но вопрос конкретно касается прокрутки вниз, что не так просто.
Southerneer