import SwiftUI
struct ContentView: View {
@State var showSecond = false
@State var showThird = false
var body: some View {
VStack(spacing: 50) {
Text("FirstView")
Button("to SecondView") {
self.showSecond = true
}
.sheet(isPresented: $showSecond) {
VStack(spacing: 50) {
Text("SecondView")
Button("to ThirdView") {
self.showThird = true
}
.sheet(isPresented: self.$showThird) {
VStack(spacing: 50) {
Text("ThirdView")
Button("back") {
self.showThird = false
}
Button("back to FirstView") {
self.showThird = false
self.showSecond = false
}
}
}
Button("back") {
self.showSecond = false
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The above code transitions from FirstView to SecondView, and transitions from SecondView to ThirdView. And the "back" button in SecondView and ThirdView returns to the previous screen normally.
However, if you tap the "back to FirstView" button in the ThirdView, SecondView will be displayed without returning to FirstView. And after this operation, when you tap the "back" button of SecondView, it does not return to FirstView.
How can I return directly from ThirdView to FirstView?
Added February 19, 2020
I have added the solution code based on answers.
Solution1: based on Asperi's plan A.
struct ContentView: View {
@State var showSecond = false
@State var showThird = false
var body: some View {
VStack(spacing: 50) {
Text("FirstView")
Button("to SecondView") {
self.showSecond = true
}
.sheet(isPresented: $showSecond) {
VStack(spacing: 50) {
Text("SecondView")
Button("to ThirdView") {
self.showThird = true
}
.sheet(isPresented: self.$showThird) {
VStack(spacing: 50) {
Text("ThirdView")
Button("back") {
self.showThird = false
}
Button("back to FirstView") {
DispatchQueue.main.async {
self.showThird = false
DispatchQueue.main.async {
self.showSecond = false
}
}
}
}
}
Button("back") {
self.showSecond = false
}
}
}
}
}
}
Solution2: based on Asperi's plan B.
struct ContentView: View {
@State var showSecond = false
@State var showThird = false
@State var backToFirst = false
var body: some View {
VStack(spacing: 50) {
Text("FirstView")
Button("to SecondView") {
self.showSecond = true
}
.sheet(isPresented: $showSecond) {
VStack(spacing: 50) {
Text("SecondView")
Button("to ThirdView") {
self.showThird = true
}
.sheet(isPresented: self.$showThird, onDismiss: {
if self.backToFirst {
self.showSecond = false
}
}) {
VStack(spacing: 50) {
Text("ThirdView")
Button("back") {
self.showThird = false
self.backToFirst = false
}
Button("back to FirstView") {
self.showThird = false
self.backToFirst = true
}
}
}
Button("back") {
self.showSecond = false
}
}
}
}
}
}
Solution3: based on Joseph's advice.
struct ContentView: View {
@State var showSecond = false
@State var showThird = false
var body: some View {
GeometryReader { geometry in
ZStack {
VStack(spacing: 50) {
Text("FirstView")
Button("to SecondView") {
self.showSecond = true
}
}
.frame(width: geometry.size.width, height: geometry.size.height)
.background(Rectangle().foregroundColor(.white))
if self.showSecond {
VStack(spacing: 50) {
Text("SecondView")
Button("to ThirdView") {
self.showThird = true
}
Button("back") {
self.showSecond = false
}
}
.frame(width: geometry.size.width, height: geometry.size.height)
.background(Rectangle().foregroundColor(.white))
if self.showThird {
VStack(spacing: 50) {
Text("ThirdView")
Button("back") {
self.showThird = false
}
Button("back to FirstView") {
self.showThird = false
self.showSecond = false
}
}
.frame(width: geometry.size.width, height: geometry.size.height)
.background(Rectangle().foregroundColor(.white))
}
}
}
}
}
}
Directly no way for now (and IMO will never be - it is two modal sessions)... I found two possible approaches that maybe worth considering:
A. sequential close from one place
Button("back to FirstView") {
DispatchQueue.main.async {
self.showThird = false
DispatchQueue.main.async {
self.showSecond = false
}
}
}
B. sequential close from different places
.sheet(isPresented: self.$showThird, onDismiss: {
self.showSecond = false // with some additional condition for forced back
})...
...
Button("back to FirstView") {
self.showThird = false
}
Another /cough/ "solution" - possibly to a slightly different scenario - but anyway:
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: SecondView()) {
Text("Show Second View")
.font(.largeTitle)
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct SecondView: View {
@Environment(\.presentationMode) var presentationMode
@State private var showModal = false
var body: some View {
GeometryReader { geometry in
ZStack {
Rectangle()
.foregroundColor(.gray)
.frame(width: geometry.size.width, height: geometry.size.height)
Button(action: {
withAnimation {
self.showModal.toggle()
}
}) {
Text("Show Third View")
.font(.largeTitle)
.foregroundColor(.white)
}
if self.showModal {
VStack {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
} ) {
Text("Show First View")
.font(.largeTitle)
.foregroundColor(.white)
}
}
.frame(width: geometry.size.width, height: geometry.size.height)
.background(Rectangle().foregroundColor(.orange))
.transition(.move(edge: .trailing))
}
}
.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
Asperi's solutions work for dismissing two screens at once, but don't work for more screens. In such cases, we need to add DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300))
in both approaches like this:
self.showFourth = false
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
self.showThird = false
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
self.showSecond = false
}
}
.milliseconds(200)
is not enought.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With