How to fix TextField popping back to root on cancel? - swift

I have a simple test watchOS application with a TextField in a secondary view (navigated to from a NavigationLink).
However, when the TextField is canceled or submitted, it will pop back out to the root view instead of staying in the current view. I can't find any information on this anywhere else. Any fixes?
ContentView:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
NavigationLink("what", destination: DestinationView())
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
DestinationView:
import SwiftUI
struct DestinationView: View {
#State private var message: String = ""
var body: some View {
TextField(
"Send Something...",
text: $message
)
}
}
struct DestinationView_Previews: PreviewProvider {
static var previews: some View {
DestinationView()
}
}

I found the issue..
I was using a NavigationView, which is deprecated. I removed it and now it's working as intended. (XCode 13.2.1, watchOS 8.3)
*facepalm*

Related

SwiftUI | Preview not updating on #Binding var value change

I am learning SwiftUI and tried to make a simple todo list but I'm having issues understanding why #Binding property doesn't update my preview.
The code is the following.
import SwiftUI
struct TodoRow: View {
#Binding var todo: Todo
var body: some View {
HStack {
Button(action: {
todo.completed.toggle()
}, label: {
Image(systemName: todo.completed ? "checkmark.square" : "square")
})
.buttonStyle(.plain)
Text(todo.title)
.strikethrough(todo.completed)
}
}
}
struct TodoRow_Previews: PreviewProvider {
static var previews: some View {
TodoRow(todo: .constant(Todo.sampleData[0]))
}
}
The preview doesn't update when I click the square button but the app works fine. Am I using it incorrectly?
EDIT:
Even without .constant(#), the preview doesn't work.
struct TodoRow_Previews: PreviewProvider {
#State private static var todo = Todo.sampleData[0]
static var previews: some View {
TodoRow(todo: $todo)
}
}
Found a solution in the article Testing SwiftUI Bindings in Xcode Previews.
In order for previews to change you must create a container view that holds state and wraps the view you're working on.
In my case what I've ended up doing was changing my preview to the following.
struct TodoRow_Previews: PreviewProvider {
// A View that simply wraps the real view we're working on
// Its only purpose is to hold state
struct TodoRowContainer: View {
#State private var todo = Todo.sampleData[0]
var body: some View {
TodoRow(todo: $todo)
}
}
static var previews: some View {
Group {
TodoRow(todo: .constant(Todo.sampleData[0]))
.previewDisplayName("Immutable Row")
TodoRowContainer()
.previewDisplayName("Mutable Row")
}
}
}

Navigation Back Button not hidden in SwiftUI

Do you know why the back button still showing after I wrote
.navigationBarBackButtonHidden(true)
it seem that Xcode doesn't read it correctly.
import SwiftUI
import CoreData
struct ContentView: View {
#State var goToHome: Bool = false
var body: some View {
NavigationStack {
VStack {
Welcome(gotoSomewhere: $goToHome)
}
.navigationDestination(isPresented: $goToHome) { Home() }
.navigationBarBackButtonHidden(true) //HERE IS THE NAVIGATIONBAR HIDDEN
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
I tried to write an NavigationLink but then shows me the alert:
... was deprecated in iOS 16.0: use NavigationLink(value:label:)
inside a NavigationStack or NavigationSplitView
This modifier .navigationBarBackButtonHidden(true) should be on home.
Home().navigationBarBackButtonHidden(true)
And yes the NavigationLink will be deprecated in the future, it's up to you whether it's important or not to include at the moment in your app.

Trouble getting EnvironmentObject to update the UI

I originally posted another question asking this in the context of a project I was trying to develop, but I can't even get it to work in a vacuum so I figured I'd start with the basics. As the title suggests, my EnvironmentObjects don't update the UI as they should; in the following code, the user enters text on the ContentView and should be able to see that text in the next screen SecondView.
EDITED:
import SwiftUI
class NameClass: ObservableObject {
#Published var name = ""
}
struct ContentView: View {
#StateObject var myName = NameClass()
var body: some View {
NavigationView {
VStack {
TextField("Type", text: $myName.name)
NavigationLink(destination: SecondView()) {
Text("View2")
}
}
}.environmentObject(myName)
}
}
struct SecondView: View {
#EnvironmentObject var myself: NameClass
var body: some View {
Text("\(myself.name)")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(NameClass())
}
}
However, the SecondView doesn't show the text that the user has written, but the default value of name (blank). What am I doing wrong here?
class NameClass: ObservableObject {
#Published var name = ""
}
struct ContentView: View {
#StateObject var myName = NameClass()
var body: some View {
NavigationView {
VStack {
TextField("Type", text: $myName.name)
NavigationLink(destination: SecondView()) {
Text("View2")
}
}
}.environmentObject(myName)
}
}
struct SecondView: View {
#EnvironmentObject var myself: NameClass
var body: some View {
Text("\(myself.name)")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(NameClass())
}
}

Why doesn't the Text update when using #Binding?

The Working works as expected.
But using the #Binding in the NotWorking example, doesn't seem to update the Text control. Why doesn't the #Binding version work, what am I missing here?
Initial Launch:
After Typing:
struct Working: View {
//Binding from #State updates both controls
#State private var text = "working"
var body: some View {
VStack {
TextField("syncs to label...", text: $text)
Text($text.wrappedValue)
}
}
}
struct NotWorking: View {
//Using the #Binding only updates the TextField
#Binding var text: String
var body: some View {
//This does not works
VStack {
TextField("won't sync to label...", text: $text)
Text($text.wrappedValue)
}
}
}
struct Working_Previews: PreviewProvider {
#State static var text = "not working"
static var previews: some View {
VStack {
Working()
NotWorking(text: $text)
}
}
}
Static #States don't work. It's the fact that it being static means that the struct Working_Previews isn't mutated when text is changed, so it won't refresh.
We can test this by changing from a PreviewProvider to an actual View:
struct ContentView: View {
#State static var text = "not working"
var body: some View {
VStack {
Working()
NotWorking(text: ContentView.$text)
}
}
}
This code gives the following runtime message:
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.
Thanks to #George_E. I define #State in a wrapper view and display that for the preview. The WrapperView simply displays the control that I want to preview but it contains the State.
struct Working_Previews: PreviewProvider {
//Define the State in a wrapper view
struct WrapperView: View {
#State var text = "Preview is now working!!!"
var body: some View {
NotWorking(text: $text)
}
}
static var previews: some View {
VStack {
Working()
//Don't display the view that needs the #Binding
//NotWorking(text: $text)
//Use the WrapperView that has #State and displays the view I want to preview.
WrapperView()
}
}
}

Why View.onDisappear not get called?

In iOS 13 beta 4, all View.onDisappear do not get called.
There is a navigation view and push to a Detail View. When a user tap navigation back button, the DetailView.onDisappear not get called.
How to fix it?
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: DetailView()) { Text("show") }
}
}
}
struct DetailView : View {
var body: some View {
Text("here")
.onDisappear {
print("onDisappear")
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
In the current beta onAppear() works great but onDisappear() doesn’t seem to get called.