0

I have a struct containing a List and within that list some other struct Views. Some of those structs contain horizontal scrollViews. When I assign to the List .refreshable that modifier applies to all scrollViews within that list. So all horizontal scrollViews have vertical refreshable properties which is not what I want. Any ideas on how to approach this? Thank you

snksnk
  • 1,566
  • 4
  • 21
  • 46
  • You could try to nest the scrollViews in groups or get the scrollViews in separated structs.?! – FrugalResolution Sep 27 '22 at 17:40
  • @Felix thank you for your reply. They are in different structs – snksnk Sep 27 '22 at 17:41
  • The secret behind SwiftUI is that a struct with all it's content gets rerendered when a value like a @State object changes. So When you refresh one scrollView of the main struct and the data is based in the main struct it will render all subviews. – FrugalResolution Sep 27 '22 at 17:43
  • @Felix I understand that but I don't get it why a scrollView in a separate struct gets the main List's modifier properties. Very weird issue and no idea how to solve it. No articles online either.. – snksnk Sep 27 '22 at 17:52
  • @flashspys I ended up using introspect scrollView in my nested views with isDirectionalLockEnabled. But since I asked there was another answer here: https://stackoverflow.com/questions/74042635/nested-scrollview-in-a-list-refreshable-strange-behaviour-in-ios-16 – snksnk Oct 21 '22 at 12:14

1 Answers1

0

The refreshable modifier puts a refreshable action in the environment. If it is cleared out, it should prevent the child scroll views from picking it up. Here's a pure SwiftUI way of doing it. This is wrapped up into a convenient modifier but all that is necessary is to clear out the refresh key from the environment. .environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)

public struct RefreshableBlocker: ViewModifier {
    public func body(content: Content) -> some View {
        if let refreshKeyPath = \EnvironmentValues.refresh as? WritableKeyPath<EnvironmentValues, RefreshAction?> {
            content.environment(refreshKeyPath, nil)
        } else {
            content
        }
    }
}

public extension View {
    func blockPullToRefresh() -> some View {
        self.modifier(RefreshableBlocker())
    }
}

Just put this somewhere above the child scrollviews in the view hierarchy. You should be able switch List for ScrollView in the example below.

ScrollView {
    LazyVStack(spacing: 12) {
        ForEach(items, id: \.self) { item in
            ItemCell(data: item).blockPullToRefresh()
        }
    }
}
Sam Corder
  • 5,374
  • 3
  • 25
  • 30