Чи можна тримати ScrollView прокрученим донизу?


87

Для програми, подібної до чату, я хочу залишити ScrollViewкомпонент прокрученим донизу, оскільки найновіші повідомлення з’являються під старими. Чи можемо ми відрегулювати положення прокрутки a ScrollView?


3
Я нічого не знаю про рідну реакцію, але майте на увазі, що іноді користувачі хочуть прокрутити вгору, щоб переглянути історію чату. Обов’язково прокручуйте чат, лише якщо чат уже прокручувався донизу, коли надходить нове повідомлення.
Девід

Це гарна ідея. Ми відстежуємо цю проблему тут: github.com/facebook/react-native/issues/107
Ерік Вісенті

Я просто відповів на таке запитання, будь ласка , перевірте його тут - stackoverflow.com/a/32652967/1541508
Kohver

Це відповідає на ваше запитання? Як прокрутити внизу React Native ListView
TripleM

Відповіді:


165

Для React Native 0.41 і пізніших версій ви можете зробити це за допомогою вбудованого scrollToEndметоду:

<ScrollView
    ref={ref => {this.scrollView = ref}}
    onContentSizeChange={() => this.scrollView.scrollToEnd({animated: true})}>
</ScrollView>

3
З рідної реакції 0.55.4 і вище мені довелося використовувати rootінакше, scrollToEnd не визначено. тобтоthis.scrollView.root.scrollToEnd
Саймон

4
В даний час використання рідної реакції 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 })}
    />
  );
};

5

спробуйте це рішення

xcode 10.0

RN: 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>

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.
Mark Amery

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>

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вниз до нижче все його зміст, а не просто прокрутка вниз.
Mark Amery

-1

У мене була подібна проблема, але після прочитання цієї сторінки (від якої мені болить голова!) Я знайшов цей дуже приємний пакет npm, який просто інвертує ListView і полегшує все для програми чату !!


-1; це дублює раніше розміщену тут відповідь (і також трохи заплутане; це питання стосується a ScrollView, а не `ListView`).
Mark Amery

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.