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

SwiftUI中点击Alert按钮获取错误数据问题排查

问题分析与解决:Map Annotation弹窗按钮内容不一致问题

问题现象

点击地图上的Annotation会弹出带“Cancel”和“Buy”按钮的Alert。点击Annotation时能正确打印对应车辆信息(比如点击Toyota Camry 2001的标注会输出正确内容),但点击Alert里的“Buy”按钮时,有时会打印错误的车辆信息(比如Ford Mustang 2000),该问题在iOS 17.5和18版本中存在。

问题原因

核心问题在于Alert的生命周期与ForEach循环的变量捕获冲突:

  • 所有Annotation的Alert共享同一个showAlert状态变量,当循环初始化多个Alert后,SwiftUI的视图更新机制可能导致Alert闭包中捕获的car变量不是当前触发弹窗的实例,而是循环中其他(比如最后一个)车辆实例,从而输出错误信息。

解决方案

方法1:用绑定变量存储当前选中车辆

通过单独的状态变量记录当前点击的车辆,确保Alert始终使用选中的目标车辆信息:

import SwiftUI
import MapKit

struct Car: Hashable {
    let make: String
    let model: String
    let year: Int
    let lat: Double
    let lng: Double
}

struct ContentView: View {
    
    let cars: [Car] = [
        .init(make: "Ford", model: "Mustang", year: 2000, lat: 40.7117, lng: -74.0049),
        .init(make: "Toyota", model: "Camry", year: 2001, lat: 40.7127, lng: -74.0059)
    ]
    
    @State private var position = MapCameraPosition.automatic
    @State private var selectedCar: Car? // 存储当前选中的车辆
    
    var body: some View {
        
        Map(position: $position) {
            ForEach(cars, id: \.self) { car in
                Annotation(coordinate: CLLocationCoordinate2D(latitude: car.lat, longitude: car.lng)) {
                    Image(systemName: "mappin.circle.fill")
                        .font(.system(size: 50))
                        .onTapGesture {
                            selectedCar = car // 点击时记录当前车辆
                            print(car.make, car.model, car.year)
                        }
                }
                label: {
                    Text(car.model)
                }
            }
        }
        .mapStyle(.standard(elevation: .automatic))
        // 将Alert移到Map外部,通过selectedCar控制弹窗
        .alert("Question", isPresented: .constant(selectedCar != nil), presenting: selectedCar) { car in
            HStack {
                Button("Cancel", role: .cancel) {
                    selectedCar = nil
                }
                Button("Buy") {
                    print(car.make, car.model, car.year)
                    selectedCar = nil
                }
            }
        } message: { _ in
            Text("Do you want to buy this car?")
        }
    }
}

#Preview {
    ContentView()
}

方法2:将Annotation封装为独立子视图

把单个Annotation的视图逻辑抽离成子视图,让每个子视图管理自己的弹窗状态,彻底避免共享状态导致的变量捕获问题:

import SwiftUI
import MapKit

struct Car: Hashable {
    let make: String
    let model: String
    let year: Int
    let lat: Double
    let lng: Double
}

// 独立的Annotation子视图,自带弹窗状态
struct CarAnnotationView: View {
    let car: Car
    @State private var showAlert = false
    
    var body: some View {
        Image(systemName: "mappin.circle.fill")
            .font(.system(size: 50))
            .onTapGesture {
                showAlert.toggle()
                print(car.make, car.model, car.year)
            }
            .alert("Question", isPresented: $showAlert) {
                HStack {
                    Button("Cancel", role: .cancel) {}
                    Button("Buy") {
                        print(car.make, car.model, car.year)
                    }
                }
            } message: {
                Text("Do you want to buy this car?")
            }
    }
}

struct ContentView: View {
    
    let cars: [Car] = [
        .init(make: "Ford", model: "Mustang", year: 2000, lat: 40.7117, lng: -74.0049),
        .init(make: "Toyota", model: "Camry", year: 2001, lat: 40.7127, lng: -74.0059)
    ]
    
    @State private var position = MapCameraPosition.automatic
    
    var body: some View {
        
        Map(position: $position) {
            ForEach(cars, id: \.self) { car in
                Annotation(coordinate: CLLocationCoordinate2D(latitude: car.lat, longitude: car.lng)) {
                    CarAnnotationView(car: car) // 使用子视图
                }
                label: {
                    Text(car.model)
                }
            }
        }
        .mapStyle(.standard(elevation: .automatic))
    }
}

#Preview {
    ContentView()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 01:52:40