SwiftUI NavigationLink navigationBarHidden with sheet flashes screen - swiftui

Sorry for the bad quality. The content shows how the screen is refreshed when a new view is shown in SwiftUI.
The Welcome screen has the navigation bar hidden:
var body: some View {
SwiftUI.Form {
NavigationLink(destination: FlashingView { self.done = true }) {
Text(UI.doSomething)
}
...
}
}
.navigationBarTitle("")
.navigationBarHidden(true)
and the FlashingView:
var body: some View {
SwiftUI.Form {
.....
}
}
.navigationBarTitle("My Title Showing", displayMode: .inline)
Any idea how to work around this?
Sample project on Dropbbox: https://www.dropbox.com/s/00yq1qns7ywds0a/NavigationTest.zip?dl=0
Update: it turns out the cause of the issue is the sheet modifier. Search for // Comment/uncomment to test in the code to test.

Related

SwiftUI extra space at top of list above section header. On testing device only

I have been trying to figure out what is causing the space at the top of the screen in my production app, so I made this test app to see if it is a bug or not. The code works as intended on a simulator but when a testing device runs the code it adds extra space. The space goes away after you start scrolling, and does not comeback until the view reloads. I have tried restarting the device and other devices. I took out .navigationTitle and .navigationBarTitleDisplayMode and it did not fix the problem. So far my best guess is that there is some problem with changing the section header in .onAppear(). Changing it to .task() seems to be a workaround for now.
struct DetailView: View {
#State var item: Item
#State private var headerText = "Header"
var body: some View {
List {
Section(header: Text("\(headerText)")) {
Text("Text")
}
HStack {
Text("Red Text")
}.listRowBackground(Color.red)
// Change to .task instead
}.onAppear {
headerText = "Change Header"
}
}
}
Edit: Here is the code for the list view, it is the default new project setup.
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink(destination: DetailView(item: item), label: {
Text(item.timestamp!, formatter: itemFormatter)
})
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
Text("Select an item")
}
}
In my case this behaviour was caused by .navigationViewStyle(StackNavigationViewStyle()).
Looks like it's a bug with NavigationView in SwiftUI 4 since it wasn't like this before.
If you are on iOS 16 use NavigationStack instead NavigationView. This fixed all my problems and I didn't even need to use the navigationViewStyle anymore.
NavigationView is deprecated in favor of NavigationStack.

Toolbar menu is closed when updates are made to UI in SwiftUI

I've noticed a problem with the toolbar menu closing when updates are made to the UI. I made a test project to verify this is the case, and the code is shown below. Here are the steps to reproduce the issue.
Open the menu
Wait 5s before an update is made to the UI (in this case the name)
The menu will automatically close
I found this problem is directly caused by the button action code: selection = String(describing: View2.self). Here are the test cases I tried to find this out.
If I comment the selection code, then the problem doesn't exist.
To test that it isn't because it's linked to the NavigationLink, I commented the NavigationLink and uncommented the selection code, but the problem still exists.
If I replace the selection code with a simple print statement, then the problem doesn't exist.
I'm using Xcode 12.5.1. Tested on iOS 14.
How can I fix this problem?
struct ContentView: View {
#State var name = "John"
#State var selection: String?
var body: some View {
NavigationView {
ZStack {
NavigationLink(destination: View2(), tag: String(describing: View2.self), selection: $selection){}
Text("Hello \(name)")
.padding()
}
.toolbar {
Menu {
Button {
selection = String(describing: View2.self)
} label: {
Text("Edit")
}
Button {
selection = String(describing: View2.self)
} label: {
Text("Help")
}
} label: {
Text("Menu")
}
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
name = "Hunter"
}
}
}
}
}

navigationBarHidden combined with inline display mode causes jump

I have a parent view, in which I don't want any navigation bar, and a child view, where I want an inline navigation bar.
If I navigate to the child view, then back again. The top of the list will have a weird jump effect when scrolling upwards.
I'm sure this is a bug, but does anyone have a workaround? If it helps, I can get access to the underlying UIScrollView/UINavigationController components - but I'm not sure if any of the properties would help.
struct ContentView: View {
var body: some View {
NavigationView {
List( 0...50, id: \.self ) { i in
NavigationLink(destination: HelloView()) {
Text("\(i)")
}
}
.navigationBarHidden( true )
}
}
}
struct HelloView: View {
var body: some View {
Text("Hello")
.navigationBarTitle("Hello", displayMode: .inline)
}
}
I realize this is odd, but this can be alleviated by setting the navigationBarTitle property. In your desired case I would recommend the following:
struct ContentView: View {
var body: some View {
NavigationView {
List( 0...50, id: \.self ) { i in
NavigationLink(destination: HelloView()) {
Text("\(i)")
}
}
.navigationBarTitle("", displayMode: .inline) /// <<--- Insert this line
.navigationBarHidden( true )
}
}
}
By setting the title attribute to blank and using the inline display mode, it rids the view of the large title and actually hides the view correctly.

