TabView (SwiftUI): Respond onTab on an already active tabItem - ios

I have a ContentView.swift which contains a TabView.
View1 has a NavigationView in it with some childViews.
View2 is just a single View.
I would like to perform some action (always return to View1, even when being in a childView of View1), when the first tabItem is pressed. Even if it is already the active tabItem.
I tried onTapGesture as seen below, which didn't seem to do anything. Adding it right before ".tag(0)" didn't change anything, either:
TabView {
View1()
.onTapGesture {
print("Test")
}
.tabItem {
Image(systemName: "doc.plaintext")
.font(.system(size: 25))
Text("View1")
}.tag(0)
View2()
.tabItem {
Image(systemName: "person.crop.circle")
.font(.system(size: 25))
Text("View2")
}.tag(1)
}

Here is possible approach. Tested with Xcode 11.4 / iOS 13.4
struct TestTabSelectionAction: View {
#State private var selectedTab = 0
var body: some View {
let selection = Binding<Int>(
get: { self.selectedTab },
set: { self.selectedTab = $0
print("Pressed tab: \($0)")
if $0 == 0 {
// <<< your action here !!
}
})
return TabView(selection: selection) {
View1()
.tabItem {
Image(systemName: "doc.plaintext")
.font(.system(size: 25))
Text("View1")
}.tag(0)
View2()
.tabItem {
Image(systemName: "person.crop.circle")
.font(.system(size: 25))
Text("View2")
}.tag(1)
}
}
}

Related

TabView animation glitch

Get that strange glitch and can't fix it.
Source of problem - TabView and changing size of child views with animation.
Changing UITabbar appearance not helped. Changing safe area options not helped.
UITabBar.appearance().isHidden = false and opaque appearance give flickering of whole tabbar.
I want to hide default UITabbar to customize my own.
Any ideas?
Flickering of bottom safe area: Demonstration GIF
Sample project:
struct ContentView: View {
#State var selected = "second"
var body: some View {
ZStack(alignment: .bottom) {
VStack(spacing: 0) {
FirstView()
TabView(selection: $selected) {
SecondView()
.tabItem({
Text("second")
})
.tag("second")
ThirdView()
.tabItem({
Text("third")
})
.tag("third")
}
}
HStack {
Image(systemName: selected == "second" ? "circle.fill" : "circle")
.onTapGesture {
selected = "second"
}
Image(systemName: selected == "third" ? "circle.fill" : "circle")
.onTapGesture {
selected = "third"
}
}
.frame(width: 70, height: 40, alignment: .center)
.background(Color.white)
.cornerRadius(10)
.padding()
}
.edgesIgnoringSafeArea(.all)
.onAppear {
UITabBar.appearance().isHidden = true
}
}
}
struct FirstView: View {
#State var height:CGFloat = 200
var body: some View {
ZStack(alignment: .bottom) {
Color.red
.frame(height: height)
Image(systemName: height == 200 ? "arrow.down" : "arrow.up")
.foregroundColor(.white)
.padding(5)
.onTapGesture {
withAnimation(.easeInOut(duration: 2)) {
height = height == 200 ? 350 : 200
}
}
}
}
}
struct SecondView: View {
var body: some View {
ZStack {
Text("hello")
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.background(Color.green)
.onAppear {
UITabBar.appearance().isHidden = true
}
}
}
struct ThirdView: View {
var body: some View {
Color.blue
.edgesIgnoringSafeArea(.bottom)
}
}
Thanks for answers :)
Since you have your own index navigation anyways, I recommend to get rid of TabView altogether and switch views by if / else or switch statements:
struct ContentView: View {
#State var selected = "second"
var body: some View {
ZStack(alignment: .bottom) {
VStack(spacing: 0) {
FirstView()
if selected == "second" {
SecondView()
} else {
ThirdView()
}
}
HStack {
Image(systemName: selected == "second" ? "circle.fill" : "circle")
.onTapGesture {
selected = "second"
}
Image(systemName: selected == "third" ? "circle.fill" : "circle")
.onTapGesture {
selected = "third"
}
}
.frame(width: 70, height: 40, alignment: .center)
.background(Color.white)
.cornerRadius(10)
.padding()
}
.edgesIgnoringSafeArea(.all)
}
}

Display the same view across multiple tabs, swiftui

