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
相关产品推荐
相关产品推荐

