When adopting NavigationStack with a TabView embedded, then using a Toolbar and ToolbarItems, I get unexpected behaviour when switching between tabs.
Expectation: I should be able to have different ToolbarItems depending on which tab is selected and when switching between tabs, the ToolbarItem in the position I specify per tab should change.
Behaviour: The ToolbarItems append to the specified position as you move across each tab.
#State var selectedIndex: Int
#State var path = NavigationPath()
var body: some View {
ZStack {
NavigationStack(path: $path) {
TabView(selection: $selectedIndex) {
Group {
TextView(tabName: .constant("Home"))
.tabItem {
Label("Home", systemImage: "house")
}
.tag(1)
TextView(tabName: .constant("Contacts"))
.tabItem {
Label("Contacts", systemImage: "person.3")
}
.tag(1)
TextView(tabName: .constant("Settings"))
.tabItem {
Label("Settings", systemImage: "gearshape")
}
.tag(1)
}
.toolbarBackground(Color.red)
.toolbarBackground(.visible, for: .navigationBar)
.toolbarColorScheme(.dark, for: .navigationBar)
}
}
}
}
}
And for the view for each tab (this is a simplified example, I have tried using seperate views but get the same behaviour);
#Binding var tabName: String
var body: some View {
Text("Tab \(tabName)")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
print("Profile ")
} label: {
Image(systemName: "person.circle")
}
}
}
}
}
Screenshot of Tab 2
I can't find any information in the developer docs on how to resolve this issue.
Related
I'm a beginner at swiftui. I need to add an exit button to .navigationBarItems. How can I add this button in the parent NavigationView to show this button on all children's views?
// a simple example on my question
struct FirstView: View {
var body: some View {
NavigationView {
ZStack{
TabView{
SubExampleViewOne()
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
SubExampleViewTwo()
.tabItem {
Image(systemName: "bookmark.circle.fill")
Text("Bookmark")
}
}
}
//here I have added a toolbar and it is perfectly visible in tabitem
//this is what I am trying to achieve, the visibility of the button on all pages
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
ButtonExitView()
}
}
}
}
}
something strange - if I add NavigationLink in this way, Image and Text("Home") are visible twice
and the ToolbarItem is no longer on the new page
struct SubExampleViewOne: View {
var body: some View {
Text("This is hime page!")
.padding()
NavigationLink(destination: SubExampleViewThree()){
Text("Navigation link")
}
}
}
struct SubExampleViewTwo: View {
var body: some View {
Text("Hello, world!")
.padding()
}
}
struct SubExampleViewThree: View {
var body: some View {
Text("This is Navigation link")
.padding()
}
}
struct ButtonExitView: View {
var body: some View {
Button(action: {}, label: {Image(systemName: "arrowshape.turn.up.right.circle")})
}
}
after learning about TabView, I thought that there should be a similar solution for the top of the page
You have to add the button to each child view separately.
And you should use .toolbar and .toolBarItem because .navigationBarItems is deprecated.
I created a TabView with 4 items but with iOS 13.x only the first view is displayed correctly.
When I click on another item's icon, the view is not shown correctly but the app only shows a white view. If I run the app on iOS > 14 I can correctly view all the views.
TabView implementation:
struct ContentView: View {
private enum Tab: Hashable {
case discovery
case qrcode
case devices
case settings
}
#State private var selectedTab: Tab = .discovery
var body: some View {
NavigationView {
TabView(selection: $selectedTab) {
DiscoveryView()
.tabItem {
VStack {
Image(systemName: "lock.rotation.open")
Text("Discovery")
}
}
.tag(0)
QrCodeView()
.tabItem {
VStack {
Image(systemName: "qrcode.viewfinder")
Text("QrCode")
}
}
.tag(1)
DevicesView()
.tabItem {
VStack {
Image(systemName: "qrcode.viewfinder")
Text("My devices")
}
}
.tag(2)
SettingsView()
.tabItem {
VStack {
Image(systemName: "gear")
Text("Settings")
}
}
.tag(3)
}
}
}
Implementation of one of the views:
struct QrCodeView: View {
var body: some View {
Text("QrCode")
}
}
Where am I doing wrong?
It might be a reason of selection... selection and tag types should be the same, so try
#State private var selectedTab: Tab = .discovery
var body: some View {
NavigationView {
TabView(selection: $selectedTab) {
DiscoveryView()
.tabItem {
VStack {
Image(systemName: "lock.rotation.open")
Text("Discovery")
}
}
.tag(.discovery) // << here !!
QrCodeView()
.tabItem {
VStack {
Image(systemName: "qrcode.viewfinder")
Text("QrCode")
}
}
.tag(.qrcode) // << here !!
// ... others the same
I'm trying replicate the behavior of iOS Photos app.
Till now the thing I can't figure how could be done is the select mode, where when I press the button select how I can change the bottom bar?
Graphically, what I intend is, in this view:
When I pressed the button, the bottom bar changes to:
In the real project the views are embed inside a NavigationView
The code of the main view is similar to
struct ContentView: View {
var body: some View {
NavigationView{
TabView{
data()
.tabItem {
Text("Data")
}
data2()
.tabItem {
Text("Data")
}
}
}
}
I'm using Xcode 12 and swiftUI 2.0
First we need Conditional modifier like that https://stackoverflow.com/a/61253769/2715636
struct conditionalModifier: ViewModifier {
var isShowing: Bool
func body(content: Content) -> some View {
Group {
if self.isShowing {
content
.toolbar {
ToolbarItem(placement: .bottomBar, content: {
Button(action: {
}){
Image(systemName: "square.and.arrow.up")
}
})
}
.toolbar {
ToolbarItem(placement: .status, content: {
Text("Toolbar")
.fontWeight(.bold)
})
}
}
}
}}
I don't need else statement cause I only want to see Toolbar
else { content }
And here is my Tabbar inside ZStack. We're gonna overlay it with Text using Conditional modifier applied to Text
struct ContentView: View {
#State private var showToolbar: Bool = false
var body: some View {
Button(action: {
showToolbar.toggle()
}, label: {
Text(showToolbar ? "Show Tabbar" : "Show Toolbar")
}).padding()
ZStack {
TabView {
someView()
.tabItem {
Image(systemName: "placeholdertext.fill")
Text("Tab 1")
}
someView()
.tabItem {
Image(systemName: "placeholdertext.fill")
Text("Tab ")
}
someView()
.tabItem {
Image(systemName: "placeholdertext.fill")
Text("Tab 3")
}
}
Text("")
.modifier(conditionalModifier(isShowing: showToolbar))
}
}}
Final result
tabbar to toolbar
There's a new view modifier in iOS 16 that let you switch the tab bar and the bottom bar.
https://developer.apple.com/documentation/swiftui/view/toolbar(_:for:)
For example,
ContentView()
.toolbar(isSelecting ? .visible : .hidden, for: .bottomBar)
.toolbar(isSelecting ? .hidden : .visible, for: .tabBar)
I have implemented tab bar in my code. I have see all button in my first tab and from that button i want to switch to second tab programmatically. When I use navigationView then it creates another tab bar and moves to that screen and this changes the index of navigation in swiftui.
struct AppTabNavigation: View {
#State var selection: Tab = .dashboard
var body: some View {
TabView(selection: $selection) {
NavigationView {
FirstTabView()
}.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
.navigationViewStyle(StackNavigationViewStyle())
.tabItem {
Label("Home", systemImage: "house.fill")
.accessibility(label: Text("Home"))
}
.tag(Tab.home)
NavigationView {
SecondView()
}.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
.navigationViewStyle(StackNavigationViewStyle())
.tabItem {
Label("Home", systemImage: "house.fill")
.accessibility(label: Text("Home"))
}
.tag(second)
}
}
}
Navigation Code:
NavigationLink(destination: AppTabNavigation(selection: Tab.home), isActive: self.$isActiveTabbar){
Text("")
} .isDetailLink(false)
Here is a demo of possible approach - the idea is to move binding for tab selection into view with buttons, so button action could change it.
Tested with Xcode 12 / iOS 14
enum Tab {
case dashboard
case home
case second
}
struct AppTabNavigation: View {
#State var selection: Tab = .home
var body: some View {
TabView(selection: $selection) {
NavigationView {
FirstTabView(tab: $selection)
}.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
.navigationViewStyle(StackNavigationViewStyle())
.tabItem {
Label("Home", systemImage: "house.fill")
.accessibility(label: Text("Home"))
}
.tag(Tab.home)
NavigationView {
Text("SecondView")
}.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
.navigationViewStyle(StackNavigationViewStyle())
.tabItem {
Label("Home", systemImage: "house.fill")
.accessibility(label: Text("Home"))
}
.tag(Tab.second)
}
}
}
struct FirstTabView: View {
#Binding var tab: Tab
var body: some View {
Button("Go Second") { self.tab = .second }
}
}
I want to create a bottom bar which should be common for all my page. Here I am posting the image:
I know how to do it using storyboard. But how can I do it in SwiftUI?
This kind of view is called tab bar in iOS and in SwiftUI it is called TabView. It is declared like this:
var body: some View {
TabView {
Text("Favourites Screen")
.tabItem {
Image(systemName: "heart.fill")
Text("Favourites")
}
Text("Friends Screen")
.tabItem {
Image(systemName: "person.fill")
Text("Friends")
}
Text("Nearby Screen")
.tabItem {
Image(systemName: "mappin.circle.fill")
Text("Nearby")
}
}
}
Alternatively, you can use Label view for giving name & icon of the tab:
struct BottomTabBar: View {
var body: some View {
TabView{
Text("Profile Contents")
.tabItem{
Label("Profile",systemImage: "person.fill")
}
Text("Tv Contents")
.tabItem{
Label("Tv",systemImage: "tv.fill")
}
}
}
}