I have an app with a TabView. One of these tabs(1) displays a CartView.
There is another tab(2) that Adds items to the cart and Also allows Navigation to the CartView. The contents of the cart are maintained in an EnvironmentObject CartModel Object.
The issue I am having is that When I add items to Cart and navigate to the CartView, I am able to see the items. Should I back out and add something else, I can still see all the items as long as I am in tab(2). However I cannot see anything when I try to access the cart via tab(1). Hoping to get some pointers on what is happening here.
Clearly the underlying model code is accessible to the CartView on one of the tabs.
Relevant code:
Tabbed View:
var body: some View {
NavigationView {
TabView(selection: $selectedTab) {
StoreByCategoryView()
.tag(0)
.tabItem { Image(systemName: "bag") }
CreditDetailsView()
.tag(1)
.tabItem { Image(systemName: "creditcard") }
CartView()
.tag(2)
.tabItem { Image(systemName: "cart") }
SettingsView()
.tag(3)
.tabItem { Image(systemName: "gearshape") }
SocialMainView()
.tag(4)
.tabItem { Image(systemName: "globe") }
}
}
.navigationViewStyle(StackNavigationViewStyle())
.navigationBarHidden(true)
.padding(.horizontal, 5)
}
CartView
struct CartView: View {
#EnvironmentObject var cartModel: CartModel
var body: some View {
let _ = print("CartView")
if cartModel.totalQuantity == 0 {
Text("Nothing present in the cart")
} else {
let _ = print("CartView --- ELSE")
VStack(alignment:.leading , spacing: 5){
let _ = print("CartView --- VSATCK")
List {
ScrollView(/*#START_MENU_TOKEN#*/.vertical/*#END_MENU_TOKEN#*/, showsIndicators: false){
ForEach(Array(cartModel.productQuantityMap.keys), id: \.self) {(product) in
CartItemView(product: product)
}
}
}
Group {
Divider()
HStack {
Spacer()
Text("Total: $A\(cartModel.totalValue)" as String)
.fontWeight(.semibold)
.foregroundColor(.primary)
}
.padding(.bottom)
Divider()
}
HStack(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/, spacing: /*#START_MENU_TOKEN#*/nil/*#END_MENU_TOKEN#*/){
Spacer()
Button(action: {
checkoutModel.checkout(productQty: cartModel.productQuantityMap, customertoken: session.customerAccessToken)
}){
Text("Checkout")
.font(.system(.title2, design: .rounded))
.fontWeight(.semibold)
.foregroundColor(.white)
}
.frame(width: 200, height: 35)
.padding(15)
.background(Color(UIColor(red: 0.39, green: 1.00, blue: 0.85, alpha: 1.00)))
.clipShape(Capsule())
Spacer()
}
}
...
}
}
}

how can I hide tab bar in specific screens in SwiftUI?

How to hide the tabBar in specific screens? I'm navigating from login to directly to tabBar. Is there any way to hide? In UIKit we're hiding by pushing and I have no idea how to do it in SwiftUI, by presenting the view not going to work.
Here is my TabBar
struct ReceiverTabBar: View {
#State private var selection: Int = 0
var body: some View {
TabView(selection: $selection){
.tabItem {
selection == 0 ? Image("")
Text("")
}
.tag(0)
ReceiverProfileView()
.tabItem {
selection == 1 ? Image("")
Text("")
}
.tag(1)
ReceiverNotificationsView()
.tabItem {
selection == 2 ? Image("")
Text("")
}
.tag(2)
ReceiverMoreView()
.tabItem {
selection == 3 ? Image("")
Text("")
}
.tag(3)
}
.accentColor(.black)
}
}
and I want hide tabBar in this view
struct MakingDonationView: View {
#Environment(\.presentationMode) var presentationMode
#State var selected = 0
var body: some View {
ScrollView(showsIndicators: false) {
Image("")
.resizable()
.aspectRatio(contentMode: .fit)
.padding(.horizontal,30)
.padding(.top,40)
.frame(height: UIScreen.main.bounds.height/5)
Text("")
.font(.custom("Poppins-SemiBold", size: 16))
.foregroundColor(Color("#252422"))
.padding(.top,20)
Text("")
.font(.custom("Poppins-SemiBold", size: 12))
.foregroundColor(Color("#5E5E5E"))
Text("")
.font(.custom("Poppins-Medium", size: 12))
.foregroundColor(Color("#A0A0A0"))
}
Spacer()
Divider()
MakingDonation(selected: $selected)
}
.padding(.all)
}
.padding(.horizontal,20)
.edgesIgnoringSafeArea(.bottom)
}
Button(action: {
}, label: {
Spacer()
Text("Confirm Donation Amount")
.font(.custom("Poppins-SemiBold", size: 13))
.foregroundColor(.black)
Spacer()
})
.frame(height:44)
.background(Color("#FFA919"))
.padding(.horizontal,20)
}
.shadow(color: .gray, radius: 1, x: 0, y: 2)
.cornerRadius(4)
}
.shadow(color: .gray, radius: 2, x: 0, y: 2)
.edgesIgnoringSafeArea(.all)
.frame(height:80)
.navigationBarBackButtonHidden(true)
.navigationBarTitle("Making Donation", displayMode: .inline)
}
}
func goBack(){
self.presentationMode.wrappedValue.dismiss()
}
}
Try this:
// Container Screen view for handling all screen
struct ContainerView: View {
#State var tabSelection: Screens = .screen1
var body: some View {
NavigationView{
TabView(selection: $tabSelection){
// Screen 1
// Hide tab bar view only for Screen 1
NavigationLink(destination: PushedView()){
VStack{
Text("Screen 1")
Text("Tap to PushedView")
}
}
.tabItem { Text("Screen 1") }
.tag(Screens.screen1)
// Screen 2
// same view using for screen 2 for directly shows on that
PushedView()
.tabItem { Text("Screen 2") }
.tag(Screens.screen2)
}
.navigationBarTitle( self.tabSelection.title)
}
}
}
// New view for pushing
struct PushedView: View {
var body: some View {
Text("Hi! This is the new View")
.navigationBarTitle("NewView")
}
}
// Tab screens
enum Screens{
case screen1, screen2
var title: String {
switch self {
case .screen1:
return "Screen1"
case .screen2:
return "Screen2"
}
}
}

