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

SwiftUI点击切换远程加载图片时视图不刷新问题解决

问题原因

@StateObject 的实例和视图生命周期绑定,仅会在视图首次创建时初始化一次。原有实现中Loader的网络请求逻辑写在初始化方法里,后续父视图刷新、传入的图片URL变更时,已经创建的Loader实例不会重新执行拉取逻辑,因此点击切换索引后图片不会更新。

修复方案

TestView 层逻辑不需要调整,仅修改RemoteImage实现即可,修改后完整代码如下:

struct RemoteImage: View {
    private enum LoadState {
        case loading, success, failure
    }
    
    private class Loader: ObservableObject {
        var data = Data()
        var state = LoadState.loading
        
        func load(from url: String) {
            // 重置加载状态
            state = .loading
            data = Data()
            
            guard let parsedURL = URL(string: url) else {
                state = .failure
                DispatchQueue.main.async {
                    self.objectWillChange.send()
                }
                return
            }
            
            URLSession.shared.dataTask(with: parsedURL) { data, response, error in
                if let data = data, data.count > 0 {
                    self.data = data
                    self.state = .success
                } else {
                    self.state = .failure
                }
                
                DispatchQueue.main.async {
                    self.objectWillChange.send()
                }
            }.resume()
        }
    }
    
    private let url: String
    @StateObject private var loader = Loader()
    private var loading: Image
    private var failure: Image
    
    var body: some View {
        selectImage()
            .resizable()
            .onAppear {
                loader.load(from: url)
            }
            .onChange(of: url) { newUrl in
                loader.load(from: newUrl)
            }
    }
    
    init(url: String, loading: Image = Image(""), failure: Image = Image(systemName: "multiply.circle")) {
        self.url = url
        self.loading = loading
        self.failure = failure
    }
    
    private func selectImage() -> Image {
        switch loader.state {
        case .loading:
            return loading
        case .failure:
            return failure
        default:
            if let image = UIImage(data: loader.data) {
                return Image(uiImage: image)
            } else {
                return failure
            }
        }
    }
}
核心改动说明
  • 将Loader中原本写在初始化方法里的网络请求逻辑抽离为独立的load(from:)方法,支持重复调用加载不同地址的图片
  • 移除init中对@StateObject的自定义初始化逻辑,避免StateObject实例生命周期和视图初始化强绑定导致的不更新问题
  • 新增onChange(of: url)修饰符,监听父视图传入的URL变化,URL更新时主动触发新的图片加载请求
  • 保留onAppear触发首次加载,保证视图首次展示时的正常拉取逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 18:06:25