如何在SwiftUI中禁用ScrollView的弹跳效果

37

swiftUI 中有什么 Modifier 可以停止 ScrollView 的反弹效果吗?

struct RoomDetailsView: View {

    var body: some View {
        ScrollView(showsIndicators: false) {
            Image("test")
            Text("Hello Text")
            ...
            ...
        }
    }
}

我尝试了下面的代码,但它对我不起作用。看起来它已经过时了。

ScrollView(alwaysBounceVertical: true) {
       Image("test")
       Text("Hello Text")
       ...
       ...
}

嗯...在所描述的情况下,我没有观察到任何反弹。那么你是想“停止”还是“添加”,因为在第二个代码块中,似乎你试图启用它,但在问题中要求禁用它。 - Asperi
我想停止ScrollView中的弹跳。之前第二个代码块是有效的。但现在我还没有找到正确的解决方案,我的当前代码看起来像第一个代码块。 - Rohit Makwana
2
你找到任何解决方案了吗? - Sandeep Maurya
无法找到解决方案。 - Rohit Makwana
2
对于ScrollView内部的静态内容,可以使用SwiftUI:仅在超出屏幕高度时使ScrollView可滚动中提供的解决方案。但是对于活动内容,这种方法不适用,因为它基于.disabled修饰符。 - Asperi
7个回答

36

尝试使用这行代码:

UIScrollView.appearance().bounces = false

你可以像这样使用它:

struct RoomDetailsView: View {
   init() {
      UIScrollView.appearance().bounces = false
   }

   var body: some View {
      ScrollView(showsIndicators: false) {
         Image("test")
         Text("Hello Text")
         ...
         ...
          }
      }
  }

或者你可以在AppDelegate中编写此行代码,以便将此行为应用于整个应用程序。

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    UIScrollView.appearance().bounces = false
 }

13
第一个选项不会改变我应用程序中的所有滚动视图,对吗? - Di Nerd Apps
1
是的,@DiNerd 第一个最终将会改变整个应用程序的外观,这是实际的声明,下面的例子只是它的实现示例。 您可以使用以下两种方式之一来实现它。 - Anshuman Singh
3
如果你只想在内容小于滚动视图大小时禁用弹跳效果,可以这样做:UIScrollView.appearance().alwaysBounceVertical = false - Tamás Sengel
警告:这会导致我们的UIPageViewControllers出现错误。显然,它们需要弹跳=true才能正常工作。确保将其设置回true以避免破坏该类。 - cbh2000

13
  1. 将 ScrollView 的 showsIndicators 参数设置为 false,这样与您的视图互动的用户就不会激活滚动指示器(即使没有启用滚动也可能发生)。
ScrollView(showsIndicators: false)

在ScrollView的onAppear修饰符中,添加此行代码。
UIScrollView.appearance().bounces = false

在ScrollView的onDisappear修饰符中,添加这行代码。
UIScrollView.appearance().bounces = true

如果在初始化中将UIScrollView.appearance().bounces设置为false,则可以防止应用程序中所有ScrollView反弹。通过在onAppear修饰符中将其设置为true,并在onDisappear修饰符中将其设置为false,可以确保它仅影响一个ScrollView。

您的ScrollView应该类似于以下内容。

ScrollView(showsIndicators: false) {
    ...
    ...
    ...
}
.onAppear {
    UIScrollView.appearance().bounces = false
}
.onDisappear {
    UIScrollView.appearance().bounces = true
}

9

9
快到了...现在我们只需要一种无条件禁用它的方法。 - cbh2000
不错的回答!SwiftUI越来越好了。 - Trev14
有时候真的不太理解 SwiftUI 团队的想法。 - undefined

6

您可以使用SwiftUI-Introspect库:

ScrollView {
    // some content
}
.introspectScrollView { scrollView in
    scrollView.alwaysBounceVertical = false
}

刚试了一下,已经添加了库。类型“ScrollView<some View>”的值没有成员“introspectScrollView”。 - chitgoks
你添加了导入吗?"导入 Introspect" - Vít Zadina
对我不起作用 :( - Jake
将"scrollView.alwaysBounceVertical = false"替换为"scrollView.bounces = false"。 - Deepansh Jagga

1
更好的解决方案是使用ViewBuilder创建自己的ScrollView,当内容大小小于ScrollView框架大小时,不会产生反弹效果。
import SwiftUI

struct BounceVerticalDisableScrollView<Content: View>: View {
    
    @State private var alwaysBounceVertical = false
    let content: Content
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    var body: some View {
        GeometryReader { scrlViewGeometry in
            ScrollView {
                content
                    .background(
                        GeometryReader {
                            // calculate height by clear background
                            Color.clear.preference(key: SizePreferenceKey.self,
                                                   value: $0.frame(in: .local).size.height)
                        }.onPreferenceChange(SizePreferenceKey.self) {
                            self.alwaysBounceVertical = $0 < scrlViewGeometry.size.height
                        }
                    )
            }
            // disable scroll when content size is less than frame of scrollview
            .disabled(self.alwaysBounceVertical)
        }
    }
}
// return size
public struct SizePreferenceKey: PreferenceKey {
    public static var defaultValue: CGFloat = .zero
    
    public static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value += nextValue()
    }
}

所以,您可以将scrollview用作:

var body: some View {
        VStack (alignment: .leading, spacing: 10) {
            BounceVerticalDisableScrollView{
                VStack (alignment: .leading, spacing: 10)  {
                   ....
                   .... 
                }.padding()
            }
            
        }
    }  
 

看起来是相同的行为。它仍然反弹了。 - chitgoks
我认为你可能只想在大小偏好减少函数中返回 nextValue(),对吧?如果你将它添加到现有值中,那么它会随着每次布局更改而变得越来越大,而不是简单地反映最近的内容高度。我假设这也是 @chitgoks 遇到问题的原因。 - Trev14

0
老实说,现有的选项并不是很好,因为它们不向后兼容,并且往往会完全禁用滚动功能。
一个解决办法是给你的滚动视图添加高度。

0
我创建了一个小的ViewModifier,以便仅在特定的视图中禁用滚动反弹。
import SwiftUI

extension View {
  func disableBounces() -> some View {
    modifier(DisableBouncesModifier())
  }
}

struct DisableBouncesModifier: ViewModifier {
  func body(content: Content) -> some View {
    content
      .onAppear {
        UIScrollView.appearance().bounces = false
      }
      .onDisappear {
        UIScrollView.appearance().bounces = true
      }
  }
}

然后你可以像任何普通的viewModifier一样,在你的视图上简单地调用它。
// Your view definition... 
.disableBounces()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接