SwiftUI,@State 由 Struct 提供,构建编辑器

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

好吧,我在搜索中找不到这个确切的问题,所以如果我忽略了搜索深处的一些东西,请原谅我。

前提: 现有系统。添加编辑器。数据在 Swift Structs 中传递,通过现有的 REST API 内容提取和更新。我对上游的东西没有太多的控制权。或者更准确地说,我目前没有时间重新架构上游的任何内容(在最近继承的项目上进行单独开发)。

问题: 如前所述,数据在结构中被推送。这是一个(简化的)示例:

struct Person: Codable, Identifiable {
    let id: Int
    let firstname: String
    let lastname: String
}

这些在视图中显示和可选择,用户可以点击其中一个来选择它,并(大概)显示编辑器。导航到编辑器是最终模型,但在本例中,为了简单起见,我只是将其推入单一/主视图中。

我的编辑器接收 Person,将值复制到 @State 变量中以进行显示和编辑,然后当用户单击“保存”时,更新将被推送到 REST 服务器,然后编辑器被关闭,并且结构在读取时被替换后退/刷新等。非常正常的工作流程。

注意:本示例中没有保存按钮和 API 代码,以保持简洁。

但是,在我的编辑器中,我从传入的 Person 结构中复制值,并且在视图的 Init 中,您可以验证值更改(请参阅此处的 Print 语句),但实际视图并未更新。大概身体没有重画?

在完全埋头于 Python 服务器端的东西大约一年后,我回到了 SwiftUI,我简直不敢相信我这么生疏。我一定错过了一些简单的东西。

我错过了什么? (代码如下)

import SwiftUI

struct ContentView: View {
    
    @State private var selectedPerson: Person?
    
    // could also do this if optionals and nils are scary
    // @State private var selectedPerson: Person = Person(
    //    id: 0,
    //    firstname: "No one",
    //    lastname: "Selected"
    //) // or whatever
    
    
    // pretend these are populated from an API REST call 
    private var people: [Person] = [
        Person(id: 1, firstname: "Bob", lastname: "Bobberson"),
        Person(id: 2, firstname: "Joe", lastname: "Smith"),
        Person(id: 3, firstname: "Taylor", lastname: "Swift") // because apparently that's what you do now
    ]
    
    var body: some View {
        VStack {
            if let sp = self.selectedPerson {
                Text("Selected: \(sp.firstname)")
                    .padding()
            } else {
                Text("No one is selected")
            }
            
            ScrollView {
                ForEach(people){ person in
                    Text(person.firstname)
                        .padding()
                        .onTapGesture {
                            selectedPerson = person
                        }
                } // all works as expected. Trouble starts below.


                if let _ = self.selectedPerson {
                    PersonEditor(person: selectedPerson!)
                }

                // or this if it's more desired or more "swifty"
                if let e = self.selectedPerson {
                    PersonEditor(person: e)
                }
            }
            
        }
        .padding()
    }
}


struct PersonEditor: View {
    // this doesn't need to exist, just including for completeness.
    private var person: Person
    
    @State private var edtFirstname: String = ""
    @State private var edtLastname: String = ""
    
    init(person: Person){
        // this doesn't need to exist. included for completeness.
        self.person = person
        
        self._edtFirstname = State(wrappedValue: person.firstname)
        self._edtLastname = State(wrappedValue: person.lastname)
        // changing to initialValue to wrappedValue seems to not matter

        // this runs and prints correct.
        print("current first name is \(self.edtFirstname)")
        // so why does this show the fed-in value after setting the state
        // but the Text and TextField objects in the body do not?
        // suggests body isn't being called despite state changing.
    }
    
    var body: some View {
        VStack {
            Text("Editing \(person.firstname)") // this updates correctly (as expected)
            Text("Editing \(self.edtFirstname)") // this does not
            
            TextField("firstname", text: $edtFirstname) // this never changes despite init running
            TextField("lastname", text: $edtLastname) // this never changes despite init running
        }
    }
}
swift swiftui struct state
1个回答
0
投票

我会像你一样通过注入 Person 来解决这个问题,但使用

.onChange
而不是
init
来设置属性

所以在 PersonEditor 中

let person: Person

然后是修改器

.onChange(of: person, initial: true) { _, newPerson in
    self.edtFirstname = person.firstname
    self.edtLastname = person.lastname
    print("current first name is \(self.edtFirstname)")
}
© www.soinside.com 2019 - 2024. All rights reserved.