Editing NavigationBarItems and NavigationBarTitle in SwiftUI inside TabView

I'm trying to change the navigation bar title and leading & trailing items in a SwiftUI app.
Here is the situation:
A user logs in, then he gets transferred to a view inside a TabView.
Before login, in the welcome screen and login & register screens I have no navigation bar title, but after login, I want to add the navigation bar items and title.
What is important to know is that my after-login-view is inside a TabView.
If I wrap my after-login-view in a new NavigationView and then change the navigation bar items and title it works! but it shows me 2 navigation bars:
which is not what I want.
here is the code I use :
This is the after-Login-View
import SwiftUI
struct DiscoveryView: View {
var body: some View {
List{
Text("List Items")
}.offset(y: 10)
.navigationBarTitle("", displayMode: .inline)
.navigationBarItems(leading:
Text("Discovery").font(Font.custom("Quicksand- Bold", size: 24))
, trailing:
Button(action: {
}) {
HStack{
Text("120").font(Font.custom("Quicksand-Bold", size: 15)).foregroundColor(Color("BlueColor"))
Text("following").font(Font.custom("Quicksand-Bold", size: 15)).foregroundColor(Color.black)
}
}
).navigationBarBackButtonHidden(true)
}
}
This is the TabView:
struct TabController : View {
#State private var selection = 0
var body: some View {
TabView(selection: $selection){
DiscoveryView()
.tabItem {
VStack {
Image(systemName: "list.dash")
Text("Discover")
}
}.tag(1)
ExploreView()
.tabItem {
VStack {
Image(systemName: "square.and.pencil")
Text("Explore")
}
}.tag(2)
SelfProfileView()
.tabItem{
VStack {
Image(systemName: "person.circle")
Text("Profile")
}
}.tag(3)
}.accentColor(Color("BlueColor"))
}
}
This is the code of the login page:
import SwiftUI
struct LoginView: View {
var body: some View {
ZStack{
VStack{
Image("mainLogo").resizable().frame(width: 250, height: 125)
Text("").frame(width: UIScreen.main.bounds.width, height: 100)
HStack{
TextField("Username / Email", text: $email)
.padding(.leading, 10)
.frame(width: 313, height: 49)
.background(RoundedRectangle(cornerRadius: 4).stroke( Color.black.opacity(0.3)).frame(width: 313, height: 39).background(Color("GrayColor")))
}
Text("").frame(width: UIScreen.main.bounds.width, height: 40)
HStack {
HStack{
if self.visable{
TextField("Password", text: $password)
.padding(.horizontal)
} else {
SecureField("Password", text: $password)
.padding(.horizontal)
}
Button(action: {
self.visable.toggle()
}){
Image(systemName: self.visable ? "eye.slash" : "eye")
.padding(.trailing, 20)
.foregroundColor(Color.black)
}
}.background(RoundedRectangle(cornerRadius: 4).stroke( Color.black.opacity(0.3)).frame(width: 313, height: 39).background(Color("GrayColor")) )
}.padding(.horizontal, 40).padding(.vertical)
Text("").frame(width: UIScreen.main.bounds.width, height: 100)
Button(action: {
//pass email password and conpassword to cognito
DSManager().signIn(username: self.email, password: self.password, error: {
error in
if let error = error{
self.error = error.errorString
self.alert.toggle()
print(error.errorString)
}
else{
DSManager().getSelfUserInformation(response: {
userInfo, userCollections,dsError in
if let dsError = dsError{
self.error = dsError.errorString
self.alert.toggle()
print(dsError.errorString)
}
if let userInfo = userInfo{
print("success")
//use userInfo to load stuff for profile page
if let userCollections = userCollections{
self.profileInfo.setProperties(userInfo: userInfo, collections: userCollections)
}
else{
self.profileInfo.setProperties(userInfo: userInfo, collections: [:])
}
self.isComplete.toggle()
}
})
}
})
}) {
Text("Login")
.fontWeight(.semibold)
.frame(width: 313, height: 48)
.background(fillAllFields() ? Color("YellowColor") : Color.gray)
.foregroundColor(Color.white)
.font(.system(size: 17))
.cornerRadius(15)
.shadow(color: Color("GrayColor"), radius: 0, x: 3, y: 3)
}.disabled(!fillAllFields())
NavigationLink(destination: ProfileView(), isActive: $isComplete) {
EmptyView()
}
Spacer()
}
.padding()
.navigationBarTitle("", displayMode: .inline)
if self.alert {
ErrorView(err: $error, alert: $alert)
}
}
}
func fillAllFields() -> Bool {
if self.email == "" || self.password == "" {
return false
}
return true
}
}
As mentioned, I'm trying to edit bar items and title of a view inside and tab view .
Thanks!
So as per your updated question, I added DiscoveryView Inside NavigationView and it worked as expected. I have added code and screenshot as well.
struct TabController : View {
#State private var selection = 0
var body: some View {
TabView(selection: $selection) {
NavigationView {
DiscoveryView()
}
.tabItem {
VStack {
Image(systemName: "list.dash")
Text("Discover")
}
}.tag(1)
Text("Explore View")
.tabItem {
VStack {
Image(systemName: "square.and.pencil")
Text("Explore")
}
}.tag(2)
Text("SelfProfileView")
.tabItem{
VStack {
Image(systemName: "person.circle")
Text("Profile")
}
}.tag(3)
}.accentColor(Color("BlueColor"))
}}

