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

SwiftUI地理定位照片应用:如何将拍摄照片同步至列表与地图?

解决方案:地理定位照片应用的照片同步与地图关联

核心思路

要实现拍摄的照片同步到列表并关联地图位置,需要统一数据管理+位置捕获+视图联动,以下是分步实现方案:


1. 定义数据模型

先创建存储照片和对应位置的结构体,让它能被SwiftUI识别和遍历:

import MapKit
import SwiftUI

struct PhotoLocation: Identifiable, Codable {
    let id = UUID()
    let imageData: Data // 存储图片二进制数据
    let coordinate: CLLocationCoordinate2D // 存储经纬度坐标
}

// 让CLLocationCoordinate2D支持Codable(可选,用于本地持久化)
extension CLLocationCoordinate2D: Codable {
    enum CodingKeys: String, CodingKey {
        case latitude, longitude
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let latitude = try container.decode(CLLocationDegrees.self, forKey: .latitude)
        let longitude = try container.decode(CLLocationDegrees.self, forKey: .longitude)
        self.init(latitude: latitude, longitude: longitude)
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(latitude, forKey: .latitude)
        try container.encode(longitude, forKey: .longitude)
    }
}

2. 创建全局数据管理类

用ObservableObject实现ViewModel,统一管理所有照片数据,同时处理位置获取逻辑:

import CoreLocation

class PhotoViewModel: ObservableObject {
    @Published var photos: [PhotoLocation] = [] // 存储所有带位置的照片
    private let locationManager = CLLocationManager()
    private var continuation: CheckedContinuation<CLLocationCoordinate2D?, Never>?

    init() {
        // 请求位置使用权限
        locationManager.requestWhenInUseAuthorization()
    }

    // 异步获取当前位置
    func getCurrentLocation() async -> CLLocationCoordinate2D? {
        guard locationManager.authorizationStatus == .authorizedWhenInUse || 
              locationManager.authorizationStatus == .authorizedAlways else {
            return nil
        }

        return await withCheckedContinuation { continuation in
            self.continuation = continuation
            locationManager.requestLocation()
            locationManager.delegate = self
        }
    }
}

extension PhotoViewModel: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.first else {
            continuation?.resume(returning: nil)
            return
        }
        continuation?.resume(returning: location.coordinate)
        continuation = nil
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        continuation?.resume(returning: nil)
        continuation = nil
    }
}

3. 修改AddView:拍摄照片并保存位置

注入ViewModel,拍摄完成后获取当前位置并将照片添加到全局数组:

struct AddView: View {
    @State private var capturedImage: UIImage? = nil
    @State private var isCustomCamaraViewPresented = false
    @ObservedObject var viewModel: PhotoViewModel // 接收ViewModel

    var body: some View {
        ZStack {
            if capturedImage != nil {
                Image(uiImage: capturedImage!)
                    .resizable()
                    .scaledToFill()
                    .ignoresSafeArea()
                // 添加保存按钮,绑定位置与照片
                VStack {
                    Spacer()
                    Button("保存照片") {
                        Task {
                            guard let image = capturedImage,
                                  let imageData = image.jpegData(compressionQuality: 0.8),
                                  let coordinate = await viewModel.getCurrentLocation() else {
                                return
                            }
                            // 将带位置的照片加入全局数组
                            viewModel.photos.append(PhotoLocation(imageData: imageData, coordinate: coordinate))
                            capturedImage = nil // 清空预览图
                        }
                    }
                    .padding()
                    .background(Color.green)
                    .foregroundColor(.white)
                    .cornerRadius(10)
                    .padding(.bottom)
                }
            } else {
                Color(UIColor.systemBackground)
            }

            VStack {
                Spacer()
                Button(action: {
                    isCustomCamaraViewPresented.toggle()
                }, label: {
                    Image("cam")
                        .padding()
                        .foregroundColor(.green)
                })
                .padding(.bottom)
                .sheet(isPresented: $isCustomCamaraViewPresented) {
                    CustomCamaraView(capturedImage: $capturedImage)
                }
            }
        }
    }
}

4. 更新ListView:展示所有带位置的照片

接收ViewModel,遍历数组显示照片及对应坐标:

struct ListView: View {
    @ObservedObject var viewModel: PhotoViewModel

    var body: some View {
        NavigationView {
            List(viewModel.photos) { photo in
                HStack {
                    Image(uiImage: UIImage(data: photo.imageData)!)
                        .resizable()
                        .frame(width: 80, height: 80)
                        .cornerRadius(8)
                    VStack(alignment: .leading) {
                        Text("纬度: \(String(format: "%.4f", photo.coordinate.latitude))")
                        Text("经度: \(String(format: "%.4f", photo.coordinate.longitude))")
                    }
                }
            }
            .navigationTitle(":o ! ! !  Treashures !! ! ¡ !")
        }
    }
}

5. 更新MapView:显示照片位置标注

接收ViewModel,在地图上添加对应坐标的照片标注:

struct MapView: View {
    @ObservedObject var viewModel: PhotoViewModel
    @State private var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 41.3879, longitude: 2.16992), span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))

    var body: some View {
        Map(coordinateRegion: $mapRegion, annotationItems: viewModel.photos) { photo in
            MapAnnotation(coordinate: photo.coordinate) {
                Image(uiImage: UIImage(data: photo.imageData)!)
                    .resizable()
                    .frame(width: 40, height: 40)
                    .cornerRadius(20)
                    .onTapGesture {
                        // 可选:点击标注跳转照片详情页
                    }
            }
        }
    }
}

6. 修改ContentView:传递ViewModel

在根视图初始化ViewModel,统一传递给三个子视图:

struct ContentView: View {
    @StateObject private var viewModel = PhotoViewModel() // 初始化全局ViewModel

    var body: some View {
        TabView {
            ListView(viewModel: viewModel)
                .tabItem {
                    Image("cadira")
                }

            MapView(viewModel: viewModel)
                .tabItem {
                    Image("mapa")
                }

            AddView(viewModel: viewModel)
                .tabItem {
                    Image("subir")
                }
        }
    }
}

注意事项

  • 必须在Info.plist中添加NSLocationWhenInUseUsageDescription字段,填写位置权限申请说明,否则无法获取位置。
  • 若需要持久化照片,可在ViewModel中添加CoreData或UserDefaults的存储逻辑。
  • 确保CustomCamaraView能正确将拍摄的图片赋值给capturedImage绑定变量。

内容的提问来源于stack exchange,提问作者Ángels Florea Jauset

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 12:18:54