简体   繁体   English

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

[英]How to only disable scroll in ScrollView but not content view?

Recently I wrote a small SwiftUI library that can rotate an array of Views and select view.最近我写了一个小的 SwiftUI 库,可以旋转视图数组并选择视图。

GIF-1

But found an issue that I can't figure out how to disable scroll in ScrollView while in normal mode.但是发现了一个问题,我无法弄清楚在正常模式下如何在 ScrollView 中禁用滚动。

GIF-2

I tried to put .disabled(true) at the end of the ScrollView unfortunately, it not only disable scroll but also all the views in ScrollView.不幸的是,我试图将.disabled(true)放在ScrollView的末尾,它不仅禁用滚动,而且禁用 ScrollView 中的所有视图。

Here's source code of the project .这是项目源代码

What modifier should I add to solve this?我应该添加什么修饰符来解决这个问题?

--Edited-- --已编辑--

I have tried to change the scroll axis but once it becomes [], scrollview will reset its content offset, wondering if there's a way to block scrolling without changing the axis.我试图改变滚动轴,但是一旦它变成[],滚动视图将重置它的内容偏移量,想知道是否有办法在不改变轴的情况下阻止滚动。

--Solved-- - 解决了 -

At last, I just add a DragGesture() to block scroll event and works fine.最后,我只是添加了一个DragGesture()来阻止滚动事件并且工作正常。

Only pass .horizontal as the scroll axis if you want the view to scroll.如果您希望视图滚动,则仅将.horizontal作为滚动轴传递。 Otherwise, pass the empty set.否则,传递空集。

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 : []
    }
}

I tried using the accepted answer provided by Rob Mayoff but the content offset being reset is a frustrating drawback to the suggested approach.我尝试使用 Rob Mayoff 提供的已接受答案,但重置的内容偏移量是建议方法的一个令人沮丧的缺点。

Using the Introspect library (which I would recommend for anyone looking for UIKit level controls for SwiftUI APIs - whilst we wait for SwiftUI 2 at WWDC 2020) - you can access the underlying isScollEnabled boolean flag of the underlying UIScrollView that powers SwiftUI's ScrollView and set is accordingly.使用Introspect库(我会建议任何寻找 SwiftUI API 的 UIKit 级别控件的人使用它——在 WWDC 2020 等待 SwiftUI 2 的同时)——您可以访问底层 UIScrollView 的底层isScollEnabled布尔标志,该标志为 SwiftUI 的ScrollView和设置是因此。 Changing the isScollEnabled has no impact on contentOffset and works exactly like it should.更改isScollEnabled对 contentOffset 没有影响,并且完全按照应有的方式工作。

It can be implemented as simply as this:它可以像这样简单地实现:

struct TestView: View {
    @Binding var shouldScroll: Bool

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

It really is that simple with the Introspect library!使用Introspect库真的就是这么简单!

I keep coming back to this question and have not been satisfied with any of the answers here.我一直回到这个问题,对这里的任何答案都不满意。 What I found has worked is to create a custom wrapper that evaluates whether a ScrollView should be used or not.我发现有效的是创建一个自定义包装器来评估是否应该使用 ScrollView。 It was surprisingly easier than I thought.这比我想象的要容易得多。

Creating a preventable scroll view创建可预防的滚动视图

Essentially we want a custom view where scrolling can be toggled.本质上,我们想要一个可以切换滚动的自定义视图。 Using a binding, anyone that uses our view will have full control over the scrolling function.使用绑定,任何使用我们视图的人都可以完全控制滚动功能。

Forgive my naming but PreventableScrollView was all I could come up with :)原谅我的命名,但我能想到的就是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()
        }
    }
}

Essentially all we need to do is decide whether to embed the content in a ScrollView or just return it directly.基本上我们需要做的就是决定是将内容嵌入到 ScrollView 中还是直接返回。 Pretty straightforward.很简单。

I created a demo to showcase how this might work with dynamic content and the ability to toggle anytime.我创建了一个演示来展示它如何与动态内容一起使用以及随时切换的能力。 As I add items it also toggles scrolling.当我添加项目时,它也会切换滚动。

Here is the code if you are interested.如果您有兴趣,这是代码。

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()!)
    }
}

I've tried all above and even more.我已经尝试了以上所有方法,甚至更多。 None of solutions works for me.没有一个解决方案对我有用。 I found next one:我找到了下一个:

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

I think you can use我认为你可以使用

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

It disables scroll in ScrollView but other gestures still work like tapGesture inside this scrollview, and ScrollViewReader also works它禁用了 ScrollView 中的滚动,但其他手势仍然可以在这个滚动视图中像 tapGesture 一样工作,并且 ScrollViewReader 也可以工作

Xcode 14 / iOS 16 Xcode 14 / iOS 16

Now we have explicit environment value for this.现在我们有明确的环境值。 So a possible solution would be所以一个可能的解决方案是

struct DemoView: View {
    @Binding var shouldScroll: Bool

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

That's OK.没关系。 can try可以尝试

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM