SwiftUIRookie
SwiftUIRookie

Reputation: 761

SwiftUI - TabView Overlay with custom view

in my last question I've asked about a method to achieve the following TabView overlay in SwiftUI:

TabView Overlay

Thanks again @davidev for the quick support.

I modified the solution with an opportunity to apply conditional view modifier. In this case .overlay().

extension View {
    @ViewBuilder
    func `if`<Content: View>(_ condition: Bool, content: (Self) -> Content) -> some View {
        if condition {
            content(self)
        }
        else {
            self
        }
    }
}

The code snipped above empowered me to implement the conditional toolbar to appear when an observedObject is toggled (e.g. by a switch from read to edit mode):

.if(class.observedObject){ view in
    view.overlay(ToolbarFromDavidev())
}

This also works but it's really buggy (e.g. the whole view navigation gets reset and I've limited styling opportunities).

This leads me to my question: Does someone of you have a reference implementation which I can use for my orientation? I would like to solve this with a ZStack that I can position over my TabView that I use for navigation. Like it's shown in the GIF above. However, I'm not able to position the ZStack over the TabView. Already tried stuff like ignoring safe areas etc.

Many Thanks!

Upvotes: 1

Views: 2536

Answers (1)

Kai Zheng
Kai Zheng

Reputation: 8130

You could overlay the TabView like so:

TabView {
    ...
}
.overlay(
    VStack {
        Spacer()
        
        //Your View
            .frame(height: /*Height of the TabBar*/)
    }
    .edgesIgnoringSafeArea(.all)
)        

Now it comes down to finding out the height of the TabBar. Since its dynamic (different iPhone screen sizes) One way would be like so:

@State private var tabBarHeight: CGFloat = .zero
...

TabView {
    //NavigationView
        .background(
            TabBarAccessor { tabBar in tabBarHeight = tabBar.bounds.height }
        )
}
.overlay(
    VStack {
        Spacer()
        
        //Your View
            .frame(height: tabBarHeight)
    }
    .edgesIgnoringSafeArea(.all)
) 

An example solution

Everything together - your code from your first answer modified.

Code:

struct ContentView: View {
    
    @State private var tabBarHeight: CGFloat = .zero
    @State var isSelecting : Bool = false
    
    var body: some View {
        TabView {
            NavigationView {
                Text("First Nav Page")
            }
            .tabItem {
                Image(systemName: "house")
                Text("Home")
                
            }.tag(0)
            .background(
                TabBarAccessor { tabBar in tabBarHeight = tabBar.bounds.height }
            )
            
            NavigationView {
                Text("Second Nav Page")
            }
            .tabItem {
                Image(systemName: "gear")
                Text("Settings")
            }.tag(1)
            
            Text("No Nav Page")
                .tabItem{
                    Image(systemName: "plus")
                    Text("Test")
                }.tag(2)
        }
        .overlay(
            VStack {
                Spacer()
                Rectangle()
                    .foregroundColor(.green)
                    .frame(height: tabBarHeight)
            }
            .edgesIgnoringSafeArea(.all)
        )
    }
}

struct TabBarAccessor: UIViewControllerRepresentable {
    var callback: (UITabBar) -> Void
    private let proxyController = ViewController()

    func makeUIViewController(context: UIViewControllerRepresentableContext<TabBarAccessor>) ->
                              UIViewController {
        proxyController.callback = callback
        return proxyController
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<TabBarAccessor>) {
    }

    typealias UIViewControllerType = UIViewController

    private class ViewController: UIViewController {
        var callback: (UITabBar) -> Void = { _ in }

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            if let tabBar = self.tabBarController {
                self.callback(tabBar.tabBar)
            }
        }
    }
}

Note

The TabBarAccessor code I have found from: Here

Upvotes: 2

Related Questions