How to properly clear TextField on submit in SwiftUI? - swiftui

I want to log some text and clear the TextField after submitting without changing the value of the logged text. How I can achieve that?
Here's example of my code:
#State private var loggedText: String = ""
#State private var showLoggedText = false
var body: some View {
VStack {
if showLoggedText {
Text(loggedText)
}
HStack {
TextField(
"Add new set",
text: $loggedText
)
.onSubmit {
showLoggedText = true
loggedText = ""
}
}
}

Going off one of the comments. It seems as though that you need to assign loggedText to another variable like so:
#State private var loggedText: String = ""
#State private var showLoggedText = false
var body: some View {
VStack {
if showLoggedText {
Text(loggedText)
}
HStack {
TextField(
"Add new set",
text: $loggedText
)
.onSubmit {
showLoggedText = true
newText = loggedText
}
}
}
This will allow you to submit the text without it being cleared. The loggedText = "" clears everything that you had. Keep in mind that this code won't "lock" whatever's in loggedText. If you change the field after submitting. The text will be updated again.

Related

Issue in Setting Value to #State variable in SwiftUI [duplicate]

Hello I am running into a problem here and I do not have a consistent behavior between my .sheet() view when running on ios13 or ios14
I got a view like this :
#State private var label: String = ""
#State private var sheetDisplayed = false
///Some code
var body: some View {
VStack {
Button(action: {
self.label = "A label"
self.isDisplayed = true
}) {
Text("test")
}
}.sheet(isPresented: $sheetDisplayed, onDismiss: {
self.label = ""
}) {
Text(self.label)
}
}
On ios 13 this work as expected btn click -> set label -> call sheet -> display "A label" in a Text view.
On ios14 I got an empty string in self.label when in sheet closure, hence it does not display anything.
Did I missed something ? Is it an iOS 14 bug or did I had it wrong on ios13 and that got corrected.
PS: I have a couple of other variables that are passed in the closure I simplified it.
Your code have expectation of view update/creation order, but in general it is undefined (and probably changed in iOS 14).
There is explicit way to pass information inside sheet - use different sheet creator, ie. .sheet(item:...
Here is working reliable example. Tested with Xcode 12 / iOS 14
struct ContentView: View {
#State private var item: Item?
struct Item: Identifiable {
let id = UUID()
var label: String = ""
}
var body: some View {
VStack {
Button(action: {
self.item = Item(label: "A label")
}) {
Text("test")
}
}.sheet(item: $item, onDismiss: {
self.item = nil
}) {
Text($0.label)
}
}
}
This is some really strange behaviour in iOS 14, which doesn't appear to be documented.
Using the other answer here and the comment on this thread, I used #Binding to solve the issue as it seemed the cleanest and most SwiftUI-esq solution.
I have no idea why this behaviour has changed, and it seems less intuitive than before, so I'm assuming its a bug!
An example:
struct MainView: View {
#State private var message = ""
#State private var showSheet = false
var body: some View {
Button(action: {
self.message = "This will display the correct message"
self.showSheet = true
}, label: {
Text("Test Button")
})
.sheet(isPresented: self.$showSheet) {
SheetView(message: self.$message)
}
}
}
struct SheetView: View {
#Binding var message: Int
var body: some View {
Text(self.message)
}
}
The behaviour changed with SwiftUI 2.0, so it affects MacOS 11 as well, just adding a binding to the view fixes it even when that binding is never used, which makes me think this is an implementation bug.
Additionally just using the details state variable in a Text() within the body of the view also fixes it.
struct MyViewController : View {
#State var details: String?
#State var showDetails = false
// #Binding var havingAbindingFixesIt: String?
var body: some View {
VStack {
// Text(details ?? "")
Text("Tap here for details")
.onTapGesture {
self.details = "These are the details"
self.showDetails.toggle()
}
.sheet(isPresented: $showDetails) { Text(details ?? "") }
}
}
}

why can't I change the value of a var named coma after and if statement

I'd like to change the value of comma to true if a string contains a comma
xcode swfitui
struct ContentView: View {
#State var dos = ""
#State var coma : Bool = false
var body: some View {
Form {
TextField("dosis", text: $dos)
if dos.contains(",") == true {
coma = true
}
}
}
}
You cannot put that type code wherever you like in a View. The view expect to show other views, and your if is not returning a view. Try this approach where your if is in a modifier, or a function:
struct ContentView: View {
#State var dos = ""
#State var coma = false
var body: some View {
Form {
TextField("dosis", text: $dos)
.onChange(of: dos) { doz in
if doz.contains(",") {
coma = true
}
print("--> dos: \(dos) coma: \(coma)")
}
}
}
}
You can also use: .onSubmit {...}

Presenting a Sheet modally, #State value is not changing

I have tried much time on it but I couldn't figure out why it is not working.
The problem is when I tap the button, the new value inside the sheet is not updated. It always show the same value which is set up in start.
#State var value:String = "empty"
#State var explorePageIsEnabled:Bool = false
VStack{
Button("tap me"){
value = "the new one"
exploreStatusIsEnabled.toggle()
}
.sheet(isPresented: $exploreStatusIsEnabled, content: {
Text(value)
})
}
Deployment target is IOS 14+
Create a separate struct view for text and use Binding.
struct SheetView: View {
#Binding var value: String
var body: some View {
Text(value)
}
}
struct ContentView: View {
#State private var value: String = "empty"
#State private var explorePageIsEnabled: Bool = false
var body: some View {
VStack{
Button("tap me"){
value = "the new one"
explorePageIsEnabled.toggle()
}
.sheet(isPresented: $explorePageIsEnabled, content: {
SheetView(value: $value)
})
}
}
}

SwiftUi: Navigating to a secondary page through Button Alert Confirmation

I'm trying to figure out how to navigate to my secondary screen through a button alert. Firstly, the user inputs some fields through textfields in the primary screen.
At the bottom of this screen the user presses a submit button which then pops up an alert asking whether they would like to be taken to the secondary screen or cancel to not be taken. All the fields that have been entered through the primary screen then get passed onto the secondary screen. The user than has the option to navigate back to the primary screen if necessary once in the secondary screen.
Here's what I've tried:
struct View1: View {
#State var txtField1 : String = ""
#State var txtField2: String = ""
#State var txtField3: String = ""
#State var txtField4: String = ""
#State var txtField5 : String = ""
#State private var showingAlert = false
#State private var showingView = false
var body: some View {
HStack{
Button(action: {
self.showingAlert = true
}) {
Text("Submit")
.alert(isPresented:$showingAlert) {
Alert(title: Text("Would you like to go to second screen?"), message: Text("The second screen will pass all data from the first screen."), primaryButton:.destructive(Text("Continue")){
self.showingView = true
}, secondaryButton: .cancel(Text("Cancel")))
}
}
}.popover(isPresented: $showingView){
NavigationView{
View2(txtField1: self.$txtField1, txtField2: self.$txtField2, txtField3: self.$txtField4, txtField5: self.$txtField5)
}
}
When using the code above it does navigate to my secondary screen (View2) however its like a sheet. View2 has no navigation properties back to View1 and this is what I'm trying to achieve. Any help on this matter is greatly appreciated, Thank you!
To get the Back button you need a NavigationLink vs a popover. You can just "hide" the NavigationLink next to your Button
import SwiftUI
struct ConfirmNavView: View {
#State var txtField1 : String = ""
#State var txtField2: String = ""
#State var txtField3: String = ""
#State var txtField4: String = ""
#State var txtField5 : String = ""
#State private var showingAlert = false
#State private var showingView = false
var body: some View {
NavigationView{
HStack{
Button(action: {
self.showingAlert = true
}) {
Text("Submit")
.alert(isPresented:$showingAlert) {
Alert(title: Text("Would you like to go to second screen?"), message: Text("The second screen will pass all data from the first screen."), primaryButton:.destructive(Text("Continue")){
self.showingView = true
}, secondaryButton: .cancel(Text("Cancel")))
}
}
NavigationLink("View2", destination: View2(txtField1: self.$txtField1,
txtField2: self.$txtField2,
txtField3: self.$txtField3,
txtField4: self.$txtField4,
txtField5: self.$txtField5), isActive: $showingView).hidden().frame(width: 0, height: 0)
}
}
}
}
struct View2: View {
#Binding var txtField1 : String
#Binding var txtField2: String
#Binding var txtField3: String
#Binding var txtField4: String
#Binding var txtField5 : String
var body: some View {
VStack{
TextField("txtField1", text: $txtField1)
TextField("txtField2", text:$txtField2)
TextField("txtField3", text:$txtField3)
TextField("txtField4", text:$txtField4)
TextField("txtField5", text:$txtField5)
}
}
}
struct ConfirmNavView_Previews: PreviewProvider {
static var previews: some View {
ConfirmNavView()
}
}

SwiftUI value in text doesn’t change

I don’t understand why the Text Value doesn’t change. if I remove the TextField, the Text value change :/ is there something about combine or SwiftUI I am missing ?
struct ContentView2: View{
#State private var numTouches: Int = 0
#State private var num: String = ""
var body: some View {
VStack{
Button("Touch me pls"){
self.numTouches += 1
}
Text("\(numTouches)")
TextField("Hello enter a number", text: $num)
}.onReceive(Just(num)) { newValue in
if newValue == "" {
self.numTouches = 0
} else {
self.numTouches = Int.init(newValue)!
}
}
}
}
What happens is that when a button is touched, it increases numTouches, which causes the view's body to re-render. .onReceive subscribes to the Just publisher that immediately publishes the value num, which is empty "" in the beginning, and that sets numTouches back to 0.
It sounds that you have really just a single variable, which is being updated from two places:
via TextField
via Button's action
So, keep it as single #State var numTouches: Int:
struct ContentView2: View{
#State private var numTouches: Int = 0
var body: some View {
VStack{
Button("Touch me pls"){
self.numTouches += 1
}
Text("\(numTouches)")
TextField("Hello enter a number",
text: $numTouches, formatter: NumberFormatter()))
// .keyboardType(.numberPad) // uncomment for number pad keyboard
}
}
}