Reputation: 823
I am working with SwiftUI, and I have some issues with the TabBar. I want to hide the TabBar on a specific subview.
Have tried with
UITabBar.appearance().isHidden = true
It only works on the direct views in the TabView. But when I place it in a subview it doesn't work.
Have anyone a solution for this?
Thanks.
Upvotes: 69
Views: 74673
Reputation: 2082
I'm a bit late to the party but this is what I found out. This solution was tested in iOS 17
, but I think it'd be fine with iOS 16
.
First, a simple snippet you can paste in a #Preview
in Xcode:
#Preview {
NavigationStack {
TabView {
NavigationStack {
NavigationLink("Tap Me") {
Text("Detail View")
.toolbar(.hidden, for: .tabBar)
.navigationTitle("Detail View")
}
}
.tabItem {
Label("Home", systemImage: "house")
}
}
.navigationTitle("Primary View")
}
}
What is the trick? Wrapping the TabView
in a NavigationStack. Without it, it's kind of works, but the tab bar appears without animation and... it's ugly. However, wrap the TabView
in a NavigationStack
and it comes to life the way we all expect: buttery smooth.
Give it a try: paste the code and comment out the outer NavigationStack
. See how it looks. Then add it back. I hope it helps!
Upvotes: 1
Reputation: 3100
Since iOS 16 we are able to use the .toolbar(.hidden, for: .tabBar).
but using this method alone on the view that I would like to remove the tabBar it would result on a jumpy tabBar after clicking on the back button.
I tried to use the .onDisappear to put back the tabBar before the parent view to appear but the method is called too late.
So in my solution (that is not perfect) I override the back button in the view that I want to hide the tabBar and I have a @State var that I use to toggle the toolbar. Something like this:
struct DetailView: View {
@Environment(\.presentationMode) var mode: Binding<PresentationMode>
@State private var showTabBar = false
var body: some View {
VStack {
Text("Hello world")
}
.toolbar(showTabBar ? .visible : .hidden, for: .tabBar)
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button(action : {
self.mode.wrappedValue.dismiss()
showTabBar.toggle()
}){
Image(systemName: "chevron.backward").bold()
Text("Back")
})
}
}
Upvotes: 1
Reputation: 554
There is my solution:
NavigationView
nested in TabView
//
// Created by Maxime Ashurov on 27/09/2023.
//
import SwiftUI
private class TabBarVisibilityObject: ObservableObject {
static let shared = TabBarVisibilityObject()
private init() {}
@Published var visibility: Visibility = .visible
}
private struct TabBarVisibilityModifier: ViewModifier {
let visibility: Visibility
@StateObject private var visibilityObject = TabBarVisibilityObject.shared
func body(content: Content) -> some View {
content
.onAppear {
visibilityObject.visibility = visibility
}
.toolbar(visibilityObject.visibility, for: .tabBar)
}
}
public extension View {
func hidesTabBar() -> some View {
self.modifier(TabBarVisibilityModifier(visibility: .hidden))
}
func showsTabBar() -> some View {
self.modifier(TabBarVisibilityModifier(visibility: .visible))
}
}
Upvotes: -2
Reputation: 115
I made a Modifier utility that hides the TabBar
for iOS 16
or older versions [I successfully tested it on iOS 16
and iOS 15
].
Create TabBarModifier
swift file and add the following:
import SwiftUI
/// adapted from [TabBarModifier](https://github.com/artemisak/DiaCompanion_iOS/blob/360258e07d4a3e3521a1ec6893f29f175d67034f/dia/Extensions/extensions.swift#L86-L87)
extension View {
func showTabBar() -> some View {
if #available(iOS 16.0, *) {
return toolbar(.visible, for: .tabBar)
} else {
return modifier(ShowTabBar())
}
}
func hideTabBar() -> some View {
if #available(iOS 16.0, *) {
return toolbar(.hidden, for: .tabBar)
} else {
return modifier(HideTabBar())
}
}
}
private struct HideTabBar: ViewModifier {
func body(content: Content) -> some View {
return content.padding(.zero).onAppear {
TabBarModifier.hideTabBar()
}
}
}
private struct ShowTabBar: ViewModifier {
func body(content: Content) -> some View {
return content.padding(.zero).onAppear {
TabBarModifier.showTabBar()
}
}
}
private struct TabBarModifier {
static func showTabBar() {
guard let keyWindow = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else {
return
}
keyWindow.allSubviews().forEach { subView in
if let tabBar = subView as? UITabBar {
tabBar.isHidden = false
}
}
}
static func hideTabBar() {
guard let keyWindow = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else {
return
}
keyWindow.allSubviews().forEach { subView in
if let tabBar = subView as? UITabBar {
tabBar.isHidden = true
}
}
}
}
private extension UIView {
func allSubviews() -> [UIView] {
var subviews = [UIView]()
subviews.append(contentsOf: self.subviews)
subviews.forEach { subview in
subviews.append(contentsOf: subview.allSubviews())
}
return subviews
}
}
Then use it like any modifier. example:
struct Example: View {
var body: some View {
StatsView()
.hideTabBar()
}
}
let me know if there is a way to cleanup or improve this :)
Upvotes: 0
Reputation: 4060
Browsed through all the answers, there are always deficiencies
I chose custom, which has the following benefits:
1.Support iOS15
2.TabBarView does not nest NavigationView
3.Do not break the logic of TabBarView
4.Requires only a small amount of code and is fully customizable
copy my code to run
core code
import SwiftUI
@main
struct HideTabBarViewApp: App {
init() {
UITabBar.appearance().isHidden = true
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State var selection = 0
var body: some View {
TabView(selection: $selection) {
TabItemView(selection: $selection, tabBarType: .home) {
PageView(pageType: .home)
}
.tag(0)
TabItemView(selection: $selection, tabBarType: .contact) {
PageView(pageType: .contact)
}
.tag(1)
TabItemView(selection: $selection, tabBarType: .search) {
PageView(pageType: .search)
}
.tag(2)
TabItemView(selection: $selection, tabBarType: .user) {
PageView(pageType: .user)
}
.tag(3)
}
}
}
Custom TabItemView
struct TabItemView<Content: View>: View {
@Binding var selection: Int
@State var tabBarType: TabBarType
@ViewBuilder let content: () -> Content
var imageArray = ["rectangle.roundedtop", "message", "magnifyingglass", "person"]
var body: some View {
NavigationView {
VStack {
content()
}
.frame(maxWidth:.infinity, maxHeight: .infinity)
.overlay(alignment: .bottom) {
tabBarView
}
}
}
var tabBarView: some View {
HStack {
ForEach(Array(imageArray.enumerated()), id: \.element) { index, image in
Button {
withAnimation {
selection = index
}
} label: {
Image(systemName: image)
.font(.title2)
.foregroundColor(index == tabBarType.rawValue ? .blue : .gray)
}
.frame(maxWidth: .infinity)
}
}
.frame(height: 40, alignment: .top)
.padding(.horizontal)
.padding(.top)
.background(.bar)
}
}
some business logic code
struct PageView: View {
@State var pageType: PageType
var body: some View {
VStack {
List {
ForEach((1...20), id: \.self) {
NavigationLink {
SettingView()
} label: {
Text("Go to setting")
}
.id($0)
}
}
}
.navigationTitle(pageType.rawValue)
.navigationBarTitleDisplayMode(.inline)
.frame(maxWidth:.infinity, maxHeight: .infinity)
}
}
struct SettingView: View {
var body: some View {
Text("Setting Page")
.font(.title)
.navigationTitle("Setting")
.navigationBarTitleDisplayMode(.inline)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
enum PageType: String {
case home = "Home"
case contact = "Contact"
case search = "Search"
case user = "User"
}
enum TabBarType: Int {
case home = 0
case contact = 1
case search = 2
case user = 3
}
Upvotes: 0
Reputation: 1362
Use .toolbar(.hidden, for: .tabBar)
.
For example:
var body: some View {
TabView {
FirstView()
.tabItem {
Text("First tab")
}
.toolbar(.hidden, for: .tabBar)
SecondView()
.tabItem {
Text("Second tab")
}
.toolbar(.hidden, for: .tabBar)
}
}
Note that .toolbar(.hidden, for: .tabBar)
is applied to each tabItem
, not to the parent view. YMMV if you have a different structure (like a parent NavigationView etc.)
Upvotes: 22
Reputation: 14329
To hide TabBar when we jumps towards next screen we just have to place NavigationView
to the right place. Makesure Embed TabView
inside NavigationView
so creating unique Navigationview
for both tabs.
As explained here https://janeshswift.com/ios/swiftui/how-to-hide-tabbar-on-push-with-swiftui/
import SwiftUI
struct TabBarView: View {
@State var tabSelection: Int = 0
@State var tabArray = ["Profile", "Settings"]
var body: some View {
NavigationView {
TabView(selection: $tabSelection){
ForEach(0 ..< tabArray.count, id: \.self) { indexValue in
NavigationLink(destination: DetailView()){
VStack{
Text("\(tabArray[indexValue]) tab -- Click to jump next view")
}
}
.tabItem {
Image(systemName: "\(indexValue).circle.fill")
Text(tabArray[indexValue])
}
.tag(indexValue)
}
}
.navigationBarTitle(tabArray[tabSelection])
}
}
}
struct DetailView: View {
var body: some View {
Text("Detail View")
.navigationBarTitle("NavigatedView")
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("helllo")
}
}
Upvotes: 12
Reputation: 2324
iOS 16
Usage of .toolbar
modifier. Create state property of type Visibility
and manage its value from the pushed view
First view
struct FirstTab: View {
@State var tabBarVisibility: Visibility = .visible
var body: some View {
NavigationView {
NavigationLink(destination: WidgetDetailView(tab: self)) {
Text("test")
}
}
.toolbar(tabBarVisibility, for: .tabBar)
}
}
Second view
struct WidgetDetailView: View {
var tab: FirstTab
var body: some View {
Rectangle()
.foregroundColor(Color.red)
.onAppear {
tab.tabBarVisibility = .hidden
}
.onDisappear {
tab.tabBarVisibility = .visible
}
}
}
Upvotes: 7
Reputation: 4107
Most answers here deal with this requirement in one of two ways:
The first one is a clean approach: it locates the underlying element that enables the desired action. However, it may be overkill for a single use case.
The second approach involves some tradeoffs and could be generally considered a smell, since it introduces hierarchy changes for the sake of working around the lack of access to the required element.
Instead, we could follow a clean, simple approach by creating a protocol extension like so:
import UIKit
protocol TabBarAppearanceDelegate {
func toggleVisibility()
func hideTabBar()
func showTabBar()
// add more methods to control appearance as needed
}
extension TabBarAppearanceDelegate {
private var tabBarController: UITabBarController? {
// this is where we access the underlying element, no need to import a framework for a one-liner
UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController?.children.first as? UITabBarController
}
func toggleVisibility() {
tabBarController?.tabBar.isHidden.toggle()
}
func hideTabBar() {
tabBarController?.tabBar.isHidden = true
}
func showTabBar() {
tabBarController?.tabBar.isHidden = false
}
}
Then we can make any object conform to this protocol, and inject it as dependency in the views as needed. This will depend on your architecture but it could go like follows.
This is where you'd keep app-wide state, an ObservableObject
(you could designate a different one, if preferred):
import Foundation
class StateController: ObservableObject {
// you would typically manage app-wide state here
}
// this is where we adopt the needed behaviour
extension StateController: TabBarAppearanceDelegate {}
We can now inject the object as a view dependency:
@main
struct TabBarVisibilityApp: App {
private let stateController = StateController()
var body: some Scene {
WindowGroup {
TabView {
NavigationView {
SampleView(tabBarAppearanceDelegate: stateController)
}
.tabItem {
Label("Home", systemImage: "house")
}
}
}
}
}
This is how you would use it (valid for in any view that requires the behaviour):
import SwiftUI
struct SampleView: View {
let tabBarAppearanceDelegate: TabBarAppearanceDelegate
var body: some View {
VStack {
Spacer()
Button(action: {
tabBarAppearanceDelegate.toggleVisibility()
} ) {
Text("Toggle tab bar visibility")
}
Spacer()
}
}
}
This approach is simple, testable, and requires no extra dependencies... until Apple provides a direct way to control tab bar visibility with a SwiftUI API.
Upvotes: 1
Reputation: 773
I have tried to use https://stackoverflow.com/a/62963499/11844048 solution but the TabBar hide in all views once I landed this view. I have modified it a bit to achieve to hide TabBar in single view.
struct AppInfoView: View {
@Environment(\.presentationMode) var mode: Binding<PresentationMode>
var body: some View {
ZStack{
}
.frame(maxWidth: .infinity)
.background(Color("homepage_bg")).ignoresSafeArea(.all)
.onAppear{
UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.allSubviews().forEach({ (v) in
if let view = v as? UITabBar {
view.isHidden = true
}
})
}
.onDisAppear(...) //it works too. But seeing TabBar shown bit delay when naviagting back. So below the customizable back button.
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button(action : {
self.mode.wrappedValue.dismiss()
UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.allSubviews().forEach({ (v) in
if let view = v as? UITabBar {
view.isHidden = false
}
})
}){
Image(systemName: "chevron.left")
})
}
extension UIView {
func allSubviews() -> [UIView] {
var res = self.subviews
for subview in self.subviews {
let riz = subview.allSubviews()
res.append(contentsOf: riz)
}
return res
}
}
Upvotes: 1
Reputation: 529
It is possible! Basically your task is to extract UITabBar somehow and then hide it programatically.
Below is the code which emulates tab bar hiding on push behaviour.
struct ContentView: View {
var body: some View {
TabView {
ForEach(titles, id: \.self) { title in
NavigationView {
view(fromTitle: title)
}
.tabItem {
Text(title)
Image(systemName: "photo")
}
.tag(title)
}
}
}
private let titles = ["one", "two"]
@ViewBuilder
private func view(fromTitle title: String) -> some View {
if title == "one" {
RegularView(title: title)
.navigationTitle(title)
.navigationBarTitleDisplayMode(.inline)
} else {
HideOnPushView(title: title)
.navigationTitle(title)
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct RegularView: View {
let title: String
var body: some View {
VStack(spacing: 20) {
Text(title)
NavigationLink("Regular push") {
Text("1111111")
}
}
}
}
struct HideOnPushView: View {
let title: String
var body: some View {
VStack(spacing: 20) {
Text(title)
NavigationLink("Hide on push") {
Text("222222")
.onAppear {
tabBar?.hide()
}
}
}
.background(
TabBarExtractor(tabBar: $tabBar)
)
.onAppear {
tabBar?.show()
}
}
@State private var tabBar: UITabBar?
}
TabBar extractor code:
import SwiftUI
struct TabBarExtractor: UIViewControllerRepresentable {
@Binding var tabBar: UITabBar?
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
func makeUIViewController(context: Context) -> some UIViewController {
let controller = ViewController()
controller.onTabBarAppearance = {
tabBar = $0
}
return controller
}
}
private extension TabBarExtractor {
class ViewController: UIViewController {
var onTabBarAppearance: ((UITabBar) -> Void)?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let tabBar = self.tabBarController?.tabBar {
onTabBarAppearance?(tabBar)
} else {
print("Could not locate TabBar! Try change extractor place in views hierarchy.")
}
}
}
}
TabBar category:
import UIKit
extension UITabBar {
func toggleVisibility() {
if isHidden {
show()
} else {
hide()
}
}
func show() {
guard isHidden else { return }
let visibleY = frame.origin.y
let hiddenY = visibleY + frame.height
frame.origin.y = hiddenY
isHidden = false
UIView.animate(withDuration: 0.3) { [weak self] in
self?.frame.origin.y = visibleY
}
}
func hide() {
guard !isHidden else { return }
let visibleY = frame.origin.y
let hiddenY = visibleY + frame.height
UIView.animate(withDuration: 0.3) { [weak self] in
self?.frame.origin.y = hiddenY
} completion: { [weak self] completed in
guard completed else { return }
self?.isHidden = true
self?.frame.origin.y = visibleY
}
}
}
Upvotes: 0
Reputation: 29
Install the Introspect SwiftPM: https://github.com/siteline/SwiftUI-Introspect
in order to use this you need to create a variable of type UITabBar in the view you want the tabbar to be hidden...
enter code here
@State private var tabBar: UITabBar?
then below the navigationView in the same view you have to add this line:
.introspectTabBarController { UITabBarController in tabBar = UITabBarController.tabBar
self.tabBar?.isHidden = true } .onDisappear() { self.tabBar?.isHidden = false }
Upvotes: 3
Reputation: 131
in general, it's nice to be able to create pages with an w/o tabbar it looks smooth and your page content doesn't change it's size while hiding tabbar on the page
solution is
here is a small sample project how your app could look like with this approach https://github.com/alexis-ag/swiftui_classic-tabview_show-hide
Upvotes: 2
Reputation: 412
This solution works well except with view modifier in the SwiftUI.TabView.
Since my TabView is in the struct that conforms App
, it looks like there still is not any UITabBar
subview in the connected scenes.
With the code below, you only need to use showTabBar()
or hiddenTabBar()
in your SwiftUI.View.
extension UIApplication {
var key: UIWindow? {
self.connectedScenes
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?
.windows
.filter({$0.isKeyWindow})
.first
}
}
extension UIView {
func allSubviews() -> [UIView] {
var subs = self.subviews
for subview in self.subviews {
let rec = subview.allSubviews()
subs.append(contentsOf: rec)
}
return subs
}
}
struct TabBarModifier {
static func showTabBar() {
UIApplication.shared.key?.allSubviews().forEach({ subView in
if let view = subView as? UITabBar {
view.isHidden = false
}
})
}
static func hideTabBar() {
UIApplication.shared.key?.allSubviews().forEach({ subView in
if let view = subView as? UITabBar {
view.isHidden = true
}
})
}
}
struct ShowTabBar: ViewModifier {
func body(content: Content) -> some View {
return content.padding(.zero).onAppear {
TabBarModifier.showTabBar()
}
}
}
struct HiddenTabBar: ViewModifier {
func body(content: Content) -> some View {
return content.padding(.zero).onAppear {
TabBarModifier.hideTabBar()
}
}
}
extension View {
func showTabBar() -> some View {
return self.modifier(ShowTabBar())
}
func hiddenTabBar() -> some View {
return self.modifier(HiddenTabBar())
}
}
Upvotes: 11
Reputation: 9743
Not ideal and hacky but the simplest thing to do for me that is working very well was to hide the navigationBar of the outer navigationView and then add another navigationView in each of the TabView's views. Works well so far:
struct LaunchView: View {
var body: some View {
NavigationView {
TabView {
ViewA()
.tabItem {
Label("TabA", systemImage: "some.image")
}
ViewB()
.tabItem {
Label("TabB", systemImage: "some.image")
}
ViewC()
.tabItem {
Label("TabC", systemImage: "some.image")
}
}
.navigationBarHidden(true)
}
}
}
struct ViewA: View {
var body: some View {
NavigationView {
// Content
.navigationTitle("Settings")
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
This way you can set the title but also the .toolBarItem's in each separate view.
Upvotes: 1
Reputation: 11
Increase the frame size of TabView like this:
.frame(width: UIScreen.main.bounds.width, height: showTabbar ? UIScreen.main.bounds.height : UIScreen.main.bounds.height + 100.00)
Upvotes: 1
Reputation: 496
Install the Introspect SwiftPM: https://github.com/siteline/SwiftUI-Introspect
struct SomeView: View{
@State var uiTabarController: UITabBarController?
var body: some View {
List {
-your code here-
}
.navigationBarTitle("Title", displayMode: .inline)
.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = true
uiTabarController = UITabBarController
}.onDisappear{
uiTabarController?.tabBar.isHidden = false
}
}
}
In this code in uiTabarController
, we are taking the reference of UITabarController
. When we go back then we enabled the Tabar
again. So, that's why this is needed.
Upvotes: 18
Reputation: 359
It's working, only changes are need to be called on the main queue
struct ShowTabBar: ViewModifier {
func body(content: Content) -> some View {
return content.padding(.zero).onAppear {
DispatchQueue.main.async {
Tool.showTabBar()
}
}
}
}
struct HiddenTabBar: ViewModifier {
func body(content: Content) -> some View {
return content.padding(.zero).onAppear {
DispatchQueue.main.async {
Tool.hiddenTabBar()
}
}
}
}
Traverse the allsubview of the window to hide the UITabBar. You can write it as ViewModifier and use it in SwiftUI or use tools to hide it. This method works for me.
extension UIView {
func allSubviews() -> [UIView] {
var res = self.subviews
for subview in self.subviews {
let riz = subview.allSubviews()
res.append(contentsOf: riz)
}
return res
}
}
struct Tool {
static func showTabBar() {
UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.allSubviews().forEach({ (v) in
if let view = v as? UITabBar {
view.isHidden = false
}
})
}
static func hiddenTabBar() {
UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.allSubviews().forEach({ (v) in
if let view = v as? UITabBar {
view.isHidden = true
}
})
}
}
struct ShowTabBar: ViewModifier {
func body(content: Content) -> some View {
return content.padding(.zero).onAppear {
Tool.showTabBar()
}
}
}
struct HiddenTabBar: ViewModifier {
func body(content: Content) -> some View {
return content.padding(.zero).onAppear {
Tool.hiddenTabBar()
}
}
}
extension View {
func showTabBar() -> some View {
return self.modifier(ShowTabBar())
}
func hiddenTabBar() -> some View {
return self.modifier(HiddenTabBar())
}
}
Upvotes: 9
Reputation: 397
Install the Introspect SwiftPM: https://github.com/siteline/SwiftUI-Introspect
var body: some View {
List {
-your code here-
}
.navigationBarTitle("Title", displayMode: .inline)
.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = true
}
}
NOTE: You have to re-enable the TabBar it in the parent view or it will still be hidden.
.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = false
}
Upvotes: 9
Reputation: 5906
It is actually possible to get the underlying UITabbarController for TabView by using this handy little framework :
https://github.com/siteline/SwiftUI-Introspect
This solution uses the MVVM pattern as an example to have programmatic control over the Tabbar visibility, and be able to show, hide, enable, disable form anywhere in the code using NSNotifications
SwiftUI View : Setup the tabview like this
struct MainTabView: View {
var viewModel: MainTabViewModel
var body: some View {
TabView() {
Text("View1")
.tabItem {
Text("View1")
}
Text("View2")
.tabItem {
Text("View2")
}
}
.introspectTabBarController { tabBarController in
// customize here the UITabBarViewController if you like
self.viewModel.tabBarController = tabBarController
}
}
}
Then for the ViewModel
final class MainTabViewModel: ObservableObject {
var tabBarController: UITabBarController?
init() {
startListeningNotifications()
}
func startListeningNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(showTabbarView), name: "showBottomTabbar", object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(hideTabbarView), name: "hideBottomTabbar", object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(enableTabbarTouch), name: "enableTouchTabbar", object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(disableTabbarTouch), name: "disableTouchTabbar", object: nil)
}
@objc func showTabbarView() {
self.tabBarController?.tabBar.isHidden = false
}
@objc func hideTabbarView() {
self.tabBarController?.tabBar.isHidden = true
}
@objc func disableTabbarTouch() {
self.tabBarController?.tabBar.isUserInteractionEnabled = false
}
@objc func enableTabbarTouch() {
self.tabBarController?.tabBar.isUserInteractionEnabled = true
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
and finally to control the tabbar, just use these fonctions from wherever you feel like (will be in the viewmodels in the pattern of this example)
public func showTabbar() {
DispatchQueue.main.async {
NotificationCenter.default.post(name: .showBottomTabbar, object: nil)
}
}
public func hideTabbar() {
DispatchQueue.main.async {
NotificationCenter.default.post(name: .hideBottomTabbar, object: nil)
}
}
public func enableTouchTabbar() {
DispatchQueue.main.async {
NotificationCenter.default.post(name: .enableTouchTabbar, object: nil)
}
}
public func disableTouchTabbar() {
DispatchQueue.main.async {
NotificationCenter.default.post(name: .disableTouchTabbar, object: nil)
}
}
Upvotes: 1
Reputation: 521
here's no way to hide TabView so I had to add TabView inside ZStack as this:
var body: some View {
ZStack {
TabView {
TabBar1().environmentObject(self.userData)
.tabItem {
Image(systemName: "1.square.fill")
Text("First")
}
TabBar2()
.tabItem {
Image(systemName: "2.square.fill")
Text("Second")
}
}
if self.userData.showFullScreen {
FullScreen().environmentObject(self.userData)
}
}
}
UserData:
final class UserData: ObservableObject {
@Published var showFullScreen = false
}
TabBar1:
struct TabBar1: View {
@EnvironmentObject var userData: UserData
var body: some View {
Text("TabBar 1")
.edgesIgnoringSafeArea(.all)
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.background(Color.green)
.onTapGesture {
self.userData.showFullScreen.toggle()
}
}
}
FullScreen:
struct FullScreen: View {
@EnvironmentObject var userData: UserData
var body: some View {
Text("FullScreen")
.edgesIgnoringSafeArea(.all)
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.background(Color.red)
.onTapGesture {
self.userData.showFullScreen.toggle()
}
}
}
check full code on Github
there's also some other ways but it depends on the structure of the views
Upvotes: 9
Reputation: 156
The basic idea I'm using is to combine ObservableObject and ZStack. I have placed TabView into ZStack with conditional subview presentation. It's look like. Look through github repo
Upvotes: 5