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

SwiftUI 相机聚焦Overlay视图多次点击后停止动画问题

问题分析与解决方案

你的问题根源在于延迟清除聚焦状态的逻辑不可靠,加上FocusView的状态管理和视图生命周期配合不当,导致多次点击后出现动画冻结。具体来说:

  • 用坐标判断是否清除focusLocation的方式存在漏洞:如果用户点击相同x坐标的不同位置,旧延迟任务会错误干扰状态;多次点击时新状态覆盖旧值,导致旧任务的条件判断失效,旧视图无法正确销毁。
  • FocusView中多余的DispatchQueue调用,让动画状态和视图生命周期不同步,引发状态混乱。

修复方案

1. 用唯一ID管理聚焦状态,替换不可靠的坐标判断

给每个聚焦动作绑定唯一UUID,确保延迟任务只对应自身的状态,避免新旧任务互相干扰:

struct HomeStory: View {
    // 定义带唯一ID的结构体,替代原有的元组
    struct FocusPoint: Equatable {
        let x: CGFloat
        let y: CGFloat
        let id: UUID
    }
    @State private var focusLocation: FocusPoint?
    
    var body: some View {
        ZStack(alignment: .bottom) {
            CameraStoryView()
                .onTapGesture { tapP in
                    UIImpactFeedbackGenerator(style: .light).impactOccurred()
                    // 每次点击生成新的唯一ID
                    let newFocus = FocusPoint(x: tapP.x, y: tapP.y, id: UUID())
                    focusLocation = newFocus
                    cameraModel.focus(at: CGPoint(x: tapP.x, y: tapP.y))
                    
                    // 用Task执行延迟清除,绑定当前ID
                    Task {
                        try await Task.sleep(nanoseconds: 750_000_000)
                        if focusLocation?.id == newFocus.id {
                            focusLocation = nil
                        }
                    }
                }

            let w = widthOrHeight(width: true)
            let h = widthOrHeight(width: false)
            if let points = focusLocation, 
               points.x > 20, points.x < w - 20, 
               points.y > 80, points.y < h - (h * 0.3) {
                FocusView()
                    .position(x: points.x, y: points.y - 35)
                    .offset(y: 35)
            }
        }
    }
}

2. 简化FocusView的动画逻辑

去掉多余的DispatchQueue调用,让动画直接和视图生命周期绑定,确保每次创建都能正确启动动画:

struct FocusView: View {
    @State private var animate = false
    
    var body: some View {
        ZStack {
            Circle()
                .fill(.white.opacity(0.75))
                .frame(width: 70, height: 70)
                .scaleEffect(animate ? 1 : 0)
                .opacity(animate ? 0 : 1)
            
            Circle()
                .fill(.white)
                .frame(width: 7, height: 4)
                .scaleEffect(animate ? 7.0 : 1)
                .opacity(animate ? 0.3 : 1)
        }
        .onAppear {
            animate = true
        }
        .onDisappear {
            animate = false
        }
        .animation(.linear(duration: 0.7), value: animate)
    }
}

关键改进点

  • 唯一ID绑定:每个聚焦动作生成独立UUID,延迟任务只会清除自身对应的状态,不会被后续点击干扰。
  • Task替代DispatchQueue:SwiftUI中Task更适配视图生命周期,视图销毁时会自动取消任务,避免无效的状态修改。
  • 简化动画触发:直接在onAppear中启动动画,去掉冗余的主线程调用,确保动画状态和视图生命周期同步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 10:25:57