For some reason, tabBarItems are not aligned horizontally.
struct MainView: View {
#Environment(\.managedObjectContext) var moc
var body: some View {
TabView {
CodesView()
.environment(\.managedObjectContext, moc)
.tabItem {
Label("Codes", systemImage: "list.dash")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gearshape.fill")
}
}
.navigationViewStyle(StackNavigationViewStyle()) //Prevent split view on both Codes and Settings
}
}
Screenshots:
Portrait:
Landscape:
Related
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 have the following code
enum Tab {
case accounts, lootbox
}
struct ContentView: View {
#State private var currentTab:Tab = .lootbox
var body: some View {
TabView(selection: $currentTab){
AccountView()
.tabItem {
Label("Accounts", systemImage: "person.crop.circle")
}
.tag(Tab.accounts)
Lootbox()
.tabItem {
Label("Lootbox", systemImage: "shippingbox")
}
.tag(Tab.lootbox)
}
.tabViewStyle(.page)
}
}
struct AccountView: View {
var body: some View {
NavigationView{
VStack{
Text("AccountView")
Spacer()
.navigationTitle("Lootbox")
}
}
.navigationViewStyle(.stack)
}
}
struct Lootbox: View {
var body: some View {
NavigationView{
VStack{
Text("Lootbox")
Spacer()
}
.navigationTitle("Lootbox")
}
.navigationViewStyle(.stack)
}
}
The VStackhides behind the navigation view title. After swiping right and left, it is as expected. Why is that? Or is this a bug of SwiftUI?
Before swipe left and right:
After (how it should be):
I have a tabview with three SwiftUIViews (HomeView, FavoritesView and ContactsView)
each of these views look like this Home View below.
struct HomeView: View {
var body: some View {
VStack {
Image(systemName: "house.fill")
Text("This is the Home View")
}
}
}
My content view looks like this:
struct ContentView: View {
var body: some View {
NavigationView {
TabView {
HomeView()
.tabItem {
Label("Home", systemImage: "house")
.navigationBarTitle("Home")
}
FavoritesView()
.tabItem {
Label("Favorites", systemImage: "star")
.navigationBarTitle("Favorites")
}
ContactsView()
.tabItem {
Label("Contacts", systemImage: "person")
.navigationBarTitle("Contacts")
}
}
}
}
}
But when I run the app each tab get the same "Home" title for the navigation title.
How can I update the navigation title with the correct tab (without adding a navigation View in each SwiftUI Views) I believe we should be able to achieve this with only one nav view? Right???
//Create an enum for your options
enum Tabs: String{
case home
case favorites
case contacts
}
struct TitledView: View {
//Control title by the selected Tab
#State var selectedTab: Tabs = .favorites
var body: some View {
NavigationView {
TabView(selection: $selectedTab) {
Text("HomeView()").tag(Tabs.home)
.tabItem {
Label("Home", systemImage: "house")
}
//Set the tag
.tag(Tabs.home)
Text("FavoritesView()")
.tabItem {
Label("Favorites", systemImage: "star")
}
//Set the tag
.tag(Tabs.favorites)
Text("ContactsView()")
.tabItem {
Label("Contacts", systemImage: "person")
}
//Set the tag
.tag(Tabs.contacts)
}.navigationTitle(selectedTab.rawValue.capitalized)
}
}
}
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)
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!