SwiftUI navigation bar items going haywire when swipe back fails
Asked Answered
C

3

16

I have a ListView, TasksView and then EditView.

The flow goes like this: you have a list cell, you tap that which takes you to TasksView When a row is tapped in TasksView it takes you to EditView.

When I half swipe back to navigate to previous view the navigation bar go bezerk and overlaps. It happens mainly if I use a navigationBarItem - (button).

In TasksView (detailView) there is a list and some navigation bar modifiers:

ZStack {
   List {
    // code here
 }

}
.onAppear { UITableView.appearance().separatorStyle = .none }
.onDisappear { UITableView.appearance().separatorStyle = .none }
.background(Color("primaryBackground"))
.edgesIgnoringSafeArea(.bottom)
.navigationBarTitle("\(listItem.name ?? "")", displayMode: .inline)
.navigationBarItems(trailing:
    Button(action: {self.deleteList()}) {
              Image(systemName: "trash.circle.fill")
          }
     )

Same can be said for EditView, that when you half swipe on EditView to get back to TasksView, the same thing happens.

Here is the bug in action:

enter image description here

Anyone got any idea how to go about fixing this error?

EDIT:

struct TasksView: View {
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var taskControl: TaskControl
@EnvironmentObject var addNewTaskData: AddNewTaskViewDataFlow
@ObservedObject var dataPickerData : DatePickerDataFlowV2


@FetchRequest(entity: ONList.entity(), sortDescriptors: []) var listsDataSource: FetchedResults<ONList>
@Environment(\.managedObjectContext) var listMOC

   var listItem: ONList
   var keyboardPublisher: AnyCancellable

 // defining the presentationMode here 

 .......

ZStack {
        NavigationLink(destination: ListOptions(listItem: listItem), tag: 1, selection: self.$navigationSelectionTag) {
            EmptyView()
        }

        Color("primaryBackground")
            .edgesIgnoringSafeArea(.top)

        //... more code here
      }

    .onAppear {

        UITableView.appearance().separatorStyle = .none
        self.taskControl.taskViewerSeperator = true
    }
    .onDisappear {
        UITableView.appearance().separatorStyle = .none
        print("Bye Task View")

        if UIDevice.current.userInterfaceIdiom == .phone {
          self.taskControl.taskViewerSeperator = false
        }

        self.keyboardPublisher.cancel()
    }
    .navigationBarTitle("\(listItem.name ?? "")", displayMode: .inline)
    .background(Color("primaryBackground"))
    .edgesIgnoringSafeArea(.bottom)
    .navigationBarItems(trailing:
        HStack {
        Button(action: {
        self.navigationSelectionTag = 1
    }, label: {
        Image(systemName: "gear")
    })

       Button(action: {
        self.deleteList()

       }) {
          Image(systemName: "trash.circle.fill")

    }.padding()

    }
  )

I am not even using the presentationMode in the deleteList() function in order to dismiss the current view when it gets deleted. However, I am still getting the same glitch as shown in the gif above.

UPDATE:

struct TestingCoreData: View {

var body: some View {
    NavigationView {
        VStack {
            NavigationLink(destination: DestinationView()) {
                 Text("This is a test")
            }


        }.navigationBarTitle(Text("Master"), displayMode: .inline)
         .navigationBarItems(trailing:
            Button(action: {
                print("tapped")
            }) {
                Text("Button")
            }

        )
    }
}
}

struct DestinationView: View {

@Environment(\.presentationMode) var presentationMode
var body: some View {

List {
    Text("DestinationView")
        .padding(.top, 100)
        .navigationBarTitle(Text("Destination"), displayMode: .inline)
        .navigationBarItems(trailing: Button(action: {
            self.presentationMode.wrappedValue.dismiss()
        }, label: {
            Text("second")
        }))
}

}
}

The code above reproduces the bug. Where when you click "This is a test" button and then you swipe back a bit, and then go back to the last View, you will see the navigation bar going haywire!

Causal answered 29/5, 2020 at 10:24 Comment(1)
How to create a Minimal, Reproducible ExampleKashmiri
D
15

I found a simple solution to fix your problem, add this to the NavigationView

    NavigationView {
        ....
    }.navigationViewStyle(StackNavigationViewStyle())

Edit: This is the code I use to test my answer on real device and various simulators. This fixes the problem, if you find a device where this does not work let me know.

import SwiftUI

struct ContentView: View {
var body: some View {
    NavigationView {
        VStack {
            NavigationLink(destination: DestinationView()) {
                Text("This is a test")
            }
        }.navigationBarTitle(Text("Master"), displayMode: .inline)
            .navigationBarItems(trailing:
                Button(action: {
                    print("tapped")
                }) {
                    Text("Button")
            })
    }.navigationViewStyle(StackNavigationViewStyle())
}
}

struct DestinationView: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
    List {
        Text("DestinationView")
            .padding(.top, 100)
            .navigationBarTitle(Text("Destination"), displayMode: .inline)
            .navigationBarItems(trailing: Button(action: {
                self.presentationMode.wrappedValue.dismiss()
            }, label: {
                Text("second")
            }))
    }
}
} 
Demetra answered 11/6, 2020 at 8:3 Comment(4)
this doesn't fix the issue.Unprejudiced
I've updated my answer to show exactly the code I'm using. I've tested this on real device and various simulators, without any problems. Could you tell us on what device/system this code does not work for you. I've used Xcode 11.5 and 11.6 beta on mac 10.15, target ios 13.5 and mac catalyst.Demetra
How will this translate to the iPad version? With StackNavigationViewStyle you are essentially removing the master detail view layout for iPads.Causal
I've tested it on iPad, it works very well with or without my addition. Have you a device where the solution does not work?Demetra
K
3

I had struggled with the same issue. So I've decided to make a custom nav bar with a view modifier, and I'm using it. But I think I found another solution now. Try to add .isDetailLink(false) right after the NavigationLink.

NavigationLink(destination: DestinationView()) {
    Text("This is a test")
}
.isDetailLink(false)

Karolyn answered 20/1, 2021 at 13:14 Comment(0)
M
0

Using .toolbar with ToolbarItem instead of older .navigationBarItems solves the problem.

Please also notice that I have moved navigation modifiers to List from Text.

Here is a working and tested example based on your UPDATE code:

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: DestinationView()) {
                    Text("This is a test")
                }
            }
            .navigationBarTitle(Text("Master"), displayMode: .inline)
            
            // use .toolbar instead of .navigationBarItems
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing){
                    Button("Button", action: {
                        print("tapped")
                    })
                }
            }
            
        }
    }
}

struct DestinationView: View {
    
    @Environment(\.presentationMode) var presentationMode
    var body: some View {
        
        List {
            Text("DestinationView")
                .padding(.top, 100)
        }
        .navigationBarTitle(Text("Destination"), displayMode: .inline)
        // use .toolbar instead of .navigationBarItems
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing){
                Button("second", action: {
                    self.presentationMode.wrappedValue.dismiss()
                })
            }
        }
        
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Mccrea answered 29/4, 2021 at 3:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.