Apakah mungkin untuk membuat ScrollView tetap digulir ke bawah?


90

Untuk aplikasi seperti obrolan, saya ingin membiarkan ScrollViewkomponen digulir ke bawah, karena pesan terbaru muncul di bawah pesan yang lebih lama. Bisakah kita menyesuaikan posisi gulir a ScrollView?


3
Saya tidak tahu apa-apa tentang react-native, tetapi perlu diingat terkadang pengguna ingin menggulir ke atas untuk melihat riwayat obrolan. Pastikan untuk hanya menggulir obrolan jika obrolan sudah digulir ke bawah saat pesan baru tiba.
David

Ini ide yang bagus. Kami melacak masalah ini di sini: github.com/facebook/react-native/issues/107
Eric

Saya baru saja menjawab pertanyaan serupa, silakan periksa di sini - stackoverflow.com/a/32652967/1541508
Kohver

Apakah ini menjawab pertanyaan Anda? Cara menggulir ke bawah React Native ListView
TripleM

Jawaban:


166

Untuk React Native 0.41 dan yang lebih baru , Anda dapat melakukan ini dengan scrollToEndmetode bawaan:

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

3
Dari react native 0.55.4 dan yang lebih besar, saya harus menggunakan rootsebaliknya, scrollToEnd tidak ditentukan. yaituthis.scrollView.root.scrollToEnd
Simon

4
Saat ini menggunakan react native 0.58.6 dan menggunakan rootbenar - benar menghasilkan kesalahan yang tidak ditentukan.
Jose Bernhardt

1
Haruskah ini: ref={ref => {this.scrollView = ref}}karena Anda tidak ingin mengembalikan tugas
Hampycalc

26

Gunakan onContentSizeChange untuk melacak Y bagian bawah tampilan gulir (tinggi)

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

var _scrollToBottomY

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

kemudian gunakan nama ref dari tampilan gulir seperti

this.refs.scrollView.scrollTo(_scrollToBottomY);

2
Perhatikan bahwa ini menggulung tampilan sepenuhnya ke bawah, sehingga tampilan pada dasarnya tidak terlihat (kecuali Anda menambahkan sesuatu ke dalamnya setelah pengukuran). Ini masuk akal karena kode menggulir ke ketinggian area yang dapat digulir, bukan ke ketinggian anak-anak yang memenuhi area yang dapat digulir (mungkin BUKAN yang diinginkan OP). Lihat stackoverflow.com/a/39563193/1526986 sebagai gantinya.
xixixao

21

Inilah solusi paling sederhana yang bisa saya kumpulkan:

 <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
Ketika saya menggunakan jawaban ini atau yang serupa lainnya, itu membuat item dalam daftar menghilang (sepertinya itu bergulir terlalu jauh, tapi saya tidak yakin). Menggulir sedikit membuat semuanya muncul kembali, dan sepertinya itu meluncur kembali ke tampilan (karena itu teori pengguliran terlalu jauh). Adakah ide yang jelas mengapa hal itu mungkin terjadi?
tscizzle

16

Bagi siapa saja yang menulis komponen fungsi yang dapat menggunakan hook,

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 })}
    />
  );
};

1
sederhana dan elegan. dengan sempurna melakukan apa yang saya inginkan! Terima kasih!
Qamar Stationwala

6

Jika ada orang di tahun 2020 atau lebih baru yang bertanya-tanya bagaimana cara mencapai ini dengan komponen fungsional. Beginilah cara saya melakukannya:

ref={ref => scrollView = ref }
onContentSizeChange={() => scrollView.scrollToEnd({ animated: true })}>

Catatan: Anda dapat menyebutkan bahwa Anda menggunakan useref. kebanyakan ppl tidak tahu bagaimana menggunakan ref dengan kait.
ʎzɐɹƆ

Iya. Masalahnya adalah itu berhasil bahkan ketika tidak menggunakan hook useRef. Tapi cara yang benar adalah dengan menggunakannya! Anda benar
Marlon Englemam

5

coba solusi ini

xcode 10.0

RN: 56.0.0

<ScrollView ref="scrollView"
             onContentSizeChange={(width,height) => this.refs.scrollView.scrollTo({y:height})}>  </ScrollView> // OR height -  width

kenapa ref=? Sepertinya peninggalan pengembang web
YungGun

4

Anda dapat membalikkan tampilan gulir / daftar menggunakan react-native-invertible-scroll-viewmodul , lalu cukup panggil ref.scrollTo(0)untuk menggulir ke bawah.

Pasang modul:

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

Kemudian impor komponen:

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

Kemudian gunakan JSX berikut sebagai ganti ScrollView:

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

Ini berfungsi karena react-native-invertible-scroll-viewmembalik seluruh konten tampilan. Perhatikan bahwa turunan tampilan juga akan dirender dalam urutan yang berlawanan dengan tampilan normal - anak pertama akan muncul di bagian bawah .


3

