让我们想象一下,这是一个带有一些元素的ScrollView,我想要在这些元素上长时间点击一些操作(例如,更改 colored颜色 ).但我也想让滚动这个视图成为可能.

下面是一个例子:

Example of scrolling and long taps together

import SwiftUI

struct TextBox: View {
  var text: String
  var color: Color
  @GestureState private var isLongPressure: Bool = false
  
  var body: some View {
    let longTap = LongPressGesture(minimumDuration: 0.3)
      .updating($isLongPressure) { state, newState, transaction in
        newState = state
        transaction.animation = .easeOut(duration: 0.2)
      }
    
    Text(text)
      .frame(width: 400, height: 200)
      .background(isLongPressure ? .white : color)
      .simultaneousGesture(longTap)
  }
}

struct TestGestures: View {
    var body: some View {
      ScrollView {
        TextBox(text: "Test 1", color: .red)
        TextBox(text: "Test 2", color: .green)
        TextBox(text: "Test 3", color: .blue)
        TextBox(text: "Test 4", color: .red)
        TextBox(text: "Test 5", color: .green)
        TextBox(text: "Test 6", color: .blue)
      }
    }
}

struct TestGestures_Previews: PreviewProvider {
    static var previews: some View {
        TestGestures()
    }
}

因此,如果我 comments .simultaneousGesture(longTap)-滚动工作,但如果我取消 comments -滚动停止工作.

附言:我试着在添加LongTap之前添加onTapGesture,但没有帮助.

提前谢谢!


Update:

感谢您提供的解决方案@nickreps:

Fixed scrolling and long tap

import SwiftUI

struct TextBox: View {
  var text: String
  var color: Color
  @GestureState private var isLongPressure: Bool = false
  
  var body: some View {
    let longTap = LongPressGesture(minimumDuration: 0.3)
      .updating($isLongPressure) { value, state, transaction in
        state = value
        transaction.animation = .easeOut(duration: 0.2)
      }
    
    Text(text)
      .frame(width: 400, height: 200)
      .background(isLongPressure ? .white : color)
      .delaysTouches(for: 0.01) {
        //some code here, if needed
      }
      .gesture(longTap)
  }
}

struct TestGestures: View {
    var body: some View {
      ScrollView {
        TextBox(text: "Test 1", color: .red)
        TextBox(text: "Test 2", color: .green)
        TextBox(text: "Test 3", color: .blue)
        TextBox(text: "Test 4", color: .red)
        TextBox(text: "Test 5", color: .green)
        TextBox(text: "Test 6", color: .blue)
      }
    }
}

extension View {
    func delaysTouches(for duration: TimeInterval = 0.25, onTap action: @escaping () -> Void = {}) -> some View {
        modifier(DelaysTouches(duration: duration, action: action))
    }
}

fileprivate struct DelaysTouches: ViewModifier {
    @State private var disabled = false
    @State private var touchDownDate: Date? = nil
    
    var duration: TimeInterval
    var action: () -> Void
    
    func body(content: Content) -> some View {
        Button(action: action) {
            content
        }
        .buttonStyle(DelaysTouchesButtonStyle(disabled: $disabled, duration: duration, touchDownDate: $touchDownDate))
        .disabled(disabled)
    }
}

fileprivate struct DelaysTouchesButtonStyle: ButtonStyle {
    @Binding var disabled: Bool
    var duration: TimeInterval
    @Binding var touchDownDate: Date?
    
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .onChange(of: configuration.isPressed, perform: handleIsPressed)
    }
    
    private func handleIsPressed(isPressed: Bool) {
        if isPressed {
            let date = Date()
            touchDownDate = date
            
            DispatchQueue.main.asyncAfter(deadline: .now() + max(duration, 0)) {
                if date == touchDownDate {
                    disabled = true
                    
                    DispatchQueue.main.async {
                        disabled = false
                    }
                }
            }
        } else {
            touchDownDate = nil
            disabled = false
        }
    }
}


struct TestGestures_Previews: PreviewProvider {
    static var previews: some View {
        TestGestures()
    }
}

