farhandika zahrir
farhandika zahrir

Reputation: 59

ScrollView Keep offset when prepending data on top

i wanted to migrate my simple chat app to explore swiftUI, Previously, I'm using collectionView for my chat view.

I uses one of messageKit's code to achieve reload data while keeping the offset of the cell

public func reloadDataAndKeepOffset() {
        // stop scrolling
        setContentOffset(contentOffset, animated: false)
        
        // calculate the offset and reloadData
        let beforeContentSize = contentSize
        reloadData()
        layoutIfNeeded()
        let afterContentSize = contentSize
        
        // reset the contentOffset after data is updated
        let newOffset = CGPoint(
            x: contentOffset.x + (afterContentSize.width - beforeContentSize.width),
            y: contentOffset.y + (afterContentSize.height - beforeContentSize.height))
        setContentOffset(newOffset, animated: false)
    }

wanted to achieve similar result using swiftui. how do I achieve it?

I'm using scrollview and lazyVStack

here's an example of my current code

struct SimpleStruct {
    let id: UUID
    let num: Int
}

struct ChatViewV2: View {
    
    @State var selectedAudio = ""
    @State var many: [SimpleStruct] = (80...100).map { num in
        .init(id: .init(), num: num)
    }
    @State var dataId: UUID?
    
    var body: some View {
        GeometryReader { geo in
            ScrollViewReader { proxy in
                ScrollView {
                    LazyVStack(alignment: .leading) {
                        AudioChatBubbleComponent(
                            selectedAudio: $selectedAudio,
                            maxWidth: geo.size.width)
                        ImageChatBubbleComponent(maxWidth: geo.size.width)
                        ForEach(Array(many.enumerated()), id: \.element.id) { (index, element) in
                            TextChatBubbleComponent(msg: "from \(element.num)")
                                .id(element.id)
                                .onAppear(perform: {
                                    if index == 3 {
                                        Task {
                                            self.many = (80...100).map { num in
                                                    .init(id: .init(), num: num)
                                                } + many
                                        }
                                    }
                                })
                        }
                    }
                    .scrollTargetLayout()
                .padding()
                }
            }
            .defaultScrollAnchor(.bottom)
        }
        .scrollPosition(id: $dataId)
    }
}

currently, the scroll behavior is not what I expected, it didn't keep the scroll offset that solution was made by someone else and uses scrollPosition, didn't work out

Thank you

Upvotes: 0

Views: 155

Answers (0)

Related Questions