Best way to handle a TabView inside a NavigationView in SwiftUI
Asked Answered
T

0

6

I am developing an app in Swift with SwiftUI.

I have this setup where I put a TabView inside a NavigationView and used the navigationBarTitle on the tabView. I did this because if I put the NavigationView inside the TabView, I cannot make the Tab bar disappear when I go to a NavigationLink: it seems currently impossible with swiftUI.

The current problem with my setup is that since I put the .navigationBarTitle on the TabView, the title does not change when I change the tab view and I cannot reset the .navigationBarTitle on the subview for some reason.

Here is a code to explain what I am saying:

ContentView :

NavigationView {
  TabView {
    SomeSubView().tabItem() { Text("SomeSubView") }
    Text("dummy2").tabItem() { Text("dummy2") }
    Text("dummy3").tabItem() { Text("dummy3") }
  }.navigationBarTitle("hello")
}

SomeSubView:

List(somearray) { element in
  NavigationLink(destination: elementDetails()) {
    SomeRowView()
  }
}

Here is a preview (the code above isn't exactly my code but just an example):

Preview

Now I woukd like to put the .navigationBarTitle inside the SomeSubView() like this:

List(somearray) { element in
  NavigationLink(destination: elementDetails()) {
    SomeRowView()
  }
}.navigationBarTitle("title2")

Instead of choosing a global title for the TabView. So in that case I would have something like:

NavigationView {
  TabView {
    SomeSubView().tabItem() { Text("SomeSubView") }
  }
}

Then I choose a .navigationBarTitle inside the SomeSubView() but it doesn't work (the title bar just does not appear, instead the tab title is changed and that's not what I want).

So I finally came up with this kind of setup:

@State private var title = Text("Default_Title")

var body: some View {
  NavigationView {
    TabView {
      SomeSubView().tabItem { Text("SomeSubView") }.onAppear { self.title = Text("new_title") }
      Text("dummy2").tabItem { Text("dummy2") }
      Text("dummy3").tabItem { Text("dummy3") }
    }.navigationBarTitle(title)
  }
}

This way, I can dynamically change the title when I click on a tab view and it works fine but I also need to set different .navigationBarItems for each tab view so I did the same thing for the leading and trailing parameters of .navigationBarItems, like this:

@State private var title = Text("Default_Title")
@State private var leading = NavBar_leading(type: "FirstView")
@State private var trailing = NavBar_trailing(type: "FirstView")

var body: some View {
  NavigationView {
    TabView {
      SomeSubView().tabItem { Text("SomeSubView") }.onAppear {
          self.title = Text("new_title")
          self.leading = NavBar_leading(type: "FirstView")
          self.trailing = NavBar_trailing(type: "FirstView")
      }
      Text("dummy2").tabItem { Text("dummy2") }
      Text("dummy3").tabItem { Text("dummy3") }
    }.navigationBarTitle(title).navigationBarItems(leading: leading, trailing: trailing)
  }
}

As you can see, I dynamically update the leading and trailing parameters of .navigationBarItems to set different items (buttons in my case) for each tab view. The NavBar_leading and NavBar_trailing view return a Button text type depending on the parameter I passed, like this:

struct NavBar_leading: View {
    var type: String
    
    var body: some View {
        if type == "FirstView" {
            return Button(action: {
                print("hello1 empty")
                }) {
                    Text("")
            }
        } else if (type == "SecondView") {
            return Button(action: {
                print("hello2")
                }) {
                    Text("SomeButton")
            }
        } else {
            return Button(action: {
                print("hello3 empty")
                }) {
                    Text("")
            }
        }
    }
}

Now the problem is that I want to return sometimes a Button Text, but sometimes a Button Image from the same NavBar_leading view but Swift does not allow me to have different types returned from the same view. I thought the Button view was one type but it seems that depending on what you put inside, it becomes another type.

I heard that the Any view type isn't a good practice in Swift and has performance impact, so I'm looking for a way to achieve what I want here.

I explained everything because I am open to any other suggestion, like if I can do something totally better I am open to restart from scratch, so here are my questions:

  • Is my handling with the TabView inside the NavigationView a good idea for what I needed to do? Is there something better that I do not know?
  • Is there any other way to display a different title for each tab view instead of a global title that I update like I did?
  • Is there any way to return different Button types from a view without using an Any view type? Anything better that I can do?

Thank you

Tamelatameless answered 9/8, 2020 at 8:37 Comment(2)
This might help you: SwiftUI Hide TabView bar inside NavigationLink viewsLandgrave
Thank you. https://mcmap.net/q/540327/-swiftui-hide-tabview-bar-inside-navigationlink-views is a way better version of what I already did, and it also shows how to handle the multiple return type with @ViewBuilder which is way better than my AnyView() solution. Well for now, it looks like this is the best solution to achieve this. I'll check this post sometimes to see if someone found something better.Tamelatameless

© 2022 - 2024 — McMap. All rights reserved.