问题
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:
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)
}
}
}
}
}
来源:https://stackoverflow.com/questions/63839270/swiftui-getting-textfield-to-array-in-foreach