SwiftUI如何将列表项重构为子视图

Posted

技术标签:

【中文标题】SwiftUI如何将列表项重构为子视图【英文标题】:SwiftUI how to refactor list item into a subview 【发布时间】:2019-11-01 23:20:41 【问题描述】:

我一直在尝试为一个简单的 SwiftUI 项目创建更小的可重用视图。但是,我得到了意想不到的结果,并且很难理解为什么。

这是我强调问题的人为示例

使用这个例子,你应该如何创建一个可重用的RowView来代替列表中的HStack

var body: some View 
    List(vm.gradings)  item in
        // how should you refactor out this ??
        HStack 
            Text(item.grade)
            Text(item.pass ? "Pass" : "Fail")
        
    

我正在使用 Firebase 来触发模型更改。即将成绩更改为通过或失败,并期望列表详细信息视图显示该更改。

上述方法按预期工作,而在列表视图中,当数据更改时,行会发生变化。

但是,当我尝试重构该视图时,只有一个按预期工作。理想情况下,我想传入 Grading 对象,但这不会导致视图刷新。

尝试不同的子视图及其结果

var body: some View 
    List(vm.gradings)  item in

        // Works
        RowA(grade: item.grade, pass: item.pass)

        // Fails
        // RowB(item: item)

        // Fails
        // RowC(item: item)

        // Default - works as expected
        // HStack 
        //  Text(item.grade)
        //  Text(item.pass ? "Pass" : "Fail")
        // 
    

这是 3 行选项

// Works
struct RowA: View 
    var grade: String
    var pass: Bool

    var body: some View 
        HStack 
            Text(grade)
            Text(pass ? "Pass" : "Fail")
        
    


// Fails
struct RowB: View 
    var item: Grading

    var body: some View 
        HStack 
            Text(item.grade)
            Text(item.pass ? "Pass" : "Fail")
        
    


// Fails
struct RowC: View 
    @State var item: Grading

    var body: some View 
        HStack 
            Text(item.grade)
            Text(item.pass ? "Pass" : "Fail")
        
    

解决方法

// Allows me to pass in just the model
struct RowD: View 
    private var item = Grading()
    private var grade: String = ""
    private var pass: Bool = false

    init(item: Grading) 
        self.item = item
        self.grade = item.grade
        self.pass = item.pass
    

    var body: some View 
        HStack 
            Text(grade)
            Text(pass ? "Pass" : "Fail")
        
    

我的视图模型

class StudentGradingsUIViewModel: ObservableObject 
    @Published var detailedstudent: DetailedStudent

    var gradings: [Grading] 
        detailedstudent.student.gradings
    

    init(student: DetailedStudent) 
        self.detailedstudent = student
    

【问题讨论】:

【参考方案1】:

如果您使用@state,我发现您所做的一切至少在这些视图部分都是正确的。

       / Works
        struct RowA: View 
            var grade: String
            var pass: Bool

            var body: some View 
                HStack 
                    Text(grade)
                    Text(pass ? "Pass" : "Fail")
                
            
        

        //// Fails
        struct RowB: View 
            var item: Grading

            var body: some View 
                HStack 
                    Text(item.grade)
                    Text(item.pass ? "Pass" : "Fail")
                
            
        
        //
        //// Fails
        struct RowC: View 
            @State var item: Grading

            var body: some View 
                HStack 
                    Text(item.grade)
                    Text(item.pass ? "Pass" : "Fail")
                
            
        

        struct  Grading: Identifiable
            var id = UUID()
            var grade : String
            var pass  : Bool
        

        struct TempGradingView: View 

            @State var gradings: [Grading] = [Grading.init(grade: "50", pass: false),Grading.init(grade: "100", pass: true),Grading.init(grade: "100", pass: true)]


        var body: some View 
            Group
            Button("change", action: 
                self.gradings[0].grade = "70"
            )
            List(gradings)  item in

                // Works
               // RowA(grade: item.grade, pass: item.pass)

                // Fails
                 RowB(item: item)

                // Fails
                // RowC(item: item)

                // Default - works as expected
                // HStack 
                //  Text(item.grade)
                //  Text(item.pass ? "Pass" : "Fail")
                // 
                
        
        

所以我认为有问题的部分,即@publisher var。部分。

【讨论】:

理想情况下,我只想传递该项目。我确实找到了一种 hack 方式来传递模型并让它刷新 - 生病了 你需要@publisher / 和可观察对象。 我有这些 - 我想只是设置不理想【参考方案2】:

这行得通 - 想知道我认为它是一种体面的方法

private func gradingRow(_ grading: Grading) -> some View 
    HStack 
        Text(grading.grade)
            .foregroundColor(grading.pass ? Color(UIColor.systemGreen) : Color(UIColor.systemRed))
        Spacer()
        Text(DateString.dateAsDDMMYYTime(from: grading.date))
            .font(.subheadline)
    

【讨论】:

以上是关于SwiftUI如何将列表项重构为子视图的主要内容,如果未能解决你的问题,请参考以下文章

如何将字典项分别添加到 SwiftUI 列表中

如何在 swiftui 中为子视图设置私有状态变量?

SwiftUI - 如何基于@ObservedObject 为每个列表视图项创建编辑视图

SwiftUI:如何让列表视图中的图像与列表项的操作不同?

如何在 SwiftUI 中创建一个列表视图,每个列表项都是一个 WKWebView(我的实现非常慢并且有问题)

为啥单击 SwiftUI 中的列表项后列表视图项变为灰色?