TabBar with cases instead of int - SwiftUI - swift

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

Related

Swiftui invisible bar on the top of the screen

I have the following in problem.
If I try to give a view a color (Color.red for example)
I get the following output:
I can just add .edgesignoressafearea(.top) and the top also gets red. But when I want to add an clickable item, the user won't be able to click it since there still is this invisible bar on the top of the screen. Does Anyone know what my problem is? The problem is in all the tabable views(Timeline, Favorits, Discover, Account). So It must be in either the first code or in tabview (second code) that I send in this post.
When the user clicks on the app first they get this view sending the user to the signin view or the app itself:
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: tabView().navigationBarHidden(true), isActive: $tabview, label: { EmptyView() })
NavigationLink(destination: loginView().navigationBarHidden(true), isActive: $login, label: { EmptyView() })
if tabview == false && login == false {
Text("loading")
.onAppear(perform: checklogin)
}
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
Then the apps sends them to tabview:
var body: some View {
TabView(selection: $selection) {
Timeline()
.tabItem {
Label("timeline", systemImage: "house")
}
.tag(0)
Favorits()
.tabItem {
Label("favorits", systemImage: "heart")
}
.tag(1)
Discover()
.tabItem {
Label("discover", systemImage: "network")
}
.tag(2)
Account()
.tabItem {
Label("account", systemImage: "person")
}
.tag(3)
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
The problem happens on all of this views.
This is the view where I made the screenshot of:
var body: some View {
ZStack {
Color.red
Text("Hello fav!")
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
add .edgesIgnoringSafeArea(.top) and remove your navigation bar if you don't want to be able to get back to your login view anytime.
That's the full code with a login page, now it depends what you want on that login page :
import SwiftUI
struct ContentView: View {
#State var showLoginView: Bool = false
var body: some View {
VStack {
if showLoginView {
MainView()
} else {
Button("Login") {
self.showLoginView = true
}
}
}
}
}
struct MainView: View {
var body: some View {
TabView {
TimeLine()
.tabItem {
Label("timeline", systemImage: "house")
}
Favorits()
.tabItem {
Label("favorits", systemImage: "heart")
}
Discover()
.tabItem {
Label("discover", systemImage: "network")
}
Account()
.tabItem {
Label("account", systemImage: "person")
}
}
}
}
struct TimeLine: View {
var body: some View {
Color.blue
.edgesIgnoringSafeArea(.top)
}
}
struct Favorits: View {
var body: some View {
ZStack {
Color.red
.edgesIgnoringSafeArea(.top)
Text("Hello fav!")
}
}
}
struct Discover: View {
var body: some View {
Color.yellow
.edgesIgnoringSafeArea(.top)
}
}
struct Account: View {
var body: some View {
Color.purple
.edgesIgnoringSafeArea(.top)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You need to set the navigation Title to "" because your View is embedded in NavigationView
Create a ViewModifier like this and apply it to your VStack
struct HiddenNavBarModifier: ViewModifier {
func body(content: Content) -> some View {
content
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(true)
}
}

SwiftUI .onDelete is not working in TabView because View is always changing

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

How do I get rid of the error 'Argument passed to call that takes no arguments' on my SwiftUI TabView

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

TabView doesn't show the next N SelectionValue where SelectionValue == UInt

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

How to set a default Tab in SwiftUIs TabView?

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