SwiftUI Navigation bar items disappear on iOS 14

Discovered in my app that navigation bar items in some views disappear when orientation of the device changes. This seems to occur only in a view that is opened using NavigationLink, on main view navigation bar items work as expected. It appears that something has changed between iOS 13.7 and iOS 14.2 related to this. Also, it does not seem to matter whether using leading or trailing items, both disappear.
Example snippet where this occurs:
struct ContentView: View {
var detailView: some View {
Text("This is detail view")
.navigationBarTitle("Detail view title", displayMode: .inline)
.navigationBarItems(trailing: Button(action: {}, label: {
Image(systemName: "pencil.circle.fill")
}))
}
var body: some View {
NavigationView {
NavigationLink(
destination: detailView,
label: {
Text("Open detail view")
})
.navigationBarTitle("Main view")
}.navigationViewStyle(StackNavigationViewStyle())
}
}
The issue occurs only when running on a real device. (iPhone 11 in my case) On simulator everything works as expected.
Anyone else seen similar issues? Workarounds/fixes?
.navigationBarTitle and .navigationBarItems are being deprecated. I think that the best "fix" is to switch to .toolbar
It's a weird issue but I guess there is a hack to make it work.
SwiftUI doesn't call body property when rotations happen. So you can add #State UIDeviceOrientation property to your view and update it every time orientation changes. One tricky thing is to use that property somewhere in the body of the view since SwiftUI smart enough to ignore #State that is not used in the body.
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(
destination: DetailsView(),
label: {
Text("Open detail view")
}).navigationBarTitle("Main view")
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct DetailsView: View {
#State var orientation: UIDeviceOrientation = UIDevice.current.orientation
var body: some View {
Text("This is detail view")
.navigationBarTitle("Detail view title")
.navigationBarItems(trailing: button)
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
orientation = UIDevice.current.orientation
}.background(Text("\(orientation.rawValue)"))
}
var button: some View {
Button(action: { print("123") }, label: {
Image(systemName: "pencil.circle.fill")
}).id("123")
}
}
In my experience when I change a Button in the toolbar from disabled to enabled, they disappear. But if I scroll to the bottom of the View, they re-appear. If I am already at the end of the View when the button is enabled, it acts normally, until I then scroll away from the bottom, the button again disappears.
Try scrolling to the bottom of your view, if you use landscape.

SwiftUI: Back button disappears when clicked on NavigationLink

I'm trying to add a NavigationLink at the top of the screen, but once I click it, it prompts me to the result and the Back button disappears.
SwiftUI Code:
NavigationView {
VStack {
NavigationLink (destination: Text("COOL")) {
Text("COOL")
}
Spacer()
}
.navigationBarHidden(true)
.navigationBarTitle(Text("Home"))
//.edgesIgnoringSafeArea([.top, .bottom])
}
The back button disappears after clicking on the NavigationLink: https://gyazo.com/9d39936c849f570a05687e41096ddeca
There is some glitch IMHO, when you use both .navigationBarHidden(true) and .navigationBarTitle(Text("Some text)). If you remove the last one, back button works as usual. Nevertheless I tried to return back button in your code snippet. It still has glitch while returning to first view, but back button don't disappear. I hope it will help and you will go further from here:
struct NotHiddenBackButton: View {
#State var hiddingNavBar = true
#State var goToSecondView = false
var body: some View {
NavigationView {
NavigationLink(destination: ViewWithBackButton(hiddingNavBar: $hiddingNavBar), isActive: $goToSecondView) {
VStack {
Text("COOL")
.onTapGesture {
self.hiddingNavBar = false
self.goToSecondView = true
}
Spacer()
}
}
.navigationBarHidden(hiddingNavBar)
.navigationBarTitle(Text("Home"))
}
}
}
struct ViewWithBackButton: View {
#Binding var hiddingNavBar: Bool
var body: some View {
Text("Second view")
.navigationBarTitle("Second view")
.onDisappear() {
self.hiddingNavBar = true
}
}
}
I believe this was a bug that has now been fixed in iOS 14