2

Back again for some SwiftUI issues. haha

So I have a scroll view, and I simulate a user switching elements in the scrollview pretty fast. (ex. Music Lyrics that adjust to where the user is listening).

My issue here is the animation are not following the speed and I was wondering if there was a way to prevent that. Cancel the previous animation for example? I haven't found anything atm to fix this problem.

The following code is used to reproduce the Animation issue.

import SwiftUI

struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()

    var body: some View {
        ScrollView(showsIndicators: false, content: {
            ScrollViewReader(content: { scrollViewProxy in
                LazyVStack(content: {
                    ForEach(viewModel.fragments, id: \.id) { fragment in
                        Text(fragment.content + fragment.id)
                            .background(fragment == viewModel.currentFragment ? Color.yellow : Color.gray)
                            .frame(height: 100)
                            .font(.largeTitle)
                            .id(fragment.id)
                    }
                })
                .id("ContentView-LazyVStack-Animation")
                .onReceive(viewModel.$currentFragment, perform: { currentFragment in
                    guard let currentFragment = currentFragment else {
                        return
                    }
                    withAnimation(.easeInOut(duration: 2)) {
                        scrollViewProxy.scrollTo(currentFragment.id, anchor: .center)
                    }
                })
            })
        })
        .id("ContentView-ScrollView-Animation")
    }
}

final class ViewModel: ObservableObject {
    @Published var fragments: [Fragment] = [Int](0..<100).map({ Fragment(id: "\($0)", content: "Some text yeah! super cool.") })
    @Published var currentFragment: Fragment?

    private var scrollingTimer: Timer?

    init() {
        currentFragment = fragments.first

        setupRandomScroll()
    }

    func setupRandomScroll() {
        scrollingTimer = Timer.scheduledTimer(withTimeInterval: 0.2,
                                              repeats: true,
                                              block: { [weak self] _ in
                                                guard let self = self else {
                                                    return
                                                }
                                                let newIndex = Int.random(in: 70..<100)
                                                self.currentFragment = self.fragments[newIndex]
                                              })
    }
}

final class Fragment: ObservableObject, Equatable, Hashable {
    var id: String
    @Published var content: String

    init(id: String, content: String) {
        self.id = id
        self.content = content
    }

    static func == (lhs: Fragment, rhs: Fragment) -> Bool {
        return lhs.id == rhs.id
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

Thanks in advance for any help given! :)

Oleg G.
  • 550
  • 5
  • 25
  • 1
    It appears that the animation of programmatic scrolling using the `ScrollViewProxy` is still not customizable. See https://stackoverflow.com/questions/62535553/swiftui-customize-animation-for-scrollto-using-scrollviewreader – Kai Zheng Jan 28 '21 at 06:50
  • I see, i will add a timer that matches the animation to block the scoll until the timer is done. Thank you!!! – Oleg G. Jan 28 '21 at 18:19

0 Answers0