Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NavigationLink freezes when trying to revisit previously clicked NavigationLink in SwiftUI

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?

like image 319
Vapidant Avatar asked Dec 06 '19 16:12

Vapidant


1 Answers

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.

like image 92
VJK Avatar answered Nov 01 '22 17:11

VJK