SwiftUI 中 DragGesture 和 ScrollView 的交互

Posted

技术标签:

【中文标题】SwiftUI 中 DragGesture 和 ScrollView 的交互【英文标题】:Interaction of DragGesture and ScrollView in SwiftUI 【发布时间】:2020-02-19 16:41:42 【问题描述】:

在我正在开发的应用程序中,有一部分主要是“前进”导航——点击按钮将显示下一张幻灯片。然而,辅助“向后”导航也是必要的。这是我使用的方法:

import SwiftUI

struct Sample: View 
    @State private var dragOffset: CGFloat = -100
    var body: some View 
        VStack 

            Text("Perhaps a title")

            ScrollView 
                VStack 
                    Text("Some scrollable content is going to be here")

                    // ...

                    Button(action: 
                        // Go to the next slide
                    )  Text("Next") 
                
            

            Text("and, maybe, something else")
        
        .overlay(
            Image(systemName: "arrow.left").offset(x: dragOffset / 2),
            alignment: .leading
        )
        .gesture(
            DragGesture()
                .onChanged
                    self.dragOffset = $0.translation.width
                
                .onEnded 
                    self.dragOffset = -100 // Hide the arrow

                    if $0.translation.width > 100 
                        // Go to the previous slide
                    
                
        )
    

有一个小指示器(左箭头),最初是隐藏的(dragOffset = -100)。当拖动手势开始时,偏移量被输入到 dragOffset 状态变量中,并且有效地显示箭头。当拖动手势结束时,箭头再次隐藏,如果达到一定的偏移量,则显示上一张幻灯片。

工作得很好,除了当用户滚动 ScrollView 中的内容时,这个手势也会被触发并更新一段时间,但我认为,它会被 ScrollView 取消,并且不会调用“onEnded”。因此,箭头指示符会停留在屏幕上。

因此问题是:做这样的手势的正确方法是什么,可以与 ScrollView 一起使用? SwiftUI 的当前状态是否有可能?

【问题讨论】:

这将是同样的问题,只是更一般的解决方案。 ***.com/questions/64424719/… 谢谢!坦率地说,很久以前,我花了一段时间才再次了解情况:)(这个冗长的玩笑是由于 *** 不接受简单的“谢谢!”) 【参考方案1】:

对于此类临时状态,最好使用GestureState,因为它会在手势取消/完成后自动重置为初始状态。

所以这是可能的方法

演示:

代码:

struct Sample: View 
    @GestureState private var dragOffset: CGFloat = -100
    var body: some View 
        VStack 

            Text("Perhaps a title")

            ScrollView 
                VStack 
                    Text("Some scrollable content is going to be here")

                    // ...

                    Button(action: 
                        // Go to the next slide
                    )  Text("Next") 
                
            

            Text("and, maybe, something else")
        
        .overlay(
            Image(systemName: "arrow.left").offset(x: dragOffset / 2),
            alignment: .leading
        )
        .gesture(
            DragGesture()
                .updating($dragOffset)  (value, gestureState, transaction) in
                    let delta = value.location.x - value.startLocation.x
                    if delta > 10  // << some appropriate horizontal threshold here
                        gestureState = delta
                    
                
                .onEnded 
                    if $0.translation.width > 100 
                        // Go to the previous slide
                    
                
        )
    

注意:dragOffset: CGFloat = -100这可能对不同的真实设备有不同的影响,所以可能最好明确计算。

【讨论】:

感谢您提供代码以及对 .updating() 和 @GestureState 的评论——它确实帮助我理解了两者。我希望 SwiftUI 能快速发展成一个更成熟和有据可查的框架:) 谢谢你,但我发现 .onEnded 永远不会到达,只有更新或 onChanged 是。你有什么想法吗?

以上是关于SwiftUI 中 DragGesture 和 ScrollView 的交互的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI ScrollView 也触发 DragGesture

如何在 SwiftUI 中实现触发 switch case 的左或右 DragGesture()?

在触发不同的 DragGesture 之前,SwiftUI DragGestures 不会更新

DragGesture 正在取消 SwiftUI 中的 LongPressGesture

SwiftUI 中的选择器选择和翻译冲突

SwiftUI 中“触摸输入”的手势