Save ScrollViews position and scroll back to it later (offset to position)
You don't need actually offset in this scenario, just store id of currently visible view (you can use any appropriate algorithm for your data of how to detect it) and then scroll to view with that id.
Here is a simplified demo of possible approach. Tested with Xcode 12.1/iOS 14.1
struct TestScrollBackView: View {
@State private var stored: Int = 0
@State private var current: [Int] = []
var body: some View {
ScrollViewReader { proxy in
VStack {
HStack {
Button("Store") {
// hard code is just for demo !!!
stored = current.sorted()[1] // 1st is out of screen by LazyVStack
print("!! stored \(stored)")
}
Button("Restore") {
proxy.scrollTo(stored, anchor: .top)
print("[x] restored \(stored)")
}
}
Divider()
ScrollView {
LazyVStack {
ForEach(0..<1000, id: \.self) { obj in
Text("Item: \(obj)")
.onAppear {
print(">> added \(obj)")
current.append(obj)
}
.onDisappear {
current.removeAll { $0 == obj }
print("<< removed \(obj)")
}.id(obj)
}
}
}
}
}
}
}
Get the current scroll position of a SwiftUI ScrollView
It was possible to read it and before. Here is a solution based on view preferences.
struct DemoScrollViewOffsetView: View {
@State private var offset = CGFloat.zero
var body: some View {
ScrollView {
VStack {
ForEach(0..<100) { i in
Text("Item \(i)").padding()
}
}.background(GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self,
value: -$0.frame(in: .named("scroll")).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self) { print("offset >> \($0)") }
}.coordinateSpace(name: "scroll")
}
}
struct ViewOffsetKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue = CGFloat.zero
static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}
Best Way: Save & Restore TextView Position in ScrollView
I am so proud to say, I got a perfect solution to this now.
Sh.... (sorry I am too excited about it. If you find any mistakes/bugs/weakness on it, please DO give me your valuable suggestions and please feel free to correct me. :-)
Cut the crap. Here you go !!!
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
final ScrollView scrollView = (ScrollView) findViewById(R.id.Trial_C_ScrollViewContainer);
final TextView textView = (TextView) scrollView.getChildAt(0);
final int firstVisableLineOffset = textView.getLayout().getLineForVertical(scrollView.getScrollY());
final int firstVisableCharacterOffset = textView.getLayout().getLineStart(firstVisableLineOffset);
outState.putInt(ScrollViewContainerTextViewFirstVisibleCharacterOffset, firstVisableCharacterOffset);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
final int firstVisableCharacterOffset = savedInstanceState.getInt(ScrollViewContainerTextViewFirstVisibleCharacterOffset);
final ScrollView scrollView = (ScrollView) findViewById(R.id.Trial_C_ScrollViewContainer);
scrollView.post(new Runnable() {
public void run() {
final TextView textView = (TextView) scrollView.getChildAt(0);
final int firstVisableLineOffset = textView.getLayout().getLineForOffset(firstVisableCharacterOffset);
final int pixelOffset = textView.getLayout().getLineTop(firstVisableLineOffset);
scrollView.scrollTo(0, pixelOffset);
}
});
}
That's it. :-)
If it helps you, please clap your hands. <-- this is important!!
And if you wish to, click that little upright triangle. (make sure you have clapped your hands first!)
How to save RecyclerView's scroll position using RecyclerView.State?
How do you plan to save last saved position with RecyclerView.State
?
You can always rely on ol' good save state. Extend RecyclerView
and override onSaveInstanceState() and onRestoreInstanceState()
:
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
LayoutManager layoutManager = getLayoutManager();
if(layoutManager != null && layoutManager instanceof LinearLayoutManager){
mScrollPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
}
SavedState newState = new SavedState(superState);
newState.mScrollPosition = mScrollPosition;
return newState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
if(state != null && state instanceof SavedState){
mScrollPosition = ((SavedState) state).mScrollPosition;
LayoutManager layoutManager = getLayoutManager();
if(layoutManager != null){
int count = layoutManager.getItemCount();
if(mScrollPosition != RecyclerView.NO_POSITION && mScrollPosition < count){
layoutManager.scrollToPosition(mScrollPosition);
}
}
}
}
static class SavedState extends android.view.View.BaseSavedState {
public int mScrollPosition;
SavedState(Parcel in) {
super(in);
mScrollPosition = in.readInt();
}
SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mScrollPosition);
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
ScrollView doesn't keep track of current position. When tapped it resets back to previous location
Its may be the problem with props in the Model which is related to scroll.
as you use ScrollView
inside the Model
, assuming that , you need a dismissable scrollview
Please checkout a sample related to that : https://gist.github.com/mmitchellgarcia/6ee4d5b5c79162e3cc13b67d743777e6#file-dismissablescrollview-js
For more : https://github.com/react-native-community/react-native-modal/issues/109
Hope it helps
How to keep scroll position after state update in React Native ScrollView
create a ref variable
const ScrollViewRef = useRef();
then in ScrollView write like this
<ScrollView
ref={ScrollViewRef}
// onLayout will make sure it scroll to Bottom initially
onLayout={() => ScrollViewRef.current.scrollToEnd()}
// We don't need `onContentSizeChanged`
// this onScroll fetches data when scroll reaches top
// then it scrolls to last position as you asked
onScroll={({ nativeEvent }) => {
if (ifCloseToTop(nativeEvent)) {
handleLoadMore()
ScrollViewRef.current.scrollTo({
y: nativeEvent.layoutMeasurement.height,
x: 0,
animated: false,
});
}
}}
scrollEventThrottle={400}>
<View style={styles.head}>
// messages
</View>
</ScrollView>
Don't forget to import useRef
at the top
import {useRef} from "react-native";
Working Example here
Related Topics
Can't Update Google Admob in iOS Project
Converting Nsdictionary to Xml
Programmatically Change Splash Screen in iOS
How to Make Collage of Images According to Different Shapes
How to Switch Programmatically to Dark Mode Swift
No Such Module Iqkeyboardmanagerswift
Can't Use @Observedobject on Real iPhone
Applying Different Attributes for Different Portions of an Nsattributedstring
How to Configure Threshold/Distance When Swiping on Uitableviewcell
What Exactly Does ': Class' Do in a Protocol Declaration
iOS Where to Put Custom Cell Design? Awakefromnib or Cellforrowatindexpath
Force UIsplitviewcontroller to Always Show Master (Only) in Landscape (On iPhone 6 Plus)
Rgba to Abgr: Inline Arm Neon Asm for iOS/Xcode
How to Loop Through and Get All The Keys of The Nested Nodes in Firebase
Uitableview Is Jumping When I Insert New Rows
How to Go to Specific Native View Controller from React-Native Code
Paste Formatted Text, Not Images or HTML
Cannot Subscript a Value of [Anyobject]? with an Index of Type Int