Unable to showing navigation with Title In swift UI - swiftui

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

Related

NavigationView scrolling broken when using TabView

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

How to hide TabView navigating from tabItem in childview in SwiftUI?

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

How to create custom Navigation Bar Swift UI?

I've been using default navigation bar (because it has the ability to enable swipes to close a View), but since my issue is to hide NavBar in a RootView and show when it disappears after Navigation to a ChildView, I faced a problem with my ChildView (it bounce up and down after manipulations with navbar). Hence I need a custom NavBar (perfectly would be with an ability to make swipes to hide it.)
Here you can see my code and issue with NavBar that was solved and triggered the one you are reading.
My RootView
struct ExploreView: View {
var body: some View {
ZStack{
VStack{
HStack{
NavigationLink(destination: MessagesView()){
Image("messages")
}
}
}
}
}.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
ChildView#
struct MessagesView: View {
#Environment(\.presentationMode) var presentationMode
var btnBack : some View {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "chevron.left")
.font(.title)
}
}
var body: some View {
ZStack{
VStack{
Spacer()
HStack {
btnBack
.padding(.leading, 10)
Spacer()
Button(action:{
self.show.toggle()
},label: {
Image("writemessage")
.foregroundColor(Color("blackAndWhite"))
}
)
}
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
A custom NavigationBar could look like this. Of course it can be individualized with colors and fontSizes etc. in whatever way you like:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
HStack {
NavigationLink(destination: MessagesView()){
Text("Go to MessagesView")
}
}
}.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
}
struct MessagesView: View {
#Environment(\.presentationMode) var presentationMode
var btnBack : some View {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "chevron.left")
.font(.title)
}
}
var body: some View {
ZStack{
VStack{
HStack {
btnBack
.padding(.leading, 10)
Spacer()
}
Spacer()
Text("MessagesView")
Spacer()
}.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
}
To keep the Swipe-back gesture working even while the standard NavigationBar is disabled you need some addition under your SceneDelegate:
extension UINavigationController: UIGestureRecognizerDelegate {
override open func viewDidLoad() {
super.viewDidLoad()
interactivePopGestureRecognizer?.delegate = self
}
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return viewControllers.count > 1
}
}

Update TabView and move to Home View after login

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)

How to perform a modal transition like the center button in the Instagram app with SwiftUI TabView?

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!