SwiftUI NavigationBar height
Asked Answered
P

2

9

How to get current NavigationBar height? In UIKit we could get

navigationController?.navigationBar.frame.height

but can't find anything for SwiftUI...

Polygenesis answered 15/2, 2020 at 17:56 Comment(2)
The approach can be the same as for TabBar in Programmatically detect Tab Bar or TabView height in SwiftUIPtolemaic
TY, this is the answerPolygenesis
P
25

Based on this post (thanks to Asperi): https://mcmap.net/q/957021/-programmatically-detect-tab-bar-or-tabview-height-in-swiftui

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

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

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

    typealias UIViewControllerType = UIViewController

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

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            if let navBar = self.navigationController {
                self.callback(navBar.navigationBar)
            }
        }
    }
}

And then we can call this from any View:

.background(NavBarAccessor { navBar in
      print(">> NavBar height: \(navBar.bounds.height)")
                // !! use as needed, in calculations, @State, etc.
 })
Polygenesis answered 17/2, 2020 at 9:8 Comment(1)
This works fine. But don't use it on the NavigationView, since navigation controller will be nil then. Also, the typealias in the NavBarAccessor can be removed.Trilbi
E
2

Building on @yoprst 's response, rather than configure calculations and @State variables, you could also return a View to insert directly into the hierarchy:

struct NavigationBarAccessor: UIViewControllerRepresentable {
    var callback: (UINavigationBar) -> (AnyView)
    private let proxyViewController = ProxyViewController()

    func makeUIViewController(context: UIViewControllerRepresentableContext<NavigationBarAccessor>) -> UIViewController {
        self.proxyViewController.callback = callback
        return proxyViewController
    }

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

    private class ProxyViewController: UIViewController {
        var callback: ((UINavigationBar) -> AnyView)?

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            if let navigationBar = self.navigationController?.navigationBar {
                _ = self.callback?(navigationBar)
            }
        }
    }
}

Usage:

VStack {
    NavigationBarAccessor { navigationBar in
        Spacer()
            .frame(height: navigationBar.frame.height)
    }
}
Emperor answered 2/6, 2021 at 22:21 Comment(1)
a generic constraint would be more preferable rather than AnyView usingHashum

© 2022 - 2024 — McMap. All rights reserved.