Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI NavigationLink pops automatically which is unexpected

I am having some issues with a NavigationLink on an iPad with split view (landscape). Here is an example:

screen recording

Here is the code to reproduce the issue:

import SwiftUI

final class MyEnvironmentObject: ObservableObject {
    @Published var isOn: Bool = false
}

struct ContentView: View {
    @EnvironmentObject var object: MyEnvironmentObject

    var body: some View {
        NavigationView {
            NavigationLink("Go to FirstDestinationView", destination: FirstDestinationView(isOn: $object.isOn))
        }
    }
}

struct FirstDestinationView: View {
    @Binding var isOn: Bool

    var body: some View {
        NavigationLink("Go to SecondDestinationView", destination: SecondDestinationView(isOn: $isOn))
    }
}

struct SecondDestinationView: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle(isOn: $isOn) {
            Text("Toggle")
        }
    }
}

// Somewhere in SceneDelegate
ContentView().environmentObject(MyEnvironmentObject())

Does anyone know a way to fix this? An easy fix is to disable split view, but that is not possible for me.

like image 918
Thomas Vos Avatar asked Dec 31 '19 14:12

Thomas Vos


1 Answers

Ok, here is my investigation results (tested with Xcode 11.2) and below is the code that works.

In iPad NavigationView got into Master/Details style, so ContentView having initial link is active and process bindings update from environmentObject, so refresh, which result in activating link of details view via same binding, thus corrupting navigation stack. (Note: this is absent in iPhone due to stack style, which deactivates root view).

So, probably this is workaround, but works - the idea is not to pass binding from view to view, but use environmentObject directly in final view. Probably this will not always be a case, but anyway in such scenarios it is needed to avoid root view refresh, so it should not have same binding in body. Something like that.

final class MyEnvironmentObject: ObservableObject {
    @Published var selection: Int? = nil
    @Published var isOn: Bool = false
}

struct ContentView: View {
    @EnvironmentObject var object: MyEnvironmentObject

    var body: some View {
        NavigationView {
            List {
                NavigationLink("Go to FirstDestinationView", destination: FirstDestinationView())
            }
        }
    }
}

struct FirstDestinationView: View {

    var body: some View {
        List {
            NavigationLink("Go to SecondDestinationView", destination: SecondDestinationView())
        }
    }
}

struct SecondDestinationView: View {
@EnvironmentObject var object: MyEnvironmentObject

    var body: some View {
        VStack {
            Toggle(isOn: $object.isOn) {
                Text("Toggle")
            }
        }
    }
}

backup

like image 129
Asperi Avatar answered Nov 05 '22 07:11

Asperi