11

I am designing an app that includes the function of retrieving JSON data and displaying a list of retrieved items in a FileBrowser type view. In this view, a user should be able to click on a folder to dive deeper into the file tree or click on a file to view some metadata about said file.

I've observed that while this is working, when I click on a file or folder then go back and click on it again, the NavigationLink is not triggered and I am stuck on the view until I click into a different NavigationLink.

Here is a gif demonstrating this problem.

Double Tap bug

As seen here, when I click on BlahBlah I am activating the NavigationLink and taken to BlahBlah, then when I navigate back and try to renavigate to BlahBlah, it becomes grey, registering that I clicked on it... but then never transports me there. Clicking on TestFile fixes this and allows me to navigate back to BlahBlah.

The list items are made with the following structs

private struct FileCell{
    var FileName: String
    var FileType: String
    var FileID: String = ""
    var isContainer: Bool
}

private struct constructedCell: View{

    var FileType: String
    var FileName: String
    var FileID: String

    var body: some View {
        return
            HStack{
                VStack(alignment: .center){
                    Image(systemName: getImage(FileType: FileType)).font(.title).frame(width: 50)
                }
                Divider()
                VStack(alignment: .leading){
                    Text(FileName).font(.headline)
                        .multilineTextAlignment(.leading)
                    Text(FileID)
                        .font(.caption)
                        .multilineTextAlignment(.leading)
                }
        }
    }
}

and called into view with navigationLinks as follows

List(cellArray, id: \.FileID) { cell in
                if (cell.isContainer) {
                    NavigationLink(destination: FileView(path: "/\(cell.FileID)", displaysLogin: self.$displaysLogin).navigationBarTitle(cell.FileName)){
                        constructedCell(FileType: cell.FileType, FileName: cell.FileName, FileID: cell.FileID)
                    }
                } else {
                    NavigationLink(destination: DetailView(FileID: cell.FileID).navigationBarTitle(cell.FileName)){
                        constructedCell(FileType: cell.FileType, FileName: cell.FileName, FileID: cell.FileID)
                    }
                }
            }

My NavigationView is initialized in the view above (the app has a tab view) this as follows

TabView(selection: $selection){
               NavigationView{
                    FileView(displaysLogin: self.$displaysLogin)
                        .navigationBarTitle("Home", displayMode: .inline)
                        .background(NavigationConfigurator { nc in
                            nc.navigationBar.barTintColor = UIColor.white
                            nc.navigationBar.titleTextAttributes = [.foregroundColor : UIColor.black]
                        })
                }
                .font(.title)
                .tabItem {
                    VStack {
                        Image(systemName: "folder.fill")
                        Text("Files")
                    }
                }
                .tag(0)
}

The NavigationConfigurator is a struct I use for handling the color of the navigationBar. It is set up like so

struct NavigationConfigurator: UIViewControllerRepresentable {
    var configure: (UINavigationController) -> Void = { _ in }

    func makeUIViewController(context: UIViewControllerRepresentableContext<NavigationConfigurator>) -> UIViewController {
        UIViewController()
    }
    func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<NavigationConfigurator>) {
        if let nc = uiViewController.navigationController {
            self.configure(nc)
        }
    }

}

I do not think my NavigationConfigurator is causing this? This bug also happens in other navigationLinks in the app, but it was easiest to demonstrate it here in the FileBrowser view.

This might be a bug in SwiftUI? If it is does anyone know a way to work around this? If it isn't, what am I doing wrong?

Vapidant
  • 2,532
  • 2
  • 12
  • 26
  • I am not really sure. But shouldn't you wrap `NavigationLink` inside a `NavigationView` and remove `NavigationView` from the `FileView`? I have seen some examples around here which do it that way. – gi097 Dec 09 '19 at 14:31
  • @GiovanniTerlingen Unless I'm misunderstanding what you are saying, this is what I'm doing. The `NavigationLink` is inside of `FileView` which is wrapped in a `NavigationView` therefore the `NavigationLink` is wrapped inside of a `NavigationView` – Vapidant Dec 09 '19 at 15:36
  • Please provide a minimum runnable project. I can't reproduce this, so probably it's not one of SwiftUI's bugs. – Mojtaba Hosseini Dec 09 '19 at 16:54
  • can you provide full source code so i can solve your issue – Hardik Bar Dec 10 '19 at 12:33
  • how did you prepare `cellArray`? – E.Coms Dec 10 '19 at 21:27
  • @Vapidant can you show FileView and DetailView code? – Trai Nguyen Dec 11 '19 at 03:52
  • What version of iOS are you using? There has been an issue with the described behaviour in iOS 13.3 beta. Should be solved in beta 4. https://stackoverflow.com/questions/59075206/simulator-vs-physical-device-navigationlink-broken-after-one-use/59208961#59208961 – simibac Dec 11 '19 at 17:18

1 Answers1

6

Had the same issue - try this. I would call this a hack to be removed when the bug in swiftUI is corrected.

struct ListView: View {
@State private var destID = 0
...
var body: some View {
...
  NavigationLink(destination: FileView(path: "/\(cell.FileID)", displaysLogin: self.$displaysLogin)
   .navigationBarTitle(cell.FileName) 
   .onDisappear() { self.destID = self.destID + 1 }
  ){
   constructedCell(FileType: cell.FileType, FileName: cell.FileName, FileID: cell.FileID) 
  }.id(destID)

Essentially it seems that in some circumstances (iOS 13.3 - Simulator?) the NavigationLink is not reset when the destination view is removed from the navigation stack. As a work around we need to regenerate the Navigation Link. This is what changing the id does. This corrected my issue.

However if you have NavigationLinks that are chained, that is a link that leads to another list of links, then this solution will create side effects; the stack returns to the origin at the second attempt to show the last view.

VJK
  • 574
  • 1
  • 5
  • 5
  • This bug is fixed in the latest IOS version. I'm marking this as the correct answer because of solving the issue as it stood, however, this shouldn't be an issue. I also like your explanation of the problem. Thank you. – Vapidant Dec 12 '19 at 14:53
  • When you say latest iOS version, you mean 13.4 or a beta version? – Ricardo Alves Dec 12 '19 at 17:08
  • I would like to know too. Which version works correct? I have 13.3 (which is the latest iOS right now) and the problem is still there. – mallow Dec 17 '19 at 08:19
  • I believe this bug was presented in a IOS 13.3 beta build but has been fixed as of IOS 13.3 Beta 4. – Vapidant Dec 18 '19 at 14:03
  • similar issue in iOS16.1. using this approach fixes it. I use .id(UUID()) so its always unique. – ngb Nov 17 '22 at 03:51