如何仅禁用 ScrollView 中的滚动而不是内容视图?

rax*_*zze 8 ios swift swiftui

最近我写了一个小的 SwiftUI 库,可以旋转一组视图并选择视图。

Gif-1

但是发现了一个问题,我无法弄清楚如何在正常模式下禁用 ScrollView 中的滚动。

Gif-2

不幸的是,我试图放在.disabled(true)最后ScrollView,它不仅禁用滚动,而且禁用 ScrollView 中的所有视图。

这里的源代码中的项目

我应该添加什么修饰符来解决这个问题?

--已编辑--

我曾尝试更改滚动轴,但是一旦它变为 [],scrollview 将重置其内容偏移量,想知道是否有办法阻止滚动而不更改轴。

- 解决了 -

最后,我只是添加了一个DragGesture()来阻止滚动事件并且工作正常。

rob*_*off 21

.horizontal如果您希望视图滚动,则仅作为滚动轴传递。否则,传递空集。

struct TestView: View {
    @Binding var shouldScroll: Bool

    var body: some View {
        ScrollView(axes, showsIndicators: false) {
            Text("Your content here")
        }
    }

    private var axes: Axis.Set {
        return shouldScroll ? .horizontal : []
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 抱歉没有提及这一点,我之前尝试过这一点,但是一旦 axis 设置为 [],scrollview 将重置其内容偏移量并导致我的视​​图出现一些问题。尽管如此,还是非常感谢。 (5认同)
  • 这不起作用...即使设置为 [ ],它仍然会滚动 (4认同)
  • 对于下一个偶然发现这篇文章的人。这在 iOS14 中运行良好。在 iOS13 中,你最终会得到有趣的偏移。 (3认同)

Dan*_*sko 17

我不断回到这个问题,并对这里的任何答案都不满意。我发现有效的是创建一个自定义包装器来评估是否应该使用 ScrollView。这比我想象的要容易得多。

创建可预防的滚动视图

本质上,我们想要一个可以切换滚动的自定义视图。使用绑定,任何使用我们视图的人都可以完全控制滚动功能。

请原谅我的命名,但这PreventableScrollView就是我能想到的:)

struct PreventableScrollView<Content>: View where Content: View {
    @Binding var canScroll: Bool
    var content: () -> Content
    
    var body: some View {
        if canScroll {
            ScrollView(.vertical, showsIndicators: false, content: content)
        } else {
            content()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

本质上我们需要做的就是决定是将内容嵌入到 ScrollView 中还是直接返回它。非常简单。

我创建了一个演示来展示它如何与动态内容一起工作以及随时切换的能力。当我添加项目时,它也会切换滚动。

如果您有兴趣,这是代码。

struct DemoView: View {
    @State private var canScroll: Bool = false
    let allColors: [UIColor] = [.purple, .systemPink, .systemGreen, .systemBlue, .black, .cyan, .magenta, .orange, .systemYellow, .systemIndigo, .systemRed]
    @State var colors: [UIColor] = [.white]
    
    var body: some View {
        VStack {
            Spacer()
            VStack {
                PreventableScrollView(canScroll: $canScroll) {
                    ForEach(colors.indices, id: \.self) { index in
                        Rectangle().fill().foregroundColor(Color(colors[index]))
                            .frame(height: 44)
                            .cornerRadius(8)
                    }
                }.fixedSize(horizontal: false, vertical: true)
                HStack {
                    Spacer()
                    Toggle(isOn: $canScroll, label: {
                        Text("Toggle Scroll")
                    }).toggleStyle(SwitchToggleStyle(tint: .orange))
                }.padding(.top)
            }
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .padding()
            .background(Rectangle().fill().foregroundColor(Color(UIColor.secondarySystemFill)).cornerRadius(8))
            HStack {
                Button(action: {
                    addField()
                }, label: {
                    Image(systemName: "plus")
                }).padding()
            }
        }
            .padding()
    }
    
    func addField() {
        canScroll.toggle()
        colors.append(allColors.randomElement()!)
    }
}
Run Code Online (Sandbox Code Playgroud)


Mic*_*bro 11

我想你可以使用

.simultaneousGesture(DragGesture(minimumDistance: 0), including: .all)
Run Code Online (Sandbox Code Playgroud)

它禁用 ScrollView 中的滚动,但其他手势仍然可以工作,例如此滚动视图中的 tapGesture,并且 ScrollViewReader 也可以工作


Asp*_*eri 10

Xcode 14 / iOS 16

现在我们对此有了明确的环境价值。所以一个可能的解决方案是

struct DemoView: View {
    @Binding var shouldScroll: Bool

    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            // ... other code
        }
        .environment(\.isScrollEnabled, shouldScroll)   // << here !!
    }
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*lay 8

我尝试使用 Rob Mayoff 提供的公认答案,但重置的内容偏移量是建议方法的一个令人沮丧的缺点。

使用Introspect库(我建议任何寻找 SwiftUI API 的 UIKit 级别控件的人使用该库 - 当我们在 WWDC 2020 等待 SwiftUI 2 时) - 您可以访问isScollEnabled支持 SwiftUI 的底层 UIScrollView的底层布尔标志,ScrollView并相应地设置。更改isScollEnabled对 contentOffset 没有影响,并且完全按照应有的方式工作。

它可以像这样简单地实现:

struct TestView: View {
    @Binding var shouldScroll: Bool

    var body: some View {
        ScrollView {
            Text("Your content here").introspectScrollView { scrollView in
                scrollView.isScrollEnabled = shouldScroll
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Introspect库真的就是这么简单!


小智 7

我已经尝试过以上所有方法,甚至更多。没有一个解决方案对我有用。我找到了下一个:

ScrollView(.horizontal) {
    YourContentView
              .introspectScrollView(customize: { scrollView in
                   scrollView.addGestureRecognizer(UIPanGestureRecognizer()) // disable scrollView scroll      
               })
}
Run Code Online (Sandbox Code Playgroud)


Pet*_*inz 6

对于 iOS 16 及更高版本,这非常简单:

ScrollView {
...
}
.scrollDisabled(true)
Run Code Online (Sandbox Code Playgroud)

但对于 iOS 版本较少,它需要一个视图修饰符:

struct DisableScrollingModifier: ViewModifier {
    var disabled: Bool
    
    func body(content: Content) -> some View {
    
        if disabled {
            content
                .simultaneousGesture(DragGesture(minimumDistance: 0))
        } else {
            content
        }
        
    }
}

extension View {
    func scrollingDisabled(_ disabled: Bool) -> some View {
        modifier(DisableScrollingModifier(disabled: disabled))
    }
}
Run Code Online (Sandbox Code Playgroud)

使用示例:

ScrollView {
    ...
}
.scrollingDisabled(true)
Run Code Online (Sandbox Code Playgroud)

  • 它禁用滚动,也禁用点击手势。我想要启用点击手势 (2认同)