0

The goal is to have a TextField that takes characters as they're being entered and display them below the TextField in a 'pile'. By pile I mean, each new character is displayed on top of the last one. Piling the letters up is easy enough - I use ForEach to loop over an array inside a ZStack. To get the characters in the array, I used a custom binding. It works, but the problem is that the characters get repeated each time a new character is typed. For example, if the user types CAT, first the C will appear, then CA on top of the C, then CAT on top of the CA and the C. In other words, there are six characters stacked up when I only wanted three.

struct ContentView: View {
    
    @State private var letter = ""
    @State private var letterArray = [String]()
    
    var body: some View {
        
        let binding = Binding<String>(
            get: { self.letter },
            set: { self.letter = $0
                self.letterArray.append(self.letter)
        })
        return VStack {
            
            TextField("Type letters and numbers", text: binding)

            ZStack {
                ForEach(letterArray, id: \.self) { letter in
                    Text(letter)
                }
            }
        }
    }
} 

UPDATE: I was making the problem a little more difficult that it was. By using an ObservableObject I was able to separate the logic from the view AND simplify the code. First I created a view model. Now, each time the user types something into a TextField, it's it's caught by didSet and converted into an array of characters. Note, I had to use map to convert from a character array to a string array because ForEach doesn't work with characters.

class ViewModel: ObservableObject {
    @Published var letterArray = [String]()
    @Published var letter = "" {
        didSet {
            letterArray = Array(letter).map { String($0) }
        }
    }
}

In ContentView, I only need @ObservedObject var vm = ViewModel()Then I refer to the variables using vm.letter or vm.letterArray

1 Answer 1

1

Uptown where I get your problem may the below code help you I modified your code as below;

struct ContentView: View {

@State private var letter = ""
@State private var letterCounter = 0
@State private var letterArray = [String]()

var body: some View {
    let binding = Binding<String>(
        get: { self.letter },
        set: { self.letter = $0
            if self.letter.count > self.letterCounter {
                if let lastLetter = self.letter.last{
                    self.letterArray.append(String(lastLetter))
                }
            }else{
                _ = self.letterArray.removeLast()
            }
            self.letterCounter = self.letter.count
    })
    return VStack {
        
    TextField("Type letters and numbers", text: binding)
        VStack {
            ForEach(letterArray, id: \.self) { letter in
                Text(letter)
            }
        }
    }
}

}

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.