【问题标题】:How to only disable scroll in ScrollView but not content view?如何仅禁用 ScrollView 中的滚动而不禁用内容视图?
【发布时间】:2019-09-14 08:02:03
【问题描述】:

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

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

不幸的是,我尝试将.disabled(true) 放在ScrollView 的末尾,它不仅禁用滚动,还禁用ScrollView 中的所有视图。

这是project 中的source code

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

--已编辑--

我试图改变滚动轴但是一旦它变成[],滚动视图将重置它的内容偏移量,想知道是否有办法在不改变轴的情况下阻止滚动。

--已解决--

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

【问题讨论】:

    标签: ios swift swiftui


    【解决方案1】:

    如果您希望视图滚动,则仅将 .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 : []
        }
    }
    

    【讨论】:

    • 抱歉没提这个,我之前试过这个,但是一旦轴设置为[],滚动视图将重置它的内容偏移量并导致我的视​​图出现一些问题。不过,非常感谢。
    • 嘿,有什么进展/解决方案吗?
    • @raxabizze 您只需要在使用水平轴时设置框架的高度,或者在使用垂直轴时设置框架的宽度。我遇到了同样的问题,并且设置了框架的高度:)
    • 这不起作用...即使设置为 [ ] 仍然滚动
    • 对于下一个偶然发现这篇文章的人。这在 iOS14 中运行良好。在 iOS13 中,你会得到有趣的偏移量。
    【解决方案2】:

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

    使用Introspect 库(我建议任何寻找 SwiftUI API 的 UIKit 级别控件的人使用它——在 WWDC 2020 等待 SwiftUI 2 的同时)——您可以访问底层 UIScrollView 的底层 isScollEnabled 布尔标志它为 SwiftUI 的 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
                }
            }
        }
    }
    

    Introspect 库真的就是这么简单!

    【讨论】:

    • 这太棒了
    【解决方案3】:

    我不断回到这个问题,对这里的任何答案都不满意。我发现有效的是创建一个自定义包装器来评估是否应该使用 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()
            }
        }
    }
    

    基本上我们需要做的就是决定是将内容嵌入到 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()!)
        }
    }
    

    【讨论】:

    • 效果很好,但布局仍然有点困难,需要在其中使用 fixedSize 标志,并在 PreventableScrollView [后半部分] 中对我的内容设置 .offset
    【解决方案4】:

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

    ScrollView(.horizontal) {
        YourContentView
                  .introspectScrollView(customize: { scrollView in
                       scrollView.addGestureRecognizer(UIPanGestureRecognizer()) // disable scrollView scroll      
                   })
    }
    

    【讨论】:

      【解决方案5】:

      我认为你可以使用

      .simultaneousGesture(DragGesture(minimumDistance: 0), including: .all)
      

      它在 ScrollView 中禁用滚动,但其他手势仍然可以在此滚动视图中像 tapGesture 一样工作,并且 ScrollViewReader 也可以工作

      【讨论】:

        【解决方案6】:

        没关系。可以试试

        ScrollView {
        }
        .allowsTightening(true)
        .disabled(true)
        

        【讨论】:

        • 请在您的回答中提供更多详细信息。正如目前所写的那样,很难理解您的解决方案。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-23
        • 2015-12-20
        • 1970-01-01
        • 2012-10-26
        • 2015-05-22
        相关资源
        最近更新 更多