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

SwiftUI中实现多指同时长按检测的技术问询

多指长按检测与多圆圈绘制实现方案

要实现多指同时长按检测并在对应位置绘制圆圈,我们可以通过DragGesture跟踪所有触摸点,结合触摸起始时间判断是否达到长按条件,同时维护每个触摸的独立状态。以下是完整的实现代码:

import SwiftUI

// 存储单根手指的触摸信息
struct TouchInfo: Identifiable {
    let id: UUID
    var location: CGPoint
    let startTime: Date
    var didTriggerLongPress: Bool = false
}

// 存储需要显示的圆圈数据
struct CircleMarker: Identifiable {
    let id: UUID
    let location: CGPoint
    let color: Color
}

struct ContentView: View {
    // 记录所有当前触摸的信息
    @State private var activeTouches: [TouchInfo] = []
    // 记录已触发长按的圆圈
    @State private var longPressCircles: [CircleMarker] = []
    // 长按所需的最短时长(秒)
    private let longPressDuration: TimeInterval = 1
    
    var multiFingerLongPressGesture: some Gesture {
        DragGesture(minimumDistance: 0, coordinateSpace: .local)
            .onChanged { value in
                // 更新当前触摸的位置,或添加新触摸
                let touchId = value.id
                if let index = activeTouches.firstIndex(where: { $0.id == touchId }) {
                    activeTouches[index].location = value.location
                    
                    // 检查是否达到长按时长且未触发过
                    let elapsedTime = Date().timeIntervalSince(activeTouches[index].startTime)
                    if elapsedTime >= longPressDuration && !activeTouches[index].didTriggerLongPress {
                        activeTouches[index].didTriggerLongPress = true
                        // 添加新的圆圈标记
                        longPressCircles.append(CircleMarker(
                            id: touchId,
                            location: value.location,
                            color: Color.random()
                        ))
                    }
                } else {
                    // 新手指触摸,记录起始时间和位置
                    activeTouches.append(TouchInfo(
                        id: touchId,
                        location: value.location,
                        startTime: Date()
                    ))
                }
            }
            .onEnded { value in
                // 手指抬起时,移除对应触摸信息和圆圈
                activeTouches.removeAll(where: { $0.id == value.id })
                longPressCircles.removeAll(where: { $0.id == value.id })
            }
    }
    
    var body: some View {
        ZStack {
            Rectangle()
                .foregroundColor(Color(.background))
                .ignoresSafeArea()
                .gesture(multiFingerLongPressGesture)
            
            // 绘制所有已触发的长按圆圈
            ForEach(longPressCircles) { circle in
                Circle()
                    .foregroundColor(circle.color)
                    .frame(width: 100, height: 100)
                    .position(circle.location)
                    .allowsHitTesting(false)
            }
            
            VStack(spacing: 8) {
                Image(systemName: "hands.sparkles")
                    .imageScale(.large)
                    .foregroundStyle(.tint)
                    .font(.largeTitle)
                Text("长按任意数量手指查看圆圈")
            }
            .padding()
        }
    }
}

// 扩展Color,生成随机颜色
extension Color {
    static func random() -> Color {
        Color(
            red: Double.random(in: 0...1),
            green: Double.random(in: 0...1),
            blue: Double.random(in: 0...1)
        )
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

核心逻辑说明

  • 触摸状态跟踪:通过TouchInfo结构体记录每根手指的唯一ID、位置、起始时间和长按触发状态,确保多手指操作互不干扰。
  • 长按判断:在DragGesture的onChanged回调中,计算触摸持续时长,当达到设定的longPressDuration且未触发过长按时,生成对应位置的圆圈。
  • 多圆圈绘制:使用ForEach遍历longPressCircles数组,动态渲染每个触发长按的手指位置的圆圈,手指抬起时自动移除对应圆圈。
  • 无距离限制的拖拽手势:设置minimumDistance: 0,让手势能够捕获手指静止时的触摸状态,完美适配长按场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 20:09:52