I have a TabView in SwiftUI and want the second tab to be the default, when starting the app.
I haven't found any documentation to provide this behavior, but it should be possible. Most of the apps have the mid tab as their default tab.
TabView {
QuestionView()
.tabItem {
Image(systemName: "questionmark")
Text("Questions")
}
DataView()
.tabItem {
Image(systemName: "chart.bar.fill")
Text("Data")
}
Text("Empty Tab right now")
.tabItem {
Image(systemName: "bolt.horizontal.fill")
Text("Trend")
}
}
#State private var selection = 3
TabView(selection:$selection) {
QuestionView()
.tabItem {
Image(systemName: "questionmark")
Text("Questions")
}
.tag(1)
DataView()
.tabItem {
Image(systemName: "chart.bar.fill")
Text("Data")
}
.tag(2)
Text("Empty Tab right now")
.tabItem {
Image(systemName: "bolt.horizontal.fill")
Text("Trend")
}
.tag(3)
}
In this case I set default selected the third Tab. Change selection to the desired Tab.
Define a State with the default value and bind it to TabView:
#State var selection = 1
,,,
TabView(selection: $selection)
,,,
Then assign a tag to each tabItem:
,,,
.tabItem {
Image(systemName: "bolt.horizontal.fill")
Text("Trend")
}.tag(1)
,,,
Now you can use them together, selection === tag
We can use enum with specific View name cases for tag rather than using Ints.
func tag<V>(_ tag: V) -> some View where V : Hashable
Please note that tag has to be confirmed to Hashable protocol so you can the enum cases like below.
enum Tab: String, CaseIterable, Identifiable {
case question
case data
case empty
var id: Self { self }
}
#State private var tab = Tab.data
TabView(selection: $tab) {
QuestionView()
.tabItem {
Image(systemName: "questionmark")
Text("Questions")
}
.tag(Tab.question)
DataView()
.tabItem {
Image(systemName: "chart.bar.fill")
Text("Data")
}
.tag(Tab.data)
Text("Empty Tab right now")
.tabItem {
Image(systemName: "bolt.horizontal.fill")
Text("Trend")
}
.tag(Tab.empty)
}
Related
I'm quite new to SwiftUI and having some troubles.
I have a Tab View:
Picker(selection: $selectedTab, label: Text("")) {
Text("1")
.tag(0)
Text("2")
.tag(1)
}
.pickerStyle(SegmentedPickerStyle())
TabView(selection: $selectedTab) {
View1()
.tag(0)
View2()
.tag(1)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
The View1 has a List and For-Each:
List {
ForEach(items) { item in
ItemRow()
}
.onDelete(perform: deleteItem)
}
.listStyle(PlainListStyle())
Now it's not possible to delete the RowItems with the onDelete function because the TabView is always changing to the View2.
One solution would be, to disable the "swipe between tabs" with for example:
.gesture(DragGesture())
But then I have to try to swipe single rows multiple times before the Delete works.
Best would be, to disable the "swipe between views" and that onDelete would work. Can you help me with that?
If the swiping isn't important just remove the TabView
struct CustomTabView1: View {
#State var selectedTab: Int = 0
var body: some View {
VStack{
Picker(selection: $selectedTab, label: Text("")) {
Text("1")
.tag(0)
Text("2")
.tag(1)
}
.pickerStyle(SegmentedPickerStyle())
switch selectedTab{
case 0:
View1()
case 1:
View2()
default:
Text("default")
}
}
}
}
Or you could do something with EditMode do adjust the View when the user needs to delete
struct CustomTabView1: View {
#Environment(\.editMode) var editMode
#State var selectedTab: Int = 0
var body: some View {
NavigationView{
VStack{
Picker(selection: $selectedTab, label: Text("")) {
Text("1")
.tag(0)
Text("2")
.tag(1)
}
.pickerStyle(SegmentedPickerStyle())
switch editMode?.wrappedValue{
case .active:
switch selectedTab{
case 0:
View1().foregroundColor(.blue)
case 1:
View2()
default:
Text("default")
}
default:
TabView(selection: $selectedTab) {
View1()
.tag(0)
View2()
.tag(1)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)).foregroundColor(.red)
}
}.toolbar(content: {
ToolbarItem(placement: .primaryAction, content: {
Button(editMode?.wrappedValue == .inactive ? "Edit" : "Done", action: {
if editMode?.wrappedValue == .inactive{
editMode?.wrappedValue = .active
}else{
editMode?.wrappedValue = .inactive
}
})
})
})
}
}
}
So I'm trying to make a tab view in the app I'm making to learn SwiftUI but the problem is I get this error saying that I'm passing an argument to a function that takes no arguments. I'll show you my code, I really don't know what's going on and any help is appreciated.
import SwiftUI
struct TabView: View {
var body: some View {
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)
}
}
struct TabView_Previews: PreviewProvider {
static var previews: some View {
TabView()
}
}
I literally copy pasted this example from Apple's website.
Name it something like MyTabView instead. "TabView" is reserved for SwiftUI's TabView.
struct MyTabView: View { /// here!
var body: some View {
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)
}
}
struct MyTabView_Previews: PreviewProvider {
static var previews: some View {
MyTabView()
}
}
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 !!
The app I am working on is based around a TabBar, and when I am on a tab I want to be able to click the tabItem again to reset the view, similar to how Twitter does it in their tabBar.
I do not know how to recognize that action though. Adding a button to the TabItem is not working, addidng a tapGesture modifier isn't either, and I can't think of anything else I could try.
struct ContentView: View {
var body: some View {
TabView() {
Text("Tab 1")
.tabItem {
Image(systemName: "star")
.onTapGesture {
print("Hello!")
}
Text("One")
}
.tag(0)
Text("Tab 2")
.tabItem {
Button(action: {
print("Hello!")
}, label: {
Image(systemName: "star.fill")
})
}
.tag(1)
}
}
}
It should't automatically reset when opening the tab again, which I have seen discussed elsewhere, but when tapping the tabItem again.
What other things am I possibly missing here?
Here is possible solution - inject proxy binding around TabView selection state and handle repeated tab tapped before bound value set, like below.
Tested with Xcode 12.1 / iOS 14.1
struct ContentView: View {
#State private var selection = 0
var handler: Binding<Int> { Binding(
get: { self.selection },
set: {
if $0 == self.selection {
print("Reset here!!")
}
self.selection = $0
}
)}
var body: some View {
TabView(selection: handler) {
Text("Tab 1")
.tabItem {
Image(systemName: "star")
Text("One")
}
.tag(0)
Text("Tab 2")
.tabItem {
Image(systemName: "star.fill")
}
.tag(1)
}
}
}
i want to set my right image frame boundaries like the left one without moving image or text. How can I do that. I tried a couple way but I couldn't find the solution.
You should use the built-in TabView. It provides all the functionality for you, and already made with accessibility in mind.
Here is an example (you can change it for your text and images):
struct ContentView: View {
#State private var selection: Int = 1
var body: some View {
TabView(selection: $selection) {
Text("Tab Content 1")
.tabItem {
Label("1", systemImage: "1.square")
}
.tag(1)
Text("Tab Content 2")
.tabItem {
Label("2", systemImage: "2.square")
}
.tag(2)
Text("Tab Content 3")
.tabItem {
Label("3", systemImage: "3.square")
}
.tag(3)
}
}
}
Result:
Custom version (highly unrecommended):
struct ContentView: View {
var body: some View {
VStack {
Spacer()
Text("Main Content")
Spacer()
HStack {
VStack {
Button {
//
} label: {
Label("1", systemImage: "1.square")
}
}.frame(maxWidth: .infinity)
VStack {
Button {
//
} label: {
Label("2", systemImage: "2.square")
}
}.frame(maxWidth: .infinity)
VStack {
Button {
//
} label: {
Label("3", systemImage: "3.square")
}
}.frame(maxWidth: .infinity)
}.frame(height: 50)
}
}
}