Jumpy toolbar items in NavigationView - swiftui

I tried to use a custom view as the navigation title inside a NavigationView. Every time when the detail view is popped up, the toolbar items are always resized quickly in a second. I also tested adding a button there as ToolbarItem, the same. Am I misuing something?
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink {
Text("Detail")
.toolbar {
VStack {
Text("title")
Text("subtitle")
}
}
} label: {
Text("Detail")
}
}
}
}

You're missing Toolbar item inside .toolbar.
.toolbar {
ToolbarItem(placement: .principal) { //You're missing this.
VStack {
Text("title")
Text("subtitle")
}
}
}

Related

SwiftUI Toolbar In a TabBar View

My first view has a NavigationView with a Tab Bar View
struct TestView: View {
var body: some View {
NavigationView {
TabTestView()
}
}
}
In the Tab Bar, I have two views, say TestView1 and TestView2.
struct TabTestView: View {
var body: some View {
TabView {
TestView1()
.tabItem {
Label("Test 1", systemImage: "list.dash")
}
TestView2()
.tabItem {
Label("Test 2", systemImage: "plus")
}
}
}
}
I now need to add a toolbar button to TestView1. Here is my attempt to do so:
struct TestView1: View {
var body: some View {
Text("Hello World")
.navigationTitle("Test View 1")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
print("Toolbar button click")
}
}
}
}
}
While this adds the Navigation Title to the page, the toolbar button is not added, as shown in the screenshot below:
If I, however, add the toolbar to the TabTestView, as below, the toolbar button does show up.
struct TabTestView: View {
var body: some View {
TabView {
TestView1()
.tabItem {
Label("Test 1", systemImage: "list.dash")
}
TestView2()
.tabItem {
Label("Test 2", systemImage: "plus")
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
print("Toolbar button click")
}
}
}
}
}
This is a problem - the code to be executed on the button click depends upon variables in TestView1, and adding the toolbar to the TabTestView doesn't feel right, semantically the toolbar "belongs" in TestView1.
Any thoughts would be greatly appreciated.
Once I had working code, I realized I had seen this before. It appears to be a bug in SwiftUI. TabView and NavigationView don't play well together. You will find a lot of my answer will say one NavigationViews at the top of the view hierarchy, which is what you have done. That will not work in this instance. The workaround is to put a NavigationViews in each of the views in the tabs separately for this to work. You will still get all of the correct NavigationViews behavior for each of those hierarchies, but it is duplicative. I don't think I ever filed a Radar on it, but I will today and will post the Radar number here.
struct TestView: View {
var body: some View {
// NavigationView { //Remove this NavigationView
TabTestView()
// }
}
}
struct TabTestView: View {
var body: some View {
TabView {
TestView1()
.tabItem {
Label("Test 1", systemImage: "list.dash")
}
TestView2()
.tabItem {
Label("Test 2", systemImage: "plus")
}
}
}
}
struct TestView1: View {
var body: some View {
NavigationView { // Add here
Text("Hello World 1")
.navigationTitle("Test View 1")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
print("Toolbar button click")
}
}
}
}
}
}
struct TestView2: View {
var body: some View {
NavigationView { // Add here
Text("Hello World 2")
.navigationTitle("Test View 2")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
print("Toolbar button click")
}
}
}
}
}
}
The Radar number is FB9727010.

Why the ToolbarItem Button is not tappable?

I have a navigationView with the ToolbarItem containing a button.
This button is not tappable correctly. To move to the next screen i have to tap underneath.
As you can see from the Debug View Hierarchy:
Here is how i am adding the button to the toolbar:
var body: some View {
ZStack { ... }
.navigationBarTitle(Text("Format"), displayMode: .inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Next") {
setCanvas()
}
}
}
}
This view is being pushed with a NavigationLink
The solution is to add an .id to the Button in order to force the Button to render it self. I was showing a modal before this and it had that position.
var body: some View {
ZStack { ... }
.navigationBarTitle(Text("Format"), displayMode: .inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Next") {
setCanvas()
}
.id(UUID())
}
}
}

NavigationView inside TabView going to next page by default

