0

I am trying to lay out a bunch of CustomTextViews which can toggle between a SwiftUI TextField or Text view.

Consider this example.

import SwiftUI

struct ContentView: View {
    
    @StateObject var doc: Document = Document()
    
    var body: some View {
        
        ForEach(doc.lines, id: \.self) { line in
            HStack {
                ForEach(line, id: \.self) { word in
                    CustomTextView(text: word, document: doc)
                        .fixedSize()
                }
                Spacer()
            }
        }
        .frame(width: 300, height: 300)
        .background(.cyan)
    }
}

struct CustomTextView: View {
    @State var text: String
    @State var isEditing: Bool = false
    @ObservedObject var document: Document
    
    var body: some View {
        if isEditing {
            TextField("", text: $text)
                .onSubmit {
                    isEditing.toggle()
                    
                    // NOTE: reset document anytime a word ends in "?"
                    if text.last! == "?" {
                        print("resetting")
                        document.lines = [["Reset"]]
                        print(document.lines)
                    }
                }
        } else {
            Text(text)
                .onTapGesture {
                    isEditing.toggle()
                }
        }
    }
}


class Document: ObservableObject {
    @Published var lines: [[String]] = [["Hello"]]
}

What I want to happen is that I should be able to indefinitely reset the text. But instead, the view only resets correctly once (see gif). All further updates to reset document.lines are not correct, even though the print statements show that the @Published property lines is clearly changing.

What am I doing wrong?

Steven-Carrot
  • 2,368
  • 2
  • 12
  • 37
  • If the intention was to edit lines in document then this code will not do that, because CustomTextView works with copy of word. It is needed deep refactoring here to transfer binding and introduce new model, because usage word as id would recreate everything once you try to enter any character. – Asperi Jul 26 '22 at 04:51
  • @Asperi How do I get the document line editing function then? Would you mind elaborating on the deep refactoring? – A is for Ambition Jul 26 '22 at 05:34

1 Answers1

0

You need to update the textfield text with document.lines[index] where index is index of line into document.lines array. So you need to update like below.

struct CustomTextView: View {
    @State var isEditing: Bool = false
    @ObservedObject var document: Document
    var body: some View {
        if isEditing {
            TextField("", text: $document.lines[index])
                .onSubmit {
                    isEditing.toggle()
                    
                    // NOTE: reset document anytime a word ends in "?"
                    if document.lines[index].last! == "?" {
                        print("resetting")
                        document.lines = [["Reset"]]
                        print(document.lines)
                    }
                }
        } else {
            Text(text)
                .onTapGesture {
                    isEditing.toggle()
                }
        }
    }
}
Dipendra
  • 34
  • 4