Hiding tab bar on a specific page in SwiftUI

I am working with a camera in my application. The app allows you to navigate to this camera view with the help of the TabView in SwiftUI. However, the problem is, when I am on the camera view, I would like to make the TabView hidden. So far I've been trying to find a solution but I cannot seem to find any.
Here is a screenshot of the code and the view with the tabview
Note: The screenshot contains an image if the preview. The camera works fine when I run it on a real device. The problem is that I need the tab bar to be hidden once I've entered the camera view.
And here is an example of the code that I am using:
import SwiftUI
struct AppView: View {
var body: some View {
TabView{
Home()
.tabItem {
// Add icon
Text("Home")
}
MomentsCam()
.tabItem {
// Add icon
Text("Camera")
}.navigationBarHidden(true)
Moments()
.tabItem{
//Add icon
Text("Moments")
}
}
}
}
I updated my solution with TabView for your situation. The same idea: you're using ZStack and #State var selection. And the idea is to use .opacity of TabView and YourCameraView (which is just Image(systemName: "plus.circle") in my example):
struct TabViewModel: View {
#State var selection: Int = 0
var body: some View {
ZStack {
GeometryReader { geometry in
TabView(selection: self.$selection) {
Text("list")
.tabItem {
Image(systemName: "list.bullet.below.rectangle")
}.tag(0)
Text("plus")
.tabItem {
Image(systemName: "camera")
}.tag(1)
Text("more categories!")
.tabItem {
Image(systemName: "square.grid.2x2")
}.tag(2)
}
.opacity(self.selection == 1 ? 0.01 : 1)
Image(systemName: "plus.circle")
.resizable()
.frame(width: 60, height: 60)
.shadow(color: .gray, radius: 2, x: 0, y: 5)
.offset(x: geometry.size.width / 2 - 30, y: geometry.size.height - 80)
.onTapGesture {
self.selection = 0
}
.opacity(self.selection == 1 ? 1 : 0)
}
}
}
}
when you tap on camera tabItem TabView becomes invisible
You may try the following code according to your needs.
struct MomentsCam: View {
var body: some View {
Text("Cam")
}
}
struct Moments: View {
var body: some View {
Text("Moments Cam")
}
}
struct AppView: View {
#State var showCamera = false
var body: some View {
GeometryReader{ p in
ZStack{
TabView{
Home()
.tabItem {
// Add icon
Text("Home")
}
Text("holder")
.tabItem {
// Add icon
Text("Camera")
}.navigationBarHidden(true).onAppear{
self.showCamera = true
print(p.size)
}
Moments()
.tabItem{
//Add icon
Text("Moments")
}
}
if self.showCamera{
MomentsCam().frame(width: p.size.width, height: p.size.height).background(Color.white)
}
}
}
}
}

Resources