ScrollView 中的 SwiftUI TapGesture 和 LongPressGesture 带有点击指示不起作用

问题描述 投票:0回答:2

我正在努力在

TapGesture
中同时实现
LongPressGesture
ScrollView
.onTapGesture
.onLongPressGesture
一切正常,但我希望当用户点击按钮时,按钮的不透明度会降低,就像正常的
Button()
一样。

但是,无论出于何种原因,

Button()
都无法选择在长按时执行某些操作。所以我尝试使用
.gesture(LongPressGesture() ... )
。此方法有效并显示点击指示。不幸的是,这不适用于
ScrollView
:你无法再滚动它!

所以我做了一些研究,发现在

LongPressGesture
之前必须有一个TapGesture,这样
ScrollView
才能正常工作。确实是这样,但我的
LongPressGesture
不再起作用了。

希望有人能提供解决方案...


struct ContentView: View {
    var body: some View {
        ScrollView(.horizontal){
            HStack{
                ForEach(0..<5){ _ in
                    Button()
                }
            }
        }
    }
}

struct Button: View{
    
    @GestureState var isDetectingLongPress = false
    @State var completedLongPress = false
    
    var body: some View{
        
        Circle()
            .foregroundColor(.red)
            .frame(width: 100, height: 100)
            .opacity(self.isDetectingLongPress ? 0 : 1)
            
            // That works, but there is no indication for the user that the UI recognized the gesture
            //                        .onTapGesture {
            //                            print("Tapped!")
            //                       }
            //                        .onLongPressGesture(minimumDuration: 0.5){
            //                            print("Long pressed!")
            //                        }
            
            
            // The approach (*) shows the press indication, but the ScrollView is stuck because there is no TapGesture
            
            // If I add a dummy TapGesture, the LongPressGesture won't work anymore but now the ScrollView works as expected
            //.onTapGesture {}
            
            // (*)
            .gesture(LongPressGesture()
                .updating(self.$isDetectingLongPress) { currentstate, gestureState,
                    transaction in
                    gestureState = currentstate
            }
            .onEnded { finished in
                self.completedLongPress = finished
                }
        )
    }
}
ios swift xcode swiftui
2个回答
12
投票

我已经尝试了多种尝试 onTapGesture + LongPressGesture + 自定义计时和动画的组合,并且许多工作/几乎/但留下了一些小烦恼。这就是我发现效果完美的方法。在 iOS 13.6 上测试。

使用此解决方案,您的滚动视图仍然滚动,您可以获得按钮按下动画,长按按钮也可以。

struct MainView: View {
    ...
    Scrollview {
        RowView().highPriorityGesture(TapGesture()
                                        .onEnded { _ in
            // The function you would expect to call in a button tap here.
        })
        
    }
}

struct RowView: View {
    
    @State var longPress = false
    var body: some View {
        Button(action: {
            if (self.longPress) {
                self.longPress.toggle()
            } else {
                // Normal button code here
            }
        }) {
            // Buttons LaF here
        }
        // RowView code here.
        .simultaneousGesture(LongPressGesture(minimumDuration: 0.5)
                                .onEnded { _ in
            self.longPress = true
        })
    }
}

0
投票

我遇到了类似的情况,并使用多个状态变量来处理点击和长按手势,并在 SwiftUI 列表中突出显示行。

代码示例

struct ContentView: View {
    @State private var selectedIndex: Int = -1
    @State private var longPressStarted = false
    @State private var tapStarted = false

    let items = ["Item 1", "Item 2", "Item 3", "Item 4"]

    var body: some View {
        List {
            ForEach(items.indices, id: \.self) { index in
                listItem(index)
            }
        }
    }

    func listItem(_ index: Int) -> some View {
        ZStack {
            VStack {
                Text(items[index])
                    .padding()
            }
            .background(selectedIndex == index ? Color(.systemGray4) : Color.white)
            .cornerRadius(8)
            .onLongPressGesture(minimumDuration: 0) {
                longPressStarted = true
                selectedIndex = index
            } onPressingChanged: { inProgress in
                selectedIndex = index // Highlight row while pressing
                
                if !inProgress && longPressStarted {
                    longPressStarted = false
                    // Long press finished, handle action here if needed
                } else if !inProgress && tapStarted {
                    tapStarted = false
                    // Delay to briefly show highlight on tap
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                        selectedIndex = -1
                    }
                } else if !inProgress {
                   // Cancel selection
                    selectedIndex = -1
                }
            }
            .highPriorityGesture(TapGesture()
                .onEnded {
                    tapStarted = true
                    selectedIndex = index
                })
        }
        .listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20))
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.