Sebenarnya jawaban @Aniruddha tidak berhasil untuk saya karena saya menggunakan "react-native": "0.59.8"dan this.scrollView.root.scrollToEndjuga tidak ditentukan

tapi saya menemukan jawabannya dan ini berhasil this.scrollView.scrollResponderScrollToEnd({animated: true});

Jika Anda menggunakan 0.59.8ini akan bekerja ATAU jika Anda menggunakan versi yang lebih baru dan ini bekerja untuk Anda. tolong tambahkan versi dalam jawaban ini agar yang lain tidak mendapat masalah.

Kode Lengkap di sini

<ScrollView
    ref={ref => this.scrollView = ref}
    onContentSizeChange={(contentWidth, contentHeight)=>{        
        this.scrollView.scrollResponderScrollToEnd({animated: true});
    }}>
</ScrollView>

1

Metode ini sedikit hack tetapi saya tidak dapat menemukan cara yang lebih baik

  1. Pertama tambahkan referensi ke ScrollView Anda.
  2. Kedua, tambahkan Tampilan tiruan sebelum menutup tag ScrollView Anda
  3. Dapatkan posisi y dari Tampilan boneka itu
  4. Kapanpun Anda ingin menggulir ke bawah gulir ke posisi Tampilan tiruan

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>
    );
  }


Ini bukan cukup apa yang diminta untuk; ini menggulir ke bawah dengan mengetuk, daripada membiarkan tampilan gulir digulir ke bawah saat konten ditambahkan. Bagaimanapun, scrollToBottommembuat pendekatan seperti ini menjadi usang dalam versi React Native yang lebih baru.
Mark Amery

1

Ini menjawab pertanyaan Anda: https://stackoverflow.com/a/39563100/1917250

Anda harus terus menggulir ke bagian bawah halaman dengan menghitung tinggi konten dikurangi tinggi scrollView-nya.


Ini adalah jawaban yang benar untuk pertanyaan tersebut. Anda bisa pintar tentang kapan harus melakukan scroll, jawabannya di link menunjukkan cara mendapatkan nomor yang Anda butuhkan. Secara khusus, jika Anda menambahkan komponen ke daftar, tinggi konten tidak akan menyertakannya saat componentDidUpdate dipanggil, jadi Anda ingin mengingat bahwa Anda ingin menggulir dan menggulir onContentSizeChange sebagai gantinya.
xixixao

1

Jika Anda menggunakan TypeScript, Anda perlu melakukan ini

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

Saya berjuang dengan ini untuk sementara waktu dan akhirnya menemukan sesuatu yang bekerja dengan sangat baik dan mulus untuk saya. Seperti banyak orang, saya mencoba .scrollToEndtidak berhasil. Solusi yang berhasil bagi saya adalah kombinasi dari onContentSizeChange,onLayout alat peraga dan sedikit lebih memikirkan secara visual tentang "apa arti menggulir ke bawah". Bagian terakhir tampaknya sepele bagi saya sekarang, tetapi saya butuh waktu lama untuk mengetahuinya.

Mengingat bahwa ScrollViewmemiliki tinggi tetap, ketika tinggi konten melebihi tinggi wadah, saya menghitung offset dengan mengurangi prevHeight(tinggi tetap dari ScrollView) untuk currHeight(tinggi konten). Jika Anda dapat membayangkannya secara visual, menggulir ke perbedaan tersebut dalam sumbu y, menggeser tinggi konten di atas penampungnya dengan offset tersebut. Saya menambah offset saya dan membuat tinggi konten saya sekarang tinggi saya sebelumnya dan terus menghitung offset baru.

Ini kodenya. Semoga bisa membantu seseorang.

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

Saya kira sudah terlambat untuk menjawab tetapi saya mendapat satu retasan! Jika Anda menggunakan FlatListAnda dapat mengaktifkan invertedproperti yang jatuh kembali ke pengaturan transform scaleke -1(yang dapat diterima untuk daftar komponen lain seperti ScrollView...). Ketika Anda merender Anda FlatListdengan data, itu akan selalu di bawah!


-1

Coba setel properti contentOffset dari tampilan gulir ke ketinggian konten saat ini.

Untuk mencapai apa yang Anda butuhkan, Anda dapat melakukan ini sebagai bagian dari acara onScroll. Namun hal ini dapat membuat pengalaman pengguna menjadi buruk sehingga mungkin terbukti lebih ramah pengguna untuk hanya melakukan ini saat pesan baru ditambahkan.


-1; nggak. Seperti banyak jawaban di sini, ini memiliki efek menggulir ScrollViewke bawah ke bawah semua kontennya, bukan hanya menggulir ke bawah.
Mark Amery


-3

Agak terlambat ... tapi lihat pertanyaan ini. Gulir ke atas ScrollView

ScrollView memiliki metode "scrollTo".


1
Ya, ada metode scrollTo di ScrollView, tetapi pertanyaannya secara khusus menanyakan tentang menggulir ke bawah, yang tidak langsung.
Southerneer
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.