首页 > 解决方案 > SwiftUI - 滚动时,行子视图中的 StateObject 被初始化并多次取消初始化

问题描述

我想将 a 添加到显示在分段列表中的行的子视图中,以便在更新属性StateObject时执行一些工作并更新子视图(例如下载图像)。该工作应在子视图出现时执行 ( )。@PublishedStateObject.onAppear { }

该行被初始化一次,并在滚动时出现/消失。添加项目时,视图会再次初始化(不一定重新绘制,但如果没有任何更改,这是正常的)。同样,子视图初始化一次,并在滚动时出现/消失。添加项目时,视图会再次初始化(不一定重新绘制,但如果没有任何更改,这是正常的)。

但是StateObject当我滚动时,子视图中的 被初始化和 deinit 多次。它不应该,因为它是 aStateObject而不是ObservedObject. 为什么会这样?

在此处输入图像描述

当我将它添加StateObject到行中时,它StateObject会正确初始化一次(即使将新项目添加到列表中)并且永​​远不会 deinit。

在此处输入图像描述

下面提供的示例代码。

import SwiftUI

struct SectionTest: Identifiable {
    var id: String { title }
    var title: String
    var items = [Item]()
}

struct Item: Identifiable {
    var id: String { name }
    var name: String
    var section: String
}

class Loader: ObservableObject {
    private var name: String
    init(name: String) {
        self.name = name
        print("Loader • init \(name)")
    }
    
    func load() {
        print("Loader • load \(name)")
    }
    
    deinit {
        print("Loader • deinit \(name)")
    }
}

struct LoaderView: View {
    @StateObject private var loader: Loader
    let name: String
    
    init(name: String) {
        print("LoaderView • init \(name)")
        self.name = name
        self._loader = StateObject(wrappedValue: Loader(name: "LoaderView ".appending(name)))
    }
    
    var body: some View {
        Rectangle()
            .frame(height: 200)
            .onAppear { loader.load() }
            
            .onAppear {
                print("LoaderView • appear \(name)")
            }
            .onDisappear {
                print("LoaderView • disappear \(name)")
            }
    }
}

struct RowView: View {
    @StateObject private var loader: Loader
    let name: String
    
    init(name: String) {
        print("RowView • init \(name)")
        self.name = name
        self._loader = StateObject(wrappedValue: Loader(name: "RowView ".appending(name)))
    }
    
    var body: some View {
        VStack(alignment: .leading) {
            Text(name)
                .foregroundColor(.red)
            LoaderView(name: name)
        }
        .onAppear {
            print("RowView • appear \(name)")
        }
        .onDisappear {
            print("RowView • disappear \(name)")
        }
    }
}

struct StateObjectListView: View {
    
    @State private var items = [
        Item(name: UUID().uuidString, section: "First"),
        Item(name: UUID().uuidString, section: "First"),
        Item(name: UUID().uuidString, section: "First"),
        Item(name: UUID().uuidString, section: "Second"),
        Item(name: UUID().uuidString, section: "Second"),
        Item(name: UUID().uuidString, section: "Third"),
        Item(name: UUID().uuidString, section: "Third"),
        Item(name: UUID().uuidString, section: "Third")
    ]
    
    private var sections: [SectionTest] {
        Dictionary(grouping: items) { $0.section }
            .map { SectionTest(title: $0.key, items: $0.value.sorted { $0.name < $1.name }) }
            .sorted { $0.title < $1.title }
    }
    
    var body: some View {
        NavigationView {
            VStack {
                Button("Add") {
                    let section = ["First", "Second", "Third"].randomElement()!
                    items.append(Item(name: UUID().uuidString, section: section))
                }
                ScrollView {
                    LazyVStack {
                        ForEach(sections) { section in
                            Section(header: Text(section.title).font(.caption2)) {
                                ForEach(section.items) { item in
                                    RowView(name: item.name)
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

标签: swiftuiswiftui-list

解决方案


推荐阅读