I've shortened my code as much as possible. The problem is that when I click on the "Generate" button inside CreateView, I am transferred to WaitView for a tenth of a second, after which I am transferred back to CreateView.
I quickly realized that the problem was a modification of the DataManager, since when I deleted this code:
dataManager.data.append(SomeData(data: newData))
this problem disappeared. But this obviously doesn’t suit me, because I need to change the data.
I also noticed that if you remove this code from ListView, then everything will work:
NavigationLink(destination: CreateView(dataManager: dataManager)) {
Text(data_item.data)
}
I can't imagine how this should affect this at all. What could be the problem?
code:
import Foundation
import SwiftUI
struct SomeData: Identifiable {
let id = UUID()
var data: String
}
class DataManager: ObservableObject {
@Published var data: [SomeData]
init() {
data = [SomeData(data: "test")]
}
}
struct ContentView: View {
@StateObject var dataManager: DataManager = DataManager()
var body: some View {
NavigationView {
ListView(dataManager: dataManager)
.navigationTitle("First View")
}
}
}
struct ListView: View {
@ObservedObject var dataManager: DataManager
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: CreateView(dataManager: dataManager)) {
Text("GENERATE")
.font(.title)
.foregroundColor(.white)
.padding()
.frame(width: 200, height: 200)
.background(Color.blue)
.clipShape(Circle())
}
.padding(100)
Spacer()
VStack (alignment: .leading) {
Text("History")
.font(.title3)
List {
ForEach(dataManager.data) {data_item in
NavigationLink(destination: CreateView(dataManager: dataManager)) { //When I remove this line everything works
Text(data_item.data)
}
}
}
.listStyle(.automatic)
.cornerRadius(20)
}
.padding()
}
}
}
}
struct CreateView: View {
@ObservedObject var dataManager: DataManager
@State var fieldData: String = "some data here"
var body: some View {
UITableView.appearance().backgroundColor = .clear
return VStack {
NavigationLink(destination: WaitView(dataManager: dataManager, newData: fieldData)) {
Text("Generate")
.padding()
.background(Color.red)
}
}
.navigationBarTitleDisplayMode(.inline)
}}
struct WaitView: View {
@ObservedObject var dataManager: DataManager
let newData: String
var body: some View {
Text("Wait for something...")
.navigationTitle("Third View")
.onAppear {
dataManager.data.append(SomeData(data: newData))//When I remove this line everything works
print("adding")
}
}
}
I solved it by embedding in a NavigationView :
var body: some View {
NavigationView {
// UITableView.appearance().backgroundColor = .clear
return VStack {
NavigationLink(destination: WaitView(dataManager: dataManager, newData: fieldData)) {
Text("Generate")
.padding()
.background(Color.red)
}
}
}
.navigationBarTitleDisplayMode(.inline)
}
Note: return is useless.
But now, the problem is you get 2 back buttons. I solved it by hidding the first back button when needed.
- cerate a hideFirstBackButton state Bool
- pass it to a Binding to CreateView where it will be set to true
struct CreateView: View {
@ObservedObject var dataManager: DataManager
@State var fieldData: String = "some data here"
@State var hideFirstBackButton = false // We can hide the back button of this NavigationView
var body: some View {
NavigationView {
// UITableView.appearance().backgroundColor = .clear
// return
VStack {
NavigationLink(destination: WaitView(dataManager: dataManager, newData: fieldData, hideFirstBackButton: $hideFirstBackButton)) {
Text("Generate")
.padding()
.background(Color.red)
}
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden(hideFirstBackButton)
.onAppear {
UITableView.appearance().backgroundColor = .clear
}
}
}
Now, you need to restore when you leave WaitView:
struct WaitView: View {
@Environment(\.isPresented) var isPresented // To detect we left the view in order to restore back button https://stackoverflow.com/questions/61930915/swiftui-detecting-the-navigationview-back-button-press
@ObservedObject var dataManager: DataManager
let newData: String
@Binding var hideFirstBackButton : Bool
var body: some View {
Text("Wait for something...")
.navigationTitle("Third View")
.onAppear {
hideFirstBackButton = true
dataManager.data.append(SomeData(data: newData))//When I remove this line everything works
print("adding")
}
.onChange(of: isPresented) { newValue in
if !newValue {
print("WaitView is dismissed")
hideFirstBackButton = false // We restore the button
}
}
}
}
Works well, but there may be a much simpler solution.
Don't use NavigationLink, but manage directly the appearance of WaitView:
@State var showWaitView = false
var body: some View {
if showWaitView {
WaitView(dataManager: dataManager, newData: fieldData)
} else {
Button(action: {
self.showWaitView.toggle()
}) {
Text("Generate")
.padding()
.background(Color.red)
}
}
}
You would have to create a back button "manually".