How do I use TabbedView in SwiftUI?
Asked Answered
L

9

16
struct ContentView : View {
    var body: some View {
        NavigationView {
            TabbedView {
                PasswordGenerator()
                    .tabItemLabel {
                        Image("KeyGlyph")
                        Text("Generator")
                }

                PasswordGeneratorSettings()
                    .tabItemLabel {
                            Image("SettingsGlyph")
                            Text("Settings")
                }
            }
        }
    }
}

This won't compile but it was used in the Swift Essentials video at WWDC (See minute 54:30) and I've seen some workarounds like the VStack workaround (but even that has many flaws, the left tab is too far to the left and the right tab is too far to the right and when switching tabs only the first one that initially loaded loads and the other tab stays blank and using tags doesn't help). So how do I have two tabs that load the views and have an Image and Text?

Libre answered 25/6, 2019 at 6:37 Comment(2)
Do you have macOS Catalina?May
@May Yes I do have macOS Catalina.Libre
A
12

With XCode beta 3 the following should work:

import SwiftUI

struct Home : View {
    @State private var currentTab = 1

    var body: some View {
        TabbedView(selection: $currentTab) {
            FirstView()
                .tabItem {
                    VStack {
                        Image(systemName: "1.circle")
                        Text("First Tab")
                    }
                }.tag(1)
            SecondView()
                .tabItem {
                    VStack {
                        Image(systemName: "2.circle")
                        Text("Second Tab")
                    }
                }.tag(2)
        }
    }
}

Enclosing the tab label in a VStack seems to be optional, though. So, you might decide to drop this, like:

import SwiftUI

struct Home : View {
    @State private var currentTab = 1

    var body: some View {
        TabbedView(selection: $currentTab) {
            FirstView()
                .tabItem {
                    Image(systemName: "1.circle")
                    Text("First Tab")
                }.tag(1)
            SecondView()
                .tabItem {
                    Image(systemName: "2.circle")
                    Text("Second Tab")
                }.tag(2)
        }
    }
}
Astragal answered 9/7, 2019 at 10:40 Comment(1)
if i have a tab item with only an image, how can i change its position or alignment ?Xanthippe
W
11

TabbedView() has been deprecated use TabView() instead.

Using integers to select views smells bad to me, from my days working with tag() of UIButton and UIView, it is better to enumerate what you are doing rather than assign a hard coded values that have a very large range. i.e. Int.min() to Int.max(). This also makes code easier to read and maintain in the future.

TabView(selection: ) can be used to select the index, and is declared as:

public struct TabView<SelectionValue, Content> : View where SelectionValue : Hashable, Content : View {
    public init(selection: Binding<SelectionValue>?, @ViewBuilder content: () -> Content)
…

This means that you can select the index with any hashable content.

We can use a enum that conforms to Hashable to contain a list of tabs, In this way can use an Observable later to help control and load state of the view. Or have the enum as part of the state of your app. I am sure there are plenty of resources you can use to find an appropriate solution that meets your needs.

 struct MainTabView: View {

 @State private var selection: Tabs = .profile

 private enum Tabs: Hashable {
     case content
     case profile
 }

 var body: some View {

    TabView(selection: $selection) {

        // Learn Content
        Text("The First Tab")
            .tabItem {
                Image(systemName: "book")
                Text("Learn")
        }.tag(Tabs.content)


        // The Users Profile View.
        ProfileView()
            .tabItem {
                Image(systemName: "person.circle")
                Text("Profile")
        }.tag(Tabs.profile)
    }

  }
}
Wisteria answered 29/12, 2019 at 23:4 Comment(0)
O
10

For those still searching for how to do this, here's an update with Xcode 11 GM.

struct Tabs: View {

    @State private var selected = 0

    var body: some View {
        TabView(selection: $selected) {
            MyFirstView()
                .tabItem {
                    Image(systemName: (selected == 0 ? "star.fill" : "star"))
                    Text("One")
                }.tag(0)
            MySecondView()
                .tabItem {
                    Image(systemName: (selected == 1 ? "star.fill" : "star"))
                    Text("Two")
                }.tag(1)
            MyThirdView()
                .tabItem {
                    Image(systemName: (selected == 2 ? "star.fill" : "star"))
                    Text("Three")
                }.tag(2)
        }
        .edgesIgnoringSafeArea(.all) // Important if you want NavigationViews to go under the status bar...
    }
}
Oldfangled answered 23/9, 2019 at 20:50 Comment(2)
Hey I have the exact problem using TabView and navigation view. However, if I set .edgesIgnoringSafeArea(.all), I would have everything expanded out of screen. How do I fix that?Spacecraft
Try edgesIgnoringSafeArea([.top, .bottom]) or possibly you want just .top ( depends on your View)Oldfangled
I
2

Your code should work, however this is a known issue, from iOS & iPadOS 13 Beta 2 Release Notes:

The tabItemLabel(_:) modifier doesn’t accept @ViewBuilder closures.

The only workaround, until this is fixed, is to use VStack as you've mentioned.

MyView()
    .tabItemLabel(VStack {
        Image("resourceName")
        Text("Item")
    })

Update:

This issue was fixed with Xcode 11 beta 3:

The tabItemLabel(:) modifier — now named tabItem(:) — now accepts @ViewBuilder closures.

Example:

.tabItem {
    Image(systemName: "circle")
    Text("Tab1")
}
Inherence answered 25/6, 2019 at 15:18 Comment(0)
D
1

TabbedView was deprecated.

You could use TabView instead now

struct AppTabbedView: View {
    @State private var selection = 3

    var body: some View {
        TabView (selection:$selection){
            Text("The First Tab")
                .tabItem {
                    Image(systemName: "1.square.fill")
                    Text("First")

            }
            .tag(1)
            Text("Another Tab")
                .tabItem {
                    Image(systemName: "2.square.fill")
                    Text("Second")
            }.tag(2)
            Text("The Last Tab")
                .tabItem {
                    Image(systemName: "3.square.fill")
                    Text("Third")
            }.tag(3)
        }
        .font(.headline)
    }
}
Downthrow answered 11/8, 2019 at 13:18 Comment(0)
L
1

as of Xcode 11 GM, this code works: (from https://developer.apple.com/documentation/swiftui/tabview)

TabView {
    Text("The First Tab")
        .tabItem {
            Image(systemName: "1.square.fill")
            Text("First")
        }
    Text("Another Tab")
        .tabItem {
            Image(systemName: "2.square.fill")
            Text("Second")
        }
    Text("The Last Tab")
        .tabItem {
            Image(systemName: "3.square.fill")
            Text("Third")
        }
}
.font(.headline)
Longfellow answered 13/9, 2019 at 10:58 Comment(0)
P
1

TabbedView is deprecated and has been renamed to TabView You can use TabView like this

TabView {
    (Text("Tab 1!").tabItem {
         Text("First")
    })
    (Text("Tab 2!").tabItem {
         Text("Second")
    })
}
Pension answered 1/10, 2019 at 12:41 Comment(0)
M
1

Xcode 16 - iOS 18 / macOS 15

Now TabView has a new type-safe syntax:

TabView {
    Tab("Generator", systemImage: "key") {
        Text("PasswordGenerator")
    }

    Tab("Settings", systemImage: "gear") {
        Text("PasswordGeneratorSettings")
    }
}
Medicable answered 10/6, 2024 at 21:44 Comment(0)
W
0

The 'TabbedView' can be used in a way similar to the following:

    struct TabView : View {

        @State private var selection = 1

        var body: some View {
            TabbedView (selection: $selection) {
                InboxList()
                    .tabItemLabel(selection == 1 ? Image("second") : Image("first"))
                    .tag(1)

                PostsList()
                    .tabItemLabel(Image("first"))
                    .tag(2)

                Something()
                    .tabItemLabel(Image("first"))
                    .tag(3)
            }
        }
    }

I'm not sure why your example doesn't compile but you miss both the selection param and the .tag property on each "tab".

Btw in the current XCode version (beta 2) you cannot show both a text and an image as label.

Wrangler answered 25/6, 2019 at 15:9 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.