SwiftUI Toolbar, Tabview & TabBarItem is not behaving as I would expect - ios

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

add an exit button to .navigationBarItems

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.

TabView does not work correctly on iOS13 SwiftUI

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

replace Tabbar with toolbar in SwiiftUI 2.0

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)

how to switch tab programmatically on button click? in swiftui

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 }
}
}

Is there a way to create BottomBar using SwiftUI?

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")
}
}
}
}

Resources