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

如何将城市名称转换为CLLocation并获取多地点天气数据

问题解决方案

一、城市名称转CLLocation的正确实现

你已经用到了CLGeocoder的geocodeAddressString方法,但当前的回调式写法在SwiftUI异步环境下不够友好,建议重构为async/await风格的方法,直接返回CLLocation对象:

修改LocationManager,添加异步解析方法:

func location(for cityName: String) async throws -> CLLocation {
    return try await withCheckedThrowingContinuation { continuation in
        let geocoder = CLGeocoder()
        geocoder.geocodeAddressString(cityName) { placemarks, error in
            if let error = error {
                continuation.resume(throwing: error)
                return
            }
            guard let placemark = placemarks?.first, let location = placemark.location else {
                continuation.resume(throwing: NSError(domain: "LocationError", code: -1, userInfo: [NSLocalizedDescriptionKey: "无法解析该城市坐标"]))
                return
            }
            continuation.resume(returning: location)
        }
    }
}

调用这个方法就能直接获取对应城市的CLLocation,无需手动处理坐标转换。

二、多地点天气数据的存储策略

不建议直接存储Weather对象,原因如下:

  • Weather包含实时数据,会随时间过期,存储过期数据没有实际意义
  • Weather是不可变对象,维护数组会增加不必要的复杂度,且不利于数据刷新

推荐方案:

  1. 保留现有weatherLocations: [String]数组,仅存储用户输入的城市名称
  2. 维护一个[String: Weather]字典,用于缓存每个城市的实时天气数据,在需要展示或刷新时,再通过城市名称转坐标请求WeatherKit数据

三、代码修改示例

修改ContentView

调整天气数据存储方式,优化每个城市的天气请求逻辑:

struct ContentView: View {
    let weatherService: WeatherService
    @ObservedObject var locationManager: LocationManager
    @State private var weatherMap: [String: Weather] = [:] // 用字典缓存城市与天气的对应关系
    @State var searchLocation: String = ""

    var body: some View {
        NavigationView {
            VStack {
                // 统一搜索栏,避免每个Tab重复渲染
                HStack {
                    TextField("输入城市名称", text: $searchLocation)
                        .padding(.horizontal)
                        .frame(width: 200)
                        .background {
                            RoundedRectangle(cornerRadius: 5)
                                .foregroundColor(Color(UIColor.lightGray))
                        }
                    Button(action: {
                        guard !searchLocation.trimmingCharacters(in: .whitespaces).isEmpty else { return }
                        locationManager.weatherLocations.append(searchLocation)
                        searchLocation = ""
                    }) {
                        Image(systemName: "plus")
                    }
                }
                .padding(.horizontal)
                
                if locationManager.weatherLocations.isEmpty {
                    Text("暂无添加的城市")
                        .padding()
                } else {
                    TabView {
                        ForEach(locationManager.weatherLocations, id: \.self) { city in
                            VStack(spacing: 16) {
                                Text(city)
                                    .font(.title)
                                
                                if let weather = weatherMap[city] {
                                    VStack(spacing: 8) {
                                        Text("更新时间:\(weather.currentWeather.date.formatted(date: .numeric, time: .shortened))")
                                        Text("当前温度:\(weather.currentWeather.temperature.formatted())")
                                    }
                                } else {
                                    ProgressView("加载天气数据中...")
                                }
                            }
                            .task {
                                do {
                                    // 解析城市坐标
                                    let location = try await locationManager.location(for: city)
                                    // 请求天气数据
                                    let weather = try await weatherService.weather(for: location)
                                    // 存入缓存字典
                                    weatherMap[city] = weather
                                } catch {
                                    print("加载\(city)天气失败:\(error.localizedDescription)")
                                }
                            }
                        }
                    }
                    .tabViewStyle(.page)
                }
            }
        }
    }
}

额外注意事项

  • 确保Info.plist中添加了NSLocationWhenInUseUsageDescription权限描述,否则地理编码可能失败
  • 地理编码存在模糊匹配风险(重名城市),可在解析后增加用户确认环节提升准确性
  • 可添加下拉刷新功能,让用户手动触发天气数据更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:06:32