The code below doesn't show Text("Second View"), because private var selection: UInt.
struct TabMenu: View {
#State private var selection: UInt = 0
var body: some View {
TabView(selection: $selection){
Text("First View")
.font(.title)
.tabItem {
Image(systemName: "house")
Text("Main")
}
.tag(0)
Text("Second View")
.font(.title)
.tabItem {
Image(systemName: "gear")
Text("Preferences")
}
.tag(1)
}
}
}
In sources of TabView, I found this:
extension TabView where SelectionValue == Int {
#available(watchOS, unavailable)
public init(#ViewBuilder content: () -> Content)
}
How I can create a custom view where SelectionValue == Uint ?
where SelectionValue == Uint
Selection must be the same type as tag is (by default both are Int).
Here is a solution. Tested with Xcode 11.4 / iOS 13.4
struct TabMenu: View {
#State private var selection: UInt = 0
var body: some View {
TabView(selection: $selection){
Text("First View")
.font(.title)
.tabItem {
Image(systemName: "house")
Text("Main")
}
.tag(UInt(0))
Text("Second View")
.font(.title)
.tabItem {
Image(systemName: "gear")
Text("Preferences")
}
.tag(UInt(1))
}
}
}
Related
Here's an edited version of the question. I'm working in Swiftui and I have two views. The first has two NavigationLinks. The second link is where I get stuck. I would like that link to go to 'tabTwo' on PageTwo. Getting to PageTwo isn't an issue... it's getting to tabTwo as a view on that page.
Here's an edited down contentView file:
import SwiftUI
struct ContentView: View {
#State var isNavigationBarHidden: Bool = true
#State private var selectedTab = ""
var body: some View {
NavigationView {
ZStack {
VStack(spacing: 0) {
VStack(spacing: 0) {
NavigationLink("Link One", destination: PageTwo()).padding(10)
.foregroundColor(Color.gray)
.background(Color.white)
Divider().frame(width: 300,height: 1).background(Color.black)
}
VStack(spacing: 0) {
NavigationLink("Link Two", destination: PageTwo())
.padding(10)
.foregroundColor(Color.gray)
.background(Color.white)
}
// If you remove the below VStack everything works.
VStack(spacing: 0) {
NavigationLink("Page Two tabTwo", destination: PageTwo(), tag: "tabTwo", selection: $selectedTab?)
.padding(10)
.foregroundColor(Color.gray)
.background(Color.white)
}
}
}
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and here's an edited down PageTwo:
import SwiftUI
struct PageTwo: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
#State var isNavigationBarHidden: Bool = true
#State private var selectedTab = ""
var body: some View {
ZStack() {
TabView(selection: $selectedTab) {
//---> Tab One
VStack(spacing: 0) {
VStack{
Text("PAGE ONE")
.padding(.bottom, 20)
}
Button(action: {
self.mode.wrappedValue.dismiss()})
{ Text("BACK") }
.font(Font.system(size:14, weight: .bold, design: .default))
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
.tabItem {Label("ONE", systemImage: "circle.fill")}
.tag("tabOne")
//---> Tab Two
VStack(spacing: 0) {
VStack{
Text("PAGE TWO")
.padding(.bottom, 20)
}
Button(action: {
self.mode.wrappedValue.dismiss()})
{ Text("BACK") }
.font(Font.system(size:14, weight: .bold, design: .default))
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
.tabItem {Label("TWO", systemImage: "circle.fill")}
.tag("tabTwo")
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(.page(backgroundDisplayMode: .never))
.onAppear {self.isNavigationBarHidden = true}
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
.background(Color.gray)
.opacity(0.75)
.indexViewStyle(.page(backgroundDisplayMode: .never))
}
}
struct PageTwo_Previews: PreviewProvider {
static var previews: some View {
PageTwo()
}
}
This throws an error at the 'some view' level of:
Failed to produce diagnostic for expression; please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project
I've submitted the report, but can't figure out how to do what I'd like. Any help is much appreciated.
I have a tvOS App with a TabView across the top. I want to use the background color of the TabView to indicate status. Initially it will be red and when something occurs in one of the views, I want to change the background color of the TabView to green.
I'm using the UITabBar.appearance().barTintColor = UIColor.red in my init() to set the initial color to red, but I can't find a way to change that to green later in execution.
struct ContentView: View {
#State private var selection = 1
init() {
UITabBar.appearance().barTintColor = UIColor.red
}
var body: some View {
TabView (selection:$selection){
Tab1View()
.tabItem {
Image(systemName: "1.square.fill")
Text("Tab 1")
}
.tag(1)
Tab2View()
.tabItem {
Image(systemName: "2.square.fill")
Text("Tab 2")
}.tag(2)
Tab3View()
.tabItem {
Image(systemName: "3.square.fill")
Text("Tab 3")
}.tag(3)
}
.font(.headline)
.accentColor(.white)
.ignoresSafeArea()
}
}
You can use the code below.
import SwiftUI
struct ContentView: View {
#State private var selectedTab = 0
var tabViewTitle = ["First Page","Second Page","Third Page"]
var body: some View {
VStack {
TabView(selection: $selectedTab){
Text(tabViewTitle[0])
.tag(0)
Text(tabViewTitle[1])
.tag(1)
Text(tabViewTitle[2])
.tag(2)
}
.frame(width: 300, height: 500)
.tabViewStyle(PageTabViewStyle())
.background(Color.purple)
.cornerRadius(15)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
HStack(spacing: 16) {
ForEach(tabViewTitle, id: \.self) { index in
Rectangle()
.fill(self.selectedTab == tabViewTitle.firstIndex(where: {$0 == index}) ? Color.purple : Color.purple.opacity(0.5))
.frame(width: self.selectedTab == tabViewTitle.firstIndex(where: {$0 == index}) ? 16 : 12,height: self.selectedTab == tabViewTitle.firstIndex(where: {$0 == index}) ? 16 : 12)
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I'm trying to reuse some old code from a question I had previously asked and it's not working anymore, did something happen under the hood?
When I lunch the app the TabBar goes to index 0 instead of 2. This is my current code:
struct iOS_TabBarView: View {
#State private var selectedTab: HostingBarCategories = .Screen3
var body: some View {
TabView(selection: $selectedTab) {
Text("1")
.tag(0)
.tabItem {
Image(systemName: "pencil.and.outline")
Text("1")
}
Text("2")
.tag(1)
.tabItem {
Image(systemName: "checkmark")
Text("2")
}
Text("3")
.tag(2)
.tabItem {
Image(systemName: "calendar.circle.fill")
Text("3")
}
Text("4")
.tag(3)
.tabItem {
Image(systemName: "flame")
Text("4")
}
Text("5")
.tag(3)
.tabItem {
Image(systemName: "slider.horizontal.3")
Text("5")
}
}
}
}
and the HostingBarCategories:
enum HostingBarCategories: Hashable, CaseIterable {
case Screen1
case Screen2
case Screen3
case Screen4
case Screen5
var string: String { String(describing: self) }
}
What's going on?
I assume tag & selection should be the same type, so try
struct iOS_TabBarView: View {
#State private var selectedTab: HostingBarCategories = .Screen3
var body: some View {
TabView(selection: $selectedTab) {
Text("1")
.tag(HostingBarCategories.Screen1) // << like this for all !!
When creating a TabBar using TabView and .tabItem, the default colors are: gray when image isn't clicked and blue when it is. Here's a sample code.
struct TabBar: View{
#State var current = 0
var body: some View{
TabView(selection: $current) {
View0()
.tag(0)
.tabItem {
Image(systemName: "anySystemImageName")
Text("")
}
View3()
.tag(1)
.tabItem {
Image(systemName: "anySystemImageName")
Text("")
}
View2()
.tag(2)
.tabItem {
Image(systemName: "anySystemImageName")
Text("")
}
View3()
.tag(3)
.tabItem {
Image(systemName: "anySystemImageName")
Text("")
}
}
}
}
How to use a custom color?
You can use accent color, like
TabView(selection: $current) {
// .. other code
}.accentColor(.red) // << here !!
.accentColor is the keyword to change the color
struct ContentView: View{
#State var current = 0
var body: some View{
TabView(selection: $current) {
Text("View 1")
.tag(0)
.tabItem {
Image(systemName: "circle")
Text("")
}
Text("View 1")
.tag(1)
.tabItem {
Image(systemName: "circle")
Text("")
}
Text("hallo")
.tag(2)
.tabItem {
Image(systemName: "circle")
Text("")
}
Text("hallo")
.tag(3)
.tabItem {
Image(systemName: "circle")
Text("")
}
}.accentColor(.red) //<< here
}
}
Is there any way to pop to root view by tapping the Tab Bar like most iOS apps, in SwiftUI?
Here's an example of the expected behavior.
I've tried to programmatically pop views using simultaneousGesture as follow:
import SwiftUI
struct TabbedView: View {
#State var selection = 0
#Environment(\.presentationMode) var presentationMode
var body: some View {
TabView(selection: $selection) {
RootView()
.tabItem {
Image(systemName: "house")
.simultaneousGesture(
TapGesture().onEnded {
self.presentationMode.wrappedValue.dismiss()
print("View popped")
}
)
}.tag(0)
Text("")
.tabItem {
Image(systemName: "line.horizontal.3")
}.tag(1)
}
}
}
struct RootView: View {
var body: some View {
NavigationView {
NavigationLink(destination: SecondView()) {
Text("Go to second view")
}
}
}
}
struct SecondView: View {
var body: some View {
Text("Tapping the house icon should pop back to root view")
}
}
But seems like those gestures were ignored.
Any suggestions or solutions are greatly appreciated
We can use tab bar selection binding to get the selected index. On this binding we can check if the tab is already selected then pop to root for navigation on selection.
struct ContentView: View {
#State var showingDetail = false
#State var selectedIndex:Int = 0
var selectionBinding: Binding<Int> { Binding(
get: {
self.selectedIndex
},
set: {
if $0 == self.selectedIndex && $0 == 0 && showingDetail {
print("Pop to root view for first tab!!")
showingDetail = false
}
self.selectedIndex = $0
}
)}
var body: some View {
TabView(selection:selectionBinding) {
NavigationView {
VStack {
Text("First View")
NavigationLink(destination: DetailView(), isActive: $showingDetail) {
Text("Go to detail")
}
}
}
.tabItem { Text("First") }.tag(0)
Text("Second View")
.tabItem { Text("Second") }.tag(1)
}
}
}
struct DetailView: View {
var body: some View {
Text("Detail")
}
}
I messed around with this for a while and this works great. I combined answers from all over and added some stuff of my own. I'm a beginner at Swift so feel free to make improvements.
Here's a demo.
This view has the NavigationView.
import SwiftUI
struct AuthenticatedView: View {
#StateObject var tabState = TabState()
var body: some View {
TabView(selection: $tabState.selectedTab) {
NavigationView {
NavigationLink(destination: TestView(titleNum: 0), isActive: $tabState.showTabRoots[0]) {
Text("GOTO TestView #1")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
}
.navigationViewStyle(.stack)
.onAppear(perform: {
tabState.lastSelectedTab = TabState.Tab.first
}).tabItem {
Label("First", systemImage: "list.dash")
}.tag(TabState.Tab.first)
NavigationView {
NavigationLink(destination: TestView(titleNum: 0), isActive: $tabState.showTabRoots[1]) {
Text("GOTO TestView #2")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}.navigationTitle("")
.navigationBarTitleDisplayMode(.inline).navigationBarTitle(Text(""), displayMode: .inline)
}
.navigationViewStyle(.stack)
.onAppear(perform: {
tabState.lastSelectedTab = TabState.Tab.second
}).tabItem {
Label("Second", systemImage: "square.and.pencil")
}.tag(TabState.Tab.second)
}
.onReceive(tabState.$selectedTab) { selection in
if selection == tabState.lastSelectedTab {
tabState.showTabRoots[selection.rawValue] = false
}
}
}
}
struct AuthenticatedView_Previews: PreviewProvider {
static var previews: some View {
AuthenticatedView()
}
}
class TabState: ObservableObject {
enum Tab: Int, CaseIterable {
case first = 0
case second = 1
}
#Published var selectedTab: Tab = .first
#Published var lastSelectedTab: Tab = .first
#Published var showTabRoots = Tab.allCases.map { _ in
false
}
}
This is my child view
import SwiftUI
struct TestView: View {
let titleNum: Int
let title: String
init(titleNum: Int) {
self.titleNum = titleNum
self.title = "TestView #\(titleNum)"
}
var body: some View {
VStack {
Text(title)
NavigationLink(destination: TestView(titleNum: titleNum + 1)) {
Text("Goto View #\(titleNum + 1)")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
NavigationLink(destination: TestView(titleNum: titleNum + 100)) {
Text("Goto View #\(titleNum + 100)")
.padding()
.foregroundColor(Color.white)
.frame(height:50)
.background(Color.purple)
.cornerRadius(8)
}
.navigationTitle(title)
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView(titleNum: 0)
}
}
You can achieve this by having the TabView within a NavigationView like so:
struct ContentView: View {
#State var selection = 0
var body: some View {
NavigationView {
TabView(selection: $selection) {
FirstTabView()
.tabItem {
Label("Home", systemImage: "house")
}
.tag(0)
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct FirstTabView: View {
var body: some View {
NavigationLink("SecondView Link", destination: SecondView())
}
}
struct SecondView: View {
var body: some View {
Text("Second View")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ContentView()
}
}
}