推荐答案

我能够通过使用按钮而不是TextView来使其工作.虽然这确实直接利用了您提供的代码,但您应该能够修改某些部分以使其满足您的需求(如果需要,我可以提供帮助!)

import SwiftUI

struct ScrollTest: View {
    let testData = [1]
    
    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            AnimatedButtonView(color: .red, text: "Test 1")
            AnimatedButtonView(color: .green, text: "Test 2")
            AnimatedButtonView(color: .blue, text: "Test 3")
        }
    }
}



struct AnimatedButtonView: View {
    @GestureState var isDetectingLongPress = false
    let color: Color
    let text: String
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 12.5, style: .continuous)
                .fill(color)
                .frame(width: UIScreen.main.bounds.width, height: 200)
                .padding(25)
                .scaleEffect(!isDetectingLongPress ? 1.0 : 0.875)
                .brightness(!isDetectingLongPress ? 0.0 : -0.125)
                .animation(.easeInOut(duration: 0.125), value: isDetectingLongPress)
            Text(text)
        }
        
        .delaysTouches(for: 0.01) {
            //some code here, if needed
        }
        .gesture(
            LongPressGesture(minimumDuration: 3)
                .updating($isDetectingLongPress) { currentState, gestureState,
                    transaction in
                    gestureState = currentState
                    transaction.animation = Animation.easeIn(duration: 2.0)
                }
                .onEnded { finished in
                    print("gesture ended")
                })
        
    }
}

extension View {
    func delaysTouches(for duration: TimeInterval = 0.25, onTap action: @escaping () -> Void = {}) -> some View {
        modifier(DelaysTouches(duration: duration, action: action))
    }
}

fileprivate struct DelaysTouches: ViewModifier {
    @State private var disabled = false
    @State private var touchDownDate: Date? = nil
    
    var duration: TimeInterval
    var action: () -> Void
    
    func body(content: Content) -> some View {
        Button(action: action) {
            content
        }
        .buttonStyle(DelaysTouchesButtonStyle(disabled: $disabled, duration: duration, touchDownDate: $touchDownDate))
        .disabled(disabled)
    }
}

fileprivate struct DelaysTouchesButtonStyle: ButtonStyle {
    @Binding var disabled: Bool
    var duration: TimeInterval
    @Binding var touchDownDate: Date?
    
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .onChange(of: configuration.isPressed, perform: handleIsPressed)
    }
    
    private func handleIsPressed(isPressed: Bool) {
        if isPressed {
            let date = Date()
            touchDownDate = date
            
            DispatchQueue.main.asyncAfter(deadline: .now() + max(duration, 0)) {
                if date == touchDownDate {
                    disabled = true
                    
                    DispatchQueue.main.async {
                        disabled = false
                    }
                }
            }
        } else {
            touchDownDate = nil
            disabled = false
        }
    }
}

Ios相关问答推荐

如何在进行滑动删除操作时保持自定义视图背景静态

第三方框架的iOS隐私声明

RN 0.63支持iOS ProMotion?

带外部笔划的圆形进度视图

如何在iOS中以编程方式推送另一个页面?

在嵌套的DISPATCH_AFTER块中正确使用strong自/弱自

当S没有身体时该如何处理

带菜单的选取器不适用于Int32,但适用于Int

确保方法调配的安全有效实施

WatchConnectivity - 从手表配套应用程序在 iOS 设备上启动 AVPlayer

SwiftUI HStack 文本字段对齐

SwiftUI - TextField - 在焦点上清除 Double 类型的值,在取消 Select 时恢复

CoreML 不能并发(多线程)工作?

.overlay 和 body 是什么 swift 概念

从远程通知启动时,iOS 应用程序加载错误

如何让我的 iOS 应用程序与 api 连接?

如何在保持纵横比的同时zoom UIView 的内容以适应目标矩形?

如何关闭情节提要弹出框

UINavigationBar - 以编程方式设置标题?

iPhone UITableView.如何开启音乐 App 之类的单字母字母列表?