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

Swift:等待所有GooglePlaces API请求完成后再更新Published变量

解决Google Places API图片加载后一次性更新UI的问题

方案一:使用DispatchGroup等待所有请求完成

通过DispatchGroup追踪所有图片请求的完成状态,等全部请求结束后再一次性更新cardModels,避免UI逐个刷新的堆叠效果:

import Foundation
import GooglePlaces

class CardsViewModel: ObservableObject {
    @Published var cardModels = [CardModel]()
    
    init() {
        GooglePlacesAPI().performNearbyRestaurantSearch { gmsplaces in
            var models = [CardModel]()
            let group = DispatchGroup()
            
            gmsplaces.forEach { place in
                guard let placeId = place.placeID else { return }
                group.enter()
                GooglePlacesAPI().getPhotoFromMetaData(id: placeId) { image in
                    defer { group.leave() }
                    guard let image = image else { return }
                    let restaurant = RestaurantModel(
                        id: placeId,
                        name: place.name,
                        uiimages: [image]
                    )
                    models.append(CardModel(restaurant: restaurant))
                }
            }
            
            group.notify(queue: .main) {
                self.cardModels = models
            }
        }
    }
}

核心要点:

  • 用DispatchGroup管理所有图片请求:每个请求开始前调用group.enter(),请求完成(无论成功失败)调用group.leave()
  • 所有请求结束后,group.notify会在主队列触发,此时将收集好的完整模型数组赋值给cardModels,实现一次性UI更新
  • 增加guard let处理可选值,避免强制解包引发崩溃

方案二:使用Async/Await(iOS 15+)

如果项目支持iOS 15及以上,推荐用Swift原生的async/await语法,代码更简洁易读:

首先将原有的回调式API包装为异步函数:

extension GooglePlacesAPI {
    func performNearbyRestaurantSearch() async throws -> [GMSPlace] {
        return try await withCheckedThrowingContinuation { continuation in
            self.performNearbyRestaurantSearch { places in
                continuation.resume(returning: places)
            }
        }
    }
    
    func getPhotoFromMetaData(id: String) async -> UIImage? {
        return await withCheckedContinuation { continuation in
            self.getPhotoFromMetaData(id: id) { image in
                continuation.resume(returning: image)
            }
        }
    }
}

然后修改ViewModel:

import Foundation
import GooglePlaces

class CardsViewModel: ObservableObject {
    @Published var cardModels = [CardModel]()
    
    init() {
        Task {
            do {
                let gmsplaces = try await GooglePlacesAPI().performNearbyRestaurantSearch()
                var models = [CardModel]()
                
                for place in gmsplaces {
                    guard let placeId = place.placeID else { continue }
                    if let image = await GooglePlacesAPI().getPhotoFromMetaData(id: placeId) {
                        let restaurant = RestaurantModel(
                            id: placeId,
                            name: place.name,
                            uiimages: [image]
                        )
                        models.append(CardModel(restaurant: restaurant))
                    }
                }
                
                DispatchQueue.main.async {
                    self.cardModels = models
                }
            } catch {
                print("搜索附近餐厅失败:\(error)")
            }
        }
    }
}

并行优化(可选)

如果想让图片请求并行执行以缩短加载时间,可使用TaskGroup:

// 替换Task内部的遍历逻辑
let models = try await withThrowingTaskGroup(of: CardModel?.self) { group in
    for place in gmsplaces {
        guard let placeId = place.placeID else { continue }
        group.addTask {
            guard let image = await GooglePlacesAPI().getPhotoFromMetaData(id: placeId) else {
                return nil
            }
            let restaurant = RestaurantModel(
                id: placeId,
                name: place.name,
                uiimages: [image]
            )
            return CardModel(restaurant: restaurant)
        }
    }
    
    var result = [CardModel]()
    for try await model in group {
        if let model = model {
            result.append(model)
        }
    }
    return result
}

DispatchQueue.main.async {
    self.cardModels = models
}

所有图片请求会并行发起,最终仍一次性更新UI,兼顾速度和视觉效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 15:31:10