为什么改变view的id时onAppear()没有被第二次调用?

问题描述 投票:0回答:2

我正在遵循“Thinking in Swift UI”一书的示例。

我正在创建一个可观察对象

final class Contact: ObservableObject, Identifiable {
    let id = UUID()
    @Published var name: String
    @Published var city: String
    @Published var profile: String
    
    init(name: String, city: String, profile: String) {
        self.name = name
        self.city = city
        self.profile = profile
    }
}

内容视图定义为选择联系人的按钮列表。它将联系人存储为普通数组属性

struct ContentView: View {
    @State var selection: Contact?
    var contacts: [Contact]
    
    var body: some View {
        HStack {
            ForEach(contacts) { contact in
                Button(contact.name) {
                    self.selection = contact
                }
            }
        }
        if let c = selection {
            Detail2(contact: c)
        }
    }
}

这里我们有详细信息视图,它调用 id 修饰符来更改视图的标识,并据说使“onAppear”再次被调用 - 这是行不通的

struct Detail: View {
    @ObservedObject var contact: Contact
    @StateObject var loader = ImageLoader()
    var body: some View {
        HStack {
            loader.imageView
            VStack {
                Text(contact.name).bold()
                Text(contact.city)
            }
        }
        .id(contact.id)
        .onAppear {
            loader.load(image: contact.profile)
        }
    }
}

在这里我嘲笑了Loader

class ImageLoader: ObservableObject {
    
    var image: String = ""
    init() {
        print("Initializer of Image Loader was called")
    }
    
    func load(image: String) {
        print("Brrr.... loading image")
        print("The image is: \(image)")
        self.image = image
    }
}

该应用程序被定义为内容视图,它传递联系人数组:

@main
struct SwiftUILearning2App: App {
    
    var body: some Scene {
        WindowGroup {
            ContentView(contacts: [ Contact(name: "name1", city: "city1", profile: "profile_1"),
                                   Contact(name: "name2", city: "city2", profile: "profile_2")])
        }
    }
}

为什么在分配新联系人时不调用 onAppear - 实际上 load.loader( ) 没有被调用? 根据书上的

.id(contact.id)
在Detail View上调用的修饰符应该能够使onAppear再次被调用。

在没有

.id(contact.id
修饰符的示例之后,书中指出:

“第一次尝试时,这似乎有效,但是当我们更改联系人对象(例如,通过选择不同的人)时,视图永远不会重新加载图像。解决此问题的最简单方法是告诉 SwiftUI 它应该更改视图的身份:“

这是完整代码的链接: https://gist.github.com/pbrewczynski/63f2dfdba96dec2efcf7d81d304622f6

ios swift swiftui
2个回答
7
投票

根据 swiftUI 文档:

当 id 参数指定的代理值发生更改时,视图的标识(例如其状态)将被重置。

视图没有调用onAppear()方法,因为它没有被再次创建,改变它的状态和参数。 我的建议是调用 onChange() 方法:

    .onChange(of: contact) { newValue in
        loader.load(image: newValue.profile)
    }

解决方案有很多,但id修饰符通常用于:

  1. 重置状态值
  2. 触发转换
  3. 提高列表视图性能

一篇文章这里我希望有帮助


4
投票

更好的方法是使用

.task(id:)
,例如

.task(id: contact.id) {
    downloads = await Contacts.download(contact)
}

它在出现和

contact.id
发生变化时调用。但最好的事情是,如果异步任务在联系人更改或消失之前尚未完成,则会自动取消。

如果您只想显示信息,请将下载内容存储在

@State
中。如果您想保留它,那么您可以将其设置在稍后执行的
@StateObject
上。

© www.soinside.com 2019 - 2024. All rights reserved.