Ying
Ying

Reputation: 1

Add an Array element programminglly an pop up "Accessing State's value outside of being installed on a View." alert

I am trying to programminglly add a UI_Element struct to UI_Group array triggered by a socketio listening event, but there is an alert:

"Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update."

It seems that I shouldn't access a state's value(item.$name) in ContentView struct, but I don't know how to fix it~QQ

Also, I tried to move the UI_element struct into Service class or ContentView struct, and it couldn't work either.

final class Service: ObservableObject {
    private var manager = SocketManager(socketURL: URL(string: "http://x.x.x.x")!, config: [.log(true), .compress])
    
    @Published var messages = [String]()
    @Published var UI_Group = [UI_Element]()
    
    init() {
        let socket = manager.defaultSocket
        socket.on(clientEvent: .connect) {(data, ack) in
            print("Connected")
            socket.emit("Python Server Port", "Hi Python Server!")
        }
        
        socket.on("iOS Client Port") {[weak self] (data, ack) in
            if let data = data[0] as? [String: String],
               let rawMessage = data["msg"] {
                DispatchQueue.main.async {
                    //self?.messages.append(rawMessage)
                    switch rawMessage {
                    case "0":
                        print("新增中")
                        self?.UI_Group.append(UI_Element())
                        if self?.UI_Group != nil {
                            guard let num_element = self?.UI_Group.count else {
                                return print("無法新增")
                            }
                            print("已新增\(num_element)個元素")
                        }
                    default:
                        print("hello")
                    }
                }
            }
        socket.connect()
    }
}

struct ContentView: View {
        
    @ObservedObject var service = Service()
    
    var body: some View {
        
        VStack{
            ForEach(service.UI_Group, id: \.id){ item in
                TextField("Please enter text.", text: item.$name)
                    .padding()
                    .accessibilityLabel("請輸入元素名稱")
                    .textFieldStyle(.roundedBorder)
                    .offset(x: CGFloat(item.center_x), y: CGFloat(item.center_y))
            }
        }
    }
}

struct UI_Element: Identifiable {

    let id = UUID()
    let type:Int
    @State var name = ""
    @State var center_x = 0
    @State var center_y = 0
    @State var ui_state = 0

    init() {
        self.type = 0
        self.name = "請輸入元素名稱"
        self.center_x = 50
        self.center_y = 50
        self.ui_state = 0
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Upvotes: 0

Views: 73

Answers (1)

burnsi
burnsi

Reputation: 7754

Your assumption:

It seems that I shouldn't access a state's value(item.$name) in ContentView struct

is wrong. The message is because you are using @State in your model 'UI_Element'

Remove the @State wrappers. As the message says they are useless if they are not in a view.

Possible solution:

If you want to use your model in the ContentView with textfield you would need to make your struct 'UI_Element' a class and ObservableObject. Then use @Published on your name property.

Upvotes: 0

Related Questions