I have the fllowing TabViews in the ContentView. For the onAppear, it works fine. However, I am trying also to reload the tabs and move to the home after the user login from LoginView.
Following is the code of the ContentView
// ContentView.swift
// Matjri
//
//
import SwiftUI
struct ContentView: View {
#ObservedObject var user = User()
var body: some View {
HStack{
if (user.tokenIsActive) {
TabView {
HomeView()
.tabItem {
VStack {
Image(systemName: "house")
Text("Home")
}
}.tag(0)
UserPostsView()
.tabItem {
VStack {
Image(systemName: "person.fill")
Text("Me")
}
}.tag(1)
NewPostView()
.tabItem {
VStack {
Image(systemName: "plus")
Text("Add")
}
}.tag(2)
SearchView()
.tabItem {
VStack{
Image(systemName: "magnifyingglass")
Text("Search")
}
}.tag(3)
}
} else {
TabView {
HomeView()
.tabItem {
VStack {
Image(systemName: "house")
Text("Home")
}
}.tag(0)
LoginView()
.tabItem {
VStack {
Image(systemName: "person.fill")
Text("Me")
}
}.tag(1)
SearchView()
.tabItem{
VStack{
Image(systemName: "magnifyingglass")
Text("Search")
}
}.tag(2)
}
}
}
.onAppear() {
self.checkLoginValidity()
}
}
func checkLoginValidity() {
let userLogged = UserDefaults.standard.object(forKey: "userIsLogged") as? Bool ?? false
if (userLogged) {
let existinLogin = UserDefaults.standard.object(forKey: "loginExpiry") as! Date
if (existinLogin > Date().addingTimeInterval(86400 * 2)){
self.user.tokenIsActive = true
} else {
self.user.tokenIsActive = false
}
} else {
self.user.tokenIsActive = false
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
When the user login from the LoginView, I set the tokenIsActive to true
struct LoginView: View {
#ObservedObject var user = User()
........
//Login successful
self.user.tokenIsActive = true
TabView is not updating. How can I achieve this and I move the user to the homeview after login.
It is used different User() instances in ContentView and LoginView. Here is a solution:
struct LoginView: View {
#ObservedObject var user: User // << only declare
and here
LoginView(user: self.user) // << inject own user instance
.tabItem {
VStack {
Image(systemName: "person.fill")
Text("Me")
}
}.tag(1)
Related
I am using tab bar for with three tab for showing the data form api call . I added the required property for showing the navigation at top with title . Any reason it not showing it at top of the app .
Here is my content view code .
import SwiftUI
struct ContentView: View {
#State private var selection = 0
#EnvironmentObject private var viewModel: FruitsViewModel
var body: some View {
TabView(selection: $selection) {
NavigationView { TabListView(fruit: viewModel.fruits)}.tabItem {
Image(systemName: "house.fill")
Text("List View")
} .navigationBarTitle("Fruit List ", displayMode: .inline)
.accentColor(.red)
.onAppear() {
UITabBar.appearance().barTintColor = .white
}
.tag(0)
NavigationView {GridListView(fruit: viewModel.fruits)}.tabItem {
Image(systemName: "bookmark.circle.fill")
Text("Collection View")
}.navigationBarTitle("Fruit List ", displayMode: .inline)
.accentColor(.red)
.onAppear() {
UITabBar.appearance().barTintColor = .white
}
.tag(1)
NavigationView {WebListView()}.tabItem {
Image(systemName: "person.crop.circle")
Text("Web View")
}.navigationBarTitle("Fruit List ", displayMode: .inline)
.accentColor(.red)
.onAppear() {
UITabBar.appearance().barTintColor = .white
}
}.tag(2)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Here is the TabList code .
import SwiftUI
struct TabListView: View {
let fruit: [Fruits]
#EnvironmentObject private var viewModel: FruitsViewModel
var body: some View {
VStack {
List {
ForEach(viewModel.fruits) { fruits in
NavigationLink(destination: FruitDetailsView(fruit: fruits)) {
FruitsRowList(fruit: fruits)
}
}
}
}
.navigationTitle("Fruit List")
.onAppear {
Task {
await viewModel.getFruits()
}
}
}
}
Here is the result on simulator .. It just collapsed with entire view when I scroll down it .
Order is very important in SwiftUI, Each tab should have its own NavigationView or NavigationStack.
tabItem should be attached to the NavigationView and navigationTitle should go inside the NavigationView.
struct ContentView: View {
#State private var selection = 0
//#EnvironmentObject private var viewModel: FruitsViewModel
var body: some View {
TabView(selection: $selection) {
NavigationView {
Text("TabListView(fruit: viewModel.fruits)")
.navigationBarTitle("Fruit List ", displayMode: .inline)
}
.tabItem {
Image(systemName: "house.fill")
Text("List View")
}
.accentColor(.red)
.onAppear() {
UITabBar.appearance().barTintColor = .white
}
.tag(0)
NavigationView {
Text("GridListView(fruit: viewModel.fruits)")
.navigationBarTitle("Fruit List ", displayMode: .inline)
}.tabItem {
Image(systemName: "bookmark.circle.fill")
Text("Collection View")
}
.accentColor(.red)
.onAppear() {
UITabBar.appearance().barTintColor = .white
}
.tag(1)
NavigationView {
Text("WebListView()")
.navigationBarTitle("Fruit List ", displayMode: .inline)
}.tabItem {
Image(systemName: "person.crop.circle")
Text("Web View")
.accentColor(.red)
.onAppear() {
UITabBar.appearance().barTintColor = .white
}
}
.tag(2)
}
}
}
With the below code I get a very weird scrolling behaviour in my TabViews, LoginView is called on app launch:
struct LoginView: View {
#State private var presentContent = false
var body: some View {
return NavigationView {
ZStack{
NavigationLink(
destination: ContentView(),
isActive: $presentContent,
label: {
EmptyView()
})
Button("TEst") {
self.presentContent.toggle()
}
}
}
}
}
struct ContentView: View {
var body: some View {
TabView{
Group{
List{
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
}
.navigationTitle("Transactions")
.tabItem {
Image(systemName: "list.dash")
Text("Transactions")
}
Group{
List{
Text("Item 11")
Text("Item 12")
Text("Item 13")
}
}
.navigationTitle("Summary")
.tabItem {
Image(systemName: "list.dash")
Text("Summary")
}
}
}
}
Any ideas what that might cause?
Here the issue in verbatim: after tapping the button in the LoginView to jump over to the ContentView, I see the first tab. Now I scroll the list up and it goes beyond the screen border which is not correct.
UPDATE: Adding the app launch code below to emphasize the point "LoginView is called on app launch:":
struct TabTestApp: App {
var body: some Scene {
WindowGroup {
LoginView()
}
}
}
Use a NavigationView inside the TabView instead of Group. The login view you have isn't presented or even used. This will fix the scrolling issue.
Edit: Adding a login add additional elements which are unclear. You'd need a data model to handle the login details yourself, but a simple approach could be something like this.
class LoginModel: ObservableObject {
#Published var loggedin: Bool = false
}
struct Login: View {
#ObservedObject var model: LoginModel
#Environment(\.dismiss) var dismiss
var body: some View {
NavigationView {
List {
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
dismiss()
print("if successful login handle here and dismiss")
}, label: {
Label("Close", systemImage: "xmark")
.labelStyle(.iconOnly)
})
}
}
}
}
}
struct ContentView: View {
#StateObject var loginmodel = LoginModel()
#State var sheet: Bool = false
var body: some View {
TabView {
NavigationView {
VStack {
if !loginmodel.loggedin {
Button(action: {
sheet.toggle()
}, label: {
Label("Login", systemImage: "person.circle.fill")
})
} else {
List {
NavigationLink("Item 1", destination: Text("Item 1"))
NavigationLink("Item 2", destination: Text("Item 2"))
NavigationLink(destination: Text("Item 3")) {
Text("Item 3")
}
}
}
}
.navigationTitle("Transactions")
}
.sheet(isPresented: $sheet) {
Login(model: loginmodel)
}
.tabItem {
Label("Transactions", systemImage: "list.dash")
}
NavigationView {
List {
Text("Item 11")
Text("Item 12")
Text("Item 13")
}
.navigationTitle("Summary")
}
.tabItem {
Label("Summary", systemImage: "chart.line.uptrend.xyaxis")
}
}
}
}
struct ContentView: View {
var body: some View {
TabView {
NavigationView {
List {
// Either or navigation link if needed
NavigationLink("Item 1", destination: Text("Item 1"))
NavigationLink("Item 2", destination: Text("Item 2"))
NavigationLink(destination: Text("Item 3")) {
Text("Item 3")
}
}
.navigationTitle("Transactions")
}
.tabItem {
Label("Transactions", systemImage: "list.dash")
}
NavigationView {
List {
Text("Item 11")
Text("Item 12")
Text("Item 13")
}
.navigationTitle("Summary")
}
.tabItem {
Label("Summary", systemImage: "chart.line.uptrend.xyaxis")
}
}
}
}
Ok, I have changed the whole code to
//
// ContentView.swift
// TabTest
//
// Created by Max on 2022-05-04.
//
import SwiftUI
struct LoginView: View {
#Environment(\.presentationMode) var presentationMode
#State private var presentContent = false
var body: some View {
return NavigationView {
ZStack{
Button("Login") {
presentationMode.wrappedValue.dismiss()
}
}
}
}
}
struct ContentView: View {
#State private var isPresented = false
var body: some View {
TabView{
NavigationView{
List{
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
.navigationTitle("Transactions")
.navigationBarBackButtonHidden(true)
}
.tabItem {
Image(systemName: "list.dash")
Text("Transactions")
}
NavigationView{
List{
Text("Item 11")
Text("Item 12")
Text("Item 13")
}
.navigationTitle("Summary")
.navigationBarBackButtonHidden(true)
}
.tabItem {
Image(systemName: "list.dash")
Text("Summary")
}
}
.onAppear{
self.isPresented = true
}
.fullScreenCover(isPresented: $isPresented, content: LoginView.init)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and now it actually seems to work. Never have thought of fullScreenCover - thanks! :)
I am using TabView in swiftui. I want navigate child view from tabview tabItem. When I navigate from taview to childview, it shows tabview in bottom. Here is the image.. that click login goes to Myview page. But in Myview, Tabview is not hiding
Here is my code..
Tabview
struct WelcomeView: View {
var body: some View {
TabView {
HomeView()
.tabItem {
Image("home_icon")
Text("Home")
}
.tag(0)
NotificationView()
.tabItem {
Image("notification_icon")
Text("Notification")
}.tag(1)
AccountView()
.tabItem {
Image("account_icon")
Text("Account")
}.tag(2)
SettingView()
.tabItem {
Image("settings_icon")
Text("Setting")
}.tag(3)
}
}
}
In SettingView tabItem:
struct SettingView: View {
#State private var isActive = false
var body: some View {
NavigationView {
VStack(alignment: .leading) {
VStack {
Button(action: {
isActive = true
}) {
Text("Login")
}
NavigationLink("", destination: MyView(), isActive: $isActive)
}
}
}
}
}
MyView:
struct MyView: View {
var body: some View {
ZStack{
Text("Hello My View")
}.navigationBarHidden(true)
.navigationBarTitleDisplayMode(.inline)
}
}
When I click login Button in SettingView tabItem, It goes to MyView page. But in MyView page tabview is not hide.
How to hide tabview from MyView page?
To hide the tab we can add a Bool that will take care of showing the view or not. Then by using #Binding we can pass it to the other child views, whatever changes you make down the chain will affect all the views.
struct WelcomeView: View {
#State var isTabViewShown = true
var body: some View {
TabView {
if isTabViewShown {
HomeView()
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
.tag(0)
NotificationView()
.tabItem {
Image(systemName: "envelope.open.fill")
Text("Notification")
}.tag(1)
AccountView()
.tabItem {
Image(systemName: "person.crop.circle")
Text("Account")
}.tag(2)
SettingView(isTabViewShown: $isTabViewShown)
.tabItem {
Image(systemName: "gearshape")
Text("Setting")
}.tag(3)
}
}
}
}
struct MyView: View {
#Binding var isTabViewShown: Bool
var body: some View {
ZStack{
Text("Hello My View")
}
.navigationBarHidden(false)
.navigationBarTitleDisplayMode(.inline)
.onAppear {
isTabViewShown = false
}
}
}
struct SettingView: View {
#State private var isActive = false
#Binding var isTabViewShown: Bool
var body: some View {
NavigationView {
VStack(alignment: .leading) {
VStack {
Button(action: {
isActive = true
}) {
Text("Login")
}
NavigationLink("", destination: MyView(isTabViewShown: $isTabViewShown), isActive: $isActive)
//Here is a button if you want to show it again
Button(action: {
isTabViewShown.toggle()
//You can also use this is you don't want to use a toggle
// isTabViewShown = true
}) {
Text("Show again")
}
}
}
}
}
}
struct HomeView: View {
var body: some View {
Text("Home View")
}
}
struct NotificationView: View {
var body: some View {
Text("Notification View")
}
}
struct AccountView: View {
var body: some View {
Text("Account View")
}
}
I'm using Navigation View inside TabView and the problem is that if I an on Tab A and with NavigationView I open other Views, when changing from tab A to B and after a while I came back to tab A I dosen't reload tab A from beginning but it show the last View open with NavitagionLink. The problem is that in each View I'm getting data from a DB and because of that if shows an empty view.
My ContentView looks like this:
struct ContentView: View {
#ObservedObject var appState = AppState()
#State var currentTab : Tab
var body: some View {
TabView(selection: $appState.currentTab) {
NavigationView {
HomeView(appState: appState)
}
.tabItem {
if appState.currentTab == .home {
Image(systemName: "house.fill")
} else {
Image(systemName: "house")
}
Text(LocalizedStringKey("HomeTabMenu"))
}.tag(Tab.home)
NavigationView {
SearchView()
}
.tabItem {
if appState.currentTab == .search {
Image(systemName: "magnifyingglass.circle.fill")
} else {
Image(systemName: "magnifyingglass")
}
Text(LocalizedStringKey("SearchTabMenu"))
}.tag(Tab.search)
NavigationView {
AddItemView(appState: appState)
}
.tabItem {
if appState.currentTab == .add {
Image(systemName: "plus.circle.fill")
} else {
Image(systemName: "plus.circle")
}
Text(LocalizedStringKey("SellTabMenu"))
}.tag(Tab.add)
NavigationView {
ShoppingCartFavoritesView()
}
.tabItem {
if appState.currentTab == .favorites {
Image(systemName: "cart.fill")
} else {
Image(systemName: "cart")
}
Text(LocalizedStringKey("CartTabMenu"))
}.tag(Tab.favorites)
NavigationView {
ProfileView(appState: appState)
}
.tabItem {
if appState.currentTab == .profile {
Image(systemName: "person.fill")
} else {
Image(systemName: "person")
}
Text(LocalizedStringKey("ProfileTabMenu"))
}.tag(Tab.profile)
}//End TabView
.accentColor(Color("ColorMainDark"))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(currentTab: Tab.home)
}
}
class AppState: ObservableObject {
#Published var currentTab : Tab = .home
}
enum Tab {
case home, search, add, favorites, profile
}
And if I open SearchView()
struct SearchView: View {
var body: some View {
NavigationLink(destination: View_2(id: "ABC")){
Text("ABC")
}
}
}
struct View_2: View {
#ObservedObject var performSearchProducts = PerformSearchInProducts()
var id : String
var body: some View {
ScollView {
ForEach(performSearchProducts.products) { product in
Text(product.name)
}
}.onAppear(perform: {
self.performSearchProducts.searchSubCategory(id: id)
})
}
}
If in SearchView I'm on View_2() and the I open another Tab, when I come back to tab SearchView it doesn't show the SearchView(), but it remains on View_2() with the back button in navigation bar.
How can I make to show SearchView() and not keep the state of NavigationLink?
It's the default behavior. Attach id to TabView.
}//End TabView
.accentColor(Color("ColorMainDark"))
.id(appState.currentTab) //<--Here
import SwiftUI
struct ContentView: View {
#State var showModal = false
var body: some View {
TabView {
Text("Home View")
.tabItem {
VStack {
Image(systemName: "house")
Text("Home")
}
}
Text("Dummy View")
.onAppear {
self.showModal = true
}
.sheet(isPresented: self.$showModal) {
Text("Camera View")
}
.tabItem {
VStack {
Image(systemName: "camera")
Text("Camera")
}
}
Text("Setting View")
.tabItem {
VStack {
Image(systemName: "person")
Text("Setting")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
When I tap the center camera button with above code,
"Dummy View" has been shown.
onAppear is called and self.showModal is set to true.
But, modal transition is not performed, and can not show the "Camera View".
How to perform a modal transition when tapped tab button with SwiftUI TabView?
Thank you krjw!
Finally, I have solved the problem with the following code:
import SwiftUI
struct ContentView: View {
#State var showModal = false
var body: some View {
TabView {
Text("Home View")
.tabItem {
VStack {
Image(systemName: "house")
Text("Home")
}
}
Text("Dummy View")
.onAppear {
DispatchQueue.main.async {
self.showModal = true
}
}
.sheet(isPresented: self.$showModal) {
Text("Camera View")
}
.tabItem {
VStack {
Image(systemName: "camera")
Text("Camera")
}
}
Text("Setting View")
.tabItem {
VStack {
Image(systemName: "person")
Text("Setting")
}
}
}
}
}
I had trouble with that and I am not sure why, but .sheet always worked best for me when I put it on the very top of my view hierarchy. Additionally I wrapped the call in .onAppear to run explicitly on the main queue (UI) which got it working:
struct ContentView: View {
#State var showModal = false
var body: some View {
TabView {
Text("Home View")
.tabItem {
VStack {
Image(systemName: "house")
Text("Home")
}
}
Text("Dummy View")
.onAppear {
print("Hallo")
DispatchQueue.main.async {
self.showModal = true
}
}
.tabItem {
VStack {
Image(systemName: "camera")
Text("Camera")
}
}
Text("Setting View")
.tabItem {
VStack {
Image(systemName: "person")
Text("Setting")
}
}
}
.sheet(isPresented: self.$showModal) {
Text("Camera View")
}
}
}
I hope this helps!