I am wrapping my navigaton view inside a tab view.
but when I load the app, my navigation view is always going to its next page by default.
How can I stop this ? I spent so much time but could not figure it out.
here is my view
ZStack {
Color.red
.edgesIgnoringSafeArea(.all)
VStack {
TabView(selection:$selectedTab) {
NavigationView {
VStack {
Text("Hello")
}
.edgesIgnoringSafeArea(.all)
}
.tag(1)
VStack {
Text("Two")
}
.tag(2)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
FirstView is a navigation view like this
NavigationView {
VStack {
...
}
.sheet(isPresented: $myForm, onDismiss:{ }) {
NewFormView(...)
}
.navigationBarHidden(true)
}
It appears like this

SwiftUI ToolbarItem doesn't present a View from a NavigationLink

I don't know if this is a bug or I am doing something wrong here. I've added a new button on the Navigation bar that would present a new view.
struct MyView: View {
#ObservedObject var viewModel = MyViewModel()
var body: some View {
List(viewModel.data, id: \.name) { data in
NavigationLink(destination: MyDetailView(data: data.name)) {
Text(data.name)
}
}
.listStyle(InsetGroupedListStyle())
.edgesIgnoringSafeArea(.all)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(destination: MyDetailView()) {
Text("New Element")
}
}
}
}
}
This is being tested on the newest iOS 14 beta (beta 6) and Xcode 12 (beta 6). As far as I know a Navigation Link presents fine the new view when on a List but in the toolbar as shown that's not the case. The button on the toolbar it's visible and active but doesn't trigger showing the new view.
I found using an HStack with an empty text as the first element also works, it lets the navigationLink act correctly.
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
HStack {
Text("")
NavigationLink(destination: SettingsView()) {
Image(systemName: "gear")
.font(.title)
}
}
}
NavigationLink should be inside NavigationView. Toolbar is not in NavigationView, put buttons in it.
So assuming you have somewhere in parent
NavigationView {
MyView()
}
here is a solution:
struct MyView: View {
#ObservedObject var viewModel = MyViewModel()
#State private var showNew = false
var body: some View {
List(viewModel.data, id: \.name) { data in
NavigationLink(destination: MyDetailView(data: data.name)) {
Text(data.name)
}
}
.listStyle(InsetGroupedListStyle())
.background(
NavigationLink(destination: MyDetailView(), isActive: $showNew) {
EmptyView()
}
)
.edgesIgnoringSafeArea(.all)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("New Element") {
self.showNew = true
}
}
}
}
}
Using Asperi solution may not work if your navigation link directs to a view with keyboard input.
After navigation link, toolbar in the new view loaded correctly but when providing input with keyboard and dismissing keyboard all toolbar items disappears.
The solution is to place NavigationLink not in View but in navigationBarItems, example:
.navigationBarItems(
leading:
NavigationLink(
destination: Creator(),
isActive: $showCreator,
label: {
Text("")
}))

How do I hide navigation bar in the tab bar's specific view in SwiftUI?

XCode11 beta3,
MacOS Catalina 10.15 Beta(19A501i)
I want to hide tabBar when push~ Any command will very helpful, Thanks~
Click me to show gif image
:
struct ContentView : View {
var body: some View {
WhenNavigationViewIsRootView()
}
}
struct WhenNavigationViewIsRootView : View {
var body: some View {
NavigationView {
TabbedView{
Rectangle().foregroundColor(.green)
.tag(0).tabItem{Text("Page1")}
VStack {
List {
ForEach(0...2) { i in
NavigationLink(
destination: Text("\(i)"),
label: {Text("\(i)")})
}
}
}.tag(1).tabItem{Text("Page2")}
}
.navigationBarHidden(true)
}
}
}
If you want to hide the navigation bar in a TabbedView, you have to set .navigationBarHidden(true) on the views nested inside TabbedView. This isn't enough, however. For whatever reason, SwiftUI requires that you first set the navigation bar title before you can hide the navigation bar.
NavigationView {
TabbedView{
Rectangle()
.foregroundColor(.green)
.tag(0)
.tabItem{
Text("Page1")
}
.navigationBarTitle("")
.navigationBarHidden(true)
List(0...2) { i in
NavigationLink(destination: Text("\(i)")) {
Text("\(i)")
}
}
.tag(1)
.tabItem {
Text("Page2")
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}