ios – Uneven scrolling with ScrollView when updating View whereas scrolling SwiftUI

[ad_1]

I’m utilizing my very own ScrollView which has scroll begin and finish callbacks, in order that I can carry out some actions based mostly on them, like hiding/exhibiting a banner.

This is my code for ScrollView

struct TrackableScrollView<Content material>: View the place Content material: View {
    personal let onScrollingStarted: () -> Void
    personal let onScrollingFinished: () -> Void
    @State var scrollViewHelper = ScrollViewHelper()
    let content material: Content material

    public init(@ViewBuilder content material: () -> Content material,
                onScrollingStarted: @escaping () -> Void = {},
                onScrollingFinished: @escaping () -> Void = {}) {
        self.content material = content material()
        self.onScrollingStarted = onScrollingStarted
        self.onScrollingFinished = onScrollingFinished
    }

    public var physique: some View {
        GeometryReader { outsideProxy in
            ScrollView(.vertical, showsIndicators: true) {
                ZStack(alignment: .high) {
                    GeometryReader { insideProxy in
                        Colour.clear
                            .desire(key: ScrollOffsetPreferenceKey.self, worth: [self.calculateContentOffset(fromOutsideProxy: outsideProxy, insideProxy: insideProxy)])
                    }
                    self.content material
                }
            }
            .onPreferenceChange(ScrollOffsetPreferenceKey.self) { worth in
                scrollViewHelper.currentOffset = worth[0]
            }
            .simultaneousGesture(
                DragGesture().onChanged { _ in
                    onScrollingStarted()
                }
            )
            .onReceive(scrollViewHelper.$offsetAtScrollEnd) { _ in
                onScrollingFinished()
            }
        }
    }

    personal func calculateContentOffset(fromOutsideProxy outsideProxy: GeometryProxy, insideProxy: GeometryProxy) -> CGFloat {
        return outsideProxy.body(in: .international).minY - insideProxy.body(in: .international).minY
    }
}

personal struct ScrollOffsetPreferenceKey: PreferenceKey {
    typealias Worth = [CGFloat]
    static var defaultValue: [CGFloat] = [0]

    static func scale back(worth: inout [CGFloat], nextValue: () -> [CGFloat]) {
        worth.append(contentsOf: nextValue())
    }
}

class ScrollViewHelper: ObservableObject {
    @Revealed var currentOffset: CGFloat = 0
    @Revealed var offsetAtScrollEnd: CGFloat = 0

    personal var cancellable: AnyCancellable?

    init() {
        cancellable = AnyCancellable($currentOffset
            .debounce(for: 0.3, scheduler: DispatchQueue.major)
            .dropFirst()
            .assign(to: .offsetAtScrollEnd, on: self))
    }
}

And here is my code to point out some content material

struct ContentView: View {
    @State var messageBannerVisisbility: Bool = false

    var physique: some View {
        VStack {
            TrackableScrollView {
                VStack(alignment: .middle, spacing: 0) {
                    ForEach(0...100, id: .self) { i in
                        Rectangle()
                            .body(width: 200, top: 100)
                            .foregroundColor(.inexperienced)
                            .overlay(Textual content("(i)"))
                            .padding()
                    }
                }
            } onScrollingStarted: {
                hideMessageBanner()
            } onScrollingFinished: {
                showMessageBanner()
            }

            if messageBannerVisisbility {
                Rectangle()
                    .body(top: 100)
                    .foregroundColor(.purple)
                    .overlay(Textual content("Random backside view"))
                    .transition(.transfer(edge: .backside).mixed(with: .opacity))
            }
        }
        .navigationBarHidden(true)
        .onAppear {
            showMessageBanner()
        }
    }
}

I’m toggling messageBannerVisisbility with animation inside showMessageBanner() operate.

If I maintain beneath code, scrolling is just not clean. May it’s as a result of I’m exhibiting/hiding the banner with animation on scroll callbacks? I assume I can simply replace the banner, as an alternative of the entire View, however I’m not certain how can I obtain that!

if messageBannerVisisbility {
                    Rectangle()
                        .body(top: 100)
                        .foregroundColor(.purple)
                        .overlay(Textual content("Random backside view"))
                        .transition(.transfer(edge: .backside).mixed(with: .opacity))
                }

What might I do to enhance scrolling expertise? My app does help iOS 13, however I’m additionally positive with implementing 2 totally different options, one for iOS 13 and different one for iOS 14 and above, if that makes life a bit simpler!

[ad_2]

Leave a Reply