You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI长按触发按钮动画实现问题及代码排查

解决长按动画按钮GestureState不更新的问题及需求实现

为什么topG从未触发更新?

  1. LongPressGesture的状态特性:LongPressGesture的updating闭包里的cstate只有在长按达到设置的minimumDuration(你代码里是1.5秒)后才会变为true,在这之前始终是false,而@GestureState private var topG = false的初始值就是false,状态没有发生变化,自然不会触发更新。
  2. 重复动画修饰符冲突:你给同一个视图添加了两个针对topG的animation修饰符,这会导致动画逻辑冲突,可能掩盖状态变化的效果。

实现目标效果的修改代码

struct ContentView: View {
    var body: some View {
        MyCustomButton(text: "My button") { print("button pressed") }
            .padding(100)
    }
}

struct MyCustomButton: View {
    let text: LocalizedStringKey
    var action: () -> ()
    
    // 标记是否已触发action,避免重复执行
    @State private var actionTriggered = false
    // 管理触摸过程中的按压状态
    @GestureState private var isPressing = false
    // 标记长按是否完成
    @State private var longPressCompleted = false
    private let radius: CGFloat = 10
    // 长按触发时长(可调整为0.7秒)
    private let longPressDuration: Double = 1.5
    // 记录触摸开始时间,用于计算进度
    @State private var gestureStartTime = Date()
    
    var body: some View {
        Text(text)
            .padding(.horizontal, 30)
            .padding(.vertical, 10)
            .background { Bg() }
            // 根据按压状态应用缩放动画,释放时用更快的恢复动画
            .scaleEffect(isPressing ? 1.2 : 1)
            .animation(isPressing ? .easeInOut.speed(0.5) : .easeOut(duration: 0.2), value: isPressing)
            .gesture(
                // 用DragGesture捕获触摸开始/结束,同时结合LongPressGesture检测长按完成
                DragGesture(minimumDistance: 0)
                    .onChanged { _ in
                        // 仅在触摸开始时记录时间
                        if !isPressing {
                            gestureStartTime = Date()
                        }
                    }
                    .updating($isPressing) { _, state, _ in
                        // 触摸开始立即触发按压状态
                        state = true
                    }
                    .onEnded { _ in
                        // 触摸结束后重置状态,触发恢复动画
                        longPressCompleted = false
                        actionTriggered = false
                    }
                    .simultaneously(with:
                        LongPressGesture(minimumDuration: longPressDuration, maximumDistance: 10)
                            .onEnded { _ in
                                // 仅在未触发过action时执行
                                guard !actionTriggered else { return }
                                actionTriggered = true
                                longPressCompleted = true
                                action()
                                // 延迟重置状态,保证动画完整显示
                                DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                                    longPressCompleted = false
                                }
                            }
                    )
            )
    }
    
    func Bg() -> some View {
        RoundedRectangle(cornerRadius: radius)
            .fill(Color.black.opacity(0.4))
            .overlay{
                ZStack {
                    RoundedRectangle(cornerRadius: radius, style: .continuous)
                        .foregroundColor(.gray.opacity(0.1))
                    
                    RoundedRectangle(cornerRadius: radius, style: .continuous)
                        // 根据按压状态和长按进度调整描边范围
                        .trim(from: 0, to: isPressing ? (longPressCompleted ? 1 : min(1, Date().timeIntervalSince(gestureStartTime)/longPressDuration)) : 0)
                        .stroke(style: StrokeStyle(lineWidth: 3, lineCap: .round))
                        .rotation(Angle(degrees: 180))
                        .animation(.easeInOut(duration: longPressDuration), value: longPressCompleted)
                }
            }
    }
}

关键修改说明

  • 触摸即时响应:用DragGesture(minimumDistance: 0)捕获触摸开始事件,立即触发按压动画,解决原代码中触摸无反馈的问题。
  • 长按触发逻辑:通过simultaneously(with:)组合手势,长按达到设定时长后自动执行action,用actionTriggered标记避免重复触发。
  • 区分动画时长:未触发action时,释放动画采用0.2秒的快速恢复;按压过程使用慢动作动画,符合需求差异。
  • 进度动画处理:实时计算长按进度,长按完成后直接拉满描边进度,触发action后延迟重置状态,保证动画完整展示。

内容的提问来源于stack exchange,提问作者Andrew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 19:15:55