Search code examples
swiftswiftuitvosnavigationview

SwiftUI - Screen Position Problem with NavigationView and Tab Bar


I am integrating a SwiftUI screen into a UIKit app for tvOS. I have a tab bar at top of screen. When the app is first run and I tab over to the SwiftUI screen, it looks as I want it. However, when I switch away and then come back, the entire screen shifts up and the navigation title gets hidden under the tab bar and the contents are all higher on the screen. The SwiftUI looks as follows:

    struct SettingsVC: View {
    var body: some View {
        NavigationView {
            HStack {
                Image("fullLogo")
                    .resizable()
                    .scaledToFit()
                    .frame(width: 800, height: 450)
                    .padding()
                    .offset(y: -75)
                List {
                    NavigationLink(destination: Text("Test")) {
                        HStack {
                            Image(systemName: "link")
                                .font(.headline)
                            Text("Link Account")
                                .font(.headline)
                        }
                    }
                }
                .navigationBarTitle("Settings")
            }
        }
    }
}

If I comment out the NavigationView the screen position remains in the same place when switching to another tab and back again. However, I need the navigation for the title and to move into the list. I have tried removing other elements such as the image just to see if it were causing issues, but it isn't. It seems on subsequent selections of this screen that the navigation bar is no longer taken into account. What might be the cause of this behavior?

Here is the tab bar portion which again is in UIKit.

private func setup() {
        self.view.insetsLayoutMarginsFromSafeArea = false

        let tvSelectionVC = TVSelectionVC()
        tvSelectionVC.screenType = .live
        tvSelectionVC.isFirstRun = true

        let onDemandVC = TVSelectionVC()
        onDemandVC.screenType = .demand
        onDemandVC.isFirstRun = false

        let settingsVC = SettingsVC()
        let settingsHostController = UIHostingController(rootView: settingsVC)
        let config = UIImage.SymbolConfiguration(pointSize: 50, weight: .bold)

        tvSelectionVC.tabBarItem = UITabBarItem(title: "Live", image: nil, tag: 0)
        onDemandVC.tabBarItem = UITabBarItem(title: "OnDemand", image: nil, tag: 1)
        settingsHostController.tabBarItem.image = UIImage(systemName: "gear",withConfiguration: config)

        let logoView = UIImageView(image: UIImage(named: "logo"))
        logoView.translatesAutoresizingMaskIntoConstraints = false
        tabBar.leadingAccessoryView.addSubview(logoView)
        logoView.topAnchor.constraint(equalTo: tabBar.leadingAccessoryView.topAnchor).isActive = true
        logoView.leadingAnchor.constraint(equalTo: tabBar.leadingAccessoryView.leadingAnchor, constant: 0).isActive = true
        logoView.heightAnchor.constraint(equalToConstant: 40).isActive = true
        logoView.widthAnchor.constraint(equalToConstant: 140).isActive = true

        viewControllers = [tvSelectionVC,onDemandVC,settingsHostController]
    }

Update: I can remove everything from the SwiftUI view and only have a Text be within the NavigationView and the issue still occurs. I can also change the first and second tabs to be empty UIViewControllers and the issue still occurs when switching to the SwiftUI after the first time.


Solution

  • I was able to solve the issue by wrapping the NavigationView tag inside a VStack tag and adding a Spacer which was required to solve this issue. interestingly as a test I created a SwiftUI tabbar with the first two tabs going to empty views and the 3rd tab going to the settings from above. This also exhibited the same issue as described above which indicates that this problem is not related to wrapping SwiftUI in a UIKit tabbar. I am not clear why this solution was necessary but it did solve the problem. Here is that SwiftUI again for the complete solution to this problem.

    struct SettingsVC: View {
        var body: some View {
            VStack {
                Spacer()
                NavigationView {
                    HStack {
                        Image("fullLogo")
                            .resizable()
                            .scaledToFit()
                            .frame(width: 800, height: 450)
                            .padding()
                            .offset(y: -75)
                        List {
                            NavigationLink(destination: Text("Test")) {
                                HStack {
                                    Image(systemName: "link")
                                        .font(.headline)
                                    Text("Link Account")
                                        .font(.headline)
                                }
                            }
                        }
                        .padding()
                        .navigationBarTitle("Settings")
                    }
                }
            